Compare commits

..

10 Commits
v020 ... v024

Author SHA1 Message Date
byuu
9fd379613a Update to bsnes v024 release.
This is an interim release between some major changes to the video mode support, which may take a long time to complete. It also fixes a bug with CGRAM access timing, re-adds the Sufami Turbo load menu, and adds support for the ST-010 coprocessor, used by F1 Race of Champions.
To load Sufami Turbo cartridges, stbios.bin must be placed inside a folder named bios in the bsnes folder. There is not currently a warning if this file is missing.
2007-10-01 09:03:44 +00:00
byuu
aabf52d678 Update to bsnes v023r01? release.
Alright, I've posted the new WIP.

             Changelog:
             - CGRAM fix for WWF Super Wrestlemania
             - Updated to blargg's snes_ntsc library to version 0.2.2
             - Added ST and ST dual cart loading menu options (*)
             - Redesigned the video mode configuration panel a bit --
let me know what you think (**)

 (*) - You have to set path.bios to an -absolute- path, ./ is
currently broken and I need to fix that. So, set it to eg
"c:/path/to/bsnes/bios" where stbios.bin is inside that folder.
             (**) - The video menu obviously doesn't do anything, it's
just there for design advice / suggestions for now.

 You'll notice the icon is gone. This is because I built this version
with MinGW 4, and I'm not sure how to add the icon to MinGW apps.
You'll also notice it's ~6% faster on Core 2 processors as a result.

 The 16% speedup was only when PGO was enabled. But I can't enable
that, because it causes bsnes to crash randomly. GCC gets too risky
with its optimizations and ends up generating bad code (the GCC manual
states as much, I'm not just trying to blame problems in my app on GCC
here.)

 So, 6% is the best speedup we can do for now. Compare to v0.023
official if you like. You probably won't see the speedup on older
processors like the Pentium IV.

 EDIT: it seems like that MinGW vsnprintf problem is based on DLL
files on the local computer. Probably the MS VisualC runtime files.
The WIP works fine on my home PC (WinXP Pro), but not on my work PC
(Win2k). I'm going to have to stick with Visual C++ builds until I am
able to completely remove all sprintf-style calls from the emulator.

 If you get an error about memory at 0xffffffff which cannot be read,
you know why. Try building with Visual C++ if you have it, or maybe
there's some way to upgrade libc DLLs that the app is binding to.
Whatever DLL has vsnprintf is the one that needs to be updated, if at
all possible.

             Here's what the video config screen looks like at the
moment.

             [image]

 I tried putting the text at the top, that way there won't be any odd
gaps between the text and combo box dropdown, due to different sized
fonts on different platforms.

[No archive available]
2007-09-25 14:20:00 +00:00
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
byuu
a1980fab09 Update to bsnes v021 release.
This is a maintainence release. I am mostly releasing this for the sake of the recently released Der Langrisser translation.
Changelog:
Windows port can once again map joypads through the Input Configuration panel
Using enter or spacebar to assign a key should no longer instantly map those keys
F11 now toggles fullscreen mode
Esc now toggles menu on and off (use F11+Esc combined to hide UI completely)
Fixed a bug in King of Dragons (J, U, E), KOFF was not cleared during S-DSP power(), thanks to FitzRoy for the report, and blargg for assistance fixing the bug
Fixed serious crashing error with File->Load on Linux/amd64 port
Hopefully fixed min/max undefined error on GCC 4.2.0, but I am unable to test to verify
Fixed many cast const char* to char* warnings for GCC 4.2.0, but some probably remain, as again, I am unable to test as I lack GCC 4.2.0
Set XV_AUTO_COLORKEY to 1 for Video/Xv renderer. Should fix some video drivers where there was no output, especially after running mplayer, etc. Thanks to sinimas for the fix
Added clear_video() to Video/Xv renderer. Green edges at the bottom and right sides of the video output are now gone, and unloading a ROM will clear video
I have finally figured out how to poll the keyboard status in real-time through Xorg: the XQueryKeymap function. I will be rewriting the Linux key capture system to use this, instead of capturing window key up / down messages through GTK+. This will finally allow me to completely abstract the UI from the hardware video, audio and input interfaces: a necessary step toward Linux joypad support.
2007-06-10 19:27:46 +00:00
byuu
ebb234ba5f Update to bsnes v020 01 release.
[No changelog available]
2007-06-05 15:50:59 +00:00
106 changed files with 3355 additions and 2600 deletions

BIN
cart.db Normal file

Binary file not shown.

63
license.txt Normal file
View File

@@ -0,0 +1,63 @@
bsnes (TM) Open Source Reference License
Copyright (C) 2004 - 2007 byuu
All rights reserved
1. Definitions
The terms "reproduce", "reproduction", "distribute" and "distribution" have the
same meaning here as under U.S. copyright law.
"The software" means this software package as a whole, including, but not
limited to, this license, binaries, source code, documentation, and data.
"You" means the licensee of the software.
"The licensor" means the copyright holder of the software, byuu.
2. Grant of Rights
Subject to the terms of this license, the licensor grants you a
non-transferable, non-exclusive, worldwide, royalty-free copyright license to
reproduce the software for non-commercial use only, provided the software
remains unmodified, and there is no charge for the software itself, its' use,
nor for the medium upon which the software is distributed. The reproduction of
modified or derivative works of the software is strictly prohibited, except when
transmitted solely to the licensor.
3. Limitations
This license does not grant you any rights to use the licensor's name, logo or
trademarks.
The software is provided "as is", and any express or implied warranties,
including, but not limited to, the implied warranties of merchantability and
fitness for a particular purpose are disclaimed. In no event shall the licensor
be liable for any direct, indirect, incidental, special, exemplary, or
consequential damages (including, but not limited to, procurement of substitute
goods or services; loss of use, data, or profits; or business interruption)
however caused and on any theory of liability, whether in contract, strict
liability, or tort (including negligence or otherwise) arising in any way out of
the use of the software, even if advised of the possibility of such damage.
In the event that this license is determined to be invalid or unenforceable, the
Grant of Rights will become null and void, and no rights shall be granted to the
licensee, within the scope of U.S. copyright law.
4. Exemptions
The software includes the work of other copyright holders, which is licensed
under different agreements, and exempt from this license. Below is a complete
list of all such software, and their respective copyright holders and licenses.
Further, respective source code files are labeled with their correct licensing
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 (*)
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.

97
readme.txt Normal file
View File

@@ -0,0 +1,97 @@
bsnes
Version 0.024
Author: byuu
--------
General:
--------
bsnes is a Super Nintendo / Super Famicom emulator that began on
October 14th, 2004.
The latest version can be downloaded from:
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 not fully emulated
- Multiply / Divide register delays not implemented
S-PPU
- 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
is enabled. This provides a major speedup, however it will cause in issues
in games that test these flags, eg the SNES Test Program Electronics Test.
Turning frameskipping off will allow RTO flag calculation on every frame
Hardware Bugs
- S-CPU.r1 HDMA crashing bug not emulated
- S-CPU<>S-SMP communication bus conflicts not emulated
---------------------
Unsupported Hardware:
---------------------
SA-1
Coprocessor used in many popular games, including:
- Dragon Ball Z Hyper Dimension
- Kirby Super Star
- Kirby's Dreamland 3
- Marvelous
- SD Gundam G-NEXT
- Super Mario RPG
Super FX
Coprocessor used in many popular games, including:
- Doom
- Star Fox
- Star Fox 2 (unreleased beta)
- Super Mario World 2: Yoshi's Island
SPC7110
Coprocessor used only by the following games:
- Far East of Eden Zero
- Far East of Eden Zero: Shounen Jump no Shou
- Momotarou Densetsu Happy
- Super Power League 4
DSP-3
Coprocessor used only by SD Gundam GX
DSP-4
Coprocessor used only by Top Gear 3000
ST011
SETA DSP used only by Quick-move Shogi Match with Nidan Rank-holder Morita
ST018
SETA RISC CPU used only by Quick-move Shogi Match with Nidan Rank-holder Morita 2
BS-X (Broadcast Satellite)
Add-on unit sold only in Japan that played specially-made games that were
downloaded via satellite
BS-X Flashcart
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
Super Scope
Justifier
Multitap (4-port and 5-port)

View File

@@ -13,7 +13,7 @@ endif
ifeq ($(PLATFORM),x-gcc-lui)
OS = unix
CC = gcc
CFLAGS = -O3 -fomit-frame-pointer -ffast-math -DPLATFORM_X -DCOMPILER_GCC -DPROCESSOR_X86 -DUI_LUI `pkg-config --cflags gtk+-2.0`
CFLAGS = -O3 -fomit-frame-pointer -DPLATFORM_X -DCOMPILER_GCC -DPROCESSOR_X86 -DUI_LUI `pkg-config --cflags gtk+-2.0`
AS = nasm
ASFLAGS = -f elf
LIBS = `pkg-config --libs gtk+-2.0` -lXv -lao
@@ -24,7 +24,7 @@ endif
ifeq ($(PLATFORM),x-gcc-lui-x64)
OS = unix
CC = gcc
CFLAGS = -O3 -fomit-frame-pointer -ffast-math -DPLATFORM_X -DCOMPILER_GCC -DPROCESSOR_X86_64 -DUI_LUI `pkg-config --cflags gtk+-2.0`
CFLAGS = -O3 -fomit-frame-pointer -DPLATFORM_X -DCOMPILER_GCC -DPROCESSOR_X86_64 -DUI_LUI `pkg-config --cflags gtk+-2.0`
AS = yasm
ASFLAGS = -f elf64
LIBS = `pkg-config --libs gtk+-2.0` -lXv -lao
@@ -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
####################################
@@ -118,7 +155,7 @@ OBJECTS = main.$(OBJ) $(LIBCO).$(OBJ) $(LIBUI).$(OBJ) \
reader.$(OBJ) cart.$(OBJ) cheat.$(OBJ) memory.$(OBJ) bmemory.$(OBJ) \
cpu.$(OBJ) scpu.$(OBJ) smp.$(OBJ) ssmp.$(OBJ) bdsp.$(OBJ) ppu.$(OBJ) \
bppu.$(OBJ) snes.$(OBJ) srtc.$(OBJ) sdd1.$(OBJ) c4.$(OBJ) dsp1.$(OBJ) \
dsp2.$(OBJ) obc1.$(OBJ)
dsp2.$(OBJ) obc1.$(OBJ) st010.$(OBJ)
ifeq ($(GZIP_SUPPORT),true)
OBJECTS += adler32.$(OBJ) compress.$(OBJ) crc32.$(OBJ) deflate.$(OBJ) \
@@ -222,12 +259,13 @@ snes.$(OBJ): snes/snes.cpp snes/* snes/scheduler/* snes/video/* snes/audio/* sne
#####################
### special chips ###
#####################
srtc.$(OBJ): chip/srtc/srtc.cpp chip/srtc/*
sdd1.$(OBJ): chip/sdd1/sdd1.cpp chip/sdd1/*
c4.$(OBJ) : chip/c4/c4.cpp chip/c4/*
dsp1.$(OBJ): chip/dsp1/dsp1.cpp chip/dsp1/*
dsp2.$(OBJ): chip/dsp2/dsp2.cpp chip/dsp2/*
obc1.$(OBJ): chip/obc1/obc1.cpp chip/obc1/*
srtc.$(OBJ) : chip/srtc/srtc.cpp chip/srtc/*
sdd1.$(OBJ) : chip/sdd1/sdd1.cpp chip/sdd1/*
c4.$(OBJ) : chip/c4/c4.cpp chip/c4/*
dsp1.$(OBJ) : chip/dsp1/dsp1.cpp chip/dsp1/*
dsp2.$(OBJ) : chip/dsp2/dsp2.cpp chip/dsp2/*
obc1.$(OBJ) : chip/obc1/obc1.cpp chip/obc1/*
st010.$(OBJ): chip/st010/st010.cpp chip/st010/*
############
### zlib ###

View File

@@ -1,4 +1,4 @@
#define BSNES_VERSION "0.020"
#define BSNES_VERSION "0.024"
#define BSNES_TITLE "bsnes v" BSNES_VERSION
#define MEMCORE bMemBus
@@ -31,18 +31,17 @@
#error "unsupported processor"
#endif
#include "lib/libinterp.h"
#include "lib/libfunctor.h"
#include "lib/libsort.h"
#include "lib/libarray.h"
#include "lib/libvector.h"
#include "lib/libfile.h"
#include "lib/libstring.h"
#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 {

View File

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

View File

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

View File

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

View File

@@ -1,3 +1,4 @@
@make -r PLATFORM=win-visualc-lui
::@make -r PLATFORM=win-visualc-lui
@make -r PLATFORM=win-visualc-lui GZIP_SUPPORT=true JMA_SUPPORT=true
@move bsnes.exe ../bsnes.exe>nul
@pause

View File

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

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

@@ -0,0 +1,86 @@
#include "../../base.h"
#include "st010_data.h"
#include "st010_op.cpp"
ST010 *st010;
int16 ST010::sin(int16 theta) {
return sin_table[(theta >> 8) & 0xff];
}
int16 ST010::cos(int16 theta) {
return sin_table[((theta + 0x4000) >> 8) & 0xff];
}
uint8 ST010::readb(uint16 addr) {
return ram[addr & 0xfff];
}
uint16 ST010::readw(uint16 addr) {
return (readb(addr + 0) << 0) |
(readb(addr + 1) << 8);
}
uint32 ST010::readd(uint16 addr) {
return (readb(addr + 0) << 0) |
(readb(addr + 1) << 8) |
(readb(addr + 2) << 16) |
(readb(addr + 3) << 24);
}
void ST010::writeb(uint16 addr, uint8 data) {
ram[addr & 0xfff] = data;
}
void ST010::writew(uint16 addr, uint16 data) {
writeb(addr + 0, data);
writeb(addr + 1, data >> 8);
}
void ST010::writed(uint16 addr, uint32 data) {
writeb(addr + 0, data);
writeb(addr + 1, data >> 8);
writeb(addr + 2, data >> 16);
writeb(addr + 3, data >> 24);
}
//
void ST010::init() {
}
void ST010::enable() {
}
void ST010::power() {
reset();
}
void ST010::reset() {
memset(ram, 0x00, sizeof ram);
}
//
uint8 ST010::read(uint16 addr) {
return readb(addr);
}
void ST010::write(uint16 addr, uint8 data) {
writeb(addr, data);
if(addr == 0x0021 && data & 0x80) {
switch(ram[0x0020]) {
case 0x01: op_01(); break;
case 0x02: op_02(); break;
case 0x03: op_03(); break;
case 0x04: op_04(); break;
case 0x05: op_05(); break;
case 0x06: op_06(); break;
case 0x07: op_07(); break;
case 0x08: op_08(); break;
}
ram[0x0021] &= ~0x80;
}
}

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

@@ -0,0 +1,40 @@
class ST010 { public:
uint8 ram[0x1000];
static const int16 sin_table[256];
static const int16 mode7_scale[176];
static const uint8 arctan[32][32];
//interfaces to sin table
int16 sin(int16 theta);
int16 cos(int16 theta);
//interfaces to ram buffer
uint8 readb (uint16 addr);
uint16 readw (uint16 addr);
uint32 readd (uint16 addr);
void writeb(uint16 addr, uint8 data);
void writew(uint16 addr, uint16 data);
void writed(uint16 addr, uint32 data);
//opcodes
void op_01();
void op_02();
void op_03();
void op_04();
void op_05();
void op_06();
void op_07();
void op_08();
void op_01(int16 x0, int16 y0, int16 &x1, int16 &y1, int16 &quadrant, int16 &theta);
//base
void init();
void enable();
void power();
void reset();
uint8 read (uint16 addr);
void write(uint16 addr, uint8 data);
};
extern ST010 *st010;

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

@@ -0,0 +1,126 @@
const int16 ST010::sin_table[256] = {
0x0000, 0x0324, 0x0648, 0x096a, 0x0c8c, 0x0fab, 0x12c8, 0x15e2,
0x18f9, 0x1c0b, 0x1f1a, 0x2223, 0x2528, 0x2826, 0x2b1f, 0x2e11,
0x30fb, 0x33df, 0x36ba, 0x398c, 0x3c56, 0x3f17, 0x41ce, 0x447a,
0x471c, 0x49b4, 0x4c3f, 0x4ebf, 0x5133, 0x539b, 0x55f5, 0x5842,
0x5a82, 0x5cb3, 0x5ed7, 0x60eb, 0x62f1, 0x64e8, 0x66cf, 0x68a6,
0x6a6d, 0x6c23, 0x6dc9, 0x6f5e, 0x70e2, 0x7254, 0x73b5, 0x7504,
0x7641, 0x776b, 0x7884, 0x7989, 0x7a7c, 0x7b5c, 0x7c29, 0x7ce3,
0x7d89, 0x7e1d, 0x7e9c, 0x7f09, 0x7f61, 0x7fa6, 0x7fd8, 0x7ff5,
0x7fff, 0x7ff5, 0x7fd8, 0x7fa6, 0x7f61, 0x7f09, 0x7e9c, 0x7e1d,
0x7d89, 0x7ce3, 0x7c29, 0x7b5c, 0x7a7c, 0x7989, 0x7884, 0x776b,
0x7641, 0x7504, 0x73b5, 0x7254, 0x70e2, 0x6f5e, 0x6dc9, 0x6c23,
0x6a6d, 0x68a6, 0x66cf, 0x64e8, 0x62f1, 0x60eb, 0x5ed7, 0x5cb3,
0x5a82, 0x5842, 0x55f5, 0x539b, 0x5133, 0x4ebf, 0x4c3f, 0x49b4,
0x471c, 0x447a, 0x41ce, 0x3f17, 0x3c56, 0x398c, 0x36ba, 0x33df,
0x30fb, 0x2e11, 0x2b1f, 0x2826, 0x2528, 0x2223, 0x1f1a, 0x1c0b,
0x18f8, 0x15e2, 0x12c8, 0x0fab, 0x0c8c, 0x096a, 0x0648, 0x0324,
0x0000, -0x0324, -0x0648, -0x096b, -0x0c8c, -0x0fab, -0x12c8, -0x15e2,
-0x18f9, -0x1c0b, -0x1f1a, -0x2223, -0x2528, -0x2826, -0x2b1f, -0x2e11,
-0x30fb, -0x33df, -0x36ba, -0x398d, -0x3c56, -0x3f17, -0x41ce, -0x447a,
-0x471c, -0x49b4, -0x4c3f, -0x4ebf, -0x5133, -0x539b, -0x55f5, -0x5842,
-0x5a82, -0x5cb3, -0x5ed7, -0x60ec, -0x62f1, -0x64e8, -0x66cf, -0x68a6,
-0x6a6d, -0x6c23, -0x6dc9, -0x6f5e, -0x70e2, -0x7254, -0x73b5, -0x7504,
-0x7641, -0x776b, -0x7884, -0x7989, -0x7a7c, -0x7b5c, -0x7c29, -0x7ce3,
-0x7d89, -0x7e1d, -0x7e9c, -0x7f09, -0x7f61, -0x7fa6, -0x7fd8, -0x7ff5,
-0x7fff, -0x7ff5, -0x7fd8, -0x7fa6, -0x7f61, -0x7f09, -0x7e9c, -0x7e1d,
-0x7d89, -0x7ce3, -0x7c29, -0x7b5c, -0x7a7c, -0x7989, -0x7883, -0x776b,
-0x7641, -0x7504, -0x73b5, -0x7254, -0x70e2, -0x6f5e, -0x6dc9, -0x6c23,
-0x6a6d, -0x68a6, -0x66cf, -0x64e8, -0x62f1, -0x60eb, -0x5ed7, -0x5cb3,
-0x5a82, -0x5842, -0x55f5, -0x539a, -0x5133, -0x4ebf, -0x4c3f, -0x49b3,
-0x471c, -0x447a, -0x41cd, -0x3f17, -0x3c56, -0x398c, -0x36b9, -0x33de,
-0x30fb, -0x2e10, -0x2b1f, -0x2826, -0x2527, -0x2223, -0x1f19, -0x1c0b,
-0x18f8, -0x15e2, -0x12c8, -0x0fab, -0x0c8b, -0x096a, -0x0647, -0x0324
};
const int16 ST010::mode7_scale[176] = {
0x0380, 0x0325, 0x02da, 0x029c, 0x0268, 0x023b, 0x0215, 0x01f3,
0x01d5, 0x01bb, 0x01a3, 0x018e, 0x017b, 0x016a, 0x015a, 0x014b,
0x013e, 0x0132, 0x0126, 0x011c, 0x0112, 0x0109, 0x0100, 0x00f8,
0x00f0, 0x00e9, 0x00e3, 0x00dc, 0x00d6, 0x00d1, 0x00cb, 0x00c6,
0x00c1, 0x00bd, 0x00b8, 0x00b4, 0x00b0, 0x00ac, 0x00a8, 0x00a5,
0x00a2, 0x009e, 0x009b, 0x0098, 0x0095, 0x0093, 0x0090, 0x008d,
0x008b, 0x0088, 0x0086, 0x0084, 0x0082, 0x0080, 0x007e, 0x007c,
0x007a, 0x0078, 0x0076, 0x0074, 0x0073, 0x0071, 0x006f, 0x006e,
0x006c, 0x006b, 0x0069, 0x0068, 0x0067, 0x0065, 0x0064, 0x0063,
0x0062, 0x0060, 0x005f, 0x005e, 0x005d, 0x005c, 0x005b, 0x005a,
0x0059, 0x0058, 0x0057, 0x0056, 0x0055, 0x0054, 0x0053, 0x0052,
0x0051, 0x0051, 0x0050, 0x004f, 0x004e, 0x004d, 0x004d, 0x004c,
0x004b, 0x004b, 0x004a, 0x0049, 0x0048, 0x0048, 0x0047, 0x0047,
0x0046, 0x0045, 0x0045, 0x0044, 0x0044, 0x0043, 0x0042, 0x0042,
0x0041, 0x0041, 0x0040, 0x0040, 0x003f, 0x003f, 0x003e, 0x003e,
0x003d, 0x003d, 0x003c, 0x003c, 0x003b, 0x003b, 0x003a, 0x003a,
0x003a, 0x0039, 0x0039, 0x0038, 0x0038, 0x0038, 0x0037, 0x0037,
0x0036, 0x0036, 0x0036, 0x0035, 0x0035, 0x0035, 0x0034, 0x0034,
0x0034, 0x0033, 0x0033, 0x0033, 0x0032, 0x0032, 0x0032, 0x0031,
0x0031, 0x0031, 0x0030, 0x0030, 0x0030, 0x0030, 0x002f, 0x002f,
0x002f, 0x002e, 0x002e, 0x002e, 0x002e, 0x002d, 0x002d, 0x002d,
0x002d, 0x002c, 0x002c, 0x002c, 0x002c, 0x002b, 0x002b, 0x002b
};
const uint8 ST010::arctan[32][32] = {
{ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 },
{ 0x80, 0xa0, 0xad, 0xb3, 0xb6, 0xb8, 0xb9, 0xba, 0xbb, 0xbb, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd,
0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xbf },
{ 0x80, 0x93, 0xa0, 0xa8, 0xad, 0xb0, 0xb3, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xb9, 0xba, 0xba, 0xbb,
0xbb, 0xbb, 0xbb, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd },
{ 0x80, 0x8d, 0x98, 0xa0, 0xa6, 0xaa, 0xad, 0xb0, 0xb1, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb7, 0xb8,
0xb8, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbc, 0xbc, 0xbc, 0xbc },
{ 0x80, 0x8a, 0x93, 0x9a, 0xa0, 0xa5, 0xa8, 0xab, 0xad, 0xaf, 0xb0, 0xb2, 0xb3, 0xb4, 0xb5, 0xb5,
0xb6, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xbb, 0xbb },
{ 0x80, 0x88, 0x90, 0x96, 0x9b, 0xa0, 0xa4, 0xa7, 0xa9, 0xab, 0xad, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3,
0xb4, 0xb4, 0xb5, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9 },
{ 0x80, 0x87, 0x8d, 0x93, 0x98, 0x9c, 0xa0, 0xa3, 0xa6, 0xa8, 0xaa, 0xac, 0xad, 0xae, 0xb0, 0xb0,
0xb1, 0xb2, 0xb3, 0xb4, 0xb4, 0xb5, 0xb5, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8 },
{ 0x80, 0x86, 0x8b, 0x90, 0x95, 0x99, 0x9d, 0xa0, 0xa3, 0xa5, 0xa7, 0xa9, 0xaa, 0xac, 0xad, 0xae,
0xaf, 0xb0, 0xb1, 0xb2, 0xb2, 0xb3, 0xb3, 0xb4, 0xb4, 0xb5, 0xb5, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7 },
{ 0x80, 0x85, 0x8a, 0x8f, 0x93, 0x97, 0x9a, 0x9d, 0xa0, 0xa2, 0xa5, 0xa6, 0xa8, 0xaa, 0xab, 0xac,
0xad, 0xae, 0xaf, 0xb0, 0xb0, 0xb1, 0xb2, 0xb2, 0xb3, 0xb3, 0xb4, 0xb4, 0xb5, 0xb5, 0xb5, 0xb5 },
{ 0x80, 0x85, 0x89, 0x8d, 0x91, 0x95, 0x98, 0x9b, 0x9e, 0xa0, 0xa0, 0xa4, 0xa6, 0xa7, 0xa9, 0xaa,
0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb0, 0xb1, 0xb1, 0xb2, 0xb2, 0xb3, 0xb3, 0xb4, 0xb4, 0xb4 },
{ 0x80, 0x84, 0x88, 0x8c, 0x90, 0x93, 0x96, 0x99, 0x9b, 0x9e, 0xa0, 0xa2, 0xa4, 0xa5, 0xa7, 0xa8,
0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xaf, 0xb0, 0xb0, 0xb1, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3 },
{ 0x80, 0x84, 0x87, 0x8b, 0x8e, 0x91, 0x94, 0x97, 0x9a, 0x9c, 0x9e, 0xa0, 0xa2, 0xa3, 0xa5, 0xa6,
0xa7, 0xa9, 0xaa, 0xab, 0xac, 0xac, 0xad, 0xae, 0xae, 0xaf, 0xb0, 0xb0, 0xb1, 0xb1, 0xb2, 0xb2 },
{ 0x80, 0x83, 0x87, 0x8a, 0x8d, 0x90, 0x93, 0x96, 0x98, 0x9a, 0x9c, 0x9e, 0xa0, 0xa2, 0xa3, 0xa5,
0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xac, 0xad, 0xae, 0xae, 0xaf, 0xb0, 0xb0, 0xb0, 0xb1 },
{ 0x80, 0x83, 0x86, 0x89, 0x8c, 0x8f, 0x92, 0x94, 0x96, 0x99, 0x9b, 0x9d, 0x9e, 0xa0, 0xa2, 0xa3,
0xa4, 0xa5, 0xa7, 0xa8, 0xa9, 0xa9, 0xaa, 0xab, 0xac, 0xac, 0xad, 0xae, 0xae, 0xaf, 0xaf, 0xb0 },
{ 0x80, 0x83, 0x86, 0x89, 0x8b, 0x8e, 0x90, 0x93, 0x95, 0x97, 0x99, 0x9b, 0x9d, 0x9e, 0xa0, 0xa1,
0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xaa, 0xab, 0xac, 0xad, 0xad, 0xae, 0xae, 0xaf },
{ 0x80, 0x83, 0x85, 0x88, 0x8b, 0x8d, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9b, 0x9d, 0x9f, 0xa0,
0xa1, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa8, 0xa9, 0xaa, 0xab, 0xab, 0xac, 0xad, 0xad, 0xae },
{ 0x80, 0x83, 0x85, 0x88, 0x8a, 0x8c, 0x8f, 0x91, 0x93, 0x95, 0x97, 0x99, 0x9a, 0x9c, 0x9d, 0x9f,
0xa0, 0xa1, 0xa2, 0xa3, 0xa5, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xaa, 0xab, 0xab, 0xac, 0xad },
{ 0x80, 0x82, 0x85, 0x87, 0x89, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x96, 0x97, 0x99, 0x9b, 0x9c, 0x9d,
0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa8, 0xa9, 0xaa, 0xaa, 0xab, 0xac },
{ 0x80, 0x82, 0x85, 0x87, 0x89, 0x8b, 0x8d, 0x8f, 0x91, 0x93, 0x95, 0x96, 0x98, 0x99, 0x9b, 0x9c,
0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa7, 0xa8, 0xa9, 0xa9, 0xaa, 0xab },
{ 0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x95, 0x97, 0x98, 0x9a, 0x9b,
0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa6, 0xa7, 0xa8, 0xa8, 0xa9, 0xaa },
{ 0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x90, 0x91, 0x93, 0x94, 0x96, 0x97, 0x99, 0x9a,
0x9b, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa5, 0xa6, 0xa7, 0xa7, 0xa8, 0xa9 },
{ 0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8b, 0x8d, 0x8f, 0x90, 0x92, 0x94, 0x95, 0x97, 0x98, 0x99,
0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa4, 0xa5, 0xa6, 0xa6, 0xa7, 0xa8 },
{ 0x80, 0x82, 0x84, 0x86, 0x87, 0x89, 0x8b, 0x8d, 0x8e, 0x90, 0x91, 0x93, 0x94, 0x96, 0x97, 0x98,
0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa3, 0xa4, 0xa5, 0xa6, 0xa6, 0xa7 },
{ 0x80, 0x82, 0x84, 0x85, 0x87, 0x89, 0x8a, 0x8c, 0x8e, 0x8f, 0x91, 0x92, 0x94, 0x95, 0x96, 0x98,
0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa2, 0xa3, 0xa4, 0xa5, 0xa5, 0xa6 },
{ 0x80, 0x82, 0x83, 0x85, 0x87, 0x88, 0x8a, 0x8c, 0x8d, 0x8f, 0x90, 0x92, 0x93, 0x94, 0x96, 0x97,
0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa2, 0xa3, 0xa4, 0xa5, 0xa5 },
{ 0x80, 0x82, 0x83, 0x85, 0x86, 0x88, 0x8a, 0x8b, 0x8d, 0x8e, 0x90, 0x91, 0x92, 0x94, 0x95, 0x96,
0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa2, 0xa3, 0xa4, 0xa4 },
{ 0x80, 0x82, 0x83, 0x85, 0x86, 0x88, 0x89, 0x8b, 0x8c, 0x8e, 0x8f, 0x90, 0x92, 0x93, 0x94, 0x95,
0x96, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa2, 0xa3, 0xa4 },
{ 0x80, 0x82, 0x83, 0x85, 0x86, 0x87, 0x89, 0x8a, 0x8c, 0x8d, 0x8e, 0x90, 0x91, 0x92, 0x93, 0x95,
0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9e, 0x9f, 0xa0, 0xa1, 0xa1, 0xa2, 0xa3 },
{ 0x80, 0x81, 0x83, 0x84, 0x86, 0x87, 0x89, 0x8a, 0x8b, 0x8d, 0x8e, 0x8f, 0x90, 0x92, 0x93, 0x94,
0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9e, 0x9f, 0xa0, 0xa1, 0xa1, 0xa2 },
{ 0x80, 0x81, 0x83, 0x84, 0x86, 0x87, 0x88, 0x8a, 0x8b, 0x8c, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93,
0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0x9f, 0xa0, 0xa1, 0xa1 },
{ 0x80, 0x81, 0x83, 0x84, 0x85, 0x87, 0x88, 0x89, 0x8b, 0x8c, 0x8d, 0x8e, 0x90, 0x91, 0x92, 0x93,
0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0x9f, 0xa0, 0xa1 },
{ 0x80, 0x81, 0x83, 0x84, 0x85, 0x87, 0x88, 0x89, 0x8a, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92,
0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9c, 0x9d, 0x9e, 0x9f, 0x9f, 0xa0 }
};

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

@@ -0,0 +1,257 @@
//ST-010 emulation code - Copyright (C) 2003 The Dumper, Matthew Kendora, Overload, Feather
//bsnes port - Copyright (C) 2007 byuu
void ST010::op_01(int16 x0, int16 y0, int16 &x1, int16 &y1, int16 &quadrant, int16 &theta) {
if((x0 < 0) && (y0 < 0)) {
x1 = -x0;
y1 = -y0;
quadrant = -0x8000;
} else if(x0 < 0) {
x1 = y0;
y1 = -x0;
quadrant = -0x4000;
} else if(y0 < 0) {
x1 = -y0;
y1 = x0;
quadrant = 0x4000;
} else {
x1 = x0;
y1 = y0;
quadrant = 0x0000;
}
while((x1 > 0x1f) || (y1 > 0x1f)) {
if(x1 > 1) { x1 >>= 1; }
if(y1 > 1) { y1 >>= 1; }
}
if(y1 == 0) { quadrant += 0x4000; }
theta = (arctan[y1][x1] << 8) ^ quadrant;
}
//
void ST010::op_01() {
int16 x0 = readw(0x0000);
int16 y0 = readw(0x0002);
int16 x1, y1, quadrant, theta;
op_01(x0, y0, x1, y1, quadrant, theta);
writew(0x0000, x1);
writew(0x0002, y1);
writew(0x0004, quadrant);
//writew(0x0006, y0); //Overload's docs note this write occurs, SNES9x disagrees
writew(0x0010, theta);
}
void ST010::op_02() {
int16 positions = readw(0x0024);
uint16 *places = (uint16*)(ram + 0x0040);
uint16 *drivers = (uint16*)(ram + 0x0080);
bool sorted;
uint16 temp;
if(positions > 1) {
do {
sorted = true;
for(int i = 0; i < positions - 1; i++) {
if(places[i] < places[i + 1]) {
temp = places[i + 1];
places[i + 1] = places[i];
places[i] = temp;
temp = drivers[i + 1];
drivers[i + 1] = drivers[i];
drivers[i] = temp;
sorted = false;
}
}
positions--;
} while(!sorted);
}
}
void ST010::op_03() {
int16 x0 = readw(0x0000);
int16 y0 = readw(0x0002);
int16 multiplier = readw(0x0004);
int32 x1, y1;
x1 = x0 * multiplier << 1;
y1 = y0 * multiplier << 1;
writed(0x0010, x1);
writed(0x0014, y1);
}
void ST010::op_04() {
int16 x = readw(0x0000);
int16 y = readw(0x0002);
int16 square;
//calculate the vector length of (x,y)
square = (int16)sqrt((double)(y * y + x * x));
writew(0x0010, square);
}
void ST010::op_05() {
int32 dx, dy;
int16 a1, b1, c1;
uint16 o1;
bool wrap = false;
//target (x,y) coordinates
int16 ypos_max = readw(0x00c0);
int16 xpos_max = readw(0x00c2);
//current coordinates and direction
int32 ypos = readd(0x00c4);
int32 xpos = readd(0x00c8);
uint16 rot = readw(0x00cc);
//physics
uint16 speed = readw(0x00d4);
uint16 accel = readw(0x00d6);
uint16 speed_max = readw(0x00d8);
//special condition acknowledgement
int16 system = readw(0x00da);
int16 flags = readw(0x00dc);
//new target coordinates
int16 ypos_new = readw(0x00de);
int16 xpos_new = readw(0x00e0);
//mask upper bit
xpos_new &= 0x7fff;
//get the current distance
dx = xpos_max - (xpos >> 16);
dy = ypos_max - (ypos >> 16);
//quirk: clear and move in9
writew(0x00d2, 0xffff);
writew(0x00da, 0x0000);
//grab the target angle
op_01(dy, dx, a1, b1, c1, (int16&)o1);
//check for wrapping
if(abs(o1 - rot) > 0x8000) {
o1 += 0x8000;
rot += 0x8000;
wrap = true;
}
uint16 old_speed = speed;
//special case
if(abs(o1 - rot) == 0x8000) {
speed = 0x100;
}
//slow down for sharp curves
else if(abs(o1 - rot) >= 0x1000) {
uint32 slow = abs(o1 - rot);
slow >>= 4; //scaling
speed -= slow;
}
//otherwise accelerate
else {
speed += accel;
if(speed > speed_max) {
speed = speed_max; //clip speed
}
}
//prevent negative/positive overflow
if(abs(old_speed - speed) > 0x8000) {
if(old_speed < speed) { speed = 0; }
else speed = 0xff00;
}
//adjust direction by so many degrees
//be careful of negative adjustments
if((o1 > rot && (o1 - rot) > 0x80) || (o1 < rot && (rot - o1) >= 0x80)) {
if(o1 < rot) { rot -= 0x280; }
else if(o1 > rot) { rot += 0x280; }
}
//turn of wrapping
if(wrap) { rot -= 0x8000; }
//now check the distances (store for later)
dx = (xpos_max << 16) - xpos;
dy = (ypos_max << 16) - ypos;
dx >>= 16;
dy >>= 16;
//if we're in so many units of the target, signal it
if((system && (dy <= 6 && dy >= -8) && (dx <= 126 && dx >= -128)) || (!system && (dx <= 6 && dx >= -8) && (dy <= 126 && dy >= -128))) {
//announce our new destination and flag it
xpos_max = xpos_new & 0x7fff;
ypos_max = ypos_new;
flags |= 0x08;
}
//update position
xpos -= (cos(rot) * 0x400 >> 15) * (speed >> 8) << 1;
ypos -= (sin(rot) * 0x400 >> 15) * (speed >> 8) << 1;
//quirk: mask upper byte
xpos &= 0x1fffffff;
ypos &= 0x1fffffff;
writew(0x00c0, ypos_max);
writew(0x00c2, xpos_max);
writed(0x00c4, ypos);
writed(0x00c8, xpos);
writew(0x00cc, rot);
writew(0x00d4, speed);
writew(0x00dc, flags);
}
void ST010::op_06() {
int16 multiplicand = readw(0x0000);
int16 multiplier = readw(0x0002);
int32 product;
product = multiplicand * multiplier << 1;
writed(0x0010, product);
}
void ST010::op_07() {
int16 theta = readw(0x0000);
int16 data;
for(int i = 0, offset = 0; i < 176; i++) {
data = mode7_scale[i] * cos(theta) >> 15;
writew(0x00f0 + offset, data);
writew(0x0510 + offset, data);
data = mode7_scale[i] * sin(theta) >> 15;
writew(0x0250 + offset, data);
if(data) { data = ~data; }
writew(0x03b0 + offset, data);
offset += 2;
}
}
void ST010::op_08() {
int16 x0 = readw(0x0000);
int16 y0 = readw(0x0002);
int16 theta = readw(0x0004);
int16 x1, y1;
x1 = (y0 * sin(theta) >> 15) + (x0 * cos(theta) >> 15);
y1 = (y0 * cos(theta) >> 15) - (x0 * sin(theta) >> 15);
writew(0x0010, x1);
writew(0x0012, y1);
}

View File

@@ -1,7 +1,10 @@
Config config_file;
namespace config {
Config& config() {
static Config config;
return config;
}
string file_updatepath(const char *req_file, const char *req_path) {
string file(req_file);
replace(file, "\\", "/");
@@ -27,68 +30,83 @@ stringarray part;
StringSetting Path::base(0, "fs.base_path",
"Path that bsnes resides in", "");
StringSetting Path::rom(&config_file, "path.rom",
StringSetting Path::rom(&config(), "path.rom",
"Default path to look for ROM files in (\"\" = use default directory)", "");
StringSetting Path::save(&config_file, "path.save",
StringSetting Path::save(&config(), "path.save",
"Default path for all save RAM and cheat files (\"\" = use current directory)", "");
StringSetting Path::bios(&config_file, "path.bios",
StringSetting Path::bios(&config(), "path.bios",
"Path where BIOS file(s) are located\n"
"Supported BIOS files:\n"
"stbios.bin - Bandai Sufami Turbo"
"", "./bios");
StringSetting Path::save_ext(&config_file, "path.save_ext",
StringSetting Path::save_ext(&config(), "path.save_ext",
"Extension to be used for all save RAM files", "srm");
IntegerSetting SNES::gamma_ramp(&config_file, "snes.colorfilter.gamma_ramp",
IntegerSetting SNES::gamma_ramp(&config(), "snes.colorfilter.gamma_ramp",
"Use precalculated TV-style gamma ramp", IntegerSetting::Boolean, true);
IntegerSetting SNES::sepia(&config_file, "snes.colorfilter.sepia",
IntegerSetting SNES::sepia(&config(), "snes.colorfilter.sepia",
"Convert color to sepia tone", IntegerSetting::Boolean, false);
IntegerSetting SNES::grayscale(&config_file, "snes.colorfilter.grayscale",
IntegerSetting SNES::grayscale(&config(), "snes.colorfilter.grayscale",
"Convert color to grayscale tone", IntegerSetting::Boolean, false);
IntegerSetting SNES::invert(&config_file, "snes.colorfilter.invert",
IntegerSetting SNES::invert(&config(), "snes.colorfilter.invert",
"Invert output image colors", IntegerSetting::Boolean, false);
IntegerSetting SNES::contrast(&config_file, "snes.colorfilter.contrast",
IntegerSetting SNES::contrast(&config(), "snes.colorfilter.contrast",
"Contrast", IntegerSetting::Decimal, 0);
IntegerSetting SNES::brightness(&config_file, "snes.colorfilter.brightness",
IntegerSetting SNES::brightness(&config(), "snes.colorfilter.brightness",
"Brightness", IntegerSetting::Decimal, 0);
IntegerSetting SNES::gamma(&config_file, "snes.colorfilter.gamma",
IntegerSetting SNES::gamma(&config(), "snes.colorfilter.gamma",
"Gamma", IntegerSetting::Decimal, 80);
IntegerSetting SNES::ntsc_merge_fields(&config_file, "snes.ntsc_merge_fields",
IntegerSetting SNES::ntsc_merge_fields(&config(), "snes.ntsc_merge_fields",
"Merge fields in NTSC video filter\n"
"Set to true if using filter at any refresh rate other than 60hz\n"
"", IntegerSetting::Boolean, true);
IntegerSetting SNES::mute(&config_file, "snes.mute", "Mutes SNES audio output when enabled",
IntegerSetting SNES::mute(&config(), "snes.mute", "Mutes SNES audio output when enabled",
IntegerSetting::Boolean, false);
IntegerSetting SNES::controller_port0(&config_file, "snes.controller_port_1",
IntegerSetting SNES::controller_port0(&config(), "snes.controller_port_1",
"Controller attached to SNES port 1", IntegerSetting::Decimal, ::SNES::DEVICEID_JOYPAD1);
IntegerSetting SNES::controller_port1(&config_file, "snes.controller_port_2",
IntegerSetting SNES::controller_port1(&config(), "snes.controller_port_2",
"Controller attached to SNES port 2", IntegerSetting::Decimal, ::SNES::DEVICEID_JOYPAD2);
IntegerSetting CPU::ntsc_clock_rate(&config_file, "cpu.ntsc_clock_rate",
IntegerSetting CPU::ntsc_clock_rate(&config(), "cpu.ntsc_clock_rate",
"NTSC S-CPU clock rate (in hz)", IntegerSetting::Decimal, 21477272);
IntegerSetting CPU::pal_clock_rate(&config_file, "cpu.pal_clock_rate",
IntegerSetting CPU::pal_clock_rate(&config(), "cpu.pal_clock_rate",
"PAL S-CPU clock rate (in hz)", IntegerSetting::Decimal, 21281370);
IntegerSetting CPU::hdma_enable(0, "cpu.hdma_enable",
"Enable HDMA effects", IntegerSetting::Boolean, true);
IntegerSetting SMP::ntsc_clock_rate(&config_file, "smp.ntsc_clock_rate",
IntegerSetting SMP::ntsc_clock_rate(&config(), "smp.ntsc_clock_rate",
"NTSC S-SMP clock rate (in hz)", IntegerSetting::Decimal, 24606720);
IntegerSetting SMP::pal_clock_rate(&config_file, "smp.pal_clock_rate",
IntegerSetting SMP::pal_clock_rate(&config(), "smp.pal_clock_rate",
"PAL S-SMP clock rate (in hz)", IntegerSetting::Decimal, 24606720);
IntegerSetting PPU::Hack::render_scanline_position(&config_file, "ppu.hack.render_scanline_position",
IntegerSetting PPU::Hack::render_scanline_position(&config(), "ppu.hack.render_scanline_position",
"Approximate HCLOCK position to render at for scanline-based renderers",
IntegerSetting::Decimal, 512);
IntegerSetting PPU::Hack::obj_cache(&config_file, "ppu.hack.obj_cache",
IntegerSetting PPU::Hack::obj_cache(&config(), "ppu.hack.obj_cache",
"Cache OAM OBJ attributes one scanline before rendering\n"
"This is technically closer to the actual operation of the SNES,\n"
"but can cause problems in some games if enabled",
IntegerSetting::Boolean, false);
IntegerSetting PPU::Hack::oam_address_invalidation(&config(), "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(), "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);

View File

@@ -1,7 +1,7 @@
extern Config config_file;
namespace config {
extern Config& config();
string file_updatepath(const char *, const char *);
extern struct Path {
@@ -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;

View File

@@ -662,7 +662,9 @@ void bDSP::power()
{
ram = (uint8*) r_smp->get_spcram_handle();
memset( &m, 0, sizeof m );
memcpy( m.regs, initial_regs, sizeof m.regs );
//memcpy( m.regs, initial_regs, sizeof m.regs );
memset(m.regs, 0, sizeof m.regs);
REG(flg) = 0xe0;
// Internal state
for ( int i = voice_count; --i >= 0; )

View File

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

View File

@@ -1,9 +1,9 @@
/*
libarray : version 0.08 ~byuu (2006-12-16)
libarray : version 0.09 ~byuu (2007-03-06)
*/
#ifndef __LIBARRAY
#define __LIBARRAY
#ifndef LIBARRAY_H
#define LIBARRAY_H
template<typename T> class array {
protected:

View File

@@ -1,5 +1,5 @@
/*
libbase : version 0.10 ~byuu (2007-05-27)
libbase : version 0.11 ~byuu (2007-09-08)
license: public domain
*/
@@ -16,7 +16,9 @@
#include <time.h>
#include <math.h>
#if defined(_MSC_VER)
#include <new>
#if defined(_MSC_VER) || defined(__MINGW32__)
#include <io.h>
#include <direct.h>
#include <shlobj.h>
@@ -32,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
@@ -39,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
@@ -55,17 +60,14 @@
#define noinline __declspec(noinline)
#define inline inline
#define alwaysinline __forceinline
#define fastcall __fastcall
#elif defined(__GNUC__)
#define noinline __attribute__((noinline))
#define inline inline
#define alwaysinline __attribute__((always_inline))
#define fastcall __attribute__((fastcall))
#else
#define noinline
#define inline inline
#define alwaysinline inline
#define fastcall
#endif
/*****
@@ -91,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());
@@ -137,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) {
@@ -171,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);

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

View File

@@ -1,16 +1,26 @@
/*
libco_x86 : version 0.10 ~byuu (2007-04-18)
libco_x86 : version 0.10 ~byuu (2007-09-08)
license: public domain
*/
#ifndef LIBCO_H
#define LIBCO_H
#define cocall fastcall
typedef void (*cothread_t);
#if !defined(fastcall)
#if defined(_MSC_VER)
#define fastcall __fastcall
#elif defined(__GNUC__)
#define fastcall __attribute__((fastcall))
#else
#error "fastcall undefined"
#endif
#endif
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);
typedef void *cothread_t;
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

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

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

View File

@@ -1,5 +1,5 @@
/*
libconfig : version 0.14 ~byuu (2007-05-27)
libconfig : version 0.14 ~byuu (2007-06-12)
license: public domain
*/
@@ -72,7 +72,7 @@ enum Format {
template<typename T> bool operator<=(T x) { return (T(data) <= x); }
template<typename T> bool operator< (T x) { return (T(data) < x); }
IntegerSetting(Config *parent, char *r_name, char *r_desc, uint r_format, uint r_data) {
IntegerSetting(Config *parent, const char *r_name, const char *r_desc, uint r_format, uint r_data) {
type = Setting::Integer;
name = strdup(r_name);
desc = strdup(r_desc);
@@ -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, char *r_name, 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);

View File

@@ -1,455 +0,0 @@
/*
libfile : version 0.07 ~byuu (2007-03-01)
*/
#ifndef __LIBFILE
#define __LIBFILE
//FreeBSD fix: do not use defines for libc functions
#undef feof
#undef ferror
/*****
* file object
*****/
class file {
public:
enum { mode_read, mode_write, mode_readwrite, mode_writeread };
enum { seek_start, seek_end, seek_back, seek_forward };
virtual void read(uint8 *data, uint length) = 0;
virtual uint8 read() = 0;
virtual void write(uint8 *data, uint length) = 0;
virtual void write(uint8 data) = 0;
virtual uint32 crc32();
virtual void seek(uint offset, uint mode = seek_start) = 0;
virtual void resize(uint size) = 0;
virtual uint offset() = 0;
virtual uint size() = 0;
virtual bool eof() = 0;
virtual bool open(const char *filename, uint mode) = 0;
virtual bool open() = 0;
virtual bool flush() = 0;
virtual bool close() = 0;
};
inline uint32 file::crc32() {
uint pos = offset(), i = size();
seek(0);
uint32 crc32 = 0xffffffff;
while(i--) {
crc32 = crc32_adjust(crc32, read());
}
seek(pos);
return ~crc32;
}
/*****
* c++ wrappers
*****/
inline void fread(file &s, uint8 *data, uint length) { s.read(data, length); }
inline uint8 fread(file &s) { return s.read(); }
inline uint8 fgetc(file &s) { return s.read(); }
inline uint fgetb(file &s) { return s.read(); }
inline void fwrite(file &s, uint8 *data, uint length) { s.write(data, length); }
inline void fwrite(file &s, uint8 data) { s.write(data); }
inline void fputc(uint8 data, file &s) { s.write(data); }
inline void fputb(file &s, uint8 data) { s.write(data); }
inline uint32 fcrc32(file &s) { return s.crc32(); }
inline void fseek(file &s, uint offset, uint mode = file::seek_start) { s.seek(offset, mode); }
inline void fresize(file &s, uint size) { s.resize(size); }
inline uint ftell(file &s) { return s.offset(); }
inline uint fsize(file &s) { return s.size(); }
inline bool feof(file &s) { return s.eof(); }
inline bool fopen(file &s, const char *filename, uint mode) { return s.open(filename, mode); }
inline bool fopen(file &s) { return s.open(); }
inline bool fflush(file &s) { return s.flush(); }
inline bool fclose(file &s) { return s.close(); }
/*****
* endian wrappers
*****/
inline uint8 fgetlb(file &s) { return fgetc(s); }
inline uint8 fgetmb(file &s) { return fgetc(s); }
inline uint16 fgetlw(file &s) {
return (fgetc(s)) | (fgetc(s) << 8);
}
inline uint16 fgetmw(file &s) {
return (fgetc(s) << 8) | (fgetc(s) << 8);
}
inline uint32 fgetld(file &s) {
return (fgetc(s)) | (fgetc(s) << 8) | (fgetc(s) << 16) | (fgetc(s) << 24);
}
inline uint32 fgetmd(file &s) {
return (fgetc(s) << 24) | (fgetc(s) << 16) | (fgetc(s) << 8) | (fgetc(s));
}
inline uint64 fgetlq(file &s) {
return ((uint64)fgetc(s) << 0) | ((uint64)fgetc(s) << 8) |
((uint64)fgetc(s) << 16) | ((uint64)fgetc(s) << 24) |
((uint64)fgetc(s) << 32) | ((uint64)fgetc(s) << 40) |
((uint64)fgetc(s) << 48) | ((uint64)fgetc(s) << 56);
}
inline uint64 fgetmq(file &s) {
return ((uint64)fgetc(s) << 56) | ((uint64)fgetc(s) << 48) |
((uint64)fgetc(s) << 40) | ((uint64)fgetc(s) << 32) |
((uint64)fgetc(s) << 24) | ((uint64)fgetc(s) << 16) |
((uint64)fgetc(s) << 8) | ((uint64)fgetc(s) << 0);
}
inline void fputlb(file &s, uint8 data) { fputc(data, s); }
inline void fputmb(file &s, uint8 data) { fputc(data, s); }
inline void fputlw(file &s, uint16 data) {
fputc(data >> 0, s);
fputc(data >> 8, s);
}
inline void fputmw(file &s, uint16 data) {
fputc(data >> 8, s);
fputc(data >> 0, s);
}
inline void fputld(file &s, uint32 data) {
fputc(data >> 0, s);
fputc(data >> 8, s);
fputc(data >> 16, s);
fputc(data >> 24, s);
}
inline void fputmd(file &s, uint32 data) {
fputc(data >> 24, s);
fputc(data >> 16, s);
fputc(data >> 8, s);
fputc(data >> 0, s);
}
inline void fputlq(file &s, uint64 data) {
fputc(data >> 0, s);
fputc(data >> 8, s);
fputc(data >> 16, s);
fputc(data >> 24, s);
fputc(data >> 32, s);
fputc(data >> 40, s);
fputc(data >> 48, s);
fputc(data >> 56, s);
}
inline void fputmq(file &s, uint64 data) {
fputc(data >> 56, s);
fputc(data >> 48, s);
fputc(data >> 40, s);
fputc(data >> 32, s);
fputc(data >> 24, s);
fputc(data >> 16, s);
fputc(data >> 8, s);
fputc(data >> 0, s);
}
/*****
* ramfile
*****/
class ramfile : public file {
private:
FILE *fp;
array<uint8> filedata;
char filename[1024];
uint filepos;
uint filesize;
uint filemode;
bool fileopen;
bool filevirtual;
public:
void read(uint8 *data, uint length) {
if(!fileopen || filemode == mode_write) { return; }
memcpy(data, filedata.get(filepos + length) + filepos, length);
filepos += length;
if(filepos > filesize)filepos = filesize;
}
uint8 read() {
if(!fileopen || filemode == mode_write) { return 0; }
if(eof() == true) { return 0xff; }
return filedata[filepos++];
}
void write(uint8 *data, uint length) {
if(!fileopen || filemode == mode_read) { return; }
memcpy(filedata.get(filepos + length) + filepos, data, length);
filepos += length;
if(filepos > filesize)filesize = filepos;
}
void write(uint8 data) {
if(!fileopen || filemode == mode_read) { return; }
filedata[filepos++] = data;
if(filepos > filesize)filesize = filepos;
}
void seek(uint offset, uint mode = seek_start) {
if(!fileopen) { return; }
switch(mode) {
case seek_start: filepos = offset; break;
case seek_end: filepos = filesize + offset; break;
case seek_back: filepos -= offset; break;
case seek_forward: filepos += offset; break;
}
if(filemode == mode_read) {
if(filepos > filesize)filepos = filesize;
} else {
if(filepos > filesize)filesize = filepos;
}
}
void resize(uint size) {
filesize = size;
if(filepos > filesize)filepos = filesize;
}
uint offset() {
if(!fileopen) { return 0; }
return filepos;
}
uint size() {
if(!fileopen) { return 0; }
return filesize;
}
bool eof() {
if(!fileopen) { return true; }
return (filepos >= filesize);
}
bool open(const char *fn, uint mode) {
if(fileopen) { return false; }
strcpy(filename, fn ? fn : "");
filevirtual = (*filename == 0);
filemode = mode;
switch(filemode) {
case mode_read:
case mode_readwrite:
if(filevirtual == true) {
filesize = 0;
} else {
fp = fopen(filename, "rb");
if(!fp) { return false; }
filesize = fsize(fp);
fread(filedata.get(filesize), 1, filesize, fp);
fclose(fp);
}
break;
default:
filesize = 0;
break;
}
filepos = 0;
fileopen = true;
return true;
}
bool open() {
return fileopen;
}
bool flush() {
if(!fileopen) { return false; }
switch(filemode) {
case mode_readwrite:
case mode_write:
case mode_writeread:
if(filevirtual == false) {
fp = fopen(filename, "wb");
if(!fp) { return false; }
fwrite(filedata.get(filesize), 1, filesize, fp);
fclose(fp);
}
break;
}
return true;
}
bool close() {
if(!fileopen) { return false; }
bool result = flush();
fileopen = false;
filedata.reset();
return result;
}
ramfile() {
fileopen = false;
}
~ramfile() {
if(fileopen) { close(); }
}
};
/*****
* diskfile
*****/
class diskfile : public file {
private:
FILE *fp;
uint filemode;
public:
void read(uint8 *data, uint length) {
if(!fp || filemode == mode_write) { return; }
fread(data, 1, length, fp);
}
uint8 read() {
if(!fp || filemode == mode_write) { return 0; }
if(eof() == true) { return 0xff; }
return fgetc(fp);
}
void write(uint8 *data, uint length) {
if(!fp || filemode == mode_read) { return; }
fwrite(data, 1, length, fp);
}
void write(uint8 data) {
if(!fp || filemode == mode_read) { return; }
fputc(data, fp);
}
void seek(uint offset, uint mode = seek_start) {
if(!fp) { return; }
switch(mode) {
default:
case seek_start: fseek(fp, offset, SEEK_SET); break;
case seek_end: fseek(fp, offset, SEEK_END); break;
case seek_back: fseek(fp, offset, SEEK_CUR); break;
case seek_forward: fseek(fp, offset, SEEK_CUR); break;
}
}
void resize(uint size) {
if(!fp) { return; }
fresize(fp, size);
}
uint offset() {
if(!fp) { return 0; }
return ftell(fp);
}
uint size() {
if(!fp) { return 0; }
uint pos = ftell(fp);
fseek(fp, 0, SEEK_END);
uint filesize = ftell(fp);
fseek(fp, pos, SEEK_SET);
return filesize;
}
bool eof() {
if(!fp) { return true; }
if(feof(fp)) {
seek(size(), seek_start);
return true;
}
return false;
}
bool open(const char *filename, uint mode) {
if(fp) { return false; }
filemode = mode;
char m[8];
switch(filemode) {
default:
case mode_read: strcpy(m, "rb"); break;
case mode_write: strcpy(m, "wb"); break;
case mode_readwrite: strcpy(m, "rb+"); break;
case mode_writeread: strcpy(m, "wb+"); break;
}
fp = fopen(filename, m);
if(!fp) { return false; }
return true;
}
bool open() {
return (fp != 0);
}
bool flush() {
if(!fp) { return false; }
fflush(fp);
return true;
}
bool close() {
if(!fp) { return false; }
fclose(fp);
fp = 0;
return true;
}
diskfile() {
fp = 0;
}
~diskfile() {
if(fp) { fclose(fp); }
}
};
/*****
* directory object
*****/
class directory {
public:
void open(const char *path) {}
void close() {}
uint read(char *filename, uint maxlength) { return 0; }
};
#endif

View File

@@ -1,341 +1,100 @@
/*
libfunctor : version 0.01 ~byuu (2007-01-04)
libfunctor : version 0.05 ~byuu (2007-09-08)
license: public domain
*/
#ifndef __LIBFUNCTOR
#define __LIBFUNCTOR
#ifndef LIBFUNCTOR_H
#define LIBFUNCTOR_H
template<typename T> struct functor;
template<typename T> struct functor_t;
//prologue
/*****
* macros
*****/
#define TN typename
#define base_functor \
functor *f; \
virtual operator bool() const { return f && (bool)(*f); } \
virtual functor *copy() const { return f->copy(); } \
functor &operator=(const functor *source) { if(f) { delete f; } f = (source->f) ? source->copy() : 0; return *this; } \
functor &operator=(const functor &source) { if(f) { delete f; } f = (source.f) ? source.copy() : 0; return *this; } \
functor(const functor &source) { /* ........................ */ f = (source.f) ? source.copy() : 0; } \
functor() : f(0) {} \
~functor() { if(f && f != this) { delete f; } }
template<typename T> class functor;
#define base_global_functor \
operator bool() const { return proc; } \
functor_t *copy() const { return new functor_t(*this); } \
functor_t(const functor_t &source) { proc = source.proc; }
//parameters = 0
#define base_member_functor \
operator bool() const { return obj && proc; } \
functor_t *copy() const { return new functor_t(*this); } \
functor_t(const functor_t &source) { obj = source.obj; proc = source.proc; } \
C *obj;
#define cat(n) n
#define TL typename R
#define PL
#define CL
/*****
* parameters = 0
*****/
#include "libfunctor_impl.h"
template<typename R>
struct functor<R (*)()> { base_functor
virtual R operator()() const { return (*f)(); }
};
//parameters = 1
template<typename R>
struct functor_t<R (*)()> : public functor<R (*)()> { base_global_functor
R (*proc)();
R operator()() const { return (*proc)(); }
functor_t(R (*_proc)()) : proc(_proc) { this->f = this; }
};
#define cat(n) , n
#define TL TN R, TN P1
#define PL P1 p1
#define CL p1
template<typename C, typename R>
struct functor_t<R (C::*)()> : public functor<R (*)()> { base_member_functor
R (C::*proc)();
R operator()() const { return (obj->*proc)(); }
functor_t(C *_obj, R (C::*_proc)()) : obj(_obj), proc(_proc) { this->f = this; }
};
#include "libfunctor_impl.h"
template<typename R>
functor_t<R (*)()> make_functor(R (*proc)()) {
return functor_t<R (*)()>(proc);
}
//parameters = 2
template<typename C, typename R>
functor_t<R (C::*)()> make_functor(C *obj, R (C::*proc)()) {
return functor_t<R (C::*)()>(obj, proc);
}
#define cat(n) , n
#define TL TN R, TN P1, TN P2
#define PL P1 p1, P2 p2
#define CL p1, p2
/*****
* parameters = 1
*****/
#include "libfunctor_impl.h"
template<typename R, typename P1>
struct functor<R (*)(P1)> { base_functor
virtual R operator()(P1 p1) const { return (*f)(p1); }
};
//parameters = 3
template<typename R, typename P1>
struct functor_t<R (*)(P1)> : public functor<R (*)(P1)> { base_global_functor
R (*proc)(P1);
R operator()(P1 p1) const { return (*proc)(p1); }
functor_t(R (*_proc)(P1)) : proc(_proc) { this->f = this; }
};
#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
template<typename C, typename R, typename P1>
struct functor_t<R (C::*)(P1)> : public functor<R (*)(P1)> { base_member_functor
R (C::*proc)(P1);
R operator()(P1 p1) const { return (obj->*proc)(p1); }
functor_t(C *_obj, R (C::*_proc)(P1)) : obj(_obj), proc(_proc) { this->f = this; }
};
#include "libfunctor_impl.h"
template<typename R, typename P1>
functor_t<R (*)(P1)> make_functor(R (*proc)(P1)) {
return functor_t<R (*)(P1)>(proc);
}
//parameters = 4
template<typename C, typename R, typename P1>
functor_t<R (C::*)(P1)> make_functor(C *obj, R (C::*proc)(P1)) {
return functor_t<R (C::*)(P1)>(obj, proc);
}
#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
/*****
* parameters = 2
*****/
#include "libfunctor_impl.h"
template<typename R, typename P1, typename P2>
struct functor<R (*)(P1, P2)> { base_functor
virtual R operator()(P1 p1, P2 p2) const { return (*f)(p1, p2); }
};
//parameters = 5
template<typename R, typename P1, typename P2>
struct functor_t<R (*)(P1, P2)> : public functor<R (*)(P1, P2)> { base_global_functor
R (*proc)(P1, P2);
R operator()(P1 p1, P2 p2) const { return (*proc)(p1, p2); }
functor_t(R (*_proc)(P1, P2)) : proc(_proc) { this->f = this; }
};
#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
template<typename C, typename R, typename P1, typename P2>
struct functor_t<R (C::*)(P1, P2)> : public functor<R (*)(P1, P2)> { base_member_functor
R (C::*proc)(P1, P2);
R operator()(P1 p1, P2 p2) const { return (obj->*proc)(p1, p2); }
functor_t(C *_obj, R (C::*_proc)(P1, P2)) : obj(_obj), proc(_proc) { this->f = this; }
};
#include "libfunctor_impl.h"
template<typename R, typename P1, typename P2>
functor_t<R (*)(P1, P2)> make_functor(R (*proc)(P1, P2)) {
return functor_t<R (*)(P1, P2)>(proc);
}
//parameters = 6
template<typename C, typename R, typename P1, typename P2>
functor_t<R (C::*)(P1, P2)> make_functor(C *obj, R (C::*proc)(P1, P2)) {
return functor_t<R (C::*)(P1, P2)>(obj, proc);
}
#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
/*****
* parameters = 3
*****/
#include "libfunctor_impl.h"
template<typename R, typename P1, typename P2, typename P3>
struct functor<R (*)(P1, P2, P3)> { base_functor
virtual R operator()(P1 p1, P2 p2, P3 p3) const { return (*f)(p1, p2, p3); }
};
//parameters = 7
template<typename R, typename P1, typename P2, typename P3>
struct functor_t<R (*)(P1, P2, P3)> : public functor<R (*)(P1, P2, P3)> { base_global_functor
R (*proc)(P1, P2, P3);
R operator()(P1 p1, P2 p2, P3 p3) const { return (*proc)(p1, p2, p3); }
functor_t(R (*_proc)(P1, P2, P3)) : proc(_proc) { this->f = this; }
};
#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
template<typename C, typename R, typename P1, typename P2, typename P3>
struct functor_t<R (C::*)(P1, P2, P3)> : public functor<R (*)(P1, P2, P3)> { base_member_functor
R (C::*proc)(P1, P2, P3);
R operator()(P1 p1, P2 p2, P3 p3) const { return (obj->*proc)(p1, p2, p3); }
functor_t(C *_obj, R (C::*_proc)(P1, P2, P3)) : obj(_obj), proc(_proc) { this->f = this; }
};
#include "libfunctor_impl.h"
template<typename R, typename P1, typename P2, typename P3>
functor_t<R (*)(P1, P2, P3)> make_functor(R (*proc)(P1, P2, P3)) {
return functor_t<R (*)(P1, P2, P3)>(proc);
}
//parameters = 8
template<typename C, typename R, typename P1, typename P2, typename P3>
functor_t<R (C::*)(P1, P2, P3)> make_functor(C *obj, R (C::*proc)(P1, P2, P3)) {
return functor_t<R (C::*)(P1, P2, P3)>(obj, proc);
}
#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
/*****
* parameters = 4
*****/
#include "libfunctor_impl.h"
template<typename R, typename P1, typename P2, typename P3, typename P4>
struct functor<R (*)(P1, P2, P3, P4)> { base_functor
virtual R operator()(P1 p1, P2 p2, P3 p3, P4 p4) const { return (*f)(p1, p2, p3, p4); }
};
//epilogue
template<typename R, typename P1, typename P2, typename P3, typename P4>
struct functor_t<R (*)(P1, P2, P3, P4)> : public functor<R (*)(P1, P2, P3, P4)> { base_global_functor
R (*proc)(P1, P2, P3, P4);
R operator()(P1 p1, P2 p2, P3 p3, P4 p4) const { return (*proc)(p1, p2, p3, p4); }
functor_t(R (*_proc)(P1, P2, P3, P4)) : proc(_proc) { this->f = this; }
};
template<typename C, typename R, typename P1, typename P2, typename P3, typename P4>
struct functor_t<R (C::*)(P1, P2, P3, P4)> : public functor<R (*)(P1, P2, P3, P4)> { base_member_functor
R (C::*proc)(P1, P2, P3, P4);
R operator()(P1 p1, P2 p2, P3 p3, P4 p4) const { return (obj->*proc)(p1, p2, p3, p4); }
functor_t(C *_obj, R (C::*_proc)(P1, P2, P3, P4)) : obj(_obj), proc(_proc) { this->f = this; }
};
template<typename R, typename P1, typename P2, typename P3, typename P4>
functor_t<R (*)(P1, P2, P3, P4)> make_functor(R (*proc)(P1, P2, P3, P4)) {
return functor_t<R (*)(P1, P2, P3, P4)>(proc);
}
template<typename C, typename R, typename P1, typename P2, typename P3, typename P4>
functor_t<R (C::*)(P1, P2, P3, P4)> make_functor(C *obj, R (C::*proc)(P1, P2, P3, P4)) {
return functor_t<R (C::*)(P1, P2, P3, P4)>(obj, proc);
}
/*****
* parameters = 5
*****/
template<typename R, typename P1, typename P2, typename P3, typename P4, typename P5>
struct functor<R (*)(P1, P2, P3, P4, P5)> { base_functor
virtual R operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) const { return (*f)(p1, p2, p3, p4, p5); }
};
template<typename R, typename P1, typename P2, typename P3, typename P4, typename P5>
struct functor_t<R (*)(P1, P2, P3, P4, P5)> : public functor<R (*)(P1, P2, P3, P4, P5)> { base_global_functor
R (*proc)(P1, P2, P3, P4, P5);
R operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) const { return (*proc)(p1, p2, p3, p4, p5); }
functor_t(R (*_proc)(P1, P2, P3, P4, P5)) : proc(_proc) { this->f = this; }
};
template<typename C, typename R, typename P1, typename P2, typename P3, typename P4, typename P5>
struct functor_t<R (C::*)(P1, P2, P3, P4, P5)> : public functor<R (*)(P1, P2, P3, P4, P5)> { base_member_functor
R (C::*proc)(P1, P2, P3, P4, P5);
R operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) const { return (obj->*proc)(p1, p2, p3, p4, p5); }
functor_t(C *_obj, R (C::*_proc)(P1, P2, P3, P4, P5)) : obj(_obj), proc(_proc) { this->f = this; }
};
template<typename R, typename P1, typename P2, typename P3, typename P4, typename P5>
functor_t<R (*)(P1, P2, P3, P4, P5)> make_functor(R (*proc)(P1, P2, P3, P4, P5)) {
return functor_t<R (*)(P1, P2, P3, P4, P5)>(proc);
}
template<typename C, typename R, typename P1, typename P2, typename P3, typename P4, typename P5>
functor_t<R (C::*)(P1, P2, P3, P4, P5)> make_functor(C *obj, R (C::*proc)(P1, P2, P3, P4, P5)) {
return functor_t<R (C::*)(P1, P2, P3, P4, P5)>(obj, proc);
}
/*****
* parameters = 6
*****/
template<typename R, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6>
struct functor<R (*)(P1, P2, P3, P4, P5, P6)> { base_functor
virtual R operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) const { return (*f)(p1, p2, p3, p4, p5, p6); }
};
template<typename R, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6>
struct functor_t<R (*)(P1, P2, P3, P4, P5, P6)> : public functor<R (*)(P1, P2, P3, P4, P5, P6)> { base_global_functor
R (*proc)(P1, P2, P3, P4, P5, P6);
R operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) const { return (*proc)(p1, p2, p3, p4, p5, p6); }
functor_t(R (*_proc)(P1, P2, P3, P4, P5, P6)) : proc(_proc) { this->f = this; }
};
template<typename C, typename R, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6>
struct functor_t<R (C::*)(P1, P2, P3, P4, P5, P6)> : public functor<R (*)(P1, P2, P3, P4, P5, P6)> { base_member_functor
R (C::*proc)(P1, P2, P3, P4, P5, P6);
R operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) const { return (obj->*proc)(p1, p2, p3, p4, p5, p6); }
functor_t(C *_obj, R (C::*_proc)(P1, P2, P3, P4, P5, P6)) : obj(_obj), proc(_proc) { this->f = this; }
};
template<typename R, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6>
functor_t<R (*)(P1, P2, P3, P4, P5, P6)> make_functor(R (*proc)(P1, P2, P3, P4, P5, P6)) {
return functor_t<R (*)(P1, P2, P3, P4, P5, P6)>(proc);
}
template<typename C, typename R, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6>
functor_t<R (C::*)(P1, P2, P3, P4, P5, P6)> make_functor(C *obj, R (C::*proc)(P1, P2, P3, P4, P5, P6)) {
return functor_t<R (C::*)(P1, P2, P3, P4, P5, P6)>(obj, proc);
}
/*****
* parameters = 7
*****/
template<typename R, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7>
struct functor<R (*)(P1, P2, P3, P4, P5, P6, P7)> { base_functor
virtual R operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7) const { return (*f)(p1, p2, p3, p4, p5, p6, p7); }
};
template<typename R, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7>
struct functor_t<R (*)(P1, P2, P3, P4, P5, P6, P7)> : public functor<R (*)(P1, P2, P3, P4, P5, P6, P7)> { base_global_functor
R (*proc)(P1, P2, P3, P4, P5, P6, P7);
R operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7) const { return (*proc)(p1, p2, p3, p4, p5, p6, p7); }
functor_t(R (*_proc)(P1, P2, P3, P4, P5, P6, P7)) : proc(_proc) { this->f = this; }
};
template<typename C, typename R, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7>
struct functor_t<R (C::*)(P1, P2, P3, P4, P5, P6, P7)> : public functor<R (*)(P1, P2, P3, P4, P5, P6, P7)> { base_member_functor
R (C::*proc)(P1, P2, P3, P4, P5, P6, P7);
R operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7) const { return (obj->*proc)(p1, p2, p3, p4, p5, p6, p7); }
functor_t(C *_obj, R (C::*_proc)(P1, P2, P3, P4, P5, P6, P7)) : obj(_obj), proc(_proc) { this->f = this; }
};
template<typename R, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7>
functor_t<R (*)(P1, P2, P3, P4, P5, P6, P7)> make_functor(R (*proc)(P1, P2, P3, P4, P5, P6, P7)) {
return functor_t<R (*)(P1, P2, P3, P4, P5, P6, P7)>(proc);
}
template<typename C, typename R, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7>
functor_t<R (C::*)(P1, P2, P3, P4, P5, P6, P7)> make_functor(C *obj, R (C::*proc)(P1, P2, P3, P4, P5, P6, P7)) {
return functor_t<R (C::*)(P1, P2, P3, P4, P5, P6, P7)>(obj, proc);
}
/*****
* parameters = 8
*****/
template<typename R, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8>
struct functor<R (*)(P1, P2, P3, P4, P5, P6, P7, P8)> { base_functor
virtual R operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8) const { return (*f)(p1, p2, p3, p4, p5, p6, p7, p8); }
};
template<typename R, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8>
struct functor_t<R (*)(P1, P2, P3, P4, P5, P6, P7, P8)> : public functor<R (*)(P1, P2, P3, P4, P5, P6, P7, P8)> { base_global_functor
R (*proc)(P1, P2, P3, P4, P5, P6, P7, P8);
R operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8) const { return (*proc)(p1, p2, p3, p4, p5, p6, p7, p8); }
functor_t(R (*_proc)(P1, P2, P3, P4, P5, P6, P7, P8)) : proc(_proc) { this->f = this; }
};
template<typename C, typename R, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8>
struct functor_t<R (C::*)(P1, P2, P3, P4, P5, P6, P7, P8)> : public functor<R (*)(P1, P2, P3, P4, P5, P6, P7, P8)> { base_member_functor
R (C::*proc)(P1, P2, P3, P4, P5, P6, P7, P8);
R operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8) const { return (obj->*proc)(p1, p2, p3, p4, p5, p6, p7, p8); }
functor_t(C *_obj, R (C::*_proc)(P1, P2, P3, P4, P5, P6, P7, P8)) : obj(_obj), proc(_proc) { this->f = this; }
};
template<typename R, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8>
functor_t<R (*)(P1, P2, P3, P4, P5, P6, P7, P8)> make_functor(R (*proc)(P1, P2, P3, P4, P5, P6, P7, P8)) {
return functor_t<R (*)(P1, P2, P3, P4, P5, P6, P7, P8)>(proc);
}
template<typename C, typename R, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8>
functor_t<R (C::*)(P1, P2, P3, P4, P5, P6, P7, P8)> make_functor(C *obj, R (C::*proc)(P1, P2, P3, P4, P5, P6, P7, P8)) {
return functor_t<R (C::*)(P1, P2, P3, P4, P5, P6, P7, P8)>(obj, proc);
}
/*****
* epilogue
*****/
#undef base_functor
#undef base_global_functor
#undef base_member_functor
#undef TN
#endif

81
src/lib/libfunctor_impl.h Normal file
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

View File

@@ -1,73 +0,0 @@
/*
libinterp : version 0.01 ~byuu (2007-02-03)
*/
#ifndef __LIBINTERP
#define __LIBINTERP
static inline
double interpolate_point(
double mu,
double y0, double y1
) {
return mu < 0.5 ? y0 : y1;
}
static inline
double interpolate_linear(
double mu,
double y0, double y1
) {
return y0 + mu * (y1 - y0);
}
static inline
double interpolate_cosine(
double mu,
double y0, double y1
) {
mu = (1.0 - cos(mu * 3.14159265)) / 2.0;
return y0 + mu * (y1 - y0);
}
static inline
double interpolate_cubic(
double mu,
double y0, double y1, double y2, double y3
) {
double a0 = y3 - y2 - y0 + y1;
double a1 = y0 - y1 - a0;
double a2 = y2 - y0;
double a3 = y1;
return a0 * mu * mu * mu +
a1 * mu * mu +
a2 * mu +
a3;
}
/*
tension: (0 = normal, -1 = low, +1 = high)
bias: (0 = even, -n = left, +n = right)
*/
static inline
double interpolate_hermite(
double mu, double tension, double bias,
double y0, double y1, double y2, double y3
) {
double mu0 = ( (y1 - y0) * (1.0 + bias) * (1.0 - tension) / 2.0 ) +
( (y2 - y1) * (1.0 - bias) * (1.0 - tension) / 2.0 );
double mu1 = ( (y2 - y1) * (1.0 + bias) * (1.0 - tension) / 2.0 ) +
( (y3 - y2) * (1.0 - bias) * (1.0 - tension) / 2.0 );
double mu2 = mu * mu;
double mu3 = mu * mu * mu;
double a0 = 2.0 * mu3 - 3.0 * mu2 + 1.0;
double a1 = mu3 - 2.0 * mu2 + mu;
double a2 = mu3 - mu2;
double a3 = -2.0 * mu3 + 3.0 * mu2;
return a0 * y1 +
a1 * mu0 +
a2 * mu1 +
a3 * y2;
}
#endif

View File

@@ -7,6 +7,28 @@
namespace keymap {
//TODO: use lookup table for find() functions
static char keytable[][64] = {
"none",
"esc",
"f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "f10", "f11", "f12",
"print_screen", "sys_req", "scroll_lock", "pause", "brk",
"grave", "tilde",
"num_1", "exclamation",
"num_2", "at",
"num_3", "pound",
"num_4", "dollar",
"num_5", "percent",
"num_6", "power",
"num_7", "ampersand",
"num_8", "asterisk",
"num_9", "lparenthesis",
"num_0", "rparenthesis",
"minus", "underscore",
"equal", "plus",
"backspace",
};
enum {
none = 0x0000,
@@ -108,6 +130,8 @@ enum {
rsuper,
menu,
limit, //not an actual key -- marks the end of linear key entries
joypad_flag = 0x8000,
joypad_nummask = 0x7f00,
joypad_keymask = 0x00ff,

View File

@@ -2,8 +2,8 @@
libsort : version 0.01 ~byuu (2006-11-15)
*/
#ifndef __LIBSORT
#define __LIBSORT
#ifndef LIBSORT_H
#define LIBSORT_H
template<typename T>
void sort(T list[], uint length) {

View File

@@ -1,9 +1,9 @@
/*
libstring : version 0.17 ~byuu (2007-05-20)
libstring : version 0.18 ~byuu (2007-06-06)
*/
#ifndef __LIBSTRING
#define __LIBSTRING
#ifndef LIBSTRING_H
#define LIBSTRING_H
#include "libbase.h"
#include "libvector.h"
@@ -189,35 +189,32 @@ uint size;
~string() { safe_free(s); }
const char *operator()();
char &operator[](const uint);
string &operator= (const int);
string &operator= (const char *);
string &operator= (const string &);
string &operator+=(const int);
string &operator+=(const char *);
string &operator+=(const string &);
bool operator==(const char *);
bool operator==(const string &);
bool operator!=(const char *);
bool operator!=(const string &);
bool operator< (const char *);
bool operator< (const string &);
bool operator<=(const char *);
bool operator<=(const string &);
bool operator> (const char *);
bool operator> (const string &);
bool operator>=(const char *);
bool operator>=(const string &);
operator const char*() const { return s; }
const char* operator()() const { return s; }
char& operator[](const uint);
string operator+(const int);
string operator+(const char *);
string operator+(const string &);
string& operator=(const int);
string& operator=(const char*);
string& operator=(const string&);
string& operator<<(const int);
string& operator<<(const char*);
string& operator<<(const string&);
bool operator==(const char*);
bool operator==(const string&);
bool operator!=(const char*);
bool operator!=(const string&);
bool operator< (const char*);
bool operator< (const string&);
bool operator<=(const char*);
bool operator<=(const string&);
bool operator> (const char*);
bool operator> (const string&);
bool operator>=(const char*);
bool operator>=(const string&);
};
string operator+(const int, const string &);
string operator+(const char *, const string &);
inline void swap(string &x, string &y) { x.swap(y); }
#endif //__LIBSTRING
#endif //LIBSTRING_H

View File

@@ -1,7 +1,3 @@
const char *string::operator()() {
return s;
}
char &string::operator[](const uint index) {
reserve(index);
return s[index];
@@ -22,17 +18,17 @@ string &string::operator=(const string &str) {
return *this;
}
string &string::operator+=(const int num) {
string& string::operator<<(const int num) {
strcat(*this, strfmt("%d", num));
return *this;
}
string &string::operator+=(const char *str) {
string& string::operator<<(const char* str) {
strcat(*this, str);
return *this;
}
string &string::operator+=(const string &str) {
string& string::operator<<(const string& str) {
strcat(*this, str);
return *this;
}
@@ -49,35 +45,3 @@ bool string::operator> (const char *str) { return strcmp(strptr(*this), str)
bool string::operator> (const string &str) { return strcmp(strptr(*this), strptr(str)) > 0; }
bool string::operator>=(const char *str) { return strcmp(strptr(*this), str) >= 0; }
bool string::operator>=(const string &str) { return strcmp(strptr(*this), strptr(str)) >= 0; }
string string::operator+(const int num) {
string temp(*this);
strcat(temp, strfmt("%d", num));
return temp;
}
string string::operator+(const char *str) {
string temp(*this);
strcat(temp, str);
return temp;
}
string string::operator+(const string &str) {
string temp(*this);
strcat(temp, str);
return temp;
}
//
string operator+(const int x, const string &y) {
string temp(strfmt("%d", x));
strcat(temp, y);
return temp;
}
string operator+(const char *x, const string &y) {
string temp(x);
strcat(temp, y);
return temp;
}

View File

@@ -40,13 +40,14 @@ uint get_screen_height() { return gdk_screen_height(); }
//
noinline bool gtk_file_load(Window &owner, char *filename, const char *filter, const char *path) {
bool file_load(Window *owner, char *filename, const char *filter, const char *path) {
strcpy(filename, "");
GtkWidget *dialog = gtk_file_chooser_dialog_new("Load File",
GTK_WINDOW(owner.info.window), GTK_FILE_CHOOSER_ACTION_OPEN,
owner ? GTK_WINDOW(owner->info.window) : (GtkWindow*)0,
GTK_FILE_CHOOSER_ACTION_OPEN,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, 0);
GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, (const gchar*)0);
if(path && strcmp(path, "")) {
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), path);
@@ -62,13 +63,14 @@ GtkWidget *dialog = gtk_file_chooser_dialog_new("Load File",
return strcmp(filename, ""); //return true if filename != ""
}
noinline bool gtk_file_save(Window &owner, char *filename, const char *filter, const char *path) {
bool file_save(Window *owner, char *filename, const char *filter, const char *path) {
strcpy(filename, "");
GtkWidget *dialog = gtk_file_chooser_dialog_new("Save File",
GTK_WINDOW(owner.info.window), GTK_FILE_CHOOSER_ACTION_SAVE,
owner ? GTK_WINDOW(owner->info.window) : (GtkWindow*)0,
GTK_FILE_CHOOSER_ACTION_SAVE,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, 0);
GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, (const gchar*)0);
if(path && strcmp(path, "")) {
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), path);
@@ -85,19 +87,6 @@ GtkWidget *dialog = gtk_file_chooser_dialog_new("Save File",
return strcmp(filename, ""); //return true if filename != ""
}
//FreeBSD 6.2-amd64 bug workaround for file_load() + file_save()
//gdb reveals stack corruption when calling gtk_file_chooset_dialog_new() from inside
//a member function. however, calling function with nesting sidesteps the bug ...
//gdb shows that the corruption occurs inside a GTK+ internal library function ...
bool file_load(Window &owner, char *filename, const char *filter, const char *path) {
return gtk_file_load(owner, filename, filter, path);
}
bool file_save(Window &owner, char *filename, const char *filter, const char *path) {
return gtk_file_save(owner, filename, filter, path);
}
//
uint16 translate_key(uint key) {

View File

@@ -1,5 +1,5 @@
/*
libui_gtk ~byuu (2007-05-27)
libui_gtk ~byuu (2007-06-06)
license: public domain
*/
@@ -28,8 +28,8 @@ bool events_pending();
uint get_screen_width();
uint get_screen_height();
bool file_load(Window &owner, char *filename, const char *filter, const char *path = "");
bool file_save(Window &owner, char *filename, const char *filter, const char *path = "");
bool file_load(Window *owner, char *filename, const char *filter, const char *path = "");
bool file_save(Window *owner, char *filename, const char *filter, const char *path = "");
uint16 translate_key(uint key);
@@ -53,20 +53,22 @@ enum Style {
MenuBar menu;
void create(uint style, uint width, uint height, const char *caption = "");
void set_text(const char *str, ...);
void set_text(const char *str);
void set_background_color(uint8 r, uint8 g, uint8 b);
void focus();
bool focused();
void move(uint x, uint y);
void resize(uint width, uint height);
virtual void show();
virtual void hide();
virtual bool close() { return true; }
virtual void keydown(uint16 key) {}
virtual void keyup(uint16 key) {}
void show(bool state);
bool visible();
void fullscreen();
void unfullscreen();
void fullscreen(bool state);
bool is_fullscreen();
virtual int message(uint id, void *param = 0) {}
virtual void clicked(Control&) {}
virtual void changed(Control&) {}
virtual bool message(uint id, uintptr_t param = 0) { return true; }
//private:
struct {

View File

@@ -321,13 +321,8 @@ void Label::create(Window &r_owner, uint style, uint x, uint y, uint width, uint
gtk_widget_show(widget);
}
void Label::set_text(const char *str, ...) {
va_list args;
va_start(args, str);
string temp;
vsprintf(temp, str, args);
va_end(args);
gtk_label_set_label(GTK_LABEL(widget), strptr(temp));
void Label::set_text(const char *str) {
gtk_label_set_label(GTK_LABEL(widget), str);
}
/*****
@@ -345,13 +340,8 @@ void Button::create(Window &r_owner, uint style, uint x, uint y, uint width, uin
g_signal_connect_swapped(G_OBJECT(widget), "clicked", G_CALLBACK(libui_control_clicked), (gpointer)this);
}
void Button::set_text(const char *str, ...) {
va_list args;
va_start(args, str);
string temp;
vsprintf(temp, str, args);
va_end(args);
gtk_button_set_label(GTK_BUTTON(widget), strptr(temp));
void Button::set_text(const char *str) {
gtk_button_set_label(GTK_BUTTON(widget), str);
}
/*****
@@ -447,17 +437,11 @@ void Editbox::create(Window &r_owner, uint style, uint x, uint y, uint width, ui
gtk_widget_show(widget);
}
void Editbox::set_text(const char *str, ...) {
va_list args;
va_start(args, str);
string temp;
vsprintf(temp, str, args);
va_end(args);
void Editbox::set_text(const char *str) {
if(multiline == false) {
gtk_entry_set_text(GTK_ENTRY(widget), strptr(temp));
gtk_entry_set_text(GTK_ENTRY(widget), str);
} else {
gtk_text_buffer_set_text(buffer, strptr(temp), -1);
gtk_text_buffer_set_text(buffer, str, -1);
}
}
@@ -539,28 +523,16 @@ void Listbox::set_column_width(uint column, uint width) {
gtk_tree_view_column_set_max_width(column_list[column], width);
}
void Listbox::add_item(const char *data, ...) {
va_list args;
va_start(args, data);
string temp;
vsprintf(temp, data, args);
va_end(args);
void Listbox::add_item(const char *data) {
stringarray part;
split(part, "|", temp);
split(part, "|", data);
gtk_list_store_append(store, &iter);
for(uint i = 0; i < count(part); i++) {
gtk_list_store_set(store, &iter, i, strptr(part[i]), -1);
}
}
void Listbox::set_item(uint index, const char *data, ...) {
va_list args;
va_start(args, data);
string temp;
vsprintf(temp, data, args);
va_end(args);
void Listbox::set_item(uint index, const char *data) {
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(subwidget));
for(uint i = 0; i <= index; i++) {
(i == 0) ?
@@ -569,14 +541,14 @@ GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(subwidget));
}
stringarray part;
split(part, "|", temp);
split(part, "|", data);
for(uint i = 0; i < count(part); i++) {
gtk_list_store_set(store, &iter, i, strptr(part[i]), -1);
}
}
int Listbox::get_selection() {
//... because gtk_tree_view_get_selected_row(GTK_TREE_VIEW(subwidget)) would be too easy ...
int Listbox::get_selection() {
GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(subwidget));
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(subwidget));
if(gtk_tree_model_get_iter_first(model, &iter) == false) { return -1; }
@@ -588,18 +560,21 @@ GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(subwidget));
return -1;
}
void Listbox::set_selection(int index) {
//... because gtk_tree_view_set_selected_row(GTK_TREE_VIEW(subwidget), index) would be too easy ...
void Listbox::set_selection(int index) {
int current = get_selection();
GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(subwidget));
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(subwidget));
gtk_tree_selection_unselect_all(selection);
if(index < 0) { return; }
if(gtk_tree_model_get_iter_first(model, &iter) == false) { return; }
if(index == 0) { gtk_tree_selection_select_iter(selection, &iter); return; }
if(index < 0) { goto end; }
if(gtk_tree_model_get_iter_first(model, &iter) == false) { goto end; }
if(index == 0) { gtk_tree_selection_select_iter(selection, &iter); goto end; }
for(uint i = 1; i < 100000; i++) {
if(gtk_tree_model_iter_next(model, &iter) == false) { return; }
if(index == i) { gtk_tree_selection_select_iter(selection, &iter); return; }
if(gtk_tree_model_iter_next(model, &iter) == false) { goto end; }
if(index == i) { gtk_tree_selection_select_iter(selection, &iter); goto end; }
}
end:
if(current != index) { owner->message(Message::Changed, (uintptr_t)this); }
}
void Listbox::reset() {

View File

@@ -147,16 +147,19 @@ class Frame : public Control { public:
};
class Label : public Control { public:
enum { ideal_height = 18 };
void create(Window &owner, uint style, uint x, uint y, uint width, uint height, const char *caption = "");
void set_text(const char *str, ...);
void set_text(const char *str);
};
class Button : public Control { public:
enum { ideal_height = 30 };
void create(Window &owner, uint style, uint x, uint y, uint width, uint height, const char *caption = "");
void set_text(const char *str, ...);
void set_text(const char *str);
};
class Checkbox : public Control { public:
enum { ideal_height = 18 };
void create(Window &owner, uint style, uint x, uint y, uint width, uint height, const char *caption = "");
void check();
void uncheck();
@@ -165,12 +168,14 @@ class Checkbox : public Control { public:
};
class Radiobox : public Control { public:
enum { ideal_height = 18 };
void create(Window &owner, ControlGroup &group, uint style, uint x, uint y, uint width, uint height, const char *caption = "");
void check();
bool checked();
};
class Editbox : public Control { public:
enum { ideal_height = 30 };
enum {
Multiline = (1 << 1),
Readonly = (1 << 2),
@@ -185,7 +190,7 @@ enum {
};
void create(Window &owner, uint style, uint x, uint y, uint width, uint height, const char *caption = "");
void set_text(const char *str, ...);
void set_text(const char *str);
uint get_text(char *str, uint length);
private:
@@ -210,8 +215,8 @@ enum {
void create(Window &owner, uint style, uint x, uint y, uint width, uint height, const char *columns = "");
void autosize_columns();
void set_column_width(uint column, uint width);
void add_item(const char *data, ...);
void set_item(uint index, const char *data, ...);
void add_item(const char *data);
void set_item(uint index, const char *data);
int get_selection();
void set_selection(int index);
void reset();
@@ -226,6 +231,7 @@ GtkTreeIter iter;
};
class Combobox : public Control { public:
enum { ideal_height = 30 };
void create(Window &owner, uint style, uint x, uint y, uint width, uint height);
void add_item(const char *data);
int get_selection();
@@ -237,12 +243,14 @@ uint counter;
};
class Progressbar : public Control { public:
enum { ideal_height = 30 };
void create(Window &owner, uint style, uint x, uint y, uint width, uint height);
void set_progress(uint progress);
uint get_progress();
};
class Slider : public Control { public:
enum { ideal_height = 25 };
enum {
Horizontal = 0,
Vertical = 1,

View File

@@ -6,25 +6,25 @@ gint libui_window_close(GtkWidget *w, GdkEventAny *any, Window *window) {
}
gint libui_window_keydown(GtkWidget *w, GdkEventKey *key, Window *window) {
if(window) { window->message(Message::KeyDown, (void*)libui::translate_key(key->keyval)); }
if(window) { window->message(Message::KeyDown, libui::translate_key(key->keyval)); }
return FALSE;
}
gint libui_window_keyup(GtkWidget *w, GdkEventKey *key, Window *window) {
if(window) { window->message(Message::KeyUp, (void*)libui::translate_key(key->keyval)); }
if(window) { window->message(Message::KeyUp, libui::translate_key(key->keyval)); }
return FALSE;
}
void libui_control_clicked(Control *control) {
if(control && control->owner) { control->owner->message(Message::Clicked, control); }
if(control && control->owner) { control->owner->message(Message::Clicked, (uintptr_t)control); }
}
void libui_control_changed(Control *control) {
if(control && control->owner) { control->owner->message(Message::Changed, control); }
if(control && control->owner) { control->owner->message(Message::Changed, (uintptr_t)control); }
}
void libui_control_double_clicked(Control *control) {
if(control && control->owner) { control->owner->message(Message::DoubleClicked, control); }
if(control && control->owner) { control->owner->message(Message::DoubleClicked, (uintptr_t)control); }
}
void Window::create(uint style, uint width, uint height, const char *caption) {
@@ -56,6 +56,10 @@ void Window::focus() {
gtk_window_present(GTK_WINDOW(info.window));
}
bool Window::focused() {
return GTK_WIDGET_HAS_FOCUS(info.window);
}
void Window::move(uint x, uint y) {
//if window was centered before, GTK+ will ignore move requests,
//therfore we must turn off auto-centering.
@@ -75,13 +79,32 @@ void Window::hide() {
gtk_widget_hide(info.window);
}
void Window::set_text(const char *str, ...) {
va_list args;
va_start(args, str);
string temp;
vsprintf(temp, str, args);
va_end(args);
gtk_window_set_title(GTK_WINDOW(info.window), strptr(temp));
void Window::show(bool state) {
(state == true) ? show() : hide();
}
bool Window::visible() {
return GTK_WIDGET_VISIBLE(info.window);
}
void Window::fullscreen() {
gtk_window_fullscreen(GTK_WINDOW(info.window));
}
void Window::unfullscreen() {
gtk_window_unfullscreen(GTK_WINDOW(info.window));
}
void Window::fullscreen(bool state) {
(state == true) ? fullscreen() : unfullscreen();
}
bool Window::is_fullscreen() {
return false;
};
void Window::set_text(const char *str) {
gtk_window_set_title(GTK_WINDOW(info.window), str);
}
void Window::set_background_color(uint8 r, uint8 g, uint8 b) {

View File

@@ -1,3 +1,5 @@
#define _WIN32_IE 0x0600
#include "libui_win.h"
#include "libui_win_window.cpp"
#include "libui_win_control.cpp"
@@ -81,7 +83,7 @@ uint get_screen_height() { return GetSystemMetrics(SM_CYSCREEN); }
//
bool file_load(Window &owner, char *filename, const char *filter, const char *path) {
bool file_load(Window *owner, char *filename, const char *filter, const char *path) {
string dir, f;
strcpy(dir, path ? path : "");
replace(dir, "/", "\\");
@@ -111,7 +113,7 @@ OPENFILENAME ofn;
strcpy(filename, "");
memset(&ofn, 0, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = owner.info.hwnd;
ofn.hwndOwner = owner ? owner->info.hwnd : 0;
ofn.lpstrFilter = pf;
ofn.lpstrInitialDir = strptr(dir);
ofn.lpstrFile = filename;
@@ -122,7 +124,7 @@ OPENFILENAME ofn;
return GetOpenFileName(&ofn);
}
bool file_save(Window &owner, char *filename, const char *filter, const char *path) {
bool file_save(Window *owner, char *filename, const char *filter, const char *path) {
string dir, f;
strcpy(dir, path ? path : "");
replace(dir, "/", "\\");
@@ -152,7 +154,7 @@ OPENFILENAME ofn;
strcpy(filename, "");
memset(&ofn, 0, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = owner.info.hwnd;
ofn.hwndOwner = owner ? owner->info.hwnd : 0;
ofn.lpstrFilter = pf;
ofn.lpstrInitialDir = strptr(dir);
ofn.lpstrFile = filename;
@@ -170,6 +172,19 @@ uint16 translate_key(uint key) {
case VK_ESCAPE: return keymap::esc;
case VK_F1: return keymap::f1;
case VK_F2: return keymap::f2;
case VK_F3: return keymap::f3;
case VK_F4: return keymap::f4;
case VK_F5: return keymap::f5;
case VK_F6: return keymap::f6;
case VK_F7: return keymap::f7;
case VK_F8: return keymap::f8;
case VK_F9: return keymap::f9;
case VK_F10: return keymap::f10;
case VK_F11: return keymap::f11;
case VK_F12: return keymap::f12;
case VK_TAB: return keymap::tab;
case VK_RETURN: return keymap::enter;
case VK_SPACE: return keymap::space;

View File

@@ -1,5 +1,5 @@
/*
libui_win ~byuu (2007-05-28)
libui_win ~byuu (2007-06-10)
license: public domain
*/
@@ -32,8 +32,8 @@ bool events_pending();
uint get_screen_width();
uint get_screen_height();
bool file_load(Window &owner, char *filename, const char *filter, const char *path = "");
bool file_save(Window &owner, char *filename, const char *filter, const char *path = "");
bool file_load(Window *owner, char *filename, const char *filter, const char *path = "");
bool file_save(Window *owner, char *filename, const char *filter, const char *path = "");
uint16 translate_key(uint key);
@@ -60,12 +60,19 @@ MenuBar menu;
void set_text(const char *str, ...);
void set_background_color(uint8 r, uint8 g, uint8 b);
void focus();
bool focused();
void move(uint x, uint y);
void resize(uint width, uint height);
virtual void show();
virtual void hide();
void show(bool state);
bool visible();
void fullscreen();
void unfullscreen();
void fullscreen(bool state);
bool is_fullscreen();
virtual int message(uint id, void *param = 0) { return 0; }
virtual bool message(uint id, uintptr_t param = 0) { return true; }
void move(Control &control, uint x, uint y);
void attach(Control &control);
@@ -78,6 +85,7 @@ MenuBar menu;
//private:
struct {
uint width, height;
bool fullscreen;
HWND hwnd;
HWND hwnd_resize;

View File

@@ -4,6 +4,10 @@ namespace libui {
* Control
*****/
void Control::move(uint x, uint y) {
SetWindowPos(hwnd, 0, x, y, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
}
void Control::resize(uint width, uint height) {
SetWindowPos(hwnd, 0, 0, 0, width, height, SWP_NOZORDER | SWP_NOMOVE);
}
@@ -137,13 +141,13 @@ void MenuCheckItem::create(MenuGroup &r_owner, const char *caption) {
void MenuCheckItem::check() {
if(checked() == true)return;
CheckMenuItem(parent, id, MF_CHECKED);
owner->message(Message::Clicked, this);
owner->message(Message::Clicked, (uintptr_t)this);
}
void MenuCheckItem::uncheck() {
if(checked() == false)return;
CheckMenuItem(parent, id, MF_UNCHECKED);
owner->message(Message::Clicked, this);
owner->message(Message::Clicked, (uintptr_t)this);
}
void MenuCheckItem::check(bool state) {
@@ -180,7 +184,7 @@ void MenuRadioItem::check() {
for(uint i = 0; i < group.count(); i++) {
CheckMenuItem(parent, group[i].id, (id == group[i].id) ? MF_CHECKED : MF_UNCHECKED);
}
owner->message(Message::Clicked, this);
owner->message(Message::Clicked, (uintptr_t)this);
}
bool MenuRadioItem::checked() {
@@ -355,11 +359,20 @@ long __stdcall label_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
BeginPaint(hwnd, &ps);
RECT rc;
char t[4096];
GetWindowText(hwnd, t, 4095);
GetWindowText(hwnd, t, 4095); //TODO: use internal buffer, so length is not limited ...
GetClientRect(hwnd, &rc);
SetTextColor(ps.hdc, RGB(0, 0, 0));
SetBkMode(ps.hdc, TRANSPARENT);
SelectObject(ps.hdc, (HGDIOBJ)libui::font.variable);
//center text if text height < control height, otherwise draw from top left corner
RECT trc;
GetClientRect(hwnd, &trc);
DrawText(ps.hdc, t, strlen(t), &trc, DT_CALCRECT);
if(trc.bottom < rc.bottom) {
rc.top = (rc.bottom - trc.bottom) / 2;
rc.bottom = rc.top + trc.bottom;
}
//
DrawText(ps.hdc, t, strlen(t), &rc, DT_END_ELLIPSIS | DT_NOPREFIX);
EndPaint(hwnd, &ps);
} break;
@@ -419,13 +432,13 @@ void Checkbox::create(Window &r_owner, uint style, uint x, uint y, uint width, u
void Checkbox::check() {
if(checked() == true)return;
SendMessage(hwnd, BM_SETCHECK, (WPARAM)TRUE, 0);
owner->message(Message::Clicked, this);
owner->message(Message::Clicked, (uintptr_t)this);
}
void Checkbox::uncheck() {
if(checked() == false)return;
SendMessage(hwnd, BM_SETCHECK, (WPARAM)FALSE, 0);
owner->message(Message::Clicked, this);
owner->message(Message::Clicked, (uintptr_t)this);
}
void Checkbox::check(bool state) {
@@ -456,7 +469,7 @@ void Radiobox::check() {
for(uint i = 0; i < group.count(); i++) {
SendMessage(group[i].hwnd, BM_SETCHECK, (group[i].hwnd == hwnd) ? (WPARAM)TRUE : (WPARAM)FALSE, 0);
}
owner->message(Message::Clicked, this);
owner->message(Message::Clicked, (uintptr_t)this);
}
bool Radiobox::checked() {

View File

@@ -25,6 +25,7 @@ namespace ControlType {
class Control { public:
uint type;
void move(uint x, uint y);
void resize(uint width, uint height);
void focus();
@@ -156,15 +157,18 @@ class Frame : public Control { public:
};
class Label : public Control { public:
enum { ideal_height = 16 };
void create(Window &owner, uint style, uint x, uint y, uint width, uint height, const char *caption = "");
void set_text(const char *str, ...);
};
class Button : public Control { public:
enum { ideal_height = 21 };
void create(Window &owner, uint style, uint x, uint y, uint width, uint height, const char *caption = "");
};
class Checkbox : public Control { public:
enum { ideal_height = 15 };
void create(Window &owner, uint style, uint x, uint y, uint width, uint height, const char *caption = "");
void check();
void uncheck();
@@ -173,6 +177,7 @@ class Checkbox : public Control { public:
};
class Radiobox : public Control { public:
enum { ideal_height = 15 };
void create(Window &owner, ControlGroup &list, uint style, uint x, uint y, uint width, uint height, const char *caption = "");
void check();
bool checked();
@@ -182,6 +187,7 @@ ControlGroup group;
};
class Editbox : public Control { public:
enum { ideal_height = 21 };
enum {
Multiline = (1 << 1),
Readonly = (1 << 2),
@@ -234,6 +240,7 @@ uint column_count;
};
class Combobox : public Control { public:
enum { ideal_height = 21 };
void create(Window &owner, uint style, uint x, uint y, uint width, uint height);
void add_item(const char *data);
void set_selection(int index);
@@ -242,12 +249,14 @@ class Combobox : public Control { public:
};
class Progressbar : public Control { public:
enum { ideal_height = 30 };
void create(Window &owner, uint style, uint x, uint y, uint width, uint height);
void set_progress(uint progress);
uint get_progress();
};
class Slider : public Control { public:
enum { ideal_height = 25 };
enum Style {
Horizontal = 0,
Vertical = 1,

View File

@@ -33,11 +33,11 @@ long Window::wndproc(HWND hwnd, uint msg, WPARAM wparam, LPARAM lparam) {
} break;
case WM_KEYDOWN: {
message(Message::KeyDown, (void*)libui::translate_key(wparam));
message(Message::KeyDown, libui::translate_key(wparam));
} break;
case WM_KEYUP: {
message(Message::KeyUp, (void*)libui::translate_key(wparam));
message(Message::KeyUp, libui::translate_key(wparam));
} break;
case WM_COMMAND: {
@@ -68,7 +68,7 @@ long Window::wndproc(HWND hwnd, uint msg, WPARAM wparam, LPARAM lparam) {
//emit Message::Clicked message directly
case ControlType::MenuItem:
case ControlType::Button: {
message(Message::Clicked, &control);
message(Message::Clicked, (uintptr_t)&control);
} break;
}
@@ -84,7 +84,7 @@ long Window::wndproc(HWND hwnd, uint msg, WPARAM wparam, LPARAM lparam) {
switch(control.type) {
case ControlType::Slider: {
message(Message::Changed, &control);
message(Message::Changed, (uintptr_t)&control);
} break;
}
@@ -104,12 +104,12 @@ long Window::wndproc(HWND hwnd, uint msg, WPARAM wparam, LPARAM lparam) {
if(((LPNMLISTVIEW)lparam)->uChanged & LVIF_STATE) {
if(ListView_GetItemState(listbox.hwnd, ((LPNMLISTVIEW)lparam)->iItem, LVIS_FOCUSED)) {
if(ListView_GetItemState(listbox.hwnd, ((LPNMLISTVIEW)lparam)->iItem, LVIS_SELECTED)) {
message(Message::Changed, &control);
message(Message::Changed, (uintptr_t)&control);
}
}
}
} else if(((LPNMHDR)lparam)->code == LVN_ITEMACTIVATE) {
message(Message::DoubleClicked, &control);
message(Message::DoubleClicked, (uintptr_t)&control);
}
} break;
@@ -131,10 +131,14 @@ void Window::attach(Control &control) {
}
void Window::focus() {
show();
if(!visible()) { show(); }
SetFocus(info.hwnd);
}
bool Window::focused() {
return (GetForegroundWindow() == info.hwnd);
}
void Window::move(Control &control, uint x, uint y) {
SetWindowPos(control.hwnd, 0, x, y, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
}
@@ -180,6 +184,13 @@ RECT rc;
}
void Window::resize(uint width, uint height) {
if(info.fullscreen == true) {
info.width = GetSystemMetrics(SM_CXSCREEN);
info.height = GetSystemMetrics(SM_CYSCREEN);
SetWindowPos(info.hwnd, 0, 0, 0, info.width, info.height, SWP_NOZORDER);
return;
}
info.width = width;
info.height = height;
@@ -212,6 +223,36 @@ void Window::hide() {
ShowWindow(info.hwnd, SW_HIDE);
}
void Window::show(bool state) {
(state == true) ? show() : hide();
}
bool Window::visible() {
return GetWindowLong(info.hwnd, GWL_STYLE) & WS_VISIBLE;
}
void Window::fullscreen() {
if(info.fullscreen)return;
info.fullscreen = true;
SetWindowLong(info.hwnd, GWL_STYLE, WS_VISIBLE | WS_POPUP);
resize(get_screen_width(), get_screen_height());
}
void Window::unfullscreen() {
if(!info.fullscreen)return;
info.fullscreen = false;
SetWindowLong(info.hwnd, GWL_STYLE, WS_POPUP | WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX | WS_VISIBLE);
resize(info.width, info.height);
}
void Window::fullscreen(bool state) {
(state == true) ? fullscreen() : unfullscreen();
}
bool Window::is_fullscreen() {
return info.fullscreen;
}
//
void Window::set_text(const char *str, ...) {
@@ -234,6 +275,7 @@ HBRUSH old_brush = info.background;
//
Window::Window() {
info.fullscreen = false;
info.background = 0;
info.center = false;
info.control_index = 1;

View File

@@ -64,10 +64,8 @@
* and speed is less critical.
*****/
#ifndef __LIBVECTOR
#define __LIBVECTOR
#include <new>
#ifndef LIBVECTOR_H
#define LIBVECTOR_H
template<typename T> class linear_vector {
protected:

View File

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

View File

@@ -41,6 +41,8 @@ enum { TYPE_WRAM, TYPE_MMIO, TYPE_CART };
void write_dsp2 (uint32 addr, uint8 data);
uint8 read_obc1 (uint32 addr);
void write_obc1 (uint32 addr, uint8 data);
uint8 read_st010 (uint32 addr);
void write_st010 (uint32 addr, uint8 data);
void cart_map_reset();
void cart_map_system();
@@ -51,6 +53,7 @@ enum { TYPE_WRAM, TYPE_MMIO, TYPE_CART };
void cart_map_dsp1();
void cart_map_dsp2();
void cart_map_obc1();
void cart_map_st010();
void power();
void reset();

View File

@@ -178,3 +178,15 @@ void bMemBus::cart_map_obc1() {
}
}
}
void bMemBus::cart_map_st010() {
//$[68-6f|e8-ef]:[0000-0fff]
for(uint bank = 0x68; bank <= 0x6f; bank++) {
for(uint page = 0x00; page <= 0x0f; page++) {
page_read [0x0000 + (bank << 8) + page] = &bMemBus::read_st010;
page_read [0x8000 + (bank << 8) + page] = &bMemBus::read_st010;
page_write[0x0000 + (bank << 8) + page] = &bMemBus::write_st010;
page_write[0x8000 + (bank << 8) + page] = &bMemBus::write_st010;
}
}
}

View File

@@ -62,3 +62,6 @@ void bMemBus::write_dsp2(uint32 addr, uint8 data) { dsp2->write(addr, data); }
uint8 bMemBus::read_obc1 (uint32 addr) { return obc1->read(addr); }
void bMemBus::write_obc1(uint32 addr, uint8 data) { obc1->write(addr, data); }
uint8 bMemBus::read_st010 (uint32 addr) { return st010->read(addr); }
void bMemBus::write_st010(uint32 addr, uint8 data) { st010->write(addr, data); }

View File

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

View File

@@ -8,7 +8,7 @@ uint16 bPPU::get_vram_address() {
uint16 addr;
addr = regs.vram_addr;
switch(regs.vram_mapping) {
case 0: break;
case 0: break; //direct
case 1: addr = (addr & 0xff00) | ((addr & 0x001f) << 3) | ((addr >> 5) & 7); break;
case 2: addr = (addr & 0xfe00) | ((addr & 0x003f) << 3) | ((addr >> 6) & 7); break;
case 3: addr = (addr & 0xfc00) | ((addr & 0x007f) << 3) | ((addr >> 7) & 7); break;
@@ -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 0x01ff. There is nothing special
//about this address, it is simply more accurate to invalidate the 'expected' address than not.
uint8 bPPU::cgram_mmio_read(uint16 addr) {
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 >= 72 && hc < 1096) {
return cgram_read(0x01ff);
}
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 >= 72 && hc < 1096) {
return cgram_write(0x01ff, 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;

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) \

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

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

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

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

View File

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

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

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;

View File

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

View File

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

View File

@@ -1,598 +0,0 @@
/* snes_ntsc 0.2.1. http://www.slack.net/~ant/ */
/* compilable in C or C++; just change the file extension */
//#include "snes_ntsc.h"
#include <assert.h>
#include <math.h>
/* Based on algorithm by NewRisingSun */
/* Copyright (C) 2006 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
more details. You should have received a copy of the GNU Lesser General
Public License along with this module; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
enum { disable_correction = 0 }; /* for debugging */
/* macro constants are used instead of enum in some places to work around compiler bug */
/* half normal range to allow for doubled hires pixels */
#define rgb_unit 0x80
/* begin mostly common NES/SNES/SMS code */
snes_ntsc_setup_t const snes_ntsc_monochrome = { 0,-1, 0, 0,.2, 0,.2,-.2,-.2,-1, 0, 1, 0, 0 };
snes_ntsc_setup_t const snes_ntsc_composite = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 };
snes_ntsc_setup_t const snes_ntsc_svideo = { 0, 0, 0, 0,.2, 0,.2, -1, -1, 0, 0, 1, 0, 0 };
snes_ntsc_setup_t const snes_ntsc_rgb = { 0, 0, 0, 0,.2, 0,.7, -1, -1,-1, 0, 1, 0, 0 };
enum { alignment_count = 3 }; /* different pixel alignments with respect to yiq quads */
enum { kernel_half = 16 };
enum { kernel_size = kernel_half * 2 + 1 };
#define rescale_in 8
enum { rescale_out = 7 };
struct ntsc_impl_t
{
float to_rgb [snes_ntsc_burst_count] [6];
float brightness;
float contrast;
float artifacts;
float fringing;
float hue_warping;
float kernel [rescale_out * kernel_size * 2];
};
#define PI 3.14159265358979323846f
static void init_ntsc_impl( struct ntsc_impl_t* impl, snes_ntsc_setup_t const* setup )
{
float kernels [kernel_size * 2];
impl->brightness = (float) setup->brightness * (0.4f * rgb_unit);
impl->contrast = (float) setup->contrast * 0.4f + 1.0f;
impl->hue_warping = (float) setup->hue_warping;
impl->artifacts = (float) setup->artifacts;
if ( impl->artifacts > 0 )
impl->artifacts *= 0.5f;
impl->artifacts += 1.0f;
impl->fringing = (float) setup->fringing;
if ( impl->fringing > 0 )
impl->fringing *= 0.5f;
impl->fringing += 1.0f;
/* generate luma (y) filter using sinc kernel */
{
/* sinc with rolloff (dsf) */
/* double precision avoids instability */
double const rolloff = 1 + setup->sharpness * 0.004;
double const maxh = 256;
double const pow_a_n = pow( rolloff, maxh );
float sum;
int i;
/* quadratic mapping to reduce negative (blurring) range */
double to_angle = setup->resolution + 1;
to_angle = PI / maxh * 0.20 * (to_angle * to_angle + 1);
kernels [kernel_size * 3 / 2] = (float) maxh;
for ( i = 0; i < kernel_half * 2 + 1; i++ )
{
int x = i - kernel_half;
double angle = x * to_angle;
/* instability occurs at center point with rolloff very close to 1.0 */
if ( x || pow_a_n > 1.01 || pow_a_n < 0.99 )
{
double rolloff_cos_a = rolloff * cos( angle );
double num = 1 - rolloff_cos_a -
pow_a_n * cos( maxh * angle ) +
pow_a_n * rolloff * cos( (maxh - 1) * angle );
double den = 1 - rolloff_cos_a - rolloff_cos_a + rolloff * rolloff;
double dsf = num / den;
kernels [kernel_size * 3 / 2 - kernel_half + i] = (float) dsf;
}
}
/* apply blackman window and find sum */
sum = 0;
for ( i = 0; i < kernel_half * 2 + 1; i++ )
{
float x = PI * 2 / (kernel_half * 2) * i;
float blackman = 0.42f - 0.5f * (float) cos( x ) + 0.08f * (float) cos( x * 2 );
sum += (kernels [kernel_size * 3 / 2 - kernel_half + i] *= blackman);
}
/* normalize kernel */
sum = 1.0f / sum;
for ( i = 0; i < kernel_half * 2 + 1; i++ )
{
int x = kernel_size * 3 / 2 - kernel_half + i;
kernels [x] *= sum;
assert( kernels [x] == kernels [x] ); /* catch numerical instability */
}
}
/* generate chroma (iq) filter using gaussian kernel */
{
float const cutoff_factor = -0.03125f;
float cutoff = (float) setup->bleed;
int i;
if ( cutoff < 0 )
{
/* keep extreme value accessible only near upper end of scale (1.0) */
cutoff *= cutoff;
cutoff *= cutoff;
cutoff *= cutoff;
cutoff *= -30.0f / 0.65f;
}
cutoff = cutoff_factor - 0.65f * cutoff_factor * cutoff;
for ( i = -kernel_half; i <= kernel_half; i++ )
kernels [kernel_size / 2 + i] = (float) exp( i * i * cutoff );
/* normalize even and odd phases separately */
for ( i = 0; i < 2; i++ )
{
float sum = 0;
int x;
for ( x = i; x < kernel_size; x += 2 )
sum += kernels [x];
sum = 1.0f / sum;
for ( x = i; x < kernel_size; x += 2 )
{
kernels [x] *= sum;
assert( kernels [x] == kernels [x] ); /* catch numerical instability */
}
}
}
/* generate linear rescale kernels */
{
int i;
for ( i = 0; i < rescale_out; i++ )
{
float* out = &impl->kernel [i * kernel_size * 2];
float second = 1.0f / rescale_in * (i + 1);
float first = 1.0f - second;
int x;
*out++ = kernels [0] * first;
for ( x = 1; x < kernel_size * 2; x++ )
*out++ = kernels [x] * first + kernels [x - 1] * second;
}
}
/* setup decoder matricies */
{
static float const default_decoder [6] =
{ 0.956f, 0.621f, -0.272f, -0.647f, -1.105f, 1.702f };
float hue = (float) setup->hue * PI;
float sat = (float) setup->saturation + 1.0f;
float const* decoder = setup->decoder_matrix;
int i;
if ( !decoder )
decoder = default_decoder;
else
hue += PI / 180 * 15;
for ( i = 0; i < snes_ntsc_burst_count; i++ )
{
float s = (float) sin( hue ) * sat;
float c = (float) cos( hue ) * sat;
float const* in = decoder;
float* out = impl->to_rgb [i];
int n;
for ( n = 3; n; --n )
{
float i = *in++;
float q = *in++;
*out++ = i * c - q * s;
*out++ = i * s + q * c;
}
hue -= PI / 180 * 120;
}
}
}
/* kernel generation */
enum { rgb_kernel_size = snes_ntsc_burst_size / alignment_count };
static float const rgb_offset = rgb_unit * 2 + 0.5f;
static ntsc_rgb_t const ntsc_rgb_bias = rgb_unit * 2 * ntsc_rgb_builder;
#define TO_RGB( y, i, q, to_rgb ) ( \
((int) (y + to_rgb [0] * i + to_rgb [1] * q) << 21) +\
((int) (y + to_rgb [2] * i + to_rgb [3] * q) << 11) +\
((int) (y + to_rgb [4] * i + to_rgb [5] * q) << 1)\
)
typedef struct pixel_info_t
{
int offset;
float negate;
float kernel [4];
} pixel_info_t;
#define PIXEL_OFFSET_( ntsc, scaled ) \
(kernel_size / 2 + ntsc + (scaled != 0) + (rescale_out - scaled) % rescale_out + \
(kernel_size * 2 * scaled))
#define PIXEL_OFFSET( ntsc, scaled ) \
PIXEL_OFFSET_( ((ntsc) - (scaled) / rescale_out * rescale_in),\
(((scaled) + rescale_out * 10) % rescale_out) ),\
(1.0f - (((ntsc) + 100) & 2))
/* Generate pixel at all burst phases and column alignments */
static void gen_kernel( struct ntsc_impl_t* impl, float y, float i, float q, ntsc_rgb_t* out )
{
/* generate for each scanline burst phase */
float const* to_rgb = impl->to_rgb [0];
do
{
static pixel_info_t const pixels [alignment_count] = {
{ PIXEL_OFFSET( -4, -9 ), { 1.0000f, 1.0000f, .6667f, .0000f } },
{ PIXEL_OFFSET( -2, -7 ), { .3333f, 1.0000f, 1.0000f, .3333f } },
{ PIXEL_OFFSET( 0, -5 ), { .0000f, .6667f, 1.0000f, 1.0000f } },
};
/* Encode yiq into *two* composite signals (to allow control over artifacting).
Convolve these with kernels which: filter respective components, apply
sharpening, and rescale horizontally. Convert resulting yiq to rgb and pack
into integer. */
pixel_info_t const* pixel = pixels;
do
{
/* negate is -1 when composite starts at odd multiple of 2 */
float const yy = y * impl->fringing * pixel->negate;
float const ic0 = (i + yy) * pixel->kernel [0];
float const qc1 = (q + yy) * pixel->kernel [1];
float const ic2 = (i - yy) * pixel->kernel [2];
float const qc3 = (q - yy) * pixel->kernel [3];
float const factor = impl->artifacts * pixel->negate;
float const ii = i * factor;
float const yc0 = (y + ii) * pixel->kernel [0];
float const yc2 = (y - ii) * pixel->kernel [2];
float const qq = q * factor;
float const yc1 = (y + qq) * pixel->kernel [1];
float const yc3 = (y - qq) * pixel->kernel [3];
float const* k = &impl->kernel [pixel->offset];
int n;
for ( n = rgb_kernel_size; n; --n )
{
float i = k[0]*ic0 + k[2]*ic2;
float q = k[1]*qc1 + k[3]*qc3;
float y = k[kernel_size+0]*yc0 + k[kernel_size+1]*yc1 +
k[kernel_size+2]*yc2 + k[kernel_size+3]*yc3 + rgb_offset;
if ( k >= &impl->kernel [kernel_size * 2 * (rescale_out - 1)] )
k -= kernel_size * 2 * (rescale_out - 1) + 2;
else
k += kernel_size * 2 - 1;
*out++ = TO_RGB( y, i, q, to_rgb ) - ntsc_rgb_bias;
}
}
while ( pixel++ < &pixels [alignment_count - 1] );
to_rgb += 6;
/* rotate -120 degrees */
{
float const sin_b = -0.866025f;
float const cos_b = -0.5f;
float t;
t = i * cos_b - q * sin_b;
q = i * sin_b + q * cos_b;
i = t;
}
}
while ( to_rgb < impl->to_rgb [snes_ntsc_burst_count] );
}
static void merge_fields( ntsc_rgb_t* io )
{
int n;
for ( n = snes_ntsc_burst_size; n; --n )
{
ntsc_rgb_t p0 = io [snes_ntsc_burst_size * 0] + ntsc_rgb_bias;
ntsc_rgb_t p1 = io [snes_ntsc_burst_size * 1] + ntsc_rgb_bias;
ntsc_rgb_t p2 = io [snes_ntsc_burst_size * 2] + ntsc_rgb_bias;
/* merge fields without losing precision */
io [snes_ntsc_burst_size * 0] =
((p0 + p1 - ((p0 ^ p1) & ntsc_rgb_builder)) >> 1) - ntsc_rgb_bias;
io [snes_ntsc_burst_size * 1] =
((p1 + p2 - ((p1 ^ p2) & ntsc_rgb_builder)) >> 1) - ntsc_rgb_bias;
io [snes_ntsc_burst_size * 2] =
((p2 + p0 - ((p2 ^ p0) & ntsc_rgb_builder)) >> 1) - ntsc_rgb_bias;
++io;
}
}
static void correct_errors( ntsc_rgb_t color, ntsc_rgb_t* out )
{
int burst;
for ( burst = 0; burst < snes_ntsc_burst_count; burst++ )
{
int i;
for ( i = 0; i < rgb_kernel_size / 2; i++ )
{
ntsc_rgb_t error = color -
out [i ] -
out [i + 3 +28] -
out [i + 5 +14] -
out [i + 7 ] -
out [(i+10)%14+28] -
out [(i+12)%14+14];
/* distribute error among four kernels */
ntsc_rgb_t fourth = (error + 2 * ntsc_rgb_builder) >> 2;
fourth &= (ntsc_rgb_bias >> 1) - ntsc_rgb_builder;
fourth -= ntsc_rgb_bias >> 2;
if ( disable_correction ) { out [i] += ntsc_rgb_bias; continue; }
out [i + 3 +28] += fourth;
out [i + 5 +14] += fourth;
out [i + 7 ] += fourth;
out [i ] += error - (fourth * 3);
}
out += alignment_count * rgb_kernel_size;
}
}
/* end common code */
void snes_ntsc_init( snes_ntsc_t* ntsc, snes_ntsc_setup_t const* setup )
{
float to_float [32];
int entry;
struct ntsc_impl_t impl;
if ( !setup )
setup = &snes_ntsc_composite;
init_ntsc_impl( &impl, setup );
{
double gamma = 1 - setup->gamma * (setup->gamma > 0 ? 0.5f : 1.5f);
int i;
for ( i = 0; i < 32; i++ )
to_float [i] = (float) pow( (1 / 31.0) * i, gamma ) * rgb_unit;
}
for ( entry = 0; entry < snes_ntsc_color_count; entry++ )
{
/* Reduce number of significant bits of source color. Clearing the
low bits of R and B were least notictable. Modifying green was too
noticeable. */
int ir = entry >> 8 & 0x1E;
int ig = entry >> 4 & 0x1F;
int ib = entry << 1 & 0x1E;
if ( setup->bsnes_colortbl )
{
int bgr15 = (ib << 10) | (ig << 5) | ir;
unsigned long rgb16 = setup->bsnes_colortbl [bgr15];
ir = rgb16 >> 11 & 0x1E;
ig = rgb16 >> 6 & 0x1F;
ib = rgb16 & 0x1E;
}
{
float r = to_float [ir];
float g = to_float [ig];
float b = to_float [ib];
float y = r * 0.299f + g * 0.587f + b * 0.114f;
float i = r * 0.596f - g * 0.275f - b * 0.321f;
float q = r * 0.212f - g * 0.523f + b * 0.311f;
float iq = i * q;
if ( impl.hue_warping && q && iq <= 0 )
{
float factor = (iq * impl.hue_warping) / (i * i + q * q);
i -= i * factor;
q += q * factor;
}
y = y * impl.contrast + impl.brightness;
{
float yy = y + rgb_offset;
ntsc_rgb_t rgb = TO_RGB( yy, i, q, impl.to_rgb [0] );
ntsc_rgb_t* out = ntsc->table [entry];
gen_kernel( &impl, y, i, q, out );
if ( setup->merge_fields )
merge_fields( out );
correct_errors( rgb, out );
}
}
}
}
/* Disable 'restrict' keyword by default. If your compiler supports it, put
#define restrict restrict
somewhere in a config header, or the equivalent in the command-line:
-Drestrict=restrict
If your compiler supports a non-standard version, like __restrict, do this:
#define restrict __restrict
Enabling this if your compiler supports it will allow better optimization. */
#ifndef restrict
#define restrict
#endif
/* Default to 16-bit RGB input and output */
#ifndef SNES_NTSC_IN_FORMAT
#define SNES_NTSC_IN_FORMAT SNES_NTSC_RGB16
#endif
#ifndef SNES_NTSC_OUT_DEPTH
#define SNES_NTSC_OUT_DEPTH 16
#endif
#include <limits.h>
#if SNES_NTSC_OUT_DEPTH > 16
#if UINT_MAX == 0xFFFFFFFF
typedef unsigned int snes_ntsc_out_t;
#elif ULONG_MAX == 0xFFFFFFFF
typedef unsigned long snes_ntsc_out_t;
#else
#error "Need 32-bit int type"
#endif
#else
#if USHRT_MAX == 0xFFFF
typedef unsigned short snes_ntsc_out_t;
#else
#error "Need 16-bit int type"
#endif
#endif
/* useful if you have a linker which doesn't remove unused code from executable */
#ifndef SNES_NTSC_NO_BLITTERS
/* Use this as a starting point for writing your own blitter. To allow easy upgrades
to new versions of this library, put your blitter in a separate source file rather
than modifying this one directly. */
void snes_ntsc_blit( snes_ntsc_t const* ntsc, unsigned short const* snes_in,
long in_row_width, int burst_phase, int in_width, int in_height,
void* rgb_out, long out_pitch )
{
int chunk_count = (in_width - 1) / snes_ntsc_in_chunk;
for ( ; in_height; --in_height )
{
/* begin row and read first input pixel */
unsigned short const* line_in = snes_in;
SNES_NTSC_LORES_ROW( ntsc, burst_phase,
snes_ntsc_black, snes_ntsc_black, *line_in++ );
snes_ntsc_out_t* restrict line_out = (snes_ntsc_out_t*) rgb_out;
int n;
/* blit main chunks, each using 3 input pixels to generate 7 output pixels */
for ( n = chunk_count; n; --n )
{
/* order of input and output pixels must not be altered */
SNES_NTSC_PIXEL_IN( 0, line_in [0] );
SNES_NTSC_LORES_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_LORES_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_PIXEL_IN( 1, line_in [1] );
SNES_NTSC_LORES_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_LORES_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_PIXEL_IN( 2, line_in [2] );
SNES_NTSC_LORES_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_LORES_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_LORES_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH );
line_in += 3;
line_out += 7;
}
/* you can eliminate the need for the final chunk below by padding
input with three extra black pixels at the end of each row */
/* finish final pixels without starting any new ones */
SNES_NTSC_PIXEL_IN( 0, snes_ntsc_black );
SNES_NTSC_LORES_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_LORES_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_PIXEL_IN( 1, snes_ntsc_black );
SNES_NTSC_LORES_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_LORES_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_PIXEL_IN( 2, snes_ntsc_black );
SNES_NTSC_LORES_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_LORES_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_LORES_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH );
/* advance burst phase and line pointers */
burst_phase = (burst_phase + 1) % snes_ntsc_burst_count;
snes_in += in_row_width;
rgb_out = (char*) rgb_out + out_pitch;
}
}
void snes_ntsc_blit_hires( snes_ntsc_t const* ntsc, unsigned short const* snes_in,
long in_row_width, int burst_phase, int in_width, int in_height,
void* rgb_out, long out_pitch )
{
int chunk_count = (in_width - 2) / (snes_ntsc_in_chunk * 2);
for ( ; in_height; --in_height )
{
unsigned short const* line_in = snes_in;
SNES_NTSC_HIRES_ROW( ntsc, burst_phase,
snes_ntsc_black, snes_ntsc_black, snes_ntsc_black, line_in [0], line_in [1] );
snes_ntsc_out_t* restrict line_out = (snes_ntsc_out_t*) rgb_out;
int n;
line_in += 2;
for ( n = chunk_count; n; --n )
{
SNES_NTSC_PIXEL_IN( 0, line_in [0] );
SNES_NTSC_HIRES_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_PIXEL_IN( 1, line_in [1] );
SNES_NTSC_HIRES_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_PIXEL_IN( 2, line_in [2] );
SNES_NTSC_HIRES_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_PIXEL_IN( 3, line_in [3] );
SNES_NTSC_HIRES_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_PIXEL_IN( 4, line_in [4] );
SNES_NTSC_HIRES_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_PIXEL_IN( 5, line_in [5] );
SNES_NTSC_HIRES_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_HIRES_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH );
line_in += 6;
line_out += 7;
}
SNES_NTSC_PIXEL_IN( 0, snes_ntsc_black );
SNES_NTSC_HIRES_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_PIXEL_IN( 1, snes_ntsc_black );
SNES_NTSC_HIRES_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_PIXEL_IN( 2, snes_ntsc_black );
SNES_NTSC_HIRES_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_PIXEL_IN( 3, snes_ntsc_black );
SNES_NTSC_HIRES_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_PIXEL_IN( 4, snes_ntsc_black );
SNES_NTSC_HIRES_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_PIXEL_IN( 5, snes_ntsc_black );
SNES_NTSC_HIRES_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_HIRES_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH );
burst_phase = (burst_phase + 1) % snes_ntsc_burst_count;
snes_in += in_row_width;
rgb_out = (char*) rgb_out + out_pitch;
}
}
#endif

View File

@@ -1,192 +0,0 @@
/* SNES NTSC composite video to RGB emulator/blitter */
/* snes_ntsc 0.2.1 */
#ifndef SNES_NTSC_H
#define SNES_NTSC_H
/* Image parameters, ranging from -1.0 to 1.0 */
typedef struct snes_ntsc_setup_t
{
/* Basic parameters */
double hue; /* -1 = -180 degrees, +1 = +180 degrees */
double saturation; /* -1 = grayscale, +1 = oversaturated colors */
double contrast;
double brightness;
double sharpness; /* edge contrast enhancement/blurring */
/* Advanced parameters */
double gamma;
double resolution; /* image resolution */
double artifacts; /* artifacts caused by color changes */
double fringing; /* color artifacts caused by brightness changes */
double bleed; /* color bleed (color resolution reduction) */
double hue_warping;/* -1 = expand purple & green, +1 = expand orange & cyan */
int merge_fields; /* if 1, merges even and odd fields together to reduce flicker */
float const* decoder_matrix; /* optional RGB decoder matrix, 6 elements */
unsigned long const* bsnes_colortbl; /* temporary feature for bsnes only; set to 0 */
} snes_ntsc_setup_t;
/* Video format presets */
extern snes_ntsc_setup_t const snes_ntsc_composite; /* color bleeding + artifacts */
extern snes_ntsc_setup_t const snes_ntsc_svideo; /* color bleeding only */
extern snes_ntsc_setup_t const snes_ntsc_rgb; /* crisp image */
extern snes_ntsc_setup_t const snes_ntsc_monochrome;/* desaturated + artifacts */
/* Initialize and adjust parameters. Can be called multiple times on the same
snes_ntsc_t object. Caller must allocate memory for snes_ntsc_t. Can pass 0
for either parameter. */
typedef struct snes_ntsc_t snes_ntsc_t;
void snes_ntsc_init( snes_ntsc_t*, snes_ntsc_setup_t const* setup );
/* Blit one or more rows of pixels. Input pixel format is set by SNES_NTSC_IN_FORMAT
and output RGB depth is set by NES_NTSC_OUT_DEPTH. Both default to 16-bit RGB.
In_row_width is the number of pixels to get to the next input row. Out_pitch
is the number of *bytes* to get to the next output row. */
void snes_ntsc_blit( snes_ntsc_t const*, unsigned short const* snes_in,
long in_row_width, int burst_phase, int in_width, int in_height,
void* rgb_out, long out_pitch );
void snes_ntsc_blit_hires( snes_ntsc_t const*, unsigned short const* snes_in,
long in_row_width, int burst_phase, int in_width, int in_height,
void* rgb_out, long out_pitch );
/* Number of output pixels written by low-res blitter for given input width. Width
might be rounded down slightly; use SNES_NTSC_IN_WIDTH() on result to find rounded
value. Guaranteed not to round 256 down at all. */
#define SNES_NTSC_OUT_WIDTH( in_width ) \
(((in_width) - 1) / snes_ntsc_in_chunk * snes_ntsc_out_chunk + snes_ntsc_out_chunk)
/* Number of low-res input pixels that will fit within given output width. Might be
rounded down slightly; use SNES_NTSC_OUT_WIDTH() on result to find rounded
value. */
#define SNES_NTSC_IN_WIDTH( out_width ) \
((out_width) / snes_ntsc_out_chunk * snes_ntsc_in_chunk - snes_ntsc_in_chunk + 1)
/* Interface for user-defined custom blitters */
enum { snes_ntsc_in_chunk = 3 }; /* number of snes pixels read per chunk */
enum { snes_ntsc_out_chunk = 7 }; /* number of output pixels generated per chunk */
enum { snes_ntsc_black = 0 }; /* palette index for black */
enum { snes_ntsc_burst_count = 3 }; /* burst phase cycles through 0, 1, and 2 */
/* Begin outputting row and start three pixels. First pixel will be cut off a bit.
Use snes_ntsc_black for unused pixels. Declares variables, so must be before first
statement in a block (unless you're using C++). */
#define SNES_NTSC_LORES_ROW( ntsc, burst, pixel0, pixel1, pixel2 ) \
char const* ktable = (char*) (ntsc)->table + burst * (snes_ntsc_burst_size * sizeof (ntsc_rgb_t));\
int const snes_pixel0_ = (pixel0);\
ntsc_rgb_t const* kernel0 = SNES_NTSC_IN_FORMAT( snes_pixel0_ );\
int const snes_pixel1_ = (pixel1);\
ntsc_rgb_t const* kernel1 = SNES_NTSC_IN_FORMAT( snes_pixel1_ );\
int const snes_pixel2_ = (pixel2);\
ntsc_rgb_t const* kernel2 = SNES_NTSC_IN_FORMAT( snes_pixel2_ );\
ntsc_rgb_t const* kernelx0;\
ntsc_rgb_t const* kernelx1 = kernel0;\
ntsc_rgb_t const* kernelx2 = kernel0
/* Begin input pixel */
#define SNES_NTSC_PIXEL_IN( in_index, color_in ) {\
unsigned n;\
kernelx##in_index = kernel##in_index;\
kernel##in_index = (n = (color_in), SNES_NTSC_IN_FORMAT( n ));\
}
/* Generate output pixel. Bits can be 24, 16, 15, or 32 (treated as 24):
24: RRRRRRRR GGGGGGGG BBBBBBBB
16: RRRRRGGG GGGBBBBB
15: RRRRRGG GGGBBBBB
0: xxxRRRRR RRRxxGGG GGGGGxxB BBBBBBBx (raw format; x = junk bits) */
#define SNES_NTSC_LORES_OUT( x, rgb_out, bits ) {\
ntsc_rgb_t raw =\
kernel0 [x ] + kernel1 [(x+12)%7+14] + kernel2 [(x+10)%7+28] +\
kernelx0 [(x+7)%14] + kernelx1 [(x+ 5)%7+21] + kernelx2 [(x+ 3)%7+35];\
SNES_NTSC_CLAMP_( raw, 1 );\
SNES_NTSC_OUT_( rgb_out, (bits), raw, 1 );\
}
/* Hires equivalents */
#define SNES_NTSC_HIRES_ROW( ntsc, burst, pixel1, pixel2, pixel3, pixel4, pixel5 ) \
char const* ktable = (char*) (ntsc)->table + burst * (snes_ntsc_burst_size * sizeof (ntsc_rgb_t));\
int const snes_pixel1_ = (pixel1);\
ntsc_rgb_t const* kernel1 = SNES_NTSC_IN_FORMAT( snes_pixel1_ );\
int const snes_pixel2_ = (pixel2);\
ntsc_rgb_t const* kernel2 = SNES_NTSC_IN_FORMAT( snes_pixel2_ );\
int const snes_pixel3_ = (pixel3);\
ntsc_rgb_t const* kernel3 = SNES_NTSC_IN_FORMAT( snes_pixel3_ );\
int const snes_pixel4_ = (pixel4);\
ntsc_rgb_t const* kernel4 = SNES_NTSC_IN_FORMAT( snes_pixel4_ );\
int const snes_pixel5_ = (pixel5);\
ntsc_rgb_t const* kernel5 = SNES_NTSC_IN_FORMAT( snes_pixel5_ );\
ntsc_rgb_t const* kernel0 = kernel1;\
ntsc_rgb_t const* kernelx0;\
ntsc_rgb_t const* kernelx1 = kernel1;\
ntsc_rgb_t const* kernelx2 = kernel1;\
ntsc_rgb_t const* kernelx3 = kernel1;\
ntsc_rgb_t const* kernelx4 = kernel1;\
ntsc_rgb_t const* kernelx5 = kernel1
#define SNES_NTSC_HIRES_OUT( x, rgb_out, bits ) {\
ntsc_rgb_t raw =\
kernel0 [ x ] + kernel2 [(x+5)%7+14] + kernel4 [(x+3)%7+28] +\
kernelx0 [(x+7)%7+7] + kernelx2 [(x+5)%7+21] + kernelx4 [(x+3)%7+35] +\
kernel1 [(x+6)%7 ] + kernel3 [(x+4)%7+14] + kernel5 [(x+2)%7+28] +\
kernelx1 [(x+6)%7+7] + kernelx3 [(x+4)%7+21] + kernelx5 [(x+2)%7+35];\
SNES_NTSC_CLAMP_( raw, 0 );\
SNES_NTSC_OUT_( rgb_out, (bits), raw, 0 );\
}
/* private */
enum { snes_ntsc_entry_size = 128 };
enum { snes_ntsc_color_count = 0x2000 };
typedef unsigned long ntsc_rgb_t;
struct snes_ntsc_t
{
ntsc_rgb_t table [snes_ntsc_color_count] [snes_ntsc_entry_size];
};
enum { snes_ntsc_burst_size = snes_ntsc_entry_size / snes_ntsc_burst_count };
enum { ntsc_rgb_builder = (1L << 21) | (1 << 11) | (1 << 1) };
enum { snes_ntsc_clamp_mask = ntsc_rgb_builder * 3 / 2 };
enum { snes_ntsc_clamp_add = ntsc_rgb_builder * 0x101 };
#define SNES_NTSC_RGB16( n ) \
(ntsc_rgb_t*) (ktable + ((n & 0x001E) | (n >> 1 & 0x03E0) | (n >> 2 & 0x3C00)) * \
(snes_ntsc_entry_size / 2 * sizeof (ntsc_rgb_t)))
#define SNES_NTSC_BGR15( n ) \
(ntsc_rgb_t*) (ktable + ((n << 9 & 0x3C00) | (n & 0x03E0) | (n >> 10 & 0x001E)) * \
(snes_ntsc_entry_size / 2 * sizeof (ntsc_rgb_t)))
#define SNES_NTSC_CLAMP_( io, shift ) {\
ntsc_rgb_t sub = io >> (9-shift) & snes_ntsc_clamp_mask;\
ntsc_rgb_t clamp = snes_ntsc_clamp_add - sub;\
io |= clamp;\
clamp -= sub;\
io &= clamp;\
}
#define SNES_NTSC_OUT_( rgb_out, bits, raw, x ) {\
if ( bits == 16 ) {\
rgb_out = (raw>>(13-x)& 0xF800)|(raw>>(8-x)&0x07E0)|(raw>>(4-x)&0x001F);\
rgb_out = ((rgb_out&0xf800)>>11)|((rgb_out&0x07c0)>>1)|((rgb_out&0x001f)<<10);\
rgb_out = snes.color_lookup_table[rgb_out];\
} else if ( bits == 24 || bits == 32 ) {\
rgb_out = (raw>>(5-x)&0xFF0000)|(raw>>(3-x)&0xFF00)|(raw>>(1-x)&0xFF);\
rgb_out = ((rgb_out&0xf80000)>>19)|((rgb_out&0x00f800)>>6)|((rgb_out&0x0000f8)<<7);\
rgb_out = snes.color_lookup_table[rgb_out];\
} else if ( bits == 15 ) {\
rgb_out = (raw>>(14-x)& 0x7C00)|(raw>>(9-x)&0x03E0)|(raw>>(4-x)&0x001F);\
rgb_out = ((rgb_out&0x7c00)>>10)|((rgb_out&0x03e0))|((rgb_out&0x001f)<<10);\
rgb_out = snes.color_lookup_table[rgb_out];\
} else {\
rgb_out = raw;\
}\
}
#endif

View File

@@ -0,0 +1,251 @@
/* snes_ntsc 0.2.2. http://www.slack.net/~ant/ */
#include "snes_ntsc.h"
/* Copyright (C) 2006-2007 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
snes_ntsc_setup_t const snes_ntsc_monochrome = { 0,-1, 0, 0,.2, 0,.2,-.2,-.2,-1, 1, 0, 0 };
snes_ntsc_setup_t const snes_ntsc_composite = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 };
snes_ntsc_setup_t const snes_ntsc_svideo = { 0, 0, 0, 0,.2, 0,.2, -1, -1, 0, 1, 0, 0 };
snes_ntsc_setup_t const snes_ntsc_rgb = { 0, 0, 0, 0,.2, 0,.7, -1, -1,-1, 1, 0, 0 };
#define alignment_count 3
#define burst_count 3
#define rescale_in 8
#define rescale_out 7
#define artifacts_mid 1.0f
#define fringing_mid 1.0f
#define std_decoder_hue 0
#define rgb_bits 7 /* half normal range to allow for doubled hires pixels */
#define gamma_size 32
#include "snes_ntsc_impl.h"
/* 3 input pixels -> 8 composite samples */
pixel_info_t const snes_ntsc_pixels [alignment_count] = {
{ PIXEL_OFFSET( -4, -9 ), { 1, 1, .6667f, 0 } },
{ PIXEL_OFFSET( -2, -7 ), { .3333f, 1, 1, .3333f } },
{ PIXEL_OFFSET( 0, -5 ), { 0, .6667f, 1, 1 } },
};
static void merge_kernel_fields( snes_ntsc_rgb_t* io )
{
int n;
for ( n = burst_size; n; --n )
{
snes_ntsc_rgb_t p0 = io [burst_size * 0] + rgb_bias;
snes_ntsc_rgb_t p1 = io [burst_size * 1] + rgb_bias;
snes_ntsc_rgb_t p2 = io [burst_size * 2] + rgb_bias;
/* merge colors without losing precision */
io [burst_size * 0] =
((p0 + p1 - ((p0 ^ p1) & snes_ntsc_rgb_builder)) >> 1) - rgb_bias;
io [burst_size * 1] =
((p1 + p2 - ((p1 ^ p2) & snes_ntsc_rgb_builder)) >> 1) - rgb_bias;
io [burst_size * 2] =
((p2 + p0 - ((p2 ^ p0) & snes_ntsc_rgb_builder)) >> 1) - rgb_bias;
++io;
}
}
static void correct_errors( snes_ntsc_rgb_t color, snes_ntsc_rgb_t* out )
{
int n;
for ( n = burst_count; n; --n )
{
unsigned i;
for ( i = 0; i < rgb_kernel_size / 2; i++ )
{
snes_ntsc_rgb_t error = color -
out [i ] - out [(i+12)%14+14] - out [(i+10)%14+28] -
out [i + 7] - out [i + 5 +14] - out [i + 3 +28];
DISTRIBUTE_ERROR( i+3+28, i+5+14, i+7 );
}
out += alignment_count * rgb_kernel_size;
}
}
void snes_ntsc_init( snes_ntsc_t* ntsc, snes_ntsc_setup_t const* setup )
{
int merge_fields;
int entry;
init_t impl;
if ( !setup )
setup = &snes_ntsc_composite;
init( &impl, setup );
merge_fields = setup->merge_fields;
if ( setup->artifacts <= -1 && setup->fringing <= -1 )
merge_fields = 1;
for ( entry = 0; entry < snes_ntsc_palette_size; entry++ )
{
/* Reduce number of significant bits of source color. Clearing the
low bits of R and B were least notictable. Modifying green was too
noticeable. */
int ir = entry >> 8 & 0x1E;
int ig = entry >> 4 & 0x1F;
int ib = entry << 1 & 0x1E;
#if SNES_NTSC_BSNES_COLORTBL
if ( setup->bsnes_colortbl )
{
int bgr15 = (ib << 10) | (ig << 5) | ir;
unsigned long rgb16 = setup->bsnes_colortbl [bgr15];
ir = rgb16 >> 11 & 0x1E;
ig = rgb16 >> 6 & 0x1F;
ib = rgb16 & 0x1E;
}
#endif
{
float rr = impl.to_float [ir];
float gg = impl.to_float [ig];
float bb = impl.to_float [ib];
float y, i, q = RGB_TO_YIQ( rr, gg, bb, y, i );
int r, g, b = YIQ_TO_RGB( y, i, q, impl.to_rgb, int, r, g );
snes_ntsc_rgb_t rgb = PACK_RGB( r, g, b );
snes_ntsc_rgb_t* out = ntsc->table [entry];
gen_kernel( &impl, y, i, q, out );
if ( merge_fields )
merge_kernel_fields( out );
correct_errors( rgb, out );
}
}
}
#ifndef SNES_NTSC_NO_BLITTERS
void snes_ntsc_blit( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, long in_row_width,
int burst_phase, int in_width, int in_height, void* rgb_out, long out_pitch )
{
int chunk_count = (in_width - 1) / snes_ntsc_in_chunk;
for ( ; in_height; --in_height )
{
SNES_NTSC_IN_T const* line_in = input;
SNES_NTSC_BEGIN_ROW( ntsc, burst_phase,
snes_ntsc_black, snes_ntsc_black, SNES_NTSC_ADJ_IN( *line_in ) );
snes_ntsc_out_t* restrict line_out = (snes_ntsc_out_t*) rgb_out;
int n;
++line_in;
for ( n = chunk_count; n; --n )
{
/* order of input and output pixels must not be altered */
SNES_NTSC_COLOR_IN( 0, SNES_NTSC_ADJ_IN( line_in [0] ) );
SNES_NTSC_RGB_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_RGB_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 1, SNES_NTSC_ADJ_IN( line_in [1] ) );
SNES_NTSC_RGB_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_RGB_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 2, SNES_NTSC_ADJ_IN( line_in [2] ) );
SNES_NTSC_RGB_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_RGB_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_RGB_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH );
line_in += 3;
line_out += 7;
}
/* finish final pixels */
SNES_NTSC_COLOR_IN( 0, snes_ntsc_black );
SNES_NTSC_RGB_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_RGB_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 1, snes_ntsc_black );
SNES_NTSC_RGB_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_RGB_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 2, snes_ntsc_black );
SNES_NTSC_RGB_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_RGB_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_RGB_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH );
burst_phase = (burst_phase + 1) % snes_ntsc_burst_count;
input += in_row_width;
rgb_out = (char*) rgb_out + out_pitch;
}
}
void snes_ntsc_blit_hires( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, long in_row_width,
int burst_phase, int in_width, int in_height, void* rgb_out, long out_pitch )
{
int chunk_count = (in_width - 2) / (snes_ntsc_in_chunk * 2);
for ( ; in_height; --in_height )
{
SNES_NTSC_IN_T const* line_in = input;
SNES_NTSC_HIRES_ROW( ntsc, burst_phase,
snes_ntsc_black, snes_ntsc_black, snes_ntsc_black,
SNES_NTSC_ADJ_IN( line_in [0] ),
SNES_NTSC_ADJ_IN( line_in [1] ) );
snes_ntsc_out_t* restrict line_out = (snes_ntsc_out_t*) rgb_out;
int n;
line_in += 2;
for ( n = chunk_count; n; --n )
{
/* twice as many input pixels per chunk */
SNES_NTSC_COLOR_IN( 0, SNES_NTSC_ADJ_IN( line_in [0] ) );
SNES_NTSC_HIRES_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 1, SNES_NTSC_ADJ_IN( line_in [1] ) );
SNES_NTSC_HIRES_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 2, SNES_NTSC_ADJ_IN( line_in [2] ) );
SNES_NTSC_HIRES_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 3, SNES_NTSC_ADJ_IN( line_in [3] ) );
SNES_NTSC_HIRES_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 4, SNES_NTSC_ADJ_IN( line_in [4] ) );
SNES_NTSC_HIRES_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 5, SNES_NTSC_ADJ_IN( line_in [5] ) );
SNES_NTSC_HIRES_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_HIRES_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH );
line_in += 6;
line_out += 7;
}
SNES_NTSC_COLOR_IN( 0, snes_ntsc_black );
SNES_NTSC_HIRES_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 1, snes_ntsc_black );
SNES_NTSC_HIRES_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 2, snes_ntsc_black );
SNES_NTSC_HIRES_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 3, snes_ntsc_black );
SNES_NTSC_HIRES_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 4, snes_ntsc_black );
SNES_NTSC_HIRES_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 5, snes_ntsc_black );
SNES_NTSC_HIRES_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_HIRES_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH );
burst_phase = (burst_phase + 1) % snes_ntsc_burst_count;
input += in_row_width;
rgb_out = (char*) rgb_out + out_pitch;
}
}
#endif

View File

@@ -0,0 +1,228 @@
/* SNES NTSC video filter */
/* snes_ntsc 0.2.2 */
#ifndef SNES_NTSC_H
#define SNES_NTSC_H
#include "snes_ntsc_config.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Image parameters, ranging from -1.0 to 1.0. Actual internal values shown
in parenthesis and should remain fairly stable in future versions. */
typedef struct snes_ntsc_setup_t
{
/* Basic parameters */
double hue; /* -1 = -180 degrees +1 = +180 degrees */
double saturation; /* -1 = grayscale (0.0) +1 = oversaturated colors (2.0) */
double contrast; /* -1 = dark (0.5) +1 = light (1.5) */
double brightness; /* -1 = dark (0.5) +1 = light (1.5) */
double sharpness; /* edge contrast enhancement/blurring */
/* Advanced parameters */
double gamma; /* -1 = dark (1.5) +1 = light (0.5) */
double resolution; /* image resolution */
double artifacts; /* artifacts caused by color changes */
double fringing; /* color artifacts caused by brightness changes */
double bleed; /* color bleed (color resolution reduction) */
int merge_fields; /* if 1, merges even and odd fields together to reduce flicker */
float const* decoder_matrix; /* optional RGB decoder matrix, 6 elements */
unsigned long const* bsnes_colortbl; /* undocumented; set to 0 */
} snes_ntsc_setup_t;
/* Video format presets */
extern snes_ntsc_setup_t const snes_ntsc_composite; /* color bleeding + artifacts */
extern snes_ntsc_setup_t const snes_ntsc_svideo; /* color bleeding only */
extern snes_ntsc_setup_t const snes_ntsc_rgb; /* crisp image */
extern snes_ntsc_setup_t const snes_ntsc_monochrome;/* desaturated + artifacts */
/* Initializes and adjusts parameters. Can be called multiple times on the same
snes_ntsc_t object. Can pass NULL for either parameter. */
typedef struct snes_ntsc_t snes_ntsc_t;
void snes_ntsc_init( snes_ntsc_t* ntsc, snes_ntsc_setup_t const* setup );
/* Filters one or more rows of pixels. Input pixel format is set by SNES_NTSC_IN_FORMAT
and output RGB depth is set by SNES_NTSC_OUT_DEPTH. Both default to 16-bit RGB.
In_row_width is the number of pixels to get to the next input row. Out_pitch
is the number of *bytes* to get to the next output row. */
void snes_ntsc_blit( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input,
long in_row_width, int burst_phase, int in_width, int in_height,
void* rgb_out, long out_pitch );
void snes_ntsc_blit_hires( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input,
long in_row_width, int burst_phase, int in_width, int in_height,
void* rgb_out, long out_pitch );
/* Number of output pixels written by low-res blitter for given input width. Width
might be rounded down slightly; use SNES_NTSC_IN_WIDTH() on result to find rounded
value. Guaranteed not to round 256 down at all. */
#define SNES_NTSC_OUT_WIDTH( in_width ) \
((((in_width) - 1) / snes_ntsc_in_chunk + 1) * snes_ntsc_out_chunk)
/* Number of low-res input pixels that will fit within given output width. Might be
rounded down slightly; use SNES_NTSC_OUT_WIDTH() on result to find rounded
value. */
#define SNES_NTSC_IN_WIDTH( out_width ) \
(((out_width) / snes_ntsc_out_chunk - 1) * snes_ntsc_in_chunk + 1)
/* Interface for user-defined custom blitters */
enum { snes_ntsc_in_chunk = 3 }; /* number of input pixels read per chunk */
enum { snes_ntsc_out_chunk = 7 }; /* number of output pixels generated per chunk */
enum { snes_ntsc_black = 0 }; /* palette index for black */
enum { snes_ntsc_burst_count = 3 }; /* burst phase cycles through 0, 1, and 2 */
/* Begins outputting row and starts three pixels. First pixel will be cut off a bit.
Use snes_ntsc_black for unused pixels. Declares variables, so must be before first
statement in a block (unless you're using C++). */
#define SNES_NTSC_BEGIN_ROW( ntsc, burst, pixel0, pixel1, pixel2 ) \
char const* ktable = \
(char const*) (ntsc)->table + burst * (snes_ntsc_burst_size * sizeof (snes_ntsc_rgb_t));\
SNES_NTSC_BEGIN_ROW_6_( pixel0, pixel1, pixel2, SNES_NTSC_IN_FORMAT, ktable )
/* Begins input pixel */
#define SNES_NTSC_COLOR_IN( index, color ) \
SNES_NTSC_COLOR_IN_( index, color, SNES_NTSC_IN_FORMAT, ktable )
/* Generates output pixel. Bits can be 24, 16, 15, 14, 32 (treated as 24), or 0:
24: RRRRRRRR GGGGGGGG BBBBBBBB (8-8-8 RGB)
16: RRRRRGGG GGGBBBBB (5-6-5 RGB)
15: RRRRRGG GGGBBBBB (5-5-5 RGB)
14: BBBBBGG GGGRRRRR (5-5-5 BGR, native SNES format)
0: xxxRRRRR RRRxxGGG GGGGGxxB BBBBBBBx (native internal format; x = junk bits) */
#define SNES_NTSC_RGB_OUT( index, rgb_out, bits ) \
SNES_NTSC_RGB_OUT_14_( index, rgb_out, bits, 1 )
/* Hires equivalents */
#define SNES_NTSC_HIRES_ROW( ntsc, burst, pixel1, pixel2, pixel3, pixel4, pixel5 ) \
char const* ktable = \
(char const*) (ntsc)->table + burst * (snes_ntsc_burst_size * sizeof (snes_ntsc_rgb_t));\
unsigned const snes_ntsc_pixel1_ = (pixel1);\
snes_ntsc_rgb_t const* kernel1 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel1_ );\
unsigned const snes_ntsc_pixel2_ = (pixel2);\
snes_ntsc_rgb_t const* kernel2 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel2_ );\
unsigned const snes_ntsc_pixel3_ = (pixel3);\
snes_ntsc_rgb_t const* kernel3 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel3_ );\
unsigned const snes_ntsc_pixel4_ = (pixel4);\
snes_ntsc_rgb_t const* kernel4 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel4_ );\
unsigned const snes_ntsc_pixel5_ = (pixel5);\
snes_ntsc_rgb_t const* kernel5 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel5_ );\
snes_ntsc_rgb_t const* kernel0 = kernel1;\
snes_ntsc_rgb_t const* kernelx0;\
snes_ntsc_rgb_t const* kernelx1 = kernel1;\
snes_ntsc_rgb_t const* kernelx2 = kernel1;\
snes_ntsc_rgb_t const* kernelx3 = kernel1;\
snes_ntsc_rgb_t const* kernelx4 = kernel1;\
snes_ntsc_rgb_t const* kernelx5 = kernel1
#define SNES_NTSC_HIRES_OUT( x, rgb_out, bits ) {\
snes_ntsc_rgb_t raw_ =\
kernel0 [ x ] + kernel2 [(x+5)%7+14] + kernel4 [(x+3)%7+28] +\
kernelx0 [(x+7)%7+7] + kernelx2 [(x+5)%7+21] + kernelx4 [(x+3)%7+35] +\
kernel1 [(x+6)%7 ] + kernel3 [(x+4)%7+14] + kernel5 [(x+2)%7+28] +\
kernelx1 [(x+6)%7+7] + kernelx3 [(x+4)%7+21] + kernelx5 [(x+2)%7+35];\
SNES_NTSC_CLAMP_( raw_, 0 );\
SNES_NTSC_RGB_OUT_( rgb_out, (bits), 0 );\
}
/* private */
enum { snes_ntsc_entry_size = 128 };
enum { snes_ntsc_palette_size = 0x2000 };
typedef unsigned long snes_ntsc_rgb_t;
struct snes_ntsc_t {
snes_ntsc_rgb_t table [snes_ntsc_palette_size] [snes_ntsc_entry_size];
};
enum { snes_ntsc_burst_size = snes_ntsc_entry_size / snes_ntsc_burst_count };
#define SNES_NTSC_RGB16( ktable, n ) \
(snes_ntsc_rgb_t const*) (ktable + ((n & 0x001E) | (n >> 1 & 0x03E0) | (n >> 2 & 0x3C00)) * \
(snes_ntsc_entry_size / 2 * sizeof (snes_ntsc_rgb_t)))
#define SNES_NTSC_BGR15( ktable, n ) \
(snes_ntsc_rgb_t const*) (ktable + ((n << 9 & 0x3C00) | (n & 0x03E0) | (n >> 10 & 0x001E)) * \
(snes_ntsc_entry_size / 2 * sizeof (snes_ntsc_rgb_t)))
/* common 3->7 ntsc macros */
#define SNES_NTSC_BEGIN_ROW_6_( pixel0, pixel1, pixel2, ENTRY, table ) \
unsigned const snes_ntsc_pixel0_ = (pixel0);\
snes_ntsc_rgb_t const* kernel0 = ENTRY( table, snes_ntsc_pixel0_ );\
unsigned const snes_ntsc_pixel1_ = (pixel1);\
snes_ntsc_rgb_t const* kernel1 = ENTRY( table, snes_ntsc_pixel1_ );\
unsigned const snes_ntsc_pixel2_ = (pixel2);\
snes_ntsc_rgb_t const* kernel2 = ENTRY( table, snes_ntsc_pixel2_ );\
snes_ntsc_rgb_t const* kernelx0;\
snes_ntsc_rgb_t const* kernelx1 = kernel0;\
snes_ntsc_rgb_t const* kernelx2 = kernel0
#define SNES_NTSC_RGB_OUT_14_( x, rgb_out, bits, shift ) {\
snes_ntsc_rgb_t raw_ =\
kernel0 [x ] + kernel1 [(x+12)%7+14] + kernel2 [(x+10)%7+28] +\
kernelx0 [(x+7)%14] + kernelx1 [(x+ 5)%7+21] + kernelx2 [(x+ 3)%7+35];\
SNES_NTSC_CLAMP_( raw_, shift );\
SNES_NTSC_RGB_OUT_( rgb_out, bits, shift );\
}
/* common ntsc macros */
#define snes_ntsc_rgb_builder ((1L << 21) | (1 << 11) | (1 << 1))
#define snes_ntsc_clamp_mask (snes_ntsc_rgb_builder * 3 / 2)
#define snes_ntsc_clamp_add (snes_ntsc_rgb_builder * 0x101)
#define SNES_NTSC_CLAMP_( io, shift ) {\
snes_ntsc_rgb_t sub = (io) >> (9-(shift)) & snes_ntsc_clamp_mask;\
snes_ntsc_rgb_t clamp = snes_ntsc_clamp_add - sub;\
io |= clamp;\
clamp -= sub;\
io &= clamp;\
}
#define SNES_NTSC_COLOR_IN_( index, color, ENTRY, table ) {\
unsigned color_;\
kernelx##index = kernel##index;\
kernel##index = (color_ = (color), ENTRY( table, color_ ));\
}
/* x is always zero except in snes_ntsc library */
/* original routine */
/*
#define SNES_NTSC_RGB_OUT_( rgb_out, bits, x ) {\
if ( bits == 16 )\
rgb_out = (raw_>>(13-x)& 0xF800)|(raw_>>(8-x)&0x07E0)|(raw_>>(4-x)&0x001F);\
if ( bits == 24 || bits == 32 )\
rgb_out = (raw_>>(5-x)&0xFF0000)|(raw_>>(3-x)&0xFF00)|(raw_>>(1-x)&0xFF);\
if ( bits == 15 )\
rgb_out = (raw_>>(14-x)& 0x7C00)|(raw_>>(9-x)&0x03E0)|(raw_>>(4-x)&0x001F);\
if ( bits == 14 )\
rgb_out = (raw_>>(24-x)& 0x001F)|(raw_>>(9-x)&0x03E0)|(raw_<<(6+x)&0x7C00);\
if ( bits == 0 )\
rgb_out = raw_ << x;\
}
*/
/* custom bsnes routine -- hooks into bsnes colortable */
#define SNES_NTSC_RGB_OUT_( rgb_out, bits, x ) {\
if ( bits == 16 ) {\
rgb_out = (raw_>>(13-x)& 0xF800)|(raw_>>(8-x)&0x07E0)|(raw_>>(4-x)&0x001F);\
rgb_out = ((rgb_out&0xf800)>>11)|((rgb_out&0x07c0)>>1)|((rgb_out&0x001f)<<10);\
rgb_out = snes.color_lookup_table[rgb_out];\
} else if ( bits == 24 || bits == 32 ) {\
rgb_out = (raw_>>(5-x)&0xFF0000)|(raw_>>(3-x)&0xFF00)|(raw_>>(1-x)&0xFF);\
rgb_out = ((rgb_out&0xf80000)>>19)|((rgb_out&0x00f800)>>6)|((rgb_out&0x0000f8)<<7);\
rgb_out = snes.color_lookup_table[rgb_out];\
} else if ( bits == 15 ) {\
rgb_out = (raw_>>(14-x)& 0x7C00)|(raw_>>(9-x)&0x03E0)|(raw_>>(4-x)&0x001F);\
rgb_out = ((rgb_out&0x7c00)>>10)|((rgb_out&0x03e0))|((rgb_out&0x001f)<<10);\
rgb_out = snes.color_lookup_table[rgb_out];\
} else {\
rgb_out = raw_ << x;\
}\
}
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,26 @@
/* Configure library by modifying this file */
#ifndef SNES_NTSC_CONFIG_H
#define SNES_NTSC_CONFIG_H
/* Format of source pixels */
/* #define SNES_NTSC_IN_FORMAT SNES_NTSC_RGB16 */
#define SNES_NTSC_IN_FORMAT SNES_NTSC_BGR15
/* The following affect the built-in blitter only; a custom blitter can
handle things however it wants. */
/* Bits per pixel of output. Can be 15, 16, 32, or 24 (same as 32). */
#define SNES_NTSC_OUT_DEPTH 16
/* Type of input pixel values */
#define SNES_NTSC_IN_T unsigned short
/* Each raw pixel input value is passed through this. You might want to mask
the pixel index if you use the high bits as flags, etc. */
#define SNES_NTSC_ADJ_IN( in ) in
/* For each pixel, this is the basic operation:
output_color = SNES_NTSC_ADJ_IN( SNES_NTSC_IN_T ) */
#endif

View File

@@ -0,0 +1,439 @@
/* snes_ntsc 0.2.2. http://www.slack.net/~ant/ */
/* Common implementation of NTSC filters */
#include <assert.h>
#include <math.h>
/* Copyright (C) 2006 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#define DISABLE_CORRECTION 0
#undef PI
#define PI 3.14159265358979323846f
#ifndef LUMA_CUTOFF
#define LUMA_CUTOFF 0.20
#endif
#ifndef gamma_size
#define gamma_size 1
#endif
#ifndef rgb_bits
#define rgb_bits 8
#endif
#ifndef artifacts_max
#define artifacts_max (artifacts_mid * 1.5f)
#endif
#ifndef fringing_max
#define fringing_max (fringing_mid * 2)
#endif
#ifndef STD_HUE_CONDITION
#define STD_HUE_CONDITION( setup ) 1
#endif
#define ext_decoder_hue (std_decoder_hue + 15)
#define rgb_unit (1 << rgb_bits)
#define rgb_offset (rgb_unit * 2 + 0.5f)
enum { burst_size = snes_ntsc_entry_size / burst_count };
enum { kernel_half = 16 };
enum { kernel_size = kernel_half * 2 + 1 };
typedef struct init_t
{
float to_rgb [burst_count * 6];
float to_float [gamma_size];
float contrast;
float brightness;
float artifacts;
float fringing;
float kernel [rescale_out * kernel_size * 2];
} init_t;
#define ROTATE_IQ( i, q, sin_b, cos_b ) {\
float t;\
t = i * cos_b - q * sin_b;\
q = i * sin_b + q * cos_b;\
i = t;\
}
static void init_filters( init_t* impl, snes_ntsc_setup_t const* setup )
{
#if rescale_out > 1
float kernels [kernel_size * 2];
#else
float* const kernels = impl->kernel;
#endif
/* generate luma (y) filter using sinc kernel */
{
/* sinc with rolloff (dsf) */
float const rolloff = 1 + (float) setup->sharpness * (float) 0.032;
float const maxh = 32;
float const pow_a_n = (float) pow( rolloff, maxh );
float sum;
int i;
/* quadratic mapping to reduce negative (blurring) range */
float to_angle = (float) setup->resolution + 1;
to_angle = PI / maxh * (float) LUMA_CUTOFF * (to_angle * to_angle + 1);
kernels [kernel_size * 3 / 2] = maxh; /* default center value */
for ( i = 0; i < kernel_half * 2 + 1; i++ )
{
int x = i - kernel_half;
float angle = x * to_angle;
/* instability occurs at center point with rolloff very close to 1.0 */
if ( x || pow_a_n > (float) 1.056 || pow_a_n < (float) 0.981 )
{
float rolloff_cos_a = rolloff * (float) cos( angle );
float num = 1 - rolloff_cos_a -
pow_a_n * (float) cos( maxh * angle ) +
pow_a_n * rolloff * (float) cos( (maxh - 1) * angle );
float den = 1 - rolloff_cos_a - rolloff_cos_a + rolloff * rolloff;
float dsf = num / den;
kernels [kernel_size * 3 / 2 - kernel_half + i] = dsf - (float) 0.5;
}
}
/* apply blackman window and find sum */
sum = 0;
for ( i = 0; i < kernel_half * 2 + 1; i++ )
{
float x = PI * 2 / (kernel_half * 2) * i;
float blackman = 0.42f - 0.5f * (float) cos( x ) + 0.08f * (float) cos( x * 2 );
sum += (kernels [kernel_size * 3 / 2 - kernel_half + i] *= blackman);
}
/* normalize kernel */
sum = 1.0f / sum;
for ( i = 0; i < kernel_half * 2 + 1; i++ )
{
int x = kernel_size * 3 / 2 - kernel_half + i;
kernels [x] *= sum;
assert( kernels [x] == kernels [x] ); /* catch numerical instability */
}
}
/* generate chroma (iq) filter using gaussian kernel */
{
float const cutoff_factor = -0.03125f;
float cutoff = (float) setup->bleed;
int i;
if ( cutoff < 0 )
{
/* keep extreme value accessible only near upper end of scale (1.0) */
cutoff *= cutoff;
cutoff *= cutoff;
cutoff *= cutoff;
cutoff *= -30.0f / 0.65f;
}
cutoff = cutoff_factor - 0.65f * cutoff_factor * cutoff;
for ( i = -kernel_half; i <= kernel_half; i++ )
kernels [kernel_size / 2 + i] = (float) exp( i * i * cutoff );
/* normalize even and odd phases separately */
for ( i = 0; i < 2; i++ )
{
float sum = 0;
int x;
for ( x = i; x < kernel_size; x += 2 )
sum += kernels [x];
sum = 1.0f / sum;
for ( x = i; x < kernel_size; x += 2 )
{
kernels [x] *= sum;
assert( kernels [x] == kernels [x] ); /* catch numerical instability */
}
}
}
/*
printf( "luma:\n" );
for ( i = kernel_size; i < kernel_size * 2; i++ )
printf( "%f\n", kernels [i] );
printf( "chroma:\n" );
for ( i = 0; i < kernel_size; i++ )
printf( "%f\n", kernels [i] );
*/
/* generate linear rescale kernels */
#if rescale_out > 1
{
float weight = 1.0f;
float* out = impl->kernel;
int n = rescale_out;
do
{
float remain = 0;
int i;
weight -= 1.0f / rescale_in;
for ( i = 0; i < kernel_size * 2; i++ )
{
float cur = kernels [i];
float m = cur * weight;
*out++ = m + remain;
remain = cur - m;
}
}
while ( --n );
}
#endif
}
static float const default_decoder [6] =
{ 0.956f, 0.621f, -0.272f, -0.647f, -1.105f, 1.702f };
static void init( init_t* impl, snes_ntsc_setup_t const* setup )
{
impl->brightness = (float) setup->brightness * (0.5f * rgb_unit) + rgb_offset;
impl->contrast = (float) setup->contrast * (0.5f * rgb_unit) + rgb_unit;
#ifdef default_palette_contrast
if ( !setup->palette )
impl->contrast *= default_palette_contrast;
#endif
impl->artifacts = (float) setup->artifacts;
if ( impl->artifacts > 0 )
impl->artifacts *= artifacts_max - artifacts_mid;
impl->artifacts = impl->artifacts * artifacts_mid + artifacts_mid;
impl->fringing = (float) setup->fringing;
if ( impl->fringing > 0 )
impl->fringing *= fringing_max - fringing_mid;
impl->fringing = impl->fringing * fringing_mid + fringing_mid;
init_filters( impl, setup );
/* generate gamma table */
if ( gamma_size > 1 )
{
float const to_float = 1.0f / (gamma_size - (gamma_size > 1));
float const gamma = 1.1333f - (float) setup->gamma * 0.5f;
/* match common PC's 2.2 gamma to TV's 2.65 gamma */
int i;
for ( i = 0; i < gamma_size; i++ )
impl->to_float [i] =
(float) pow( i * to_float, gamma ) * impl->contrast + impl->brightness;
}
/* setup decoder matricies */
{
float hue = (float) setup->hue * PI + PI / 180 * ext_decoder_hue;
float sat = (float) setup->saturation + 1;
float const* decoder = setup->decoder_matrix;
if ( !decoder )
{
decoder = default_decoder;
if ( STD_HUE_CONDITION( setup ) )
hue += PI / 180 * (std_decoder_hue - ext_decoder_hue);
}
{
float s = (float) sin( hue ) * sat;
float c = (float) cos( hue ) * sat;
float* out = impl->to_rgb;
int n;
n = burst_count;
do
{
float const* in = decoder;
int n = 3;
do
{
float i = *in++;
float q = *in++;
*out++ = i * c - q * s;
*out++ = i * s + q * c;
}
while ( --n );
if ( burst_count <= 1 )
break;
ROTATE_IQ( s, c, 0.866025f, -0.5f ); /* +120 degrees */
}
while ( --n );
}
}
}
/* kernel generation */
#define RGB_TO_YIQ( r, g, b, y, i ) (\
(y = (r) * 0.299f + (g) * 0.587f + (b) * 0.114f),\
(i = (r) * 0.596f - (g) * 0.275f - (b) * 0.321f),\
((r) * 0.212f - (g) * 0.523f + (b) * 0.311f)\
)
#define YIQ_TO_RGB( y, i, q, to_rgb, type, r, g ) (\
r = (type) (y + to_rgb [0] * i + to_rgb [1] * q),\
g = (type) (y + to_rgb [2] * i + to_rgb [3] * q),\
(type) (y + to_rgb [4] * i + to_rgb [5] * q)\
)
#define PACK_RGB( r, g, b ) ((r) << 21 | (g) << 11 | (b) << 1)
enum { rgb_kernel_size = burst_size / alignment_count };
enum { rgb_bias = rgb_unit * 2 * snes_ntsc_rgb_builder };
typedef struct pixel_info_t
{
int offset;
float negate;
float kernel [4];
} pixel_info_t;
#if rescale_in > 1
#define PIXEL_OFFSET_( ntsc, scaled ) \
(kernel_size / 2 + ntsc + (scaled != 0) + (rescale_out - scaled) % rescale_out + \
(kernel_size * 2 * scaled))
#define PIXEL_OFFSET( ntsc, scaled ) \
PIXEL_OFFSET_( ((ntsc) - (scaled) / rescale_out * rescale_in),\
(((scaled) + rescale_out * 10) % rescale_out) ),\
(1.0f - (((ntsc) + 100) & 2))
#else
#define PIXEL_OFFSET( ntsc, scaled ) \
(kernel_size / 2 + (ntsc) - (scaled)),\
(1.0f - (((ntsc) + 100) & 2))
#endif
extern pixel_info_t const snes_ntsc_pixels [alignment_count];
/* Generate pixel at all burst phases and column alignments */
static void gen_kernel( init_t* impl, float y, float i, float q, snes_ntsc_rgb_t* out )
{
/* generate for each scanline burst phase */
float const* to_rgb = impl->to_rgb;
int burst_remain = burst_count;
y -= rgb_offset;
do
{
/* Encode yiq into *two* composite signals (to allow control over artifacting).
Convolve these with kernels which: filter respective components, apply
sharpening, and rescale horizontally. Convert resulting yiq to rgb and pack
into integer. Based on algorithm by NewRisingSun. */
pixel_info_t const* pixel = snes_ntsc_pixels;
int alignment_remain = alignment_count;
do
{
/* negate is -1 when composite starts at odd multiple of 2 */
float const yy = y * impl->fringing * pixel->negate;
float const ic0 = (i + yy) * pixel->kernel [0];
float const qc1 = (q + yy) * pixel->kernel [1];
float const ic2 = (i - yy) * pixel->kernel [2];
float const qc3 = (q - yy) * pixel->kernel [3];
float const factor = impl->artifacts * pixel->negate;
float const ii = i * factor;
float const yc0 = (y + ii) * pixel->kernel [0];
float const yc2 = (y - ii) * pixel->kernel [2];
float const qq = q * factor;
float const yc1 = (y + qq) * pixel->kernel [1];
float const yc3 = (y - qq) * pixel->kernel [3];
float const* k = &impl->kernel [pixel->offset];
int n;
++pixel;
for ( n = rgb_kernel_size; n; --n )
{
float i = k[0]*ic0 + k[2]*ic2;
float q = k[1]*qc1 + k[3]*qc3;
float y = k[kernel_size+0]*yc0 + k[kernel_size+1]*yc1 +
k[kernel_size+2]*yc2 + k[kernel_size+3]*yc3 + rgb_offset;
if ( rescale_out <= 1 )
k--;
else if ( k < &impl->kernel [kernel_size * 2 * (rescale_out - 1)] )
k += kernel_size * 2 - 1;
else
k -= kernel_size * 2 * (rescale_out - 1) + 2;
{
int r, g, b = YIQ_TO_RGB( y, i, q, to_rgb, int, r, g );
*out++ = PACK_RGB( r, g, b ) - rgb_bias;
}
}
}
while ( alignment_count > 1 && --alignment_remain );
if ( burst_count <= 1 )
break;
to_rgb += 6;
ROTATE_IQ( i, q, -0.866025f, -0.5f ); /* -120 degrees */
}
while ( --burst_remain );
}
static void correct_errors( snes_ntsc_rgb_t color, snes_ntsc_rgb_t* out );
#if DISABLE_CORRECTION
#define CORRECT_ERROR( a ) { out [i] += rgb_bias; }
#define DISTRIBUTE_ERROR( a, b, c ) { out [i] += rgb_bias; }
#else
#define CORRECT_ERROR( a ) { out [a] += error; }
#define DISTRIBUTE_ERROR( a, b, c ) {\
snes_ntsc_rgb_t fourth = (error + 2 * snes_ntsc_rgb_builder) >> 2;\
fourth &= (rgb_bias >> 1) - snes_ntsc_rgb_builder;\
fourth -= rgb_bias >> 2;\
out [a] += fourth;\
out [b] += fourth;\
out [c] += fourth;\
out [i] += error - (fourth * 3);\
}
#endif
#define RGB_PALETTE_OUT( rgb, out_ )\
{\
unsigned char* out = (out_);\
snes_ntsc_rgb_t clamped = (rgb);\
SNES_NTSC_CLAMP_( clamped, (8 - rgb_bits) );\
out [0] = (unsigned char) (clamped >> 21);\
out [1] = (unsigned char) (clamped >> 11);\
out [2] = (unsigned char) (clamped >> 1);\
}
/* blitter related */
#ifndef restrict
#if defined (__GNUC__)
#define restrict __restrict__
#elif defined (_MSC_VER) && _MSC_VER > 1300
#define restrict __restrict
#else
/* no support for restricted pointers */
#define restrict
#endif
#endif
#include <limits.h>
#if SNES_NTSC_OUT_DEPTH <= 16
#if USHRT_MAX == 0xFFFF
typedef unsigned short snes_ntsc_out_t;
#else
#error "Need 16-bit int type"
#endif
#else
#if UINT_MAX == 0xFFFFFFFF
typedef unsigned int snes_ntsc_out_t;
#elif ULONG_MAX == 0xFFFFFFFF
typedef unsigned long snes_ntsc_out_t;
#else
#error "Need 32-bit int type"
#endif
#endif

View File

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

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

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

View File

@@ -9,60 +9,72 @@ struct System {
static IntegerSetting regulate_speed, speed;
static IntegerSetting speed_slowest, speed_slow, speed_normal, speed_fast, speed_fastest;
} system;
StringSetting System::video(&config_file, "system.video", "Video hardware interface", "");
StringSetting System::audio(&config_file, "system.audio", "Audio hardware interface", "");
StringSetting System::input(&config_file, "system.input", "Input hardware interface", "");
StringSetting System::video(&config(), "system.video", "Video hardware interface", "");
StringSetting System::audio(&config(), "system.audio", "Audio hardware interface", "");
StringSetting System::input(&config(), "system.input", "Input hardware interface", "");
StringSetting System::video_flags(&config_file, "system.video_flags", "Video hardware interface flags", "");
StringSetting System::audio_flags(&config_file, "system.audio_flags", "Audio hardware interface flags", "");
StringSetting System::input_flags(&config_file, "system.input_flags", "Input hardware interface flags", "");
StringSetting System::video_flags(&config(), "system.video_flags", "Video hardware interface flags", "");
StringSetting System::audio_flags(&config(), "system.audio_flags", "Audio hardware interface flags", "");
StringSetting System::input_flags(&config(), "system.input_flags", "Input hardware interface flags", "");
IntegerSetting System::regulate_speed(&config_file, "system.regulate_speed", "Regulate speed to 60hz (NTSC) / 50hz (PAL)", IntegerSetting::Boolean, true);
IntegerSetting System::regulate_speed(&config(), "system.regulate_speed", "Regulate speed to 60hz (NTSC) / 50hz (PAL)", IntegerSetting::Boolean, true);
IntegerSetting System::speed (0, "system.speed", "Current speed regulation setting (1-5)", IntegerSetting::Decimal, 3);
IntegerSetting System::speed_slowest (&config_file, "system.speed_slowest", "Slowest speed setting", IntegerSetting::Decimal, 50);
IntegerSetting System::speed_slow (&config_file, "system.speed_slow", "Slow speed setting", IntegerSetting::Decimal, 75);
IntegerSetting System::speed_normal (&config_file, "system.speed_normal", "Normal speed setting", IntegerSetting::Decimal, 100);
IntegerSetting System::speed_fast (&config_file, "system.speed_fast", "Fast speed setting", IntegerSetting::Decimal, 150);
IntegerSetting System::speed_fastest (&config_file, "system.speed_fastest", "Fastest speed setting", IntegerSetting::Decimal, 200);
IntegerSetting System::speed_slowest (&config(), "system.speed_slowest", "Slowest speed setting", IntegerSetting::Decimal, 50);
IntegerSetting System::speed_slow (&config(), "system.speed_slow", "Slow speed setting", IntegerSetting::Decimal, 75);
IntegerSetting System::speed_normal (&config(), "system.speed_normal", "Normal speed setting", IntegerSetting::Decimal, 100);
IntegerSetting System::speed_fast (&config(), "system.speed_fast", "Fast speed setting", IntegerSetting::Decimal, 150);
IntegerSetting System::speed_fastest (&config(), "system.speed_fastest", "Fastest speed setting", IntegerSetting::Decimal, 200);
struct Video {
static IntegerSetting synchronize;
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;
} video;
IntegerSetting Video::synchronize(&config_file, "video.synchronize", "Synchronize to video refresh rate.", IntegerSetting::Boolean, false);
IntegerSetting Video::multiplier(&config_file, "video.multiplier", "Video output size multiplier (1-5x)\n"
IntegerSetting Video::synchronize(&config(), "video.synchronize", "Synchronize to video refresh rate.", IntegerSetting::Boolean, false);
IntegerSetting Video::fullscreen(0, "video.fullscreen", "", IntegerSetting::Boolean, false);
IntegerSetting Video::multiplier(&config(), "video.multiplier", "Video output size multiplier (1-5x)\n"
"1 = 1x (~256x224)\n"
"2 = 2x (~512x448)\n"
"etc.",
IntegerSetting::Decimal, 2);
IntegerSetting Video::aspect_correction(&config_file, "video.aspect_correction", "Correct video aspect ratio", IntegerSetting::Boolean, true);
IntegerSetting Video::region(&config_file, "video.region", "Video output region\n"
IntegerSetting Video::aspect_correction(&config(), "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(), "video.region", "Video output region\n"
"0 = NTSC, 1 = PAL",
IntegerSetting::Decimal, 0);
IntegerSetting Video::hardware_filter(&config_file, "video.hardware_filter", "Video hardware filter\n"
IntegerSetting Video::aspect_ntsc_x(&config(), "video.aspect_ntsc_x", "", IntegerSetting::Decimal, 54);
IntegerSetting Video::aspect_ntsc_y(&config(), "video.aspect_ntsc_y", "", IntegerSetting::Decimal, 47);
IntegerSetting Video::aspect_pal_x (&config(), "video.aspect_pal_x", "", IntegerSetting::Decimal, 32);
IntegerSetting Video::aspect_pal_y (&config(), "video.aspect_pal_y", "", IntegerSetting::Decimal, 23);
IntegerSetting Video::hardware_filter(&config(), "video.hardware_filter", "Video hardware filter\n"
"0 = Point\n"
"1 = Linear\n",
IntegerSetting::Decimal, 1);
IntegerSetting Video::software_filter(&config_file, "video.software_filter", "Video software filter\n"
IntegerSetting Video::software_filter(&config(), "video.software_filter", "Video software filter\n"
"0 = None\n"
"1 = NTSC\n"
"2 = HQ2x\n"
"3 = Scale2x\n",
IntegerSetting::Decimal, 0);
IntegerSetting Video::frameskip(0, "video.frameskip", "Video frameskip", IntegerSetting::Decimal, 0);
IntegerSetting Video::use_vram(&config_file, "video.use_vram", "Use Video RAM instead of System RAM", IntegerSetting::Boolean, true);
IntegerSetting Video::use_vram(&config(), "video.use_vram", "Use Video RAM instead of System RAM", IntegerSetting::Boolean, true);
struct Audio {
static IntegerSetting synchronize;
static IntegerSetting frequency;
static IntegerSetting latency;
} audio;
IntegerSetting Audio::synchronize(&config_file, "audio.synchronize", "Synchronize to audio sample rate.", IntegerSetting::Boolean, true);
IntegerSetting Audio::frequency(&config_file, "audio.frequency", "Default audio playback frequency.", IntegerSetting::Decimal, 32000);
IntegerSetting Audio::latency(&config_file, "audio.latency", "Audio playback latency in milliseconds.\n"
IntegerSetting Audio::synchronize(&config(), "audio.synchronize", "Synchronize to audio sample rate.", IntegerSetting::Boolean, true);
IntegerSetting Audio::frequency(&config(), "audio.frequency", "Default audio playback frequency.", IntegerSetting::Decimal, 32000);
IntegerSetting Audio::latency(&config(), "audio.latency", "Audio playback latency in milliseconds.\n"
"Specifies how long audio playback is delayed compared to a real SNES.\n"
"A delay is necessary to allow smooth audio playback via buffering.\n"
"Raising this value may help with audio playback problems, but will decrease\n"
@@ -82,7 +94,7 @@ struct Input {
} joypad2;
} input;
IntegerSetting Input::axis_resistance(&config_file, "input.axis_resistance",
IntegerSetting Input::axis_resistance(&config(), "input.axis_resistance",
"Axis resistance for all analog joypads\n"
"Affects responsiveness of analog stick movement by specifying what percentage\n"
"in any given direction the axis must be pressed to trigger a button press.\n"
@@ -94,34 +106,40 @@ IntegerSetting Input::axis_resistance(&config_file, "input.axis_resistance",
"Note: Values below 10 or above 90 are not recommended and may not work at all.",
IntegerSetting::Decimal, 75);
IntegerSetting Input::allow_invalid_input(&config_file, "input.allow_invalid_input",
IntegerSetting Input::allow_invalid_input(&config(), "input.allow_invalid_input",
"Allow up+down and left+right combinations (not recommended)",
IntegerSetting::Boolean, false);
StringSetting Input::Joypad1::up (&config_file, "input.joypad1.up", "", "up");
StringSetting Input::Joypad1::down (&config_file, "input.joypad1.down", "", "down");
StringSetting Input::Joypad1::left (&config_file, "input.joypad1.left", "", "left");
StringSetting Input::Joypad1::right (&config_file, "input.joypad1.right", "", "right");
StringSetting Input::Joypad1::a (&config_file, "input.joypad1.a", "", "x");
StringSetting Input::Joypad1::b (&config_file, "input.joypad1.b", "", "z");
StringSetting Input::Joypad1::x (&config_file, "input.joypad1.x", "", "s");
StringSetting Input::Joypad1::y (&config_file, "input.joypad1.y", "", "a");
StringSetting Input::Joypad1::l (&config_file, "input.joypad1.l", "", "d");
StringSetting Input::Joypad1::r (&config_file, "input.joypad1.r", "", "c");
StringSetting Input::Joypad1::select(&config_file, "input.joypad1.select", "", "rshift");
StringSetting Input::Joypad1::start (&config_file, "input.joypad1.start", "", "enter");
StringSetting Input::Joypad1::up (&config(), "input.joypad1.up", "", "up");
StringSetting Input::Joypad1::down (&config(), "input.joypad1.down", "", "down");
StringSetting Input::Joypad1::left (&config(), "input.joypad1.left", "", "left");
StringSetting Input::Joypad1::right (&config(), "input.joypad1.right", "", "right");
StringSetting Input::Joypad1::a (&config(), "input.joypad1.a", "", "x");
StringSetting Input::Joypad1::b (&config(), "input.joypad1.b", "", "z");
StringSetting Input::Joypad1::x (&config(), "input.joypad1.x", "", "s");
StringSetting Input::Joypad1::y (&config(), "input.joypad1.y", "", "a");
StringSetting Input::Joypad1::l (&config(), "input.joypad1.l", "", "d");
StringSetting Input::Joypad1::r (&config(), "input.joypad1.r", "", "c");
StringSetting Input::Joypad1::select(&config(), "input.joypad1.select", "", "rshift");
StringSetting Input::Joypad1::start (&config(), "input.joypad1.start", "", "enter");
StringSetting Input::Joypad2::up (&config_file, "input.joypad2.up", "", "t");
StringSetting Input::Joypad2::down (&config_file, "input.joypad2.down", "", "g");
StringSetting Input::Joypad2::left (&config_file, "input.joypad2.left", "", "f");
StringSetting Input::Joypad2::right (&config_file, "input.joypad2.right", "", "h");
StringSetting Input::Joypad2::a (&config_file, "input.joypad2.a", "", "k");
StringSetting Input::Joypad2::b (&config_file, "input.joypad2.b", "", "j");
StringSetting Input::Joypad2::x (&config_file, "input.joypad2.x", "", "i");
StringSetting Input::Joypad2::y (&config_file, "input.joypad2.y", "", "u");
StringSetting Input::Joypad2::l (&config_file, "input.joypad2.l", "", "o");
StringSetting Input::Joypad2::r (&config_file, "input.joypad2.r", "", "l");
StringSetting Input::Joypad2::select(&config_file, "input.joypad2.select", "", "lbracket");
StringSetting Input::Joypad2::start (&config_file, "input.joypad2.start", "", "rbracket");
StringSetting Input::Joypad2::up (&config(), "input.joypad2.up", "", "t");
StringSetting Input::Joypad2::down (&config(), "input.joypad2.down", "", "g");
StringSetting Input::Joypad2::left (&config(), "input.joypad2.left", "", "f");
StringSetting Input::Joypad2::right (&config(), "input.joypad2.right", "", "h");
StringSetting Input::Joypad2::a (&config(), "input.joypad2.a", "", "k");
StringSetting Input::Joypad2::b (&config(), "input.joypad2.b", "", "j");
StringSetting Input::Joypad2::x (&config(), "input.joypad2.x", "", "i");
StringSetting Input::Joypad2::y (&config(), "input.joypad2.y", "", "u");
StringSetting Input::Joypad2::l (&config(), "input.joypad2.l", "", "o");
StringSetting Input::Joypad2::r (&config(), "input.joypad2.r", "", "l");
StringSetting Input::Joypad2::select(&config(), "input.joypad2.select", "", "lbracket");
StringSetting Input::Joypad2::start (&config(), "input.joypad2.start", "", "rbracket");
struct Misc {
static IntegerSetting show_frame_counter;
} misc;
IntegerSetting Misc::show_frame_counter(&config(), "misc.show_frame_counter", "Display frame counter", IntegerSetting::Boolean, true);
};

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)) {

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();

View File

@@ -0,0 +1 @@
#include "input.h"

View File

@@ -5,9 +5,7 @@ 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() {}
virtual void term() {}

View File

@@ -1 +0,0 @@
#include "inputui.h"

View File

@@ -1,26 +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; }
};
#endif

143
src/ui/input/xinput.cpp Normal file
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
src/ui/input/xinput.h Normal file
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

View File

@@ -1,115 +1,144 @@
class InputManager { public:
struct Joypad {
uint16 up, down, left, right, a, b, x, y, l, r, select, start;
} joypad1, joypad2;
struct Joystat {
bool up, down, left, right, a, b, x, y, l, r, select, start;
} joystat1, joystat2;
void bind();
void poll();
bool get_status(uint device, uint button);
} input_manager;
void InputManager::bind() {
joypad1.up = keymap::find(config::input.joypad1.up);
joypad1.down = keymap::find(config::input.joypad1.down);
joypad1.left = keymap::find(config::input.joypad1.left);
joypad1.right = keymap::find(config::input.joypad1.right);
joypad1.a = keymap::find(config::input.joypad1.a);
joypad1.b = keymap::find(config::input.joypad1.b);
joypad1.x = keymap::find(config::input.joypad1.x);
joypad1.y = keymap::find(config::input.joypad1.y);
joypad1.l = keymap::find(config::input.joypad1.l);
joypad1.r = keymap::find(config::input.joypad1.r);
joypad1.select = keymap::find(config::input.joypad1.select);
joypad1.start = keymap::find(config::input.joypad1.start);
joystat1.up = joystat1.down = joystat1.left = joystat1.right =
joystat1.a = joystat1.b = joystat1.x = joystat1.y =
joystat1.l = joystat1.r = joystat1.select = joystat1.start = false;
joypad2.up = keymap::find(config::input.joypad2.up);
joypad2.down = keymap::find(config::input.joypad2.down);
joypad2.left = keymap::find(config::input.joypad2.left);
joypad2.right = keymap::find(config::input.joypad2.right);
joypad2.a = keymap::find(config::input.joypad2.a);
joypad2.b = keymap::find(config::input.joypad2.b);
joypad2.x = keymap::find(config::input.joypad2.x);
joypad2.y = keymap::find(config::input.joypad2.y);
joypad2.l = keymap::find(config::input.joypad2.l);
joypad2.r = keymap::find(config::input.joypad2.r);
joypad2.select = keymap::find(config::input.joypad2.select);
joypad2.start = keymap::find(config::input.joypad2.start);
joystat2.up = joystat2.down = joystat2.left = joystat2.right =
joystat2.a = joystat2.b = joystat2.x = joystat2.y =
joystat2.l = joystat2.r = joystat2.select = joystat2.start = false;
}
void InputManager::poll() {
joystat1.up = uiInput->key_down(joypad1.up);
joystat1.down = uiInput->key_down(joypad1.down);
joystat1.left = uiInput->key_down(joypad1.left);
joystat1.right = uiInput->key_down(joypad1.right);
joystat1.a = uiInput->key_down(joypad1.a);
joystat1.b = uiInput->key_down(joypad1.b);
joystat1.x = uiInput->key_down(joypad1.x);
joystat1.y = uiInput->key_down(joypad1.y);
joystat1.l = uiInput->key_down(joypad1.l);
joystat1.r = uiInput->key_down(joypad1.r);
joystat1.select = uiInput->key_down(joypad1.select);
joystat1.start = uiInput->key_down(joypad1.start);
joystat2.up = uiInput->key_down(joypad2.up);
joystat2.down = uiInput->key_down(joypad2.down);
joystat2.left = uiInput->key_down(joypad2.left);
joystat2.right = uiInput->key_down(joypad2.right);
joystat2.a = uiInput->key_down(joypad2.a);
joystat2.b = uiInput->key_down(joypad2.b);
joystat2.x = uiInput->key_down(joypad2.x);
joystat2.y = uiInput->key_down(joypad2.y);
joystat2.l = uiInput->key_down(joypad2.l);
joystat2.r = uiInput->key_down(joypad2.r);
joystat2.select = uiInput->key_down(joypad2.select);
joystat2.start = uiInput->key_down(joypad2.start);
}
bool InputManager::get_status(uint device, uint button) {
switch(device) {
case SNES::DEVICEID_JOYPAD1:
switch(button) {
case SNES::JOYPAD_UP: return joystat1.up;
case SNES::JOYPAD_DOWN: return joystat1.down;
case SNES::JOYPAD_LEFT: return joystat1.left;
case SNES::JOYPAD_RIGHT: return joystat1.right;
case SNES::JOYPAD_A: return joystat1.a;
case SNES::JOYPAD_B: return joystat1.b;
case SNES::JOYPAD_X: return joystat1.x;
case SNES::JOYPAD_Y: return joystat1.y;
case SNES::JOYPAD_L: return joystat1.l;
case SNES::JOYPAD_R: return joystat1.r;
case SNES::JOYPAD_SELECT: return joystat1.select;
case SNES::JOYPAD_START: return joystat1.start;
}
break;
case SNES::DEVICEID_JOYPAD2:
switch(button) {
case SNES::JOYPAD_UP: return joystat2.up;
case SNES::JOYPAD_DOWN: return joystat2.down;
case SNES::JOYPAD_LEFT: return joystat2.left;
case SNES::JOYPAD_RIGHT: return joystat2.right;
case SNES::JOYPAD_A: return joystat2.a;
case SNES::JOYPAD_B: return joystat2.b;
case SNES::JOYPAD_X: return joystat2.x;
case SNES::JOYPAD_Y: return joystat2.y;
case SNES::JOYPAD_L: return joystat2.l;
case SNES::JOYPAD_R: return joystat2.r;
case SNES::JOYPAD_SELECT: return joystat2.select;
case SNES::JOYPAD_START: return joystat2.start;
}
break;
}
return false;
}
class InputManager { public:
struct Joypad {
uint16 up, down, left, right, a, b, x, y, l, r, select, start;
} joypad1, joypad2;
struct Joystat {
bool up, down, left, right, a, b, x, y, l, r, select, start;
} joystat1, joystat2;
uint16 scan();
void bind();
void poll();
bool get_status(uint device, uint button);
} input_manager;
//search all key bindings, return keymap::none if no keys are active
uint16 InputManager::scan() {
uiInput->poll();
for(uint i = 0; i < keymap::limit; i++) {
if(uiInput->key_down(i)) { return i; }
}
for(uint j = 0; j < 16; j++) {
if(uiInput->key_down(keymap::joypad_flag | (j << 16) | keymap::joypad_up)) {
return (keymap::joypad_flag | (j << 16) | keymap::joypad_up);
}
if(uiInput->key_down(keymap::joypad_flag | (j << 16) | keymap::joypad_down)) {
return (keymap::joypad_flag | (j << 16) | keymap::joypad_down);
}
if(uiInput->key_down(keymap::joypad_flag | (j << 16) | keymap::joypad_left)) {
return (keymap::joypad_flag | (j << 16) | keymap::joypad_left);
}
if(uiInput->key_down(keymap::joypad_flag | (j << 16) | keymap::joypad_right)) {
return (keymap::joypad_flag | (j << 16) | keymap::joypad_right);
}
for(uint i = 0; i < 128; i++) {
if(uiInput->key_down(keymap::joypad_flag | (j << 16) | i)) {
return (keymap::joypad_flag | (j << 16) | i);
}
}
}
return keymap::none;
}
void InputManager::bind() {
joypad1.up = keymap::find(config::input.joypad1.up);
joypad1.down = keymap::find(config::input.joypad1.down);
joypad1.left = keymap::find(config::input.joypad1.left);
joypad1.right = keymap::find(config::input.joypad1.right);
joypad1.a = keymap::find(config::input.joypad1.a);
joypad1.b = keymap::find(config::input.joypad1.b);
joypad1.x = keymap::find(config::input.joypad1.x);
joypad1.y = keymap::find(config::input.joypad1.y);
joypad1.l = keymap::find(config::input.joypad1.l);
joypad1.r = keymap::find(config::input.joypad1.r);
joypad1.select = keymap::find(config::input.joypad1.select);
joypad1.start = keymap::find(config::input.joypad1.start);
joystat1.up = joystat1.down = joystat1.left = joystat1.right =
joystat1.a = joystat1.b = joystat1.x = joystat1.y =
joystat1.l = joystat1.r = joystat1.select = joystat1.start = false;
joypad2.up = keymap::find(config::input.joypad2.up);
joypad2.down = keymap::find(config::input.joypad2.down);
joypad2.left = keymap::find(config::input.joypad2.left);
joypad2.right = keymap::find(config::input.joypad2.right);
joypad2.a = keymap::find(config::input.joypad2.a);
joypad2.b = keymap::find(config::input.joypad2.b);
joypad2.x = keymap::find(config::input.joypad2.x);
joypad2.y = keymap::find(config::input.joypad2.y);
joypad2.l = keymap::find(config::input.joypad2.l);
joypad2.r = keymap::find(config::input.joypad2.r);
joypad2.select = keymap::find(config::input.joypad2.select);
joypad2.start = keymap::find(config::input.joypad2.start);
joystat2.up = joystat2.down = joystat2.left = joystat2.right =
joystat2.a = joystat2.b = joystat2.x = joystat2.y =
joystat2.l = joystat2.r = joystat2.select = joystat2.start = false;
}
void InputManager::poll() {
joystat1.up = uiInput->key_down(joypad1.up);
joystat1.down = uiInput->key_down(joypad1.down);
joystat1.left = uiInput->key_down(joypad1.left);
joystat1.right = uiInput->key_down(joypad1.right);
joystat1.a = uiInput->key_down(joypad1.a);
joystat1.b = uiInput->key_down(joypad1.b);
joystat1.x = uiInput->key_down(joypad1.x);
joystat1.y = uiInput->key_down(joypad1.y);
joystat1.l = uiInput->key_down(joypad1.l);
joystat1.r = uiInput->key_down(joypad1.r);
joystat1.select = uiInput->key_down(joypad1.select);
joystat1.start = uiInput->key_down(joypad1.start);
joystat2.up = uiInput->key_down(joypad2.up);
joystat2.down = uiInput->key_down(joypad2.down);
joystat2.left = uiInput->key_down(joypad2.left);
joystat2.right = uiInput->key_down(joypad2.right);
joystat2.a = uiInput->key_down(joypad2.a);
joystat2.b = uiInput->key_down(joypad2.b);
joystat2.x = uiInput->key_down(joypad2.x);
joystat2.y = uiInput->key_down(joypad2.y);
joystat2.l = uiInput->key_down(joypad2.l);
joystat2.r = uiInput->key_down(joypad2.r);
joystat2.select = uiInput->key_down(joypad2.select);
joystat2.start = uiInput->key_down(joypad2.start);
}
bool InputManager::get_status(uint device, uint button) {
switch(device) {
case SNES::DEVICEID_JOYPAD1:
switch(button) {
case SNES::JOYPAD_UP: return joystat1.up;
case SNES::JOYPAD_DOWN: return joystat1.down;
case SNES::JOYPAD_LEFT: return joystat1.left;
case SNES::JOYPAD_RIGHT: return joystat1.right;
case SNES::JOYPAD_A: return joystat1.a;
case SNES::JOYPAD_B: return joystat1.b;
case SNES::JOYPAD_X: return joystat1.x;
case SNES::JOYPAD_Y: return joystat1.y;
case SNES::JOYPAD_L: return joystat1.l;
case SNES::JOYPAD_R: return joystat1.r;
case SNES::JOYPAD_SELECT: return joystat1.select;
case SNES::JOYPAD_START: return joystat1.start;
}
break;
case SNES::DEVICEID_JOYPAD2:
switch(button) {
case SNES::JOYPAD_UP: return joystat2.up;
case SNES::JOYPAD_DOWN: return joystat2.down;
case SNES::JOYPAD_LEFT: return joystat2.left;
case SNES::JOYPAD_RIGHT: return joystat2.right;
case SNES::JOYPAD_A: return joystat2.a;
case SNES::JOYPAD_B: return joystat2.b;
case SNES::JOYPAD_X: return joystat2.x;
case SNES::JOYPAD_Y: return joystat2.y;
case SNES::JOYPAD_L: return joystat2.l;
case SNES::JOYPAD_R: return joystat2.r;
case SNES::JOYPAD_SELECT: return joystat2.select;
case SNES::JOYPAD_START: return joystat2.start;
}
break;
}
return false;
}

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

View File

@@ -1,12 +1,12 @@
namespace event {
void update_frame_counter() {
if(r_ppu->status.frames_updated) {
r_ppu->status.frames_updated = false;
if(bool(true) == true) { //TODO: add config file variable to toggle fps counter
window_main.set_text("%s [%d]", BSNES_TITLE, r_ppu->status.frames_executed);
}
}
namespace event {
void update_frame_counter() {
if(r_ppu->status.frames_updated) {
r_ppu->status.frames_updated = false;
if(config::misc.show_frame_counter == true) {
window_main.set_text(string() << BSNES_TITLE << " [" << r_ppu->status.frames_executed << "]");
}
}
}
void update_video_settings() {
@@ -16,10 +16,25 @@ uint multiplier = minmax<1, 5>(uint(config::video.multiplier));
width *= multiplier;
height *= multiplier;
if(config::video.aspect_correction == true) {
width = uint( double(width) * 8.0 / 7.0 );
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) );
}
}
if(config::video.fullscreen) {
//window_main.menu.hide();
window_main.fullscreen();
window_main.view.move((ui::get_screen_width() - width) / 2, (ui::get_screen_height() - height) / 2);
window_main.view.resize(width, height);
} else {
//window_main.menu.show();
window_main.unfullscreen();
window_main.resize(width, height);
window_main.view.move(0, 0);
window_main.view.resize(width, height);
}
window_main.resize(width, height);
window_main.view.resize(width, height);
}
void update_raster_settings() {
@@ -40,6 +55,16 @@ uint filter, standard;
snes.set_video_standard(standard);
}
void toggle_menu() {
window_main.menu.show(!window_main.menu.visible());
update_video_settings();
}
void toggle_fullscreen() {
config::video.fullscreen = !config::video.fullscreen;
update_video_settings();
}
//
bool load_rom(char *fn) {
@@ -57,7 +82,7 @@ stringarray dir;
strcat(dir[0], dir[1]);
}
return ui::file_load(window_main, fn,
return ui::file_load(&window_main, fn,
"SNES images;*.smc,*.sfc,*.swc,*.fig,*.ufo,*.gd3,*.078,*.st"
#if defined(GZIP_SUPPORT)
",*.gz,*.z,*.zip"
@@ -70,7 +95,7 @@ stringarray dir;
}
void load_rom() {
char fn[4096];
char fn[PATH_MAX];
if(load_rom(fn) == false)return;
if(cartridge.loaded() == true)cartridge.unload();
@@ -78,11 +103,43 @@ char fn[4096];
cartridge.load(fn);
cartridge.load_end();
snes.power();
window_cheat_editor.refresh();
}
void load_rom_st() {
char fn[PATH_MAX];
if(load_rom(fn) == false)return;
if(cartridge.loaded() == true)cartridge.unload();
cartridge.load_begin(Cartridge::CART_ST);
cartridge.load(fn);
cartridge.load_end();
snes.power();
window_cheat_editor.refresh();
}
void load_rom_stdual() {
char fn_a[PATH_MAX], fn_b[PATH_MAX];
if(load_rom(fn_a) == false)return;
if(load_rom(fn_b) == false)return;
if(cartridge.loaded() == true)cartridge.unload();
cartridge.load_begin(Cartridge::CART_STDUAL);
cartridge.load(fn_a);
cartridge.load(fn_b);
cartridge.load_end();
snes.power();
window_cheat_editor.refresh();
}
void unload_rom() {
cartridge.unload();
uiAudio->clear_audio();
if(cartridge.loaded() == true) {
cartridge.unload();
uiVideo->clear_video();
uiAudio->clear_audio();
}
window_main.set_text(BSNES_TITLE);
window_cheat_editor.refresh();
}
void reset() {

View File

@@ -1,11 +1,15 @@
namespace event {
void update_frame_counter();
void update_video_settings();
void update_raster_settings();
void toggle_menu();
void toggle_fullscreen();
bool load_rom(char *fn);
void load_rom();
void load_rom_st();
void load_rom_stdual();
void unload_rom();
void reset();
void power();

View File

@@ -1,3 +1,5 @@
void run();
#if defined(PLATFORM_WIN)
#include "../../lib/libui_win.h"
#elif defined(PLATFORM_X)
@@ -16,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);
@@ -25,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);
@@ -34,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);
@@ -43,31 +45,6 @@ va_list args;
fprintf(stdout, "[%d]: %s\r\n", source, str);
}
#if 0
void set_config_filename(const char *filename) {
realpath(filename, config::filename);
//if argv[0] does not contain path information, obtain from getcwd()
//otherwise, it was retrieved from argv[0] + realpath()
if(strchr(config::filename, '/') == 0 && strchr(config::filename, '\\') == 0) {
getcwd(config::filename, PATH_MAX);
strcat(config::filename, "/");
}
//convert all path delimiters to '/'
for(int i = 0; i < strlen(config::filename); i++) {
if(config::filename[i] == '\\') { config::filename[i] = '/'; }
}
//remove program name from filename
char *p = strrchr(config::filename, '/');
if(p) { *p = 0; }
//finally, append config file name
strcat(config::filename, "/bsnes.cfg");
}
#endif
void set_config_filename() {
userpath(config::filename);
strcat(config::filename, "/.bsnes");
@@ -75,8 +52,46 @@ void set_config_filename() {
strcat(config::filename, "/bsnes.cfg");
}
void get_base_path() {
#if defined(PLATFORM_WIN)
int __stdcall WinMain(HINSTANCE hinstance, HINSTANCE hprevinstance, LPSTR lpcmdline, int ncmdshow) {
char full_name[PATH_MAX];
GetFullPathName(__argv[0], PATH_MAX, full_name, 0);
string t;
strcpy(t, full_name);
if(strlen(t) != 0) {
//remove program name
replace(t, "\\", "/");
for(int i = strlen(t) - 1; i >= 0; i--) {
if(strptr(t)[i] == '/' || strptr(t)[i] == '\\') {
strptr(t)[i] = 0;
break;
}
}
}
if(strend(t, "/") == false) { strcat(t, "/"); }
config::path.base = strptr(t);
#endif
}
void run() {
while(ui::events_pending() == true) { ui::run(); }
if(cartridge.loaded() == true) {
snes.runtoframe();
event::update_frame_counter();
} else { //prevent bsnes from consuming 100% CPU resources when idle
#if defined(PLATFORM_WIN)
Sleep(1);
#elif defined(PLATFORM_X)
usleep(1);
#endif
}
}
#if defined(PLATFORM_WIN)
int __stdcall WinMain(HINSTANCE, HINSTANCE, LPSTR, int) {
int argc = __argc;
char **argv = __argv;
#else
@@ -85,24 +100,28 @@ int main(int argc, char *argv[]) {
//int main(int argc, char *argv[]) {
set_config_filename();
get_base_path();
ui::init();
config_file.load(config::filename);
config_file.save(config::filename); //in case program crashes on first run, config file settings can be modified
config::config().load(config::filename);
config::config().save(config::filename); //in case program crashes on first run, config file settings can be modified
init_snes();
ui_init();
while(_term_ == false) {
while(ui::events_pending() == true) { ui::run(); }
if(cartridge.loaded() == true) {
snes.runtoframe();
event::update_frame_counter();
}
if(argc >= 2) {
cartridge.load_begin(Cartridge::CART_NORMAL);
cartridge.load(argv[1]);
cartridge.load_end();
snes.power();
}
if(cartridge.loaded() == true) { cartridge.unload(); }
while(_term_ == false) {
run();
}
config_file.save(config::filename);
event::unload_rom();
config::config().save(config::filename);
term_snes();
ui_term();
ui::term();

View File

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

View File

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

View File

@@ -1,26 +1,82 @@
int CheatEditorWindow::message(uint id, void *param) {
return 0;
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;
}
void CheatEditorWindow::setup() {
create(0, 475, 355);
int x = 0, y = 0;
list.create(*this, ui::Listbox::Header | ui::Listbox::VerticalScrollAlways, x, y, 475, 285, "Status|Code|Description");
list.add_item("Enabled|0123-4567|Infinite Energy");
list.add_item("Disabled|89ab-cdef|Infinite Lives");
list.autosize_columns();
y += 290;
int bh = ui::Button::ideal_height;
int eh = ui::Editbox::ideal_height;
int lh = 355 - bh - eh - 10;
list.create(*this, ui::Listbox::Header | ui::Listbox::VerticalScrollAlways, x, y, 475, lh, "Status|Code|Description");
y += lh + 5;
add_code.create (*this, 0, x, y, 155, 30, "Add Code");
toggle_code.create(*this, 0, x + 160, y, 155, 30, "Toggle Status");
delete_code.create(*this, 0, x + 320, y, 155, 30, "Delete Code");
y += 35;
add_code.create (*this, 0, x, y, 155, bh, "Add Code");
toggle_code.create(*this, 0, x + 160, y, 155, bh, "Toggle Status");
delete_code.create(*this, 0, x + 320, y, 155, bh, "Delete Code");
y += bh + 5;
code.create(*this, 0, x, y, 155, 30, "<code>");
desc.create(*this, 0, x + 160, y, 315, 30, "<description>");
code.create(*this, 0, x, y, 155, eh, "<code>");
desc.create(*this, 0, x + 160, y, 315, eh, "<description>");
add_code.disable();
toggle_code.disable();
delete_code.disable();
refresh();
}

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;
int message(uint id, void *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;

View File

@@ -58,7 +58,7 @@ uint InputConfigWindow::get_value(uint index) {
return keymap::none;
}
void InputConfigWindow::set_value(uint index, uint value) {
void InputConfigWindow::set_value(uint index, uint16 value) {
switch(index) {
case 0: config::input.joypad1.up = keymap::find(value); break;
case 1: config::input.joypad1.down = keymap::find(value); break;
@@ -92,60 +92,74 @@ void InputConfigWindow::set_value(uint index, uint value) {
void InputConfigWindow::refresh_list() {
for(uint i = 0; i < 24; i++) {
list.set_item(i, "%s|%s", list_index[i], keymap::find(get_value(i)));
list.set_item(i, string() << list_index[i] << "|" << keymap::find(get_value(i)));
}
list.autosize_columns();
}
int InputConfigWindow::message(uint id, void *param) {
if(id == ui::Message::Changed && param == &list) {
bool InputConfigWindow::message(uint id, uintptr_t param) {
ui::Control *control = (ui::Control*)param;
if(id == ui::Message::Changed && control == &list) {
int pos = list.get_selection();
setkey.enable(pos >= 0);
clrkey.enable(pos >= 0);
} else if((id == ui::Message::DoubleClicked && param == &list) ||
(id == ui::Message::Clicked && param == &setkey)) {
return true;
}
if((id == ui::Message::DoubleClicked && control == &list) ||
(id == ui::Message::Clicked && control == &setkey)) {
int pos = list.get_selection();
if(pos < 0) { return 0; }
if(pos < 0) { return true; }
window_input_capture.index = pos;
window_input_capture.label.set_text("Please press a key to assign to '%s' ...", list_index[pos]);
window_input_capture.label.set_text(string() << "Please press a key to assign to " << list_index[pos] << " ...");
window_input_capture.show();
window_input_capture.focus();
} else if(id == ui::Message::Clicked && param == &clrkey) {
return true;
}
if(id == ui::Message::Clicked && control == &clrkey) {
int pos = list.get_selection();
if(pos < 0) { return 0; }
if(pos < 0) { return true; }
set_value(pos, keymap::none);
refresh_list();
return true;
}
return 0;
return true;
}
void InputConfigWindow::setup() {
create(0, 475, 355);
int x = 0, y = 0;
lportA.create(*this, 0, x, y + 7, 105, 15, "Controller Port A:");
portA.create(*this, 0, x + 110, y, 125, 30);
int lh = ui::Label::ideal_height;
int ch = ui::Combobox::ideal_height;
int bh = ui::Button::ideal_height;
int lbh = 355 - lh - ch - bh - 10;
lportA.create(*this, 0, x, y, 235, lh, "Controller Port A:");
lportB.create(*this, 0, x + 240, y, 235, lh, "Controller Port B:");
y += lh;
portA.create (*this, 0, x, y, 235, ch);
portA.add_item("None");
portA.add_item("Joypad 1");
portA.add_item("Joypad 2");
portA.set_selection(1);
portA.disable();
lportB.create(*this, 0, x + 240, y + 7, 105, 15, "Controller Port B:");
portB.create(*this, 0, x + 350, y, 125, 30);
portB.create (*this, 0, x + 240, y, 235, ch);
portB.add_item("None");
portB.add_item("Joypad 1");
portB.add_item("Joypad 2");
portB.set_selection(2);
portB.disable();
y += 35;
y += ch + 5;
list.create(*this, ui::Listbox::Header | ui::Listbox::VerticalScrollAlways, x, y, 475, 285, "Name|Value");
list.create(*this, ui::Listbox::Header | ui::Listbox::VerticalScrollAlways, x, y, 475, lbh, "Name|Value");
for(uint i = 0; i < 24; i++) { list.add_item("???|???"); }
y += 290;
y += lbh + 5;
setkey.create(*this, 0, x, y, 235, 30, "Assign Key");
clrkey.create(*this, 0, x + 240, y, 235, 30, "Unassign Key");
setkey.create(*this, 0, x, y, 235, bh, "Assign Key");
clrkey.create(*this, 0, x + 240, y, 235, bh, "Unassign Key");
refresh_list();
window_input_capture.setup();
@@ -156,19 +170,42 @@ int x = 0, y = 0;
//
int InputCaptureWindow::message(uint id, void *param) {
bool InputCaptureWindow::message(uint id, uintptr_t param) {
if(id == ui::Message::Close) {
hide();
return false;
} else if(id == ui::Message::KeyUp) {
hide();
window_input_config.set_value(index, uint(param));
window_input_config.refresh_list();
}
return 0;
return true;
}
void InputCaptureWindow::show() {
uiInput->clear_input();
//enter and spacebar can be used to activate key capture,
//set lock on these keys if they are held down to prevent
//them from being instantly assigned to selected entry ...
uiInput->poll();
key_lock = (uiInput->key_down(keymap::enter) || uiInput->key_down(keymap::space));
Window::show();
Window::focus();
while(_term_ == false && visible() == true) {
run();
uint16 key = input_manager.scan();
if(key == keymap::none) { key_lock = false; continue; }
if(key_lock && (key == keymap::enter || key == keymap::space)) { continue; }
hide();
window_input_config.set_value(index, key);
window_input_config.refresh_list();
break;
}
uiInput->clear_input();
}
void InputCaptureWindow::setup() {
create(ui::Window::Center, 350, 100, "bsnes Key Capture");
label.create(*this, 0, 5, 5, 340, 90);
int lh = ui::Label::ideal_height;
create(ui::Window::Center, 350, lh * 3, "bsnes Key Capture");
label.create(*this, 0, 5, lh, 340, lh);
}

View File

@@ -1,7 +1,9 @@
class InputCaptureWindow : public ui::Window { public:
ui::Label label;
uint index;
int message(uint id, void *param = 0);
bool key_lock;
bool message(uint id, uintptr_t param = 0);
void show();
void setup();
InputCaptureWindow() : index(0) {}
} window_input_capture;
@@ -17,9 +19,9 @@ ui::Button clrkey;
static const char list_index[][64];
int message(uint id, void *param = 0);
bool message(uint id, uintptr_t param = 0);
uint get_value(uint index);
void set_value(uint index, uint value);
void set_value(uint index, uint16 value);
void refresh_list();
void setup();
} window_input_config;

View File

@@ -1,49 +1,50 @@
int RasterSettingsWindow::message(uint id, void *param) {
bool RasterSettingsWindow::message(uint id, uintptr_t param) {
ui::Control *control = (ui::Control*)param;
if(id == ui::Message::Changed) {
if(param == &contrast) {
if(control == &contrast) {
if(config::snes.contrast != contrast.get_position() - 96) {
config::snes.contrast = contrast.get_position() - 96;
lcontrast.set_text("Contrast: %d", int(config::snes.contrast));
lcontrast.set_text(string() << "Contrast: " << config::snes.contrast);
snes.update_color_lookup_table();
}
} else if(param == &brightness) {
} else if(control == &brightness) {
if(config::snes.brightness != brightness.get_position() - 96) {
config::snes.brightness = brightness.get_position() - 96;
lbrightness.set_text("Brightness: %d", int(config::snes.brightness));
lbrightness.set_text(string() << "Brightness: " << config::snes.brightness);
snes.update_color_lookup_table();
}
} else if(param == &gamma) {
} else if(control == &gamma) {
if(config::snes.gamma != gamma.get_position() + 10) {
config::snes.gamma = gamma.get_position() + 10;
lgamma.set_text("Gamma: %0.2f", double(config::snes.gamma) / 100.0);
lgamma.set_text(string() << "Gamma: " << config::snes.gamma); //TODO: print gamma as "%0.2f" / 100.0
snes.update_color_lookup_table();
}
}
return 0;
return true;
}
if(id == ui::Message::Clicked) {
if(param == &gamma_ramp) {
if(control == &gamma_ramp) {
if(config::snes.gamma_ramp != gamma_ramp.checked()) {
config::snes.gamma_ramp = gamma_ramp.checked();
snes.update_color_lookup_table();
}
} else if(param == &sepia) {
} else if(control == &sepia) {
if(config::snes.sepia != sepia.checked()) {
config::snes.sepia = sepia.checked();
snes.update_color_lookup_table();
}
} else if(param == &grayscale) {
} else if(control == &grayscale) {
if(config::snes.grayscale != grayscale.checked()) {
config::snes.grayscale = grayscale.checked();
snes.update_color_lookup_table();
}
} else if(param == &invert) {
} else if(control == &invert) {
if(config::snes.invert != invert.checked()) {
config::snes.invert = invert.checked();
snes.update_color_lookup_table();
}
} else if(param == &preset_optimal) {
} else if(control == &preset_optimal) {
config::snes.contrast = 0;
config::snes.brightness = 0;
config::snes.gamma = 80;
@@ -52,7 +53,7 @@ int RasterSettingsWindow::message(uint id, void *param) {
config::snes.grayscale = false;
config::snes.invert = false;
sync_ui();
} else if(param == &preset_standard) {
} else if(control == &preset_standard) {
config::snes.contrast = 0;
config::snes.brightness = 0;
config::snes.gamma = 100;
@@ -62,20 +63,20 @@ int RasterSettingsWindow::message(uint id, void *param) {
config::snes.invert = false;
sync_ui();
}
return 0;
return true;
}
return 0;
return true;
}
//update all UI controls to match config file values ...
void RasterSettingsWindow::sync_ui() {
contrast.set_position(config::snes.contrast + 96);
lcontrast.set_text("Contrast: %d", int(config::snes.contrast));
lcontrast.set_text(string() << "Contrast: " << config::snes.contrast);
brightness.set_position(config::snes.brightness + 96);
lbrightness.set_text("Brightness: %d", int(config::snes.brightness));
lbrightness.set_text(string() << "Brightness: " << config::snes.brightness);
gamma.set_position(config::snes.gamma - 10);
lgamma.set_text("Gamma: %0.2f", double(config::snes.gamma) / 100.0);
lgamma.set_text(string() << "Gamma: " << config::snes.gamma); //TODO: print gamma as "%0.2f" / 100.0
gamma_ramp.check(config::snes.gamma_ramp);
sepia.check(config::snes.sepia);
grayscale.check(config::snes.grayscale);
@@ -87,29 +88,32 @@ void RasterSettingsWindow::setup() {
create(0, 475, 355);
int x = 0, y = 0;
lcontrast.create(*this, 0, x, y + 5, 100, 15);
contrast.create(*this, 0, x + 100, y, 375, 25, 192);
y += 25;
int sh = ui::Slider::ideal_height;
int kh = ui::Checkbox::ideal_height;
int bh = ui::Button::ideal_height;
lcontrast.create(*this, 0, x, y, 100, sh);
contrast.create (*this, 0, x + 100, y, 375, sh, 192);
y += sh;
lbrightness.create(*this, 0, x, y + 5, 100, 15);
brightness.create(*this, 0, x + 100, y, 375, 25, 192);
y += 25;
lbrightness.create(*this, 0, x, y, 100, sh);
brightness.create (*this, 0, x + 100, y, 375, sh, 192);
y += sh;
lgamma.create(*this, 0, x, y + 5, 100, 15);
gamma.create(*this, 0, x + 100, y, 375, 25, 191);
y += 25;
lgamma.create(*this, 0, x, y, 100, sh);
gamma.create (*this, 0, x + 100, y, 375, sh, 191);
y += sh;
gamma_ramp.create(*this, 0, x, y, 235, 20, "Gamma ramp");
sepia.create(*this, 0, x + 240, y, 235, 20, "Sepia");
y += 20;
gamma_ramp.create(*this, 0, x, y, 235, kh, "Gamma ramp");
sepia.create(*this, 0, x + 240, y, 235, kh, "Sepia");
y += kh;
grayscale.create(*this, 0, x, y, 235, 20, "Grayscale");
invert.create(*this, 0, x + 240, y, 235, 20, "Invert colors");
y += 20;
grayscale.create(*this, 0, x, y, 235, kh, "Grayscale");
invert.create(*this, 0, x + 240, y, 235, kh, "Invert colors");
y += kh + 5;
y += 5;
preset_optimal.create (*this, 0, x, y, 235, 30, "Optimal Preset");
preset_standard.create(*this, 0, x + 240, y, 235, 30, "Standard Preset");
preset_optimal.create (*this, 0, x, y, 235, bh, "Optimal Preset");
preset_standard.create(*this, 0, x + 240, y, 235, bh, "Standard Preset");
y += bh;
sync_ui();
}

View File

@@ -14,7 +14,7 @@ ui::Checkbox invert;
ui::Button preset_optimal;
ui::Button preset_standard;
int message(uint id, void *param);
bool message(uint id, uintptr_t param);
void setup();
void sync_ui();
} window_raster_settings;

View File

@@ -1,9 +1,10 @@
void SettingsWindow::setup() {
create(ui::Window::Center, 640, 365, "bsnes Configuration Settings");
panel_list.create(*this, 0, 5, 5, 150, 355);
//panel_list.add_item("Video Settings");
panel_list.add_item("Raster Settings");
panel_list.add_item("Input Configuration");
//panel_list.add_item("Cheat Code Editor");
panel_list.add_item("Cheat Code Editor");
panel_list.add_item("Advanced");
panel.create(*this, 0, 160, 5, 475, 355);
@@ -15,22 +16,23 @@ void SettingsWindow::show() {
panel_list.focus();
}
int SettingsWindow::message(uint id, void *param) {
bool SettingsWindow::message(uint id, uintptr_t param) {
if(id == ui::Message::Close) {
hide();
return false;
}
if(id == ui::Message::Changed && param == &panel_list) {
if(id == ui::Message::Changed && (ui::Control*)param == &panel_list) {
switch(panel_list.get_selection()) {
//case 0: panel.attach(window_video_settings); break;
case 0: panel.attach(window_raster_settings); break;
case 1: panel.attach(window_input_config); break;
//case 2: panel.attach(window_cheat_editor); break;
case 2: panel.attach(window_advanced); break;
case 2: panel.attach(window_cheat_editor); break;
case 3: panel.attach(window_advanced); break;
}
panel_list.focus();
return 0;
return true;
}
return 0;
return true;
}

View File

@@ -1,7 +1,7 @@
class SettingsWindow : public ui::Window { public:
ui::Listbox panel_list;
ui::Panel panel;
int message(uint id, void *param);
bool message(uint id, uintptr_t param = 0);
void show();
void setup();
} window_settings;

View File

@@ -0,0 +1,59 @@
bool VideoSettingsWindow::message(uint id, uintptr_t param) {
return true;
}
void VideoSettingsWindow::setup() {
create(0, 475, 355);
int x = 0, y = 0;
int lh = ui::Label::ideal_height;
int ch = ui::Combobox::ideal_height;
int kh = ui::Checkbox::ideal_height;
int eh = ui::Editbox::ideal_height;
label_select.create(*this, 0, x, y, 475, lh, "Select video mode to configure:");
y += lh;
mode_select.create(*this, 0, x, y, 475, ch);
mode_select.add_item("Windowed mode");
mode_select.add_item("Pseudo-fullscreen mode");
mode_select.add_item("Fullscreen mode");
y += ch + 5;
label_region.create(*this, 0, x, y, 235, lh, "Region:");
label_scalar.create(*this, 0, x + 240, y, 235, lh, "Scale:");
y += lh;
region.create(*this, 0, x, y, 235, ch);
region.add_item("NTSC");
region.add_item("PAL");
scalar.create(*this, 0, x + 240, y, 235, ch);
scalar.add_item("100%");
scalar.add_item("200%");
scalar.add_item("300%");
scalar.add_item("400%");
scalar.add_item("500%");
y += ch;
label_hwfilter.create(*this, 0, x, y, 235, lh, "Hardware filter:");
label_swfilter.create(*this, 0, x + 240, y, 235, lh, "Software filter:");
y += lh;
hwfilter.create(*this, 0, x, y, 235, ch);
hwfilter.add_item("Point");
hwfilter.add_item("Linear");
swfilter.create(*this, 0, x + 240, y, 235, ch);
swfilter.add_item("None");
swfilter.add_item("NTSC");
swfilter.add_item("HQ2x");
swfilter.add_item("Scale2x");
y += ch + 5;
aspect.create(*this, 0, x, y, 235, kh, "Correct aspect ratio");
vsync.create(*this, 0, x + 240, y, 235, kh, "Sync to vertical retrace");
y += kh + 5;
label_fsmode.create(*this, 0, x, y, 475, lh, "Fullscreen mode (width x height x refresh rate):");
y += lh;
fs_width.create(*this, 0, x, y, 155, eh, "<width>");
fs_height.create(*this, 0, x + 160, y, 155, eh, "<height>");
fs_refresh.create(*this, 0, x + 320, y, 155, eh, "<refresh_rate>");
y += eh + 5;
}

View File

@@ -0,0 +1,14 @@
class VideoSettingsWindow : public ui::Window { public:
ui::Label label_select;
ui::Combobox mode_select;
ui::Label label_region, label_scalar, label_hwfilter, label_swfilter;
ui::Combobox region, scalar, hwfilter, swfilter;
ui::Checkbox aspect, vsync;
ui::Label label_fsmode;
ui::Editbox fs_width, fs_height, fs_refresh;
bool message(uint, uintptr_t);
void setup();
} window_video_settings;

View File

@@ -2,6 +2,7 @@
#include "ui_about.cpp"
#include "settings/ui_settings.cpp"
#include "settings/ui_videosettings.cpp"
#include "settings/ui_rastersettings.cpp"
#include "settings/ui_inputconfig.cpp"
#include "settings/ui_cheateditor.cpp"
@@ -20,14 +21,14 @@
#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();
window_video_settings.setup();
window_raster_settings.setup();
window_input_config.setup();
window_cheat_editor.setup();
@@ -49,7 +50,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 +61,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();

View File

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

View File

@@ -13,11 +13,11 @@ void AboutWindow::setup() {
about.create(*this, 0, 5, 5, 290, 120, about_text);
}
int AboutWindow::message(uint id, void *param) {
bool AboutWindow::message(uint id, uintptr_t param) {
if(id == ui::Message::Close) {
hide();
return false;
}
return 0;
return true;
}

View File

@@ -1,6 +1,6 @@
class AboutWindow : public ui::Window { public:
ui::Label about;
static const char about_text[4096];
int message(uint id, void *param);
bool message(uint id, uintptr_t param);
void setup();
} window_about;

View File

@@ -1,4 +1,15 @@
int MainWindow::message(uint id, void *param) {
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) {
_term_ = true;
hide();
@@ -7,114 +18,125 @@ int MainWindow::message(uint id, void *param) {
if(id == ui::Message::Block) {
if(uiAudio) { uiAudio->clear_audio(); }
return true;
}
if(id == ui::Message::KeyDown) {
if(uiInput) { uiInput->signal_key_down((int)param); }
return 0;
}
if(id == ui::Message::KeyUp) {
if(uiInput) { uiInput->signal_key_up((int)param); }
return 0;
if(param == keymap::esc) { event::toggle_menu(); }
if(param == keymap::f11) { event::toggle_fullscreen(); }
return true;
}
if(id == ui::Message::Clicked) {
if(param == &menu_file_load) {
if(control == &menu_file_load) {
event::load_rom();
}
if(param == &menu_file_unload) {
if(control == &menu_file_load_st) {
event::load_rom_st();
}
if(control == &menu_file_load_stdual) {
event::load_rom_stdual();
}
if(control == &menu_file_unload) {
event::unload_rom();
}
if(param == &menu_file_reset) {
if(control == &menu_file_reset) {
event::reset();
}
if(param == &menu_file_power) {
if(control == &menu_file_power) {
event::power();
}
if(param == &menu_file_exit) {
if(control == &menu_file_exit) {
message(ui::Message::Close);
}
if(param == &menu_settings_videomode_1x) { config::video.multiplier = 1; event::update_video_settings(); }
if(param == &menu_settings_videomode_2x) { config::video.multiplier = 2; event::update_video_settings(); }
if(param == &menu_settings_videomode_3x) { config::video.multiplier = 3; event::update_video_settings(); }
if(param == &menu_settings_videomode_4x) { config::video.multiplier = 4; event::update_video_settings(); }
if(param == &menu_settings_videomode_5x) { config::video.multiplier = 5; event::update_video_settings(); }
if(control == &menu_settings_videomode_1x) { config::video.multiplier = 1; event::update_video_settings(); }
if(control == &menu_settings_videomode_2x) { config::video.multiplier = 2; event::update_video_settings(); }
if(control == &menu_settings_videomode_3x) { config::video.multiplier = 3; event::update_video_settings(); }
if(control == &menu_settings_videomode_4x) { config::video.multiplier = 4; event::update_video_settings(); }
if(control == &menu_settings_videomode_5x) { config::video.multiplier = 5; event::update_video_settings(); }
if(param == &menu_settings_videomode_aspect_correction) {
if(control == &menu_settings_videomode_aspect_correction) {
config::video.aspect_correction = menu_settings_videomode_aspect_correction.checked();
event::update_video_settings();
}
if(param == &menu_settings_videomode_ntsc) { config::video.region = 0; event::update_raster_settings(); event::update_video_settings(); }
if(param == &menu_settings_videomode_pal) { config::video.region = 1; event::update_raster_settings(); event::update_video_settings(); }
if(control == &menu_settings_videomode_ntsc) { config::video.region = 0; event::update_raster_settings(); event::update_video_settings(); }
if(control == &menu_settings_videomode_pal) { config::video.region = 1; event::update_raster_settings(); event::update_video_settings(); }
if(param == &menu_settings_videofilter_hwpoint) { config::video.hardware_filter = 0; uiVideo->update_hardware_filter(); }
if(param == &menu_settings_videofilter_hwlinear) { config::video.hardware_filter = 1; uiVideo->update_hardware_filter(); }
if(control == &menu_settings_videofilter_hwpoint) { config::video.hardware_filter = 0; uiVideo->update_hardware_filter(); }
if(control == &menu_settings_videofilter_hwlinear) { config::video.hardware_filter = 1; uiVideo->update_hardware_filter(); }
if(param == &menu_settings_videofilter_swnone) { config::video.software_filter = 0; event::update_raster_settings(); }
if(param == &menu_settings_videofilter_swntsc) { config::video.software_filter = 1; event::update_raster_settings(); }
if(param == &menu_settings_videofilter_swhq2x) { config::video.software_filter = 2; event::update_raster_settings(); }
if(param == &menu_settings_videofilter_swscale2x) { config::video.software_filter = 3; event::update_raster_settings(); }
if(control == &menu_settings_videofilter_swnone) { config::video.software_filter = 0; event::update_raster_settings(); }
if(control == &menu_settings_videofilter_swntsc) { config::video.software_filter = 1; event::update_raster_settings(); }
if(control == &menu_settings_videofilter_swhq2x) { config::video.software_filter = 2; event::update_raster_settings(); }
if(control == &menu_settings_videofilter_swscale2x) { config::video.software_filter = 3; event::update_raster_settings(); }
if(param == &menu_settings_videoframeskip_0) { config::video.frameskip = 0; }
if(param == &menu_settings_videoframeskip_1) { config::video.frameskip = 1; }
if(param == &menu_settings_videoframeskip_2) { config::video.frameskip = 2; }
if(param == &menu_settings_videoframeskip_3) { config::video.frameskip = 3; }
if(param == &menu_settings_videoframeskip_4) { config::video.frameskip = 4; }
if(param == &menu_settings_videoframeskip_5) { config::video.frameskip = 5; }
if(param == &menu_settings_videoframeskip_6) { config::video.frameskip = 6; }
if(param == &menu_settings_videoframeskip_7) { config::video.frameskip = 7; }
if(param == &menu_settings_videoframeskip_8) { config::video.frameskip = 8; }
if(param == &menu_settings_videoframeskip_9) { config::video.frameskip = 9; }
if(control == &menu_settings_videoframeskip_0) { config::video.frameskip = 0; }
if(control == &menu_settings_videoframeskip_1) { config::video.frameskip = 1; }
if(control == &menu_settings_videoframeskip_2) { config::video.frameskip = 2; }
if(control == &menu_settings_videoframeskip_3) { config::video.frameskip = 3; }
if(control == &menu_settings_videoframeskip_4) { config::video.frameskip = 4; }
if(control == &menu_settings_videoframeskip_5) { config::video.frameskip = 5; }
if(control == &menu_settings_videoframeskip_6) { config::video.frameskip = 6; }
if(control == &menu_settings_videoframeskip_7) { config::video.frameskip = 7; }
if(control == &menu_settings_videoframeskip_8) { config::video.frameskip = 8; }
if(control == &menu_settings_videoframeskip_9) { config::video.frameskip = 9; }
if(param == &menu_settings_mute) {
if(control == &menu_settings_mute) {
config::snes.mute = menu_settings_mute.checked();
}
if(param == &menu_settings_speedreg_enable) {
if(control == &menu_settings_speedreg_enable) {
config::system.regulate_speed = menu_settings_speedreg_enable.checked();
}
if(param == &menu_settings_speedreg_slowest) { config::system.speed = 1; uiAudio->update_frequency(); }
if(param == &menu_settings_speedreg_slow) { config::system.speed = 2; uiAudio->update_frequency(); }
if(param == &menu_settings_speedreg_normal) { config::system.speed = 3; uiAudio->update_frequency(); }
if(param == &menu_settings_speedreg_fast) { config::system.speed = 4; uiAudio->update_frequency(); }
if(param == &menu_settings_speedreg_fastest) { config::system.speed = 5; uiAudio->update_frequency(); }
if(control == &menu_settings_speedreg_slowest) { config::system.speed = 1; uiAudio->update_frequency(); }
if(control == &menu_settings_speedreg_slow) { config::system.speed = 2; uiAudio->update_frequency(); }
if(control == &menu_settings_speedreg_normal) { config::system.speed = 3; uiAudio->update_frequency(); }
if(control == &menu_settings_speedreg_fast) { config::system.speed = 4; uiAudio->update_frequency(); }
if(control == &menu_settings_speedreg_fastest) { config::system.speed = 5; uiAudio->update_frequency(); }
if(param == &menu_settings_config) { window_settings.show(); }
if(control == &menu_settings_config) { window_settings.show(); }
if(param == &menu_misc_logaudio) {
if(control == &menu_misc_logaudio) {
(menu_misc_logaudio.checked() == true) ? snes.log_audio_enable() : snes.log_audio_disable();
}
if(param == &menu_misc_about) {
if(control == &menu_misc_about) {
window_about.focus();
}
return 0;
return true;
}
return 0;
return true;
}
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_unload.create(menu_file, "Unload");
menu_file_load.create(menu_file, "Load Cartridge ...");
menu_file_load_special.create(menu_file, "Load Special");
menu_file_load_st.create(menu_file_load_special, "Load ST Cartridge ...");
menu_file_load_stdual.create(menu_file_load_special, "Load ST Dual Cartridge ...");
menu_file_load_special.finish();
menu_file_unload.create(menu_file, "Unload Cartridge");
menu_file_sep1.create(menu_file);
menu_file_reset.create(menu_file, "Reset");
menu_file_power.create(menu_file, "Power");
menu_file_reset.create(menu_file, "Reset System");
menu_file_power.create(menu_file, "Power Cycle System");
menu_file_sep2.create(menu_file);
menu_file_exit.create(menu_file, "Exit");
menu_file.finish();
@@ -171,7 +193,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");
@@ -206,7 +228,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");

Some files were not shown because too many files have changed in this diff Show More