moved fceu to its own folder to make room for other projects

[[Split portion of a mixed commit.]]
This commit is contained in:
zeromus 2006-07-29 05:46:15 +00:00
parent fd8e229ccf
commit 80d0d46d73
500 changed files with 138746 additions and 0 deletions

28
AUTHORS Normal file
View File

@ -0,0 +1,28 @@
A list of people who have contributed code to FCE Ultra, or have had their code placed in
FCE Ultra by others.
Please note that the "Contributions" field may not be all inclusive;
the coder may have done more than what is listed.
Name/Alias Contributions Contact Information
------------------------------------------------------------------------------
Aaron Oneal Many changes to compile http://www.morphgear.com
with MSVC and first frame
skipping code.
Lukas Sabota build system punkrockguy318 at comcast.net
BERO Base FCE code. bero at geocities.co.jp
CaH4e3 Some mapper code. CaH4e3 at mail dot ru
Joe Nahmias man pages.
Mitsutaka YM2413 emulator.
Okazaki
Paul Various code for the official kuliniew at purdue.edu
Kuliniewicz SDL port.
Quietust VRC7 "translation" code. quietust at ircN dot org
Xodnizel Most of the FCE Ultra code. http://xodnizel.net/

341
COPYING Normal file
View File

@ -0,0 +1,341 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

2183
ChangeLog Normal file

File diff suppressed because it is too large Load Diff

3
INSTALL Normal file
View File

@ -0,0 +1,3 @@
To compile and install FCE Ultra, follow these steps:
1. Ensure that SCons is installed on your system.
2. Run "scons"

1
NEWS Normal file
View File

@ -0,0 +1 @@
NEWS!

0
README Normal file
View File

16
SConstruct Normal file
View File

@ -0,0 +1,16 @@
CCFLAGS= ''
LINKFLAGS = ''
env = Environment(CCFLAGS=CCFLAGS, LINKFLAGS=LINKFLAGS)
conf = Configure(env)
if not conf.CheckLib('SDL'):
print 'Did not find libSDL.a or SDL.lib, exiting!'
Exit(1)
if not conf.CheckLib('z'):
print 'Did not find libz.a or z.lib, exiting!'
Exit(1)
env = conf.Finish()
Export('env')
SConscript(['src/SConscript'])

46
TODO-PROJECT Normal file
View File

@ -0,0 +1,46 @@
Items to be completed before 2.0 release
Linux build - soules
* clean-up scons files to manage SDL variables properly
* go through #def variables and figure out which we need and which we don't
* lots of testing and code clean-up
Windows build - zeromus
* verify rerecording
* verify debugging
* verify avi writing
Investigate OSX build [ http://www.lamer0.com/ ] - zeromus
Commandline parsing - lukas
* verify in windows - zeromus
Configfile parsing - lukas
* verify in windows - zeromus
Merge garnet and sf repos - zeromus
* Ensure gnome frontend is in repo
Do we really need vc7 project? Cah4e3? - zeromus
Source code docs cleaning - zeromus
Homepage docs - zeromus
Move to sourceforge SVN = zeromus
Move to scons - lukas
Freenode registration - lukas
* homepage update
Default hotkey philosophy (there are different philosophies from XD and TAS) - ALL
Netplay - FUTURE STUFF
Move server code into tree
Ensure netplay compiles
--strategic--
Release:
* Press Release - ??
* Homepage updates
- eliminate old news, add new links

17
attic/.gitignore vendored Normal file
View File

@ -0,0 +1,17 @@
# A simulation of Subversion default ignores, generated by reposurgeon.
*.o
*.lo
*.la
*.al
*.libs
*.so
*.so.[0-9]*
*.a
*.pyc
*.pyo
*.rej
*~
*.#*
.*.swp
.DS_store
# Simulated Subversion default ignores end here

1
attic/Makefile.am Normal file
View File

@ -0,0 +1 @@
SUBDIRS = src

357
attic/acinclude.m4 Normal file
View File

@ -0,0 +1,357 @@
# Configure paths for SDL
# Sam Lantinga 9/21/99
# stolen from Manish Singh
# stolen back from Frank Belew
# stolen from Manish Singh
# Shamelessly stolen from Owen Taylor
dnl AM_PATH_SDL([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]])
dnl Test for SDL, and define SDL_CFLAGS and SDL_LIBS
dnl
AC_DEFUN(AM_PATH_SDL,
[dnl
dnl Get the cflags and libraries from the sdl-config script
dnl
AC_ARG_WITH(sdl-prefix,[ --with-sdl-prefix=PFX Prefix where SDL is installed (optional)],
sdl_prefix="$withval", sdl_prefix="")
AC_ARG_WITH(sdl-exec-prefix,[ --with-sdl-exec-prefix=PFX Exec prefix where SDL is installed (optional)],
sdl_exec_prefix="$withval", sdl_exec_prefix="")
AC_ARG_ENABLE(sdltest, [ --disable-sdltest Do not try to compile and run a test SDL program],
, enable_sdltest=yes)
if test x$sdl_exec_prefix != x ; then
sdl_args="$sdl_args --exec-prefix=$sdl_exec_prefix"
if test x${SDL_CONFIG+set} != xset ; then
SDL_CONFIG=$sdl_exec_prefix/bin/sdl-config
fi
fi
if test x$sdl_prefix != x ; then
sdl_args="$sdl_args --prefix=$sdl_prefix"
if test x${SDL_CONFIG+set} != xset ; then
SDL_CONFIG=$sdl_prefix/bin/sdl-config
fi
fi
AC_PATH_PROG(SDL_CONFIG, sdl-config, no)
min_sdl_version=ifelse([$1], ,0.11.0,$1)
AC_MSG_CHECKING(for SDL - version >= $min_sdl_version)
no_sdl=""
if test "$SDL_CONFIG" = "no" ; then
no_sdl=yes
else
SDL_CFLAGS=`$SDL_CONFIG $sdlconf_args --cflags`
SDL_LIBS=`$SDL_CONFIG $sdlconf_args --libs`
sdl_major_version=`$SDL_CONFIG $sdl_args --version | \
sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
sdl_minor_version=`$SDL_CONFIG $sdl_args --version | \
sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
sdl_micro_version=`$SDL_CONFIG $sdl_config_args --version | \
sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
if test "x$enable_sdltest" = "xyes" ; then
ac_save_CFLAGS="$CFLAGS"
ac_save_LIBS="$LIBS"
CFLAGS="$CFLAGS $SDL_CFLAGS"
LIBS="$LIBS $SDL_LIBS"
dnl
dnl Now check if the installed SDL is sufficiently new. (Also sanity
dnl checks the results of sdl-config to some extent
dnl
rm -f conf.sdltest
AC_TRY_RUN([
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <SDL.h>
char*
my_strdup (char *str)
{
char *new_str;
if (str)
{
new_str = (char *)malloc ((strlen (str) + 1) * sizeof(char));
strcpy (new_str, str);
}
else
new_str = NULL;
return new_str;
}
int main (int argc, char *argv[])
{
int major, minor, micro;
char *tmp_version;
/* This hangs on some systems (?)
system ("touch conf.sdltest");
*/
{ FILE *fp = fopen("conf.sdltest", "a"); if ( fp ) fclose(fp); }
/* HP/UX 9 (%@#!) writes to sscanf strings */
tmp_version = my_strdup("$min_sdl_version");
if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, &micro) != 3) {
printf("%s, bad version string\n", "$min_sdl_version");
exit(1);
}
if (($sdl_major_version > major) ||
(($sdl_major_version == major) && ($sdl_minor_version > minor)) ||
(($sdl_major_version == major) && ($sdl_minor_version == minor) && ($sdl_micro_version >= micro)))
{
return 0;
}
else
{
printf("\n*** 'sdl-config --version' returned %d.%d.%d, but the minimum version\n", $sdl_major_version, $sdl_minor_version, $sdl_micro_version);
printf("*** of SDL required is %d.%d.%d. If sdl-config is correct, then it is\n", major, minor, micro);
printf("*** best to upgrade to the required version.\n");
printf("*** If sdl-config was wrong, set the environment variable SDL_CONFIG\n");
printf("*** to point to the correct copy of sdl-config, and remove the file\n");
printf("*** config.cache before re-running configure\n");
return 1;
}
}
],, no_sdl=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
CFLAGS="$ac_save_CFLAGS"
LIBS="$ac_save_LIBS"
fi
fi
if test "x$no_sdl" = x ; then
AC_MSG_RESULT(yes)
ifelse([$2], , :, [$2])
else
AC_MSG_RESULT(no)
if test "$SDL_CONFIG" = "no" ; then
echo "*** The sdl-config script installed by SDL could not be found"
echo "*** If SDL was installed in PREFIX, make sure PREFIX/bin is in"
echo "*** your path, or set the SDL_CONFIG environment variable to the"
echo "*** full path to sdl-config."
else
if test -f conf.sdltest ; then
:
else
echo "*** Could not run SDL test program, checking why..."
CFLAGS="$CFLAGS $SDL_CFLAGS"
LIBS="$LIBS $SDL_LIBS"
AC_TRY_LINK([
#include <stdio.h>
#include <SDL.h>
], [ return 0; ],
[ echo "*** The test program compiled, but did not run. This usually means"
echo "*** that the run-time linker is not finding SDL or finding the wrong"
echo "*** version of SDL. If it is not finding SDL, you'll need to set your"
echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
echo "*** to the installed location Also, make sure you have run ldconfig if that"
echo "*** is required on your system"
echo "***"
echo "*** If you have an old version installed, it is best to remove it, although"
echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"],
[ echo "*** The test program failed to compile or link. See the file config.log for the"
echo "*** exact error that occured. This usually means SDL was incorrectly installed"
echo "*** or that you have moved SDL since it was installed. In the latter case, you"
echo "*** may want to edit the sdl-config script: $SDL_CONFIG" ])
CFLAGS="$ac_save_CFLAGS"
LIBS="$ac_save_LIBS"
fi
fi
SDL_CFLAGS=""
SDL_LIBS=""
ifelse([$3], , :, [$3])
fi
AC_SUBST(SDL_CFLAGS)
AC_SUBST(SDL_LIBS)
rm -f conf.sdltest
])
/* Steven G. Johnson <stevenj@alum.mit.edu> and Alejandro Forero Cuervo <bachue@bachue.com> */
AC_DEFUN([ACX_PTHREAD], [
AC_REQUIRE([AC_CANONICAL_HOST])
AC_LANG_SAVE
AC_LANG_C
acx_pthread_ok=no
# We used to check for pthread.h first, but this fails if pthread.h
# requires special compiler flags (e.g. on True64 or Sequent).
# It gets checked for in the link test anyway.
# First of all, check if the user has set any of the PTHREAD_LIBS,
# etcetera environment variables, and if threads linking works using
# them:
if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
save_LIBS="$LIBS"
LIBS="$PTHREAD_LIBS $LIBS"
AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes)
AC_MSG_RESULT($acx_pthread_ok)
if test x"$acx_pthread_ok" = xno; then
PTHREAD_LIBS=""
PTHREAD_CFLAGS=""
fi
LIBS="$save_LIBS"
CFLAGS="$save_CFLAGS"
fi
# We must check for the threads library under a number of different
# names; the ordering is very important because some systems
# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
# libraries is broken (non-POSIX).
# Create a list of thread flags to try. Items starting with a "-" are
# C compiler flags, and other items are library names, except for "none"
# which indicates that we try without any flags at all.
acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt"
# The ordering *is* (sometimes) important. Some notes on the
# individual items follow:
# pthreads: AIX (must check this before -lpthread)
# none: in case threads are in libc; should be tried before -Kthread and
# other compiler flags to prevent continual compiler warnings
# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
# -pthreads: Solaris/gcc
# -mthreads: Mingw32/gcc, Lynx/gcc
# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
# doesn't hurt to check since this sometimes defines pthreads too;
# also defines -D_REENTRANT)
# pthread: Linux, etcetera
# --thread-safe: KAI C++
case "${host_cpu}-${host_os}" in
*solaris*)
# On Solaris (at least, for some versions), libc contains stubbed
# (non-functional) versions of the pthreads routines, so link-based
# tests will erroneously succeed. (We need to link with -pthread or
# -lpthread.) (The stubs are missing pthread_cleanup_push, or rather
# a function called by this macro, so we could check for that, but
# who knows whether they'll stub that too in a future libc.) So,
# we'll just look for -pthreads and -lpthread first:
acx_pthread_flags="-pthread -pthreads pthread -mt $acx_pthread_flags"
;;
esac
if test x"$acx_pthread_ok" = xno; then
for flag in $acx_pthread_flags; do
case $flag in
none)
AC_MSG_CHECKING([whether pthreads work without any flags])
;;
-*)
AC_MSG_CHECKING([whether pthreads work with $flag])
PTHREAD_CFLAGS="$flag"
;;
*)
AC_MSG_CHECKING([for the pthreads library -l$flag])
PTHREAD_LIBS="-l$flag"
;;
esac
save_LIBS="$LIBS"
save_CFLAGS="$CFLAGS"
LIBS="$PTHREAD_LIBS $LIBS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
# Check for various functions. We must include pthread.h,
# since some functions may be macros. (On the Sequent, we
# need a special flag -Kthread to make this header compile.)
# We check for pthread_join because it is in -lpthread on IRIX
# while pthread_create is in libc. We check for pthread_attr_init
# due to DEC craziness with -lpthreads. We check for
# pthread_cleanup_push because it is one of the few pthread
# functions on Solaris that doesn't have a non-functional libc stub.
# We try pthread_create on general principles.
AC_TRY_LINK([#include <pthread.h>],
[pthread_t th; pthread_join(th, 0);
pthread_attr_init(0); pthread_cleanup_push(0, 0);
pthread_create(0,0,0,0); pthread_cleanup_pop(0); ],
[acx_pthread_ok=yes])
LIBS="$save_LIBS"
CFLAGS="$save_CFLAGS"
AC_MSG_RESULT($acx_pthread_ok)
if test "x$acx_pthread_ok" = xyes; then
break;
fi
PTHREAD_LIBS=""
PTHREAD_CFLAGS=""
done
fi
# Various other checks:
if test "x$acx_pthread_ok" = xyes; then
save_LIBS="$LIBS"
LIBS="$PTHREAD_LIBS $LIBS"
save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
# Detect AIX lossage: threads are created detached by default
# and the JOINABLE attribute has a nonstandard name (UNDETACHED).
AC_MSG_CHECKING([for joinable pthread attribute])
AC_TRY_LINK([#include <pthread.h>],
[int attr=PTHREAD_CREATE_JOINABLE;],
ok=PTHREAD_CREATE_JOINABLE, ok=unknown)
if test x"$ok" = xunknown; then
AC_TRY_LINK([#include <pthread.h>],
[int attr=PTHREAD_CREATE_UNDETACHED;],
ok=PTHREAD_CREATE_UNDETACHED, ok=unknown)
fi
if test x"$ok" != xPTHREAD_CREATE_JOINABLE; then
AC_DEFINE(PTHREAD_CREATE_JOINABLE, $ok,
[Define to the necessary symbol if this constant
uses a non-standard name on your system.])
fi
AC_MSG_RESULT(${ok})
if test x"$ok" = xunknown; then
AC_MSG_WARN([we do not know how to create joinable pthreads])
fi
AC_MSG_CHECKING([if more special flags are required for pthreads])
flag=no
case "${host_cpu}-${host_os}" in
*-aix* | *-freebsd*) flag="-D_THREAD_SAFE";;
*solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";;
esac
AC_MSG_RESULT(${flag})
if test "x$flag" != xno; then
PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
fi
LIBS="$save_LIBS"
CFLAGS="$save_CFLAGS"
# More AIX lossage: must compile with cc_r
AC_CHECK_PROG(PTHREAD_CC, cc_r, cc_r, ${CC})
else
PTHREAD_CC="$CC"
fi
AC_SUBST(PTHREAD_LIBS)
AC_SUBST(PTHREAD_CFLAGS)
AC_SUBST(PTHREAD_CC)
# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
if test x"$acx_pthread_ok" = xyes; then
ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1])
:
else
acx_pthread_ok=no
$2
fi
AC_LANG_RESTORE
])dnl ACX_PTHREAD

879
attic/aclocal.m4 vendored Normal file
View File

@ -0,0 +1,879 @@
# generated automatically by aclocal 1.9.5 -*- Autoconf -*-
# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
# 2005 Free Software Foundation, Inc.
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
# Copyright (C) 2002, 2003, 2005 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_AUTOMAKE_VERSION(VERSION)
# ----------------------------
# Automake X.Y traces this macro to ensure aclocal.m4 has been
# generated from the m4 files accompanying Automake X.Y.
AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version="1.9"])
# AM_SET_CURRENT_AUTOMAKE_VERSION
# -------------------------------
# Call AM_AUTOMAKE_VERSION so it can be traced.
# This function is AC_REQUIREd by AC_INIT_AUTOMAKE.
AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
[AM_AUTOMAKE_VERSION([1.9.5])])
# AM_AUX_DIR_EXPAND -*- Autoconf -*-
# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets
# $ac_aux_dir to `$srcdir/foo'. In other projects, it is set to
# `$srcdir', `$srcdir/..', or `$srcdir/../..'.
#
# Of course, Automake must honor this variable whenever it calls a
# tool from the auxiliary directory. The problem is that $srcdir (and
# therefore $ac_aux_dir as well) can be either absolute or relative,
# depending on how configure is run. This is pretty annoying, since
# it makes $ac_aux_dir quite unusable in subdirectories: in the top
# source directory, any form will work fine, but in subdirectories a
# relative path needs to be adjusted first.
#
# $ac_aux_dir/missing
# fails when called from a subdirectory if $ac_aux_dir is relative
# $top_srcdir/$ac_aux_dir/missing
# fails if $ac_aux_dir is absolute,
# fails when called from a subdirectory in a VPATH build with
# a relative $ac_aux_dir
#
# The reason of the latter failure is that $top_srcdir and $ac_aux_dir
# are both prefixed by $srcdir. In an in-source build this is usually
# harmless because $srcdir is `.', but things will broke when you
# start a VPATH build or use an absolute $srcdir.
#
# So we could use something similar to $top_srcdir/$ac_aux_dir/missing,
# iff we strip the leading $srcdir from $ac_aux_dir. That would be:
# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"`
# and then we would define $MISSING as
# MISSING="\${SHELL} $am_aux_dir/missing"
# This will work as long as MISSING is not called from configure, because
# unfortunately $(top_srcdir) has no meaning in configure.
# However there are other variables, like CC, which are often used in
# configure, and could therefore not use this "fixed" $ac_aux_dir.
#
# Another solution, used here, is to always expand $ac_aux_dir to an
# absolute PATH. The drawback is that using absolute paths prevent a
# configured tree to be moved without reconfiguration.
AC_DEFUN([AM_AUX_DIR_EXPAND],
[dnl Rely on autoconf to set up CDPATH properly.
AC_PREREQ([2.50])dnl
# expand $ac_aux_dir to an absolute path
am_aux_dir=`cd $ac_aux_dir && pwd`
])
# AM_CONDITIONAL -*- Autoconf -*-
# Copyright (C) 1997, 2000, 2001, 2003, 2004, 2005
# Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 7
# AM_CONDITIONAL(NAME, SHELL-CONDITION)
# -------------------------------------
# Define a conditional.
AC_DEFUN([AM_CONDITIONAL],
[AC_PREREQ(2.52)dnl
ifelse([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])],
[$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl
AC_SUBST([$1_TRUE])
AC_SUBST([$1_FALSE])
if $2; then
$1_TRUE=
$1_FALSE='#'
else
$1_TRUE='#'
$1_FALSE=
fi
AC_CONFIG_COMMANDS_PRE(
[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then
AC_MSG_ERROR([[conditional "$1" was never defined.
Usually this means the macro was only invoked conditionally.]])
fi])])
# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005
# Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 8
# There are a few dirty hacks below to avoid letting `AC_PROG_CC' be
# written in clear, in which case automake, when reading aclocal.m4,
# will think it sees a *use*, and therefore will trigger all it's
# C support machinery. Also note that it means that autoscan, seeing
# CC etc. in the Makefile, will ask for an AC_PROG_CC use...
# _AM_DEPENDENCIES(NAME)
# ----------------------
# See how the compiler implements dependency checking.
# NAME is "CC", "CXX", "GCJ", or "OBJC".
# We try a few techniques and use that to set a single cache variable.
#
# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was
# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular
# dependency, and given that the user is not expected to run this macro,
# just rely on AC_PROG_CC.
AC_DEFUN([_AM_DEPENDENCIES],
[AC_REQUIRE([AM_SET_DEPDIR])dnl
AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl
AC_REQUIRE([AM_MAKE_INCLUDE])dnl
AC_REQUIRE([AM_DEP_TRACK])dnl
ifelse([$1], CC, [depcc="$CC" am_compiler_list=],
[$1], CXX, [depcc="$CXX" am_compiler_list=],
[$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'],
[$1], GCJ, [depcc="$GCJ" am_compiler_list='gcc3 gcc'],
[depcc="$$1" am_compiler_list=])
AC_CACHE_CHECK([dependency style of $depcc],
[am_cv_$1_dependencies_compiler_type],
[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
# We make a subdir and do the tests there. Otherwise we can end up
# making bogus files that we don't know about and never remove. For
# instance it was reported that on HP-UX the gcc test will end up
# making a dummy file named `D' -- because `-MD' means `put the output
# in D'.
mkdir conftest.dir
# Copy depcomp to subdir because otherwise we won't find it if we're
# using a relative directory.
cp "$am_depcomp" conftest.dir
cd conftest.dir
# We will build objects and dependencies in a subdirectory because
# it helps to detect inapplicable dependency modes. For instance
# both Tru64's cc and ICC support -MD to output dependencies as a
# side effect of compilation, but ICC will put the dependencies in
# the current directory while Tru64 will put them in the object
# directory.
mkdir sub
am_cv_$1_dependencies_compiler_type=none
if test "$am_compiler_list" = ""; then
am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp`
fi
for depmode in $am_compiler_list; do
# Setup a source with many dependencies, because some compilers
# like to wrap large dependency lists on column 80 (with \), and
# we should not choose a depcomp mode which is confused by this.
#
# We need to recreate these files for each test, as the compiler may
# overwrite some of them when testing with obscure command lines.
# This happens at least with the AIX C compiler.
: > sub/conftest.c
for i in 1 2 3 4 5 6; do
echo '#include "conftst'$i'.h"' >> sub/conftest.c
# Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
# Solaris 8's {/usr,}/bin/sh.
touch sub/conftst$i.h
done
echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
case $depmode in
nosideeffect)
# after this tag, mechanisms are not by side-effect, so they'll
# only be used when explicitly requested
if test "x$enable_dependency_tracking" = xyes; then
continue
else
break
fi
;;
none) break ;;
esac
# We check with `-c' and `-o' for the sake of the "dashmstdout"
# mode. It turns out that the SunPro C++ compiler does not properly
# handle `-M -o', and we need to detect this.
if depmode=$depmode \
source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \
depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
$SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \
>/dev/null 2>conftest.err &&
grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 &&
${MAKE-make} -s -f confmf > /dev/null 2>&1; then
# icc doesn't choke on unknown options, it will just issue warnings
# or remarks (even with -Werror). So we grep stderr for any message
# that says an option was ignored or not supported.
# When given -MP, icc 7.0 and 7.1 complain thusly:
# icc: Command line warning: ignoring option '-M'; no argument required
# The diagnosis changed in icc 8.0:
# icc: Command line remark: option '-MP' not supported
if (grep 'ignoring option' conftest.err ||
grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
am_cv_$1_dependencies_compiler_type=$depmode
break
fi
fi
done
cd ..
rm -rf conftest.dir
else
am_cv_$1_dependencies_compiler_type=none
fi
])
AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type])
AM_CONDITIONAL([am__fastdep$1], [
test "x$enable_dependency_tracking" != xno \
&& test "$am_cv_$1_dependencies_compiler_type" = gcc3])
])
# AM_SET_DEPDIR
# -------------
# Choose a directory name for dependency files.
# This macro is AC_REQUIREd in _AM_DEPENDENCIES
AC_DEFUN([AM_SET_DEPDIR],
[AC_REQUIRE([AM_SET_LEADING_DOT])dnl
AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl
])
# AM_DEP_TRACK
# ------------
AC_DEFUN([AM_DEP_TRACK],
[AC_ARG_ENABLE(dependency-tracking,
[ --disable-dependency-tracking speeds up one-time build
--enable-dependency-tracking do not reject slow dependency extractors])
if test "x$enable_dependency_tracking" != xno; then
am_depcomp="$ac_aux_dir/depcomp"
AMDEPBACKSLASH='\'
fi
AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno])
AC_SUBST([AMDEPBACKSLASH])
])
# Generate code to set up dependency tracking. -*- Autoconf -*-
# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005
# Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
#serial 3
# _AM_OUTPUT_DEPENDENCY_COMMANDS
# ------------------------------
AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
[for mf in $CONFIG_FILES; do
# Strip MF so we end up with the name of the file.
mf=`echo "$mf" | sed -e 's/:.*$//'`
# Check whether this is an Automake generated Makefile or not.
# We used to match only the files named `Makefile.in', but
# some people rename them; so instead we look at the file content.
# Grep'ing the first line is not enough: some people post-process
# each Makefile.in and add a new line on top of each file to say so.
# So let's grep whole file.
if grep '^#.*generated by automake' $mf > /dev/null 2>&1; then
dirpart=`AS_DIRNAME("$mf")`
else
continue
fi
# Extract the definition of DEPDIR, am__include, and am__quote
# from the Makefile without running `make'.
DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
test -z "$DEPDIR" && continue
am__include=`sed -n 's/^am__include = //p' < "$mf"`
test -z "am__include" && continue
am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
# When using ansi2knr, U may be empty or an underscore; expand it
U=`sed -n 's/^U = //p' < "$mf"`
# Find all dependency output files, they are included files with
# $(DEPDIR) in their names. We invoke sed twice because it is the
# simplest approach to changing $(DEPDIR) to its actual value in the
# expansion.
for file in `sed -n "
s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do
# Make sure the directory exists.
test -f "$dirpart/$file" && continue
fdir=`AS_DIRNAME(["$file"])`
AS_MKDIR_P([$dirpart/$fdir])
# echo "creating $dirpart/$file"
echo '# dummy' > "$dirpart/$file"
done
done
])# _AM_OUTPUT_DEPENDENCY_COMMANDS
# AM_OUTPUT_DEPENDENCY_COMMANDS
# -----------------------------
# This macro should only be invoked once -- use via AC_REQUIRE.
#
# This code is only required when automatic dependency tracking
# is enabled. FIXME. This creates each `.P' file that we will
# need in order to bootstrap the dependency handling code.
AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
[AC_CONFIG_COMMANDS([depfiles],
[test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS],
[AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"])
])
# Do all the work for Automake. -*- Autoconf -*-
# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
# Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 12
# This macro actually does too much. Some checks are only needed if
# your package does certain things. But this isn't really a big deal.
# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE])
# AM_INIT_AUTOMAKE([OPTIONS])
# -----------------------------------------------
# The call with PACKAGE and VERSION arguments is the old style
# call (pre autoconf-2.50), which is being phased out. PACKAGE
# and VERSION should now be passed to AC_INIT and removed from
# the call to AM_INIT_AUTOMAKE.
# We support both call styles for the transition. After
# the next Automake release, Autoconf can make the AC_INIT
# arguments mandatory, and then we can depend on a new Autoconf
# release and drop the old call support.
AC_DEFUN([AM_INIT_AUTOMAKE],
[AC_PREREQ([2.58])dnl
dnl Autoconf wants to disallow AM_ names. We explicitly allow
dnl the ones we care about.
m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl
AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl
AC_REQUIRE([AC_PROG_INSTALL])dnl
# test to see if srcdir already configured
if test "`cd $srcdir && pwd`" != "`pwd`" &&
test -f $srcdir/config.status; then
AC_MSG_ERROR([source directory already configured; run "make distclean" there first])
fi
# test whether we have cygpath
if test -z "$CYGPATH_W"; then
if (cygpath --version) >/dev/null 2>/dev/null; then
CYGPATH_W='cygpath -w'
else
CYGPATH_W=echo
fi
fi
AC_SUBST([CYGPATH_W])
# Define the identity of the package.
dnl Distinguish between old-style and new-style calls.
m4_ifval([$2],
[m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl
AC_SUBST([PACKAGE], [$1])dnl
AC_SUBST([VERSION], [$2])],
[_AM_SET_OPTIONS([$1])dnl
AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl
AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl
_AM_IF_OPTION([no-define],,
[AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package])
AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])dnl
# Some tools Automake needs.
AC_REQUIRE([AM_SANITY_CHECK])dnl
AC_REQUIRE([AC_ARG_PROGRAM])dnl
AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version})
AM_MISSING_PROG(AUTOCONF, autoconf)
AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version})
AM_MISSING_PROG(AUTOHEADER, autoheader)
AM_MISSING_PROG(MAKEINFO, makeinfo)
AM_PROG_INSTALL_SH
AM_PROG_INSTALL_STRIP
AC_REQUIRE([AM_PROG_MKDIR_P])dnl
# We need awk for the "check" target. The system "awk" is bad on
# some platforms.
AC_REQUIRE([AC_PROG_AWK])dnl
AC_REQUIRE([AC_PROG_MAKE_SET])dnl
AC_REQUIRE([AM_SET_LEADING_DOT])dnl
_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])],
[_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])],
[_AM_PROG_TAR([v7])])])
_AM_IF_OPTION([no-dependencies],,
[AC_PROVIDE_IFELSE([AC_PROG_CC],
[_AM_DEPENDENCIES(CC)],
[define([AC_PROG_CC],
defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl
AC_PROVIDE_IFELSE([AC_PROG_CXX],
[_AM_DEPENDENCIES(CXX)],
[define([AC_PROG_CXX],
defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl
])
])
# When config.status generates a header, we must update the stamp-h file.
# This file resides in the same directory as the config header
# that is generated. The stamp files are numbered to have different names.
# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the
# loop where config.status creates the headers, so we can generate
# our stamp files there.
AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK],
[# Compute $1's index in $config_headers.
_am_stamp_count=1
for _am_header in $config_headers :; do
case $_am_header in
$1 | $1:* )
break ;;
* )
_am_stamp_count=`expr $_am_stamp_count + 1` ;;
esac
done
echo "timestamp for $1" >`AS_DIRNAME([$1])`/stamp-h[]$_am_stamp_count])
# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_PROG_INSTALL_SH
# ------------------
# Define $install_sh.
AC_DEFUN([AM_PROG_INSTALL_SH],
[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
install_sh=${install_sh-"$am_aux_dir/install-sh"}
AC_SUBST(install_sh)])
# Copyright (C) 2003, 2005 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 2
# Check whether the underlying file-system supports filenames
# with a leading dot. For instance MS-DOS doesn't.
AC_DEFUN([AM_SET_LEADING_DOT],
[rm -rf .tst 2>/dev/null
mkdir .tst 2>/dev/null
if test -d .tst; then
am__leading_dot=.
else
am__leading_dot=_
fi
rmdir .tst 2>/dev/null
AC_SUBST([am__leading_dot])])
# Check to see how 'make' treats includes. -*- Autoconf -*-
# Copyright (C) 2001, 2002, 2003, 2005 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 3
# AM_MAKE_INCLUDE()
# -----------------
# Check to see how make treats includes.
AC_DEFUN([AM_MAKE_INCLUDE],
[am_make=${MAKE-make}
cat > confinc << 'END'
am__doit:
@echo done
.PHONY: am__doit
END
# If we don't find an include directive, just comment out the code.
AC_MSG_CHECKING([for style of include used by $am_make])
am__include="#"
am__quote=
_am_result=none
# First try GNU make style include.
echo "include confinc" > confmf
# We grep out `Entering directory' and `Leaving directory'
# messages which can occur if `w' ends up in MAKEFLAGS.
# In particular we don't look at `^make:' because GNU make might
# be invoked under some other name (usually "gmake"), in which
# case it prints its new name instead of `make'.
if test "`$am_make -s -f confmf 2> /dev/null | grep -v 'ing directory'`" = "done"; then
am__include=include
am__quote=
_am_result=GNU
fi
# Now try BSD make style include.
if test "$am__include" = "#"; then
echo '.include "confinc"' > confmf
if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then
am__include=.include
am__quote="\""
_am_result=BSD
fi
fi
AC_SUBST([am__include])
AC_SUBST([am__quote])
AC_MSG_RESULT([$_am_result])
rm -f confinc confmf
])
# Copyright (C) 1999, 2000, 2001, 2003, 2005 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 3
# AM_PROG_CC_C_O
# --------------
# Like AC_PROG_CC_C_O, but changed for automake.
AC_DEFUN([AM_PROG_CC_C_O],
[AC_REQUIRE([AC_PROG_CC_C_O])dnl
AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
# FIXME: we rely on the cache variable name because
# there is no other way.
set dummy $CC
ac_cc=`echo $[2] | sed ['s/[^a-zA-Z0-9_]/_/g;s/^[0-9]/_/']`
if eval "test \"`echo '$ac_cv_prog_cc_'${ac_cc}_c_o`\" != yes"; then
# Losing compiler, so override with the script.
# FIXME: It is wrong to rewrite CC.
# But if we don't then we get into trouble of one sort or another.
# A longer-term fix would be to have automake use am__CC in this case,
# and then we could set am__CC="\$(top_srcdir)/compile \$(CC)"
CC="$am_aux_dir/compile $CC"
fi
])
# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*-
# Copyright (C) 1997, 1999, 2000, 2001, 2003, 2005
# Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 4
# AM_MISSING_PROG(NAME, PROGRAM)
# ------------------------------
AC_DEFUN([AM_MISSING_PROG],
[AC_REQUIRE([AM_MISSING_HAS_RUN])
$1=${$1-"${am_missing_run}$2"}
AC_SUBST($1)])
# AM_MISSING_HAS_RUN
# ------------------
# Define MISSING if not defined so far and test if it supports --run.
# If it does, set am_missing_run to use it, otherwise, to nothing.
AC_DEFUN([AM_MISSING_HAS_RUN],
[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing"
# Use eval to expand $SHELL
if eval "$MISSING --run true"; then
am_missing_run="$MISSING --run "
else
am_missing_run=
AC_MSG_WARN([`missing' script is too old or missing])
fi
])
# Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_PROG_MKDIR_P
# ---------------
# Check whether `mkdir -p' is supported, fallback to mkinstalldirs otherwise.
#
# Automake 1.8 used `mkdir -m 0755 -p --' to ensure that directories
# created by `make install' are always world readable, even if the
# installer happens to have an overly restrictive umask (e.g. 077).
# This was a mistake. There are at least two reasons why we must not
# use `-m 0755':
# - it causes special bits like SGID to be ignored,
# - it may be too restrictive (some setups expect 775 directories).
#
# Do not use -m 0755 and let people choose whatever they expect by
# setting umask.
#
# We cannot accept any implementation of `mkdir' that recognizes `-p'.
# Some implementations (such as Solaris 8's) are not thread-safe: if a
# parallel make tries to run `mkdir -p a/b' and `mkdir -p a/c'
# concurrently, both version can detect that a/ is missing, but only
# one can create it and the other will error out. Consequently we
# restrict ourselves to GNU make (using the --version option ensures
# this.)
AC_DEFUN([AM_PROG_MKDIR_P],
[if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then
# We used to keeping the `.' as first argument, in order to
# allow $(mkdir_p) to be used without argument. As in
# $(mkdir_p) $(somedir)
# where $(somedir) is conditionally defined. However this is wrong
# for two reasons:
# 1. if the package is installed by a user who cannot write `.'
# make install will fail,
# 2. the above comment should most certainly read
# $(mkdir_p) $(DESTDIR)$(somedir)
# so it does not work when $(somedir) is undefined and
# $(DESTDIR) is not.
# To support the latter case, we have to write
# test -z "$(somedir)" || $(mkdir_p) $(DESTDIR)$(somedir),
# so the `.' trick is pointless.
mkdir_p='mkdir -p --'
else
# On NextStep and OpenStep, the `mkdir' command does not
# recognize any option. It will interpret all options as
# directories to create, and then abort because `.' already
# exists.
for d in ./-p ./--version;
do
test -d $d && rmdir $d
done
# $(mkinstalldirs) is defined by Automake if mkinstalldirs exists.
if test -f "$ac_aux_dir/mkinstalldirs"; then
mkdir_p='$(mkinstalldirs)'
else
mkdir_p='$(install_sh) -d'
fi
fi
AC_SUBST([mkdir_p])])
# Helper functions for option handling. -*- Autoconf -*-
# Copyright (C) 2001, 2002, 2003, 2005 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 3
# _AM_MANGLE_OPTION(NAME)
# -----------------------
AC_DEFUN([_AM_MANGLE_OPTION],
[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])])
# _AM_SET_OPTION(NAME)
# ------------------------------
# Set option NAME. Presently that only means defining a flag for this option.
AC_DEFUN([_AM_SET_OPTION],
[m4_define(_AM_MANGLE_OPTION([$1]), 1)])
# _AM_SET_OPTIONS(OPTIONS)
# ----------------------------------
# OPTIONS is a space-separated list of Automake options.
AC_DEFUN([_AM_SET_OPTIONS],
[AC_FOREACH([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])])
# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET])
# -------------------------------------------
# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
AC_DEFUN([_AM_IF_OPTION],
[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
# Check to make sure that the build environment is sane. -*- Autoconf -*-
# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005
# Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 4
# AM_SANITY_CHECK
# ---------------
AC_DEFUN([AM_SANITY_CHECK],
[AC_MSG_CHECKING([whether build environment is sane])
# Just in case
sleep 1
echo timestamp > conftest.file
# Do `set' in a subshell so we don't clobber the current shell's
# arguments. Must try -L first in case configure is actually a
# symlink; some systems play weird games with the mod time of symlinks
# (eg FreeBSD returns the mod time of the symlink's containing
# directory).
if (
set X `ls -Lt $srcdir/configure conftest.file 2> /dev/null`
if test "$[*]" = "X"; then
# -L didn't work.
set X `ls -t $srcdir/configure conftest.file`
fi
rm -f conftest.file
if test "$[*]" != "X $srcdir/configure conftest.file" \
&& test "$[*]" != "X conftest.file $srcdir/configure"; then
# If neither matched, then we have a broken ls. This can happen
# if, for instance, CONFIG_SHELL is bash and it inherits a
# broken ls alias from the environment. This has actually
# happened. Such a system could not be considered "sane".
AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken
alias in your environment])
fi
test "$[2]" = conftest.file
)
then
# Ok.
:
else
AC_MSG_ERROR([newly created file is older than distributed files!
Check your system clock])
fi
AC_MSG_RESULT(yes)])
# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_PROG_INSTALL_STRIP
# ---------------------
# One issue with vendor `install' (even GNU) is that you can't
# specify the program used to strip binaries. This is especially
# annoying in cross-compiling environments, where the build's strip
# is unlikely to handle the host's binaries.
# Fortunately install-sh will honor a STRIPPROG variable, so we
# always use install-sh in `make install-strip', and initialize
# STRIPPROG with the value of the STRIP variable (set by the user).
AC_DEFUN([AM_PROG_INSTALL_STRIP],
[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
# Installed binaries are usually stripped using `strip' when the user
# run `make install-strip'. However `strip' might not be the right
# tool to use in cross-compilation environments, therefore Automake
# will honor the `STRIP' environment variable to overrule this program.
dnl Don't test for $cross_compiling = yes, because it might be `maybe'.
if test "$cross_compiling" != no; then
AC_CHECK_TOOL([STRIP], [strip], :)
fi
INSTALL_STRIP_PROGRAM="\${SHELL} \$(install_sh) -c -s"
AC_SUBST([INSTALL_STRIP_PROGRAM])])
# Check how to create a tarball. -*- Autoconf -*-
# Copyright (C) 2004, 2005 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 2
# _AM_PROG_TAR(FORMAT)
# --------------------
# Check how to create a tarball in format FORMAT.
# FORMAT should be one of `v7', `ustar', or `pax'.
#
# Substitute a variable $(am__tar) that is a command
# writing to stdout a FORMAT-tarball containing the directory
# $tardir.
# tardir=directory && $(am__tar) > result.tar
#
# Substitute a variable $(am__untar) that extract such
# a tarball read from stdin.
# $(am__untar) < result.tar
AC_DEFUN([_AM_PROG_TAR],
[# Always define AMTAR for backward compatibility.
AM_MISSING_PROG([AMTAR], [tar])
m4_if([$1], [v7],
[am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'],
[m4_case([$1], [ustar],, [pax],,
[m4_fatal([Unknown tar format])])
AC_MSG_CHECKING([how to create a $1 tar archive])
# Loop over all known methods to create a tar archive until one works.
_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none'
_am_tools=${am_cv_prog_tar_$1-$_am_tools}
# Do not fold the above two line into one, because Tru64 sh and
# Solaris sh will not grok spaces in the rhs of `-'.
for _am_tool in $_am_tools
do
case $_am_tool in
gnutar)
for _am_tar in tar gnutar gtar;
do
AM_RUN_LOG([$_am_tar --version]) && break
done
am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"'
am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"'
am__untar="$_am_tar -xf -"
;;
plaintar)
# Must skip GNU tar: if it does not support --format= it doesn't create
# ustar tarball either.
(tar --version) >/dev/null 2>&1 && continue
am__tar='tar chf - "$$tardir"'
am__tar_='tar chf - "$tardir"'
am__untar='tar xf -'
;;
pax)
am__tar='pax -L -x $1 -w "$$tardir"'
am__tar_='pax -L -x $1 -w "$tardir"'
am__untar='pax -r'
;;
cpio)
am__tar='find "$$tardir" -print | cpio -o -H $1 -L'
am__tar_='find "$tardir" -print | cpio -o -H $1 -L'
am__untar='cpio -i -H $1 -d'
;;
none)
am__tar=false
am__tar_=false
am__untar=false
;;
esac
# If the value was cached, stop now. We just wanted to have am__tar
# and am__untar set.
test -n "${am_cv_prog_tar_$1}" && break
# tar/untar a dummy directory, and stop if the command works
rm -rf conftest.dir
mkdir conftest.dir
echo GrepMe > conftest.dir/file
AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar])
rm -rf conftest.dir
if test -s conftest.tar; then
AM_RUN_LOG([$am__untar <conftest.tar])
grep GrepMe conftest.dir/file >/dev/null 2>&1 && break
fi
done
rm -rf conftest.dir
AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool])
AC_MSG_RESULT([$am_cv_prog_tar_$1])])
AC_SUBST([am__tar])
AC_SUBST([am__untar])
]) # _AM_PROG_TAR
m4_include([acinclude.m4])

99
attic/compile Executable file
View File

@ -0,0 +1,99 @@
#! /bin/sh
# Wrapper for compilers which do not understand `-c -o'.
# Copyright 1999, 2000 Free Software Foundation, Inc.
# Written by Tom Tromey <tromey@cygnus.com>.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
# Usage:
# compile PROGRAM [ARGS]...
# `-o FOO.o' is removed from the args passed to the actual compile.
prog=$1
shift
ofile=
cfile=
args=
while test $# -gt 0; do
case "$1" in
-o)
# configure might choose to run compile as `compile cc -o foo foo.c'.
# So we do something ugly here.
ofile=$2
shift
case "$ofile" in
*.o | *.obj)
;;
*)
args="$args -o $ofile"
ofile=
;;
esac
;;
*.c)
cfile=$1
args="$args $1"
;;
*)
args="$args $1"
;;
esac
shift
done
if test -z "$ofile" || test -z "$cfile"; then
# If no `-o' option was seen then we might have been invoked from a
# pattern rule where we don't need one. That is ok -- this is a
# normal compilation that the losing compiler can handle. If no
# `.c' file was seen then we are probably linking. That is also
# ok.
exec "$prog" $args
fi
# Name of file we expect compiler to create.
cofile=`echo $cfile | sed -e 's|^.*/||' -e 's/\.c$/.o/'`
# Create the lock directory.
# Note: use `[/.-]' here to ensure that we don't use the same name
# that we are using for the .o file. Also, base the name on the expected
# object file name, since that is what matters with a parallel build.
lockdir=`echo $cofile | sed -e 's|[/.-]|_|g'`.d
while true; do
if mkdir $lockdir > /dev/null 2>&1; then
break
fi
sleep 1
done
# FIXME: race condition here if user kills between mkdir and trap.
trap "rmdir $lockdir; exit 1" 1 2 15
# Run the compile.
"$prog" $args
status=$?
if test -f "$cofile"; then
mv "$cofile" "$ofile"
fi
rmdir $lockdir
exit $status

1317
attic/config.guess vendored Executable file

File diff suppressed because it is too large Load Diff

1411
attic/config.sub vendored Executable file

File diff suppressed because it is too large Load Diff

1
attic/configure-mingw32 Normal file
View File

@ -0,0 +1 @@
CFLAGS="-mno-cygwin" CXXFLAGS="-mno-cygwin" LDFLAGS="-mno-cygwin" ./configure --host=mingw32

101
attic/configure.ac Normal file
View File

@ -0,0 +1,101 @@
AC_INIT([src/fceu.cpp])
AC_CANONICAL_HOST
AC_CANONICAL_TARGET
AM_INIT_AUTOMAKE([fceu], $FCEU_VERSION)
AC_PROG_CC
AC_PROG_CPP
AM_PROG_CC_C_O
AC_PROG_CXX
AC_PROG_CXXCPP
AC_PROG_INSTALL
AC_DEFINE(_GNU_SOURCE)
AC_CHECK_FUNC(asprintf, AC_DEFINE([HAVE_ASPRINTF]), [])
AC_CHECK_FUNC(alloca, AC_DEFINE([HAVE_ALLOCA]), [])
dnl Check for zlib
AC_CHECK_LIB([z], [zlibVersion],[], AC_MSG_ERROR([*** zlib not found!]))
LIBS="$LIBS -lz"
AM_CONDITIONAL(WIN32, false)
AM_CONDITIONAL(UNIX, false)
AM_CONDITIONAL(USE_SEXYAL, false)
AM_CONDITIONAL(OPENGL, false)
AM_CONDITIONAL(HAVE_GTK, false)
AC_DEFINE([NETWORK],[1])
if expr x"$target" : 'x.*beos' > /dev/null; then
CFLAGS="-no-fpic $CFLAGS"
CPPFLAGS="-no-fpic $CPPFLAGS"
AC_DEFINE([PSS_STYLE],[1])
elif expr x"$target" : 'x.*mingw' > /dev/null; then
AC_DEFINE([PSS_STYLE],[2])
AC_DEFINE([WIN32])
AM_CONDITIONAL(WIN32, true)
else
AM_CONDITIONAL(UNIX, true)
AC_DEFINE([UNIX])
AC_DEFINE([PSS_STYLE],[1])
AC_DEFINE([NETWORK],[1])
dnl AC_CHECK_HEADER([sys/soundcard.h],
dnl AM_CONDITIONAL(USE_SEXYAL, true)
dnl AC_DEFINE([USE_SEXYAL]),[])
fi
dnl if expr x"$target" : 'x.*darwin' > /dev/null; then
dnl AC_DEFINE([MACOSX])
dnl fi
if test x$use_nativewin32 = xno; then
dnl Check for SDL
SDL_VERSION=1.2.0
AM_PATH_SDL($SDL_VERSION, [:],
AC_MSG_ERROR([*** SDL version $SDL_VERSION not found!]))
AC_LANG_CPLUSPLUS
LIBS="$LIBS $SDL_LIBS"
CFLAGS="-Wall -fomit-frame-pointer $CFLAGS $SDL_CFLAGS"
CPPFLAGS="-Wall -fomit-frame-pointer $CPPFLAGS $SDL_CFLAGS"
dnl Check for SDL_net for net play
HAS_SDL_NET=no
dnl AC_CHECK_LIB([SDL_net], [SDLNet_Init],[HAS_SDL_NET=yes])
dnl if test x$HAS_SDL_NET = xyes; then
dnl AC_DEFINE([NETWORK])
dnl LIBS="$LIBS -lSDL_net"
dnl fi
AC_ARG_WITH(opengl,
[AC_HELP_STRING([--with-opengl],
[use OpenGL])],
use_opengl=$withval,
use_opengl="no")
if [[ "$use_opengl" = "yes" ]] ; then
dnl Check for OpenGL
AC_CHECK_HEADER([GL/gl.h],[AC_DEFINE([OPENGL]) AM_CONDITIONAL(OPENGL, true)],
[
AC_CHECK_HEADER([OpenGL/gl.h],[AC_DEFINE([OPENGL]) AM_CONDITIONAL(OPENGL, true)],[])
AC_DEFINE([APPLEOPENGL])
])
fi
AC_DEFINE([SDL],[1])
fi
if expr match "$target_cpu" 'i.86' > /dev/null; then
AC_DEFINE([C80x86])
fi
AC_C_BIGENDIAN([], [AC_DEFINE([LSB_FIRST])])
AC_DEFINE(FRAMESKIP)
dnl Output Makefiles
AC_OUTPUT([Makefile src/Makefile])

423
attic/depcomp Executable file
View File

@ -0,0 +1,423 @@
#! /bin/sh
# depcomp - compile a program generating dependencies as side-effects
# Copyright 1999, 2000 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
# This program 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 General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
# 02111-1307, USA.
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
# Originally written by Alexandre Oliva <oliva@dcc.unicamp.br>.
if test -z "$depmode" || test -z "$source" || test -z "$object"; then
echo "depcomp: Variables source, object and depmode must be set" 1>&2
exit 1
fi
# `libtool' can also be set to `yes' or `no'.
if test -z "$depfile"; then
base=`echo "$object" | sed -e 's,^.*/,,' -e 's,\.\([^.]*\)$,.P\1,'`
dir=`echo "$object" | sed 's,/.*$,/,'`
if test "$dir" = "$object"; then
dir=
fi
# FIXME: should be _deps on DOS.
depfile="$dir.deps/$base"
fi
tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
rm -f "$tmpdepfile"
# Some modes work just like other modes, but use different flags. We
# parameterize here, but still list the modes in the big case below,
# to make depend.m4 easier to write. Note that we *cannot* use a case
# here, because this file can only contain one case statement.
if test "$depmode" = hp; then
# HP compiler uses -M and no extra arg.
gccflag=-M
depmode=gcc
fi
if test "$depmode" = dashXmstdout; then
# This is just like dashmstdout with a different argument.
dashmflag=-xM
depmode=dashmstdout
fi
case "$depmode" in
gcc3)
## gcc 3 implements dependency tracking that does exactly what
## we want. Yay! Note: for some reason libtool 1.4 doesn't like
## it if -MD -MP comes after the -MF stuff. Hmm.
"$@" -MT "$object" -MD -MP -MF "$tmpdepfile"
stat=$?
if test $stat -eq 0; then :
else
rm -f "$tmpdepfile"
exit $stat
fi
mv "$tmpdepfile" "$depfile"
;;
gcc)
## There are various ways to get dependency output from gcc. Here's
## why we pick this rather obscure method:
## - Don't want to use -MD because we'd like the dependencies to end
## up in a subdir. Having to rename by hand is ugly.
## (We might end up doing this anyway to support other compilers.)
## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
## -MM, not -M (despite what the docs say).
## - Using -M directly means running the compiler twice (even worse
## than renaming).
if test -z "$gccflag"; then
gccflag=-MD,
fi
"$@" -Wp,"$gccflag$tmpdepfile"
stat=$?
if test $stat -eq 0; then :
else
rm -f "$tmpdepfile"
exit $stat
fi
rm -f "$depfile"
echo "$object : \\" > "$depfile"
alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
## The second -e expression handles DOS-style file names with drive letters.
sed -e 's/^[^:]*: / /' \
-e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
## This next piece of magic avoids the `deleted header file' problem.
## The problem is that when a header file which appears in a .P file
## is deleted, the dependency causes make to die (because there is
## typically no way to rebuild the header). We avoid this by adding
## dummy dependencies for each header file. Too bad gcc doesn't do
## this for us directly.
tr ' ' '
' < "$tmpdepfile" |
## Some versions of gcc put a space before the `:'. On the theory
## that the space means something, we add a space to the output as
## well.
## Some versions of the HPUX 10.20 sed can't process this invocation
## correctly. Breaking it into two sed invocations is a workaround.
sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
rm -f "$tmpdepfile"
;;
hp)
# This case exists only to let depend.m4 do its work. It works by
# looking at the text of this script. This case will never be run,
# since it is checked for above.
exit 1
;;
sgi)
if test "$libtool" = yes; then
"$@" "-Wp,-MDupdate,$tmpdepfile"
else
"$@" -MDupdate "$tmpdepfile"
fi
stat=$?
if test $stat -eq 0; then :
else
rm -f "$tmpdepfile"
exit $stat
fi
rm -f "$depfile"
if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files
echo "$object : \\" > "$depfile"
# Clip off the initial element (the dependent). Don't try to be
# clever and replace this with sed code, as IRIX sed won't handle
# lines with more than a fixed number of characters (4096 in
# IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines;
# the IRIX cc adds comments like `#:fec' to the end of the
# dependency line.
tr ' ' '
' < "$tmpdepfile" \
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \
tr '
' ' ' >> $depfile
echo >> $depfile
# The second pass generates a dummy entry for each header file.
tr ' ' '
' < "$tmpdepfile" \
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
>> $depfile
else
# The sourcefile does not contain any dependencies, so just
# store a dummy comment line, to avoid errors with the Makefile
# "include basename.Plo" scheme.
echo "#dummy" > "$depfile"
fi
rm -f "$tmpdepfile"
;;
aix)
# The C for AIX Compiler uses -M and outputs the dependencies
# in a .u file. This file always lives in the current directory.
# Also, the AIX compiler puts `$object:' at the start of each line;
# $object doesn't have directory information.
stripped=`echo "$object" | sed -e 's,^.*/,,' -e 's/\(.*\)\..*$/\1/'`
tmpdepfile="$stripped.u"
outname="$stripped.o"
if test "$libtool" = yes; then
"$@" -Wc,-M
else
"$@" -M
fi
stat=$?
if test $stat -eq 0; then :
else
rm -f "$tmpdepfile"
exit $stat
fi
if test -f "$tmpdepfile"; then
# Each line is of the form `foo.o: dependent.h'.
# Do two passes, one to just change these to
# `$object: dependent.h' and one to simply `dependent.h:'.
sed -e "s,^$outname:,$object :," < "$tmpdepfile" > "$depfile"
sed -e "s,^$outname: \(.*\)$,\1:," < "$tmpdepfile" >> "$depfile"
else
# The sourcefile does not contain any dependencies, so just
# store a dummy comment line, to avoid errors with the Makefile
# "include basename.Plo" scheme.
echo "#dummy" > "$depfile"
fi
rm -f "$tmpdepfile"
;;
tru64)
# The Tru64 compiler uses -MD to generate dependencies as a side
# effect. `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'.
# At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
# dependencies in `foo.d' instead, so we check for that too.
# Subdirectories are respected.
dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
test "x$dir" = "x$object" && dir=
base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
if test "$libtool" = yes; then
tmpdepfile1="$dir.libs/$base.lo.d"
tmpdepfile2="$dir.libs/$base.d"
"$@" -Wc,-MD
else
tmpdepfile1="$dir$base.o.d"
tmpdepfile2="$dir$base.d"
"$@" -MD
fi
stat=$?
if test $stat -eq 0; then :
else
rm -f "$tmpdepfile1" "$tmpdepfile2"
exit $stat
fi
if test -f "$tmpdepfile1"; then
tmpdepfile="$tmpdepfile1"
else
tmpdepfile="$tmpdepfile2"
fi
if test -f "$tmpdepfile"; then
sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
# That's a space and a tab in the [].
sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
else
echo "#dummy" > "$depfile"
fi
rm -f "$tmpdepfile"
;;
#nosideeffect)
# This comment above is used by automake to tell side-effect
# dependency tracking mechanisms from slower ones.
dashmstdout)
# Important note: in order to support this mode, a compiler *must*
# always write the proprocessed file to stdout, regardless of -o.
"$@" || exit $?
# Remove the call to Libtool.
if test "$libtool" = yes; then
while test $1 != '--mode=compile'; do
shift
done
shift
fi
# Remove `-o $object'. We will use -o /dev/null later,
# however we can't do the remplacement now because
# `-o $object' might simply not be used
IFS=" "
for arg
do
case $arg in
-o)
shift
;;
$object)
shift
;;
*)
set fnord "$@" "$arg"
shift # fnord
shift # $arg
;;
esac
done
test -z "$dashmflag" && dashmflag=-M
"$@" -o /dev/null $dashmflag | sed 's:^[^:]*\:[ ]*:'"$object"'\: :' > "$tmpdepfile"
rm -f "$depfile"
cat < "$tmpdepfile" > "$depfile"
tr ' ' '
' < "$tmpdepfile" | \
## Some versions of the HPUX 10.20 sed can't process this invocation
## correctly. Breaking it into two sed invocations is a workaround.
sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
rm -f "$tmpdepfile"
;;
dashXmstdout)
# This case only exists to satisfy depend.m4. It is never actually
# run, as this mode is specially recognized in the preamble.
exit 1
;;
makedepend)
"$@" || exit $?
# X makedepend
shift
cleared=no
for arg in "$@"; do
case $cleared in
no)
set ""; shift
cleared=yes ;;
esac
case "$arg" in
-D*|-I*)
set fnord "$@" "$arg"; shift ;;
-*)
;;
*)
set fnord "$@" "$arg"; shift ;;
esac
done
obj_suffix="`echo $object | sed 's/^.*\././'`"
touch "$tmpdepfile"
${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
rm -f "$depfile"
cat < "$tmpdepfile" > "$depfile"
sed '1,2d' "$tmpdepfile" | tr ' ' '
' | \
## Some versions of the HPUX 10.20 sed can't process this invocation
## correctly. Breaking it into two sed invocations is a workaround.
sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
rm -f "$tmpdepfile" "$tmpdepfile".bak
;;
cpp)
# Important note: in order to support this mode, a compiler *must*
# always write the proprocessed file to stdout.
"$@" || exit $?
# Remove the call to Libtool.
if test "$libtool" = yes; then
while test $1 != '--mode=compile'; do
shift
done
shift
fi
# Remove `-o $object'.
IFS=" "
for arg
do
case $arg in
-o)
shift
;;
$object)
shift
;;
*)
set fnord "$@" "$arg"
shift # fnord
shift # $arg
;;
esac
done
"$@" -E |
sed -n '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' |
sed '$ s: \\$::' > "$tmpdepfile"
rm -f "$depfile"
echo "$object : \\" > "$depfile"
cat < "$tmpdepfile" >> "$depfile"
sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
rm -f "$tmpdepfile"
;;
msvisualcpp)
# Important note: in order to support this mode, a compiler *must*
# always write the proprocessed file to stdout, regardless of -o,
# because we must use -o when running libtool.
"$@" || exit $?
IFS=" "
for arg
do
case "$arg" in
"-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
set fnord "$@"
shift
shift
;;
*)
set fnord "$@" "$arg"
shift
shift
;;
esac
done
"$@" -E |
sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::echo "`cygpath -u \\"\1\\"`":p' | sort | uniq > "$tmpdepfile"
rm -f "$depfile"
echo "$object : \\" > "$depfile"
. "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s:: \1 \\:p' >> "$depfile"
echo " " >> "$depfile"
. "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::\1\::p' >> "$depfile"
rm -f "$tmpdepfile"
;;
none)
exec "$@"
;;
*)
echo "Unknown depmode $depmode" 1>&2
exit 1
;;
esac
exit 0

251
attic/install-sh Executable file
View File

@ -0,0 +1,251 @@
#!/bin/sh
#
# install - install a program, script, or datafile
# This comes from X11R5 (mit/util/scripts/install.sh).
#
# Copyright 1991 by the Massachusetts Institute of Technology
#
# Permission to use, copy, modify, distribute, and sell this software and its
# documentation for any purpose is hereby granted without fee, provided that
# the above copyright notice appear in all copies and that both that
# copyright notice and this permission notice appear in supporting
# documentation, and that the name of M.I.T. not be used in advertising or
# publicity pertaining to distribution of the software without specific,
# written prior permission. M.I.T. makes no representations about the
# suitability of this software for any purpose. It is provided "as is"
# without express or implied warranty.
#
# Calling this script install-sh is preferred over install.sh, to prevent
# `make' implicit rules from creating a file called install from it
# when there is no Makefile.
#
# This script is compatible with the BSD install script, but was written
# from scratch. It can only install one file at a time, a restriction
# shared with many OS's install programs.
# set DOITPROG to echo to test this script
# Don't use :- since 4.3BSD and earlier shells don't like it.
doit="${DOITPROG-}"
# put in absolute paths if you don't have them in your path; or use env. vars.
mvprog="${MVPROG-mv}"
cpprog="${CPPROG-cp}"
chmodprog="${CHMODPROG-chmod}"
chownprog="${CHOWNPROG-chown}"
chgrpprog="${CHGRPPROG-chgrp}"
stripprog="${STRIPPROG-strip}"
rmprog="${RMPROG-rm}"
mkdirprog="${MKDIRPROG-mkdir}"
transformbasename=""
transform_arg=""
instcmd="$mvprog"
chmodcmd="$chmodprog 0755"
chowncmd=""
chgrpcmd=""
stripcmd=""
rmcmd="$rmprog -f"
mvcmd="$mvprog"
src=""
dst=""
dir_arg=""
while [ x"$1" != x ]; do
case $1 in
-c) instcmd="$cpprog"
shift
continue;;
-d) dir_arg=true
shift
continue;;
-m) chmodcmd="$chmodprog $2"
shift
shift
continue;;
-o) chowncmd="$chownprog $2"
shift
shift
continue;;
-g) chgrpcmd="$chgrpprog $2"
shift
shift
continue;;
-s) stripcmd="$stripprog"
shift
continue;;
-t=*) transformarg=`echo $1 | sed 's/-t=//'`
shift
continue;;
-b=*) transformbasename=`echo $1 | sed 's/-b=//'`
shift
continue;;
*) if [ x"$src" = x ]
then
src=$1
else
# this colon is to work around a 386BSD /bin/sh bug
:
dst=$1
fi
shift
continue;;
esac
done
if [ x"$src" = x ]
then
echo "install: no input file specified"
exit 1
else
:
fi
if [ x"$dir_arg" != x ]; then
dst=$src
src=""
if [ -d $dst ]; then
instcmd=:
chmodcmd=""
else
instcmd=$mkdirprog
fi
else
# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
# might cause directories to be created, which would be especially bad
# if $src (and thus $dsttmp) contains '*'.
if [ -f "$src" ] || [ -d "$src" ]
then
:
else
echo "install: $src does not exist"
exit 1
fi
if [ x"$dst" = x ]
then
echo "install: no destination specified"
exit 1
else
:
fi
# If destination is a directory, append the input filename; if your system
# does not like double slashes in filenames, you may need to add some logic
if [ -d $dst ]
then
dst="$dst"/`basename $src`
else
:
fi
fi
## this sed command emulates the dirname command
dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
# Make sure that the destination directory exists.
# this part is taken from Noah Friedman's mkinstalldirs script
# Skip lots of stat calls in the usual case.
if [ ! -d "$dstdir" ]; then
defaultIFS='
'
IFS="${IFS-${defaultIFS}}"
oIFS="${IFS}"
# Some sh's can't handle IFS=/ for some reason.
IFS='%'
set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
IFS="${oIFS}"
pathcomp=''
while [ $# -ne 0 ] ; do
pathcomp="${pathcomp}${1}"
shift
if [ ! -d "${pathcomp}" ] ;
then
$mkdirprog "${pathcomp}"
else
:
fi
pathcomp="${pathcomp}/"
done
fi
if [ x"$dir_arg" != x ]
then
$doit $instcmd $dst &&
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else : ; fi &&
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else : ; fi &&
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else : ; fi &&
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else : ; fi
else
# If we're going to rename the final executable, determine the name now.
if [ x"$transformarg" = x ]
then
dstfile=`basename $dst`
else
dstfile=`basename $dst $transformbasename |
sed $transformarg`$transformbasename
fi
# don't allow the sed command to completely eliminate the filename
if [ x"$dstfile" = x ]
then
dstfile=`basename $dst`
else
:
fi
# Make a temp file name in the proper directory.
dsttmp=$dstdir/#inst.$$#
# Move or copy the file name to the temp name
$doit $instcmd $src $dsttmp &&
trap "rm -f ${dsttmp}" 0 &&
# and set any options; do chmod last to preserve setuid bits
# If any of these fail, we abort the whole thing. If we want to
# ignore errors from any of these, just make sure not to ignore
# errors from the above "$doit $instcmd $src $dsttmp" command.
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else :;fi &&
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else :;fi &&
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else :;fi &&
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else :;fi &&
# Now rename the file to the real destination.
$doit $rmcmd -f $dstdir/$dstfile &&
$doit $mvcmd $dsttmp $dstdir/$dstfile
fi &&
exit 0

336
attic/missing Executable file
View File

@ -0,0 +1,336 @@
#! /bin/sh
# Common stub for a few missing GNU programs while installing.
# Copyright (C) 1996, 1997, 1999, 2000, 2002 Free Software Foundation, Inc.
# Originally by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
# This program 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 General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
# 02111-1307, USA.
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
if test $# -eq 0; then
echo 1>&2 "Try \`$0 --help' for more information"
exit 1
fi
run=:
# In the cases where this matters, `missing' is being run in the
# srcdir already.
if test -f configure.ac; then
configure_ac=configure.ac
else
configure_ac=configure.in
fi
case "$1" in
--run)
# Try to run requested program, and just exit if it succeeds.
run=
shift
"$@" && exit 0
;;
esac
# If it does not exist, or fails to run (possibly an outdated version),
# try to emulate it.
case "$1" in
-h|--h|--he|--hel|--help)
echo "\
$0 [OPTION]... PROGRAM [ARGUMENT]...
Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an
error status if there is no known handling for PROGRAM.
Options:
-h, --help display this help and exit
-v, --version output version information and exit
--run try to run the given command, and emulate it if it fails
Supported PROGRAM values:
aclocal touch file \`aclocal.m4'
autoconf touch file \`configure'
autoheader touch file \`config.h.in'
automake touch all \`Makefile.in' files
bison create \`y.tab.[ch]', if possible, from existing .[ch]
flex create \`lex.yy.c', if possible, from existing .c
help2man touch the output file
lex create \`lex.yy.c', if possible, from existing .c
makeinfo touch the output file
tar try tar, gnutar, gtar, then tar without non-portable flags
yacc create \`y.tab.[ch]', if possible, from existing .[ch]"
;;
-v|--v|--ve|--ver|--vers|--versi|--versio|--version)
echo "missing 0.4 - GNU automake"
;;
-*)
echo 1>&2 "$0: Unknown \`$1' option"
echo 1>&2 "Try \`$0 --help' for more information"
exit 1
;;
aclocal*)
if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
# We have it, but it failed.
exit 1
fi
echo 1>&2 "\
WARNING: \`$1' is missing on your system. You should only need it if
you modified \`acinclude.m4' or \`${configure_ac}'. You might want
to install the \`Automake' and \`Perl' packages. Grab them from
any GNU archive site."
touch aclocal.m4
;;
autoconf)
if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
# We have it, but it failed.
exit 1
fi
echo 1>&2 "\
WARNING: \`$1' is missing on your system. You should only need it if
you modified \`${configure_ac}'. You might want to install the
\`Autoconf' and \`GNU m4' packages. Grab them from any GNU
archive site."
touch configure
;;
autoheader)
if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
# We have it, but it failed.
exit 1
fi
echo 1>&2 "\
WARNING: \`$1' is missing on your system. You should only need it if
you modified \`acconfig.h' or \`${configure_ac}'. You might want
to install the \`Autoconf' and \`GNU m4' packages. Grab them
from any GNU archive site."
files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}`
test -z "$files" && files="config.h"
touch_files=
for f in $files; do
case "$f" in
*:*) touch_files="$touch_files "`echo "$f" |
sed -e 's/^[^:]*://' -e 's/:.*//'`;;
*) touch_files="$touch_files $f.in";;
esac
done
touch $touch_files
;;
automake*)
if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
# We have it, but it failed.
exit 1
fi
echo 1>&2 "\
WARNING: \`$1' is missing on your system. You should only need it if
you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'.
You might want to install the \`Automake' and \`Perl' packages.
Grab them from any GNU archive site."
find . -type f -name Makefile.am -print |
sed 's/\.am$/.in/' |
while read f; do touch "$f"; done
;;
autom4te)
if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
# We have it, but it failed.
exit 1
fi
echo 1>&2 "\
WARNING: \`$1' is needed, and you do not seem to have it handy on your
system. You might have modified some files without having the
proper tools for further handling them.
You can get \`$1Help2man' as part of \`Autoconf' from any GNU
archive site."
file=`echo "$*" | sed -n 's/.*--output[ =]*\([^ ]*\).*/\1/p'`
test -z "$file" && file=`echo "$*" | sed -n 's/.*-o[ ]*\([^ ]*\).*/\1/p'`
if test -f "$file"; then
touch $file
else
test -z "$file" || exec >$file
echo "#! /bin/sh"
echo "# Created by GNU Automake missing as a replacement of"
echo "# $ $@"
echo "exit 0"
chmod +x $file
exit 1
fi
;;
bison|yacc)
echo 1>&2 "\
WARNING: \`$1' is missing on your system. You should only need it if
you modified a \`.y' file. You may need the \`Bison' package
in order for those modifications to take effect. You can get
\`Bison' from any GNU archive site."
rm -f y.tab.c y.tab.h
if [ $# -ne 1 ]; then
eval LASTARG="\${$#}"
case "$LASTARG" in
*.y)
SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'`
if [ -f "$SRCFILE" ]; then
cp "$SRCFILE" y.tab.c
fi
SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'`
if [ -f "$SRCFILE" ]; then
cp "$SRCFILE" y.tab.h
fi
;;
esac
fi
if [ ! -f y.tab.h ]; then
echo >y.tab.h
fi
if [ ! -f y.tab.c ]; then
echo 'main() { return 0; }' >y.tab.c
fi
;;
lex|flex)
echo 1>&2 "\
WARNING: \`$1' is missing on your system. You should only need it if
you modified a \`.l' file. You may need the \`Flex' package
in order for those modifications to take effect. You can get
\`Flex' from any GNU archive site."
rm -f lex.yy.c
if [ $# -ne 1 ]; then
eval LASTARG="\${$#}"
case "$LASTARG" in
*.l)
SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'`
if [ -f "$SRCFILE" ]; then
cp "$SRCFILE" lex.yy.c
fi
;;
esac
fi
if [ ! -f lex.yy.c ]; then
echo 'main() { return 0; }' >lex.yy.c
fi
;;
help2man)
if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
# We have it, but it failed.
exit 1
fi
echo 1>&2 "\
WARNING: \`$1' is missing on your system. You should only need it if
you modified a dependency of a manual page. You may need the
\`Help2man' package in order for those modifications to take
effect. You can get \`Help2man' from any GNU archive site."
file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'`
if test -z "$file"; then
file=`echo "$*" | sed -n 's/.*--output=\([^ ]*\).*/\1/p'`
fi
if [ -f "$file" ]; then
touch $file
else
test -z "$file" || exec >$file
echo ".ab help2man is required to generate this page"
exit 1
fi
;;
makeinfo)
if test -z "$run" && (makeinfo --version) > /dev/null 2>&1; then
# We have makeinfo, but it failed.
exit 1
fi
echo 1>&2 "\
WARNING: \`$1' is missing on your system. You should only need it if
you modified a \`.texi' or \`.texinfo' file, or any other file
indirectly affecting the aspect of the manual. The spurious
call might also be the consequence of using a buggy \`make' (AIX,
DU, IRIX). You might want to install the \`Texinfo' package or
the \`GNU make' package. Grab either from any GNU archive site."
file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'`
if test -z "$file"; then
file=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'`
file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $file`
fi
touch $file
;;
tar)
shift
if test -n "$run"; then
echo 1>&2 "ERROR: \`tar' requires --run"
exit 1
fi
# We have already tried tar in the generic part.
# Look for gnutar/gtar before invocation to avoid ugly error
# messages.
if (gnutar --version > /dev/null 2>&1); then
gnutar "$@" && exit 0
fi
if (gtar --version > /dev/null 2>&1); then
gtar "$@" && exit 0
fi
firstarg="$1"
if shift; then
case "$firstarg" in
*o*)
firstarg=`echo "$firstarg" | sed s/o//`
tar "$firstarg" "$@" && exit 0
;;
esac
case "$firstarg" in
*h*)
firstarg=`echo "$firstarg" | sed s/h//`
tar "$firstarg" "$@" && exit 0
;;
esac
fi
echo 1>&2 "\
WARNING: I can't seem to be able to run \`tar' with the given arguments.
You may want to install GNU tar or Free paxutils, or check the
command line arguments."
exit 1
;;
*)
echo 1>&2 "\
WARNING: \`$1' is needed, and you do not seem to have it handy on your
system. You might have modified some files without having the
proper tools for further handling them. Check the \`README' file,
it often tells you about the needed prerequirements for installing
this package. You may also peek at any GNU archive site, in case
some other package would contain this missing \`$1' program."
exit 1
;;
esac
exit 0

99
attic/mkinstalldirs Executable file
View File

@ -0,0 +1,99 @@
#! /bin/sh
# mkinstalldirs --- make directory hierarchy
# Author: Noah Friedman <friedman@prep.ai.mit.edu>
# Created: 1993-05-16
# Public domain
errstatus=0
dirmode=""
usage="\
Usage: mkinstalldirs [-h] [--help] [-m mode] dir ..."
# process command line arguments
while test $# -gt 0 ; do
case "${1}" in
-h | --help | --h* ) # -h for help
echo "${usage}" 1>&2; exit 0 ;;
-m ) # -m PERM arg
shift
test $# -eq 0 && { echo "${usage}" 1>&2; exit 1; }
dirmode="${1}"
shift ;;
-- ) shift; break ;; # stop option processing
-* ) echo "${usage}" 1>&2; exit 1 ;; # unknown option
* ) break ;; # first non-opt arg
esac
done
for file
do
if test -d "$file"; then
shift
else
break
fi
done
case $# in
0) exit 0 ;;
esac
case $dirmode in
'')
if mkdir -p -- . 2>/dev/null; then
echo "mkdir -p -- $*"
exec mkdir -p -- "$@"
fi ;;
*)
if mkdir -m "$dirmode" -p -- . 2>/dev/null; then
echo "mkdir -m $dirmode -p -- $*"
exec mkdir -m "$dirmode" -p -- "$@"
fi ;;
esac
for file
do
set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'`
shift
pathcomp=
for d
do
pathcomp="$pathcomp$d"
case "$pathcomp" in
-* ) pathcomp=./$pathcomp ;;
esac
if test ! -d "$pathcomp"; then
echo "mkdir $pathcomp"
mkdir "$pathcomp" || lasterr=$?
if test ! -d "$pathcomp"; then
errstatus=$lasterr
else
if test ! -z "$dirmode"; then
echo "chmod $dirmode $pathcomp"
lasterr=""
chmod "$dirmode" "$pathcomp" || lasterr=$?
if test ! -z "$lasterr"; then
errstatus=$lasterr
fi
fi
fi
fi
pathcomp="$pathcomp/"
done
done
exit $errstatus
# Local Variables:
# mode: shell-script
# sh-indentation: 3
# End:
# mkinstalldirs ends here

17
documentation/.gitignore vendored Normal file
View File

@ -0,0 +1,17 @@
# A simulation of Subversion default ignores, generated by reposurgeon.
*.o
*.lo
*.la
*.al
*.libs
*.so
*.so.[0-9]*
*.a
*.pyc
*.pyo
*.rej
*~
*.#*
.*.swp
.DS_store
# Simulated Subversion default ignores end here

30
documentation/authors Normal file
View File

@ -0,0 +1,30 @@
A list of people who have contributed code to FCE Ultra follows.
Please note that the "Code Contributions" field may not be all inclusive;
the coder may have done more than what is listed.
Name/Alias Code Contributions Contact Information
------------------------------------------------------------------------------
Aaron Oneal Many changes to compile http://www.morphgear.com
with MSVC and first frame
skipping code.
BERO Base FCE code. bero@geocities.co.jp
CaH4e3 Some mapper code. CaH4e3 at mail dot ru
\Firebug\ VGA register setting code. ??
Joe Nahmias man pages.
LULU SDL network play code. ??
Paul Various code for the official kuliniew@purdue.edu
Kuliniewicz SDL port.
Quietust VRC7 "translation" code. quietust@ircN.org
Tatsuyuki OPL2 emulator. ??
Satoh
Xodnizel Most of the FCE Ultra code. http://xodnizel.net/

1674
documentation/changelog Normal file

File diff suppressed because it is too large Load Diff

313
documentation/cheat.html Normal file
View File

@ -0,0 +1,313 @@
<html>
<head>
<title>FCE Ultra Cheat Guide</title>
</head>
<body>
<center><h1>FCE Ultra Cheat Guide</h1></center>
<center><i>Last updated November 12, 2003<br />Valid as of FCE Ultra 0.97.4</i></center>
<p>
<b>Table of Contents:</b>
<ul>
<li><a href="#intro">Introduction</a>
<ul>
<li><a href="#cheatfiles">Cheat Files</a>
</ul>
<li><a href="#windows">The Windows Interface</a>
<ul>
<li><a href="#windows-search">Cheat Search Interface</a>
</ul>
<li><ba href="#text">The Text Interface(TODO)</ba>
<li><a href="#examples">Examples</a>
<ul>
<li><a href="#examples-mm3">"Mega Man 3" Windows Example</a>
<li><a href="#examples-oh">"Over Horizon" Text Interface Example</a>
</ul>
<li><a href="#tips">Tips</a>
</ul>
<hr width="100%">
<a name="tips"><h2>Introduction</h2></a>
<p>
FCE Ultra allows cheating by the periodic "patching" of arbitrary addresses
in the 6502's memory space with arbitrary values, as well as read substitution.
"Read substitution" is the method that would be used on a real NES/Famicom,
such as done by the Game Genie and Pro Action Replay. It is required
to support GG and PAR codes, but since it is relatively slow when done
in emulation, it is not the preferred method when a RAM patch will
suffice. Also, in FCE Ultra, read substitution will not work properly with
zero-page addressing modes(instructions that operate on RAM at $0000 through
$00FF).
</p>
<p>
The RAM patches are all applied a short time before the emulated
vertical blanking period. This detail shouldn't concern most people, though.
However, this does mean that cheating with games that use
bank-switched RAM may be problematic. Fortunately, such games are not very
common(in relation to the total number of NES and Famicom games).
</p>
<a name="cheatfiles"><h3>Cheat Files</h3></a>
<p>
Cheats are stored in the "cheats" subdirectory under the base FCE Ultra
directory. The files are in a simple plain-text format. Each line represents
a one-byte memory patch. The format is as follows(text in brackets []
represents optional parameters):
</p>
<p>
<blockquote>
[S][C][:]Address(hex):Value(hex):[Compare value:]Description
</blockquote>
Example:
<blockquote> 040e:05:Infinite super power.</blockquote>
</p>
<p>
A colon(:) near the beginning of the line is used to disable the cheat.
"S" denotes a cheat that is a read-substitute-style cheat(such as with Game
Genie cheats), and a "C" denotes that the cheat has a compare value.
</p>
<hr width="100%">
<a name="windows"><h2>The Windows Interface</h2></a>
<p>
All addresses listed in the cheats window are in unsigned
16-bit hexadecimal format and all values in these windows are in an
unsigned 8-bit decimal format(the range for values is 0 through 255).
</p>
<p>
The cheats window contains the list of cheats for the currently loaded game
on the right side. Existing cheats can be selected, edited, and updated
using the "Update" button.
</p>
<a name="windows-search"><h3>Cheat Search Interface</h2></a>
<p>
The cheat search interface consists of several components: a list of
addresses and associated data for a search, several command buttons,
and the search parameters.
</p>
<p>
Each entry in the list is in the format of:
<blockquote>Address:Original Value:Current Value</blockquote>
</p>
<p>
The address is the location in the 6502's address space, the original
value is the value that was stored at this address when the search was
reset, and the current value is the value that is currently stored at
that address. Selecting an item in this list will automatically cause
the "Address" field in the cheat information box on the right side of the
window to be updated with the selected address.
</p>
<p>
The "Reset Search" button resets the search process; all valid addresses
are displayed in the cheat list and the data values at those addresses noted.
</p>
<p>
The "Do Search" buttons performs a search based on the search parameters
and removes any non-matching addresses from the address list.
</p>
<p>
The "Set Original to Current" button sets the remembered original values
to the current values. It is like the "Reset Search" button, but it does
not affect which addresses are shown in the address list. This command is
especially useful when used in conjunction with the "O!=C" search filter.
</p>
<p>
The "Unhide Excluded" button shows all addresses that are excluded as a
result of any previous searches. It is like the "Reset Search" button
except that it does not affect the remembered original values.
</p>
<p>
The numbers assigned the names "V1" and "V2" have different meanings based
on which filter is selected. A list of the names of the filters and detailed
information on what they do follows("original value" corresponds to the value
remembered for a given addres and "current value" is the value currently
at that address. Also, if a value is not explicitly said to be shown
under a certain condition, then it is obviously excluded.):
<p>
"O==V1 && C==V2":
<blockquote>
Show the address if the original value is equal to "V1" AND
the current value is equal to "V2".
</blockquote>
</p>
<p>
"O==V1 && |O-C|==V2":
<blockquote>
Show the address if the original value is equal to "V1" AND
the difference between the current value and the original
value is equal to "V2".
</blockquote>
</p>
<p>
"|O-C|==V2":
<blockquote>
Show the address if the difference between the current value
and the original value is equal to "V2".
</blockquote>
</p>
<p>
"O!=C":
<blockquote>
Show the address if the original value does not equal the
current value.
</blockquote>
</p>
<p>
The following cheat methods/filters automatically perform the function
of the "Set Original to Current" button after "Do Search" is pressed.
</p>
<p>
"Value decreased."
<blockquote>
Show the address if the value has decreased.
</blockquote>
</p>
<p>
"Value increased."
<blockquote>
Show the address if the value has increased.
</blockquote>
</p>
<hr width="100%">
<a name="examples"><h2>Examples</h2></a>
<a name="examples-mm3"><h3>"Mega Man 3" Windows Example</h3></a>
<p>
This example will give Mega Man unlimited energy.
Immediately after entering the Top Man stage, make your way to the
"Add Cheat" window. Push "Reset Search".
Go back to playing and move right until the first enemy appears. Allow
yourself to be hit twice. Each hit does "2" damage, so you've lost 4 energy
bars. Go to the "Add Cheat" window again and select the third filter
("|O-C|==V2") and enter the value 4 next to "V2". Then push "Do Search".
</p>
<p>
Several addresses will appear in the address list. You can try to find
the address you want through trial and error, or you can narrow the results
down further. We will do the latter.
</p>
<p>
Go back to playing MM3 and get hit one more time and make your way back
to the "Add Cheat" window. Your damage is now "6". You can probably
see which address that contains your life(it is 00A2). If not, change
V2 to 6 and push "Do Search" again. This should leave only 00A2.
</p>
<p>
Select that entry in the address list. Shift your attention to the "Add
Cheat" box to the right. Type in a meaningful name and the desired value(156;
it was the value when you had no damage, so it's safe to assume it's the
maximum value you can use). Push the "Add" button and a new entry will
appear in the cheats list. The cheat has been added.
</p>
<a name="examples-oh"><h3>"Over Horizon" Text Interface Example</h3></a>
<p>
This example will give you infinite lives in the NTSC(Japanese) version
of "Over Horizon".
</p>
<p>
Start a new game. Notice that when you press "Start" during gameplay,
the number of lives you have left is indicated. With no cheating, you
start with 3 lives(2 lives left).
</p>
<p>
Activate the cheat interface immediately after starting a new game.
Select the "New Cheats" menu and "Reset Search".
</p>
<p>
I'll assume that the number of lives left shown in the game is the same number
that's stored in RAM. Now, "Do Search". You're going to use the first search
filter. For V1, enter the value 2. For V2, enter the same value. This,
coupled with the fact that you just reset the search, will allow you to search
for a value "absolutely"(as opposed to changes in the value).
</p>
<p>
Now, "Show Results". When I did it, I received 11 results:
</p>
<pre>
1) $0000:002:002
2) $001c:002:002
3) $001e:002:002
4) $009d:002:002
5) $00b9:002:002
6) $00e3:002:002
7) $0405:002:002
8) $0406:002:002
9) $0695:002:002
10) $07d5:002:002
11) $07f8:002:002
</pre>
<p>
You really can't do much yet(unless you want to spend time doing trial
and error cheat additions). Return to the game.
</p>
<p>
After losing a life, go back to the cheat interface, to the "New Cheats"
menu, and "Show Results". Here are my results:
</p>
<pre>
1) $0000:002:002
2) $001c:002:002
3) $001e:002:002
4) $009d:002:002
5) $00b9:002:041
6) $00e3:002:002
7) $0405:002:001
8) $0406:002:002
9) $0695:002:002
10) $07d5:002:001
11) $07f8:002:002
</pre>
<p>
Notice that two addresses seem to hold the number of lives($0405 and
$07d5). You can lose another life and go "Show Results" again, and you
should see that $07d5 is the address that holds the number of lives.
</p>
<p>
Now that you know the address that holds the number of lives, you can
add a cheat. You can either type in the number from the cheat results list
corresponding to the address you want to add a cheat for, or you can
remember the address and select "Add Cheat" from the "New Cheats" menu.
Do the former.
</p>
<p>
Now you will need to enter a name for the cheat. I suggest something short,
but descriptive. "Infinite lives" will work fine. Next, a prompt for
the address will show up. Since you selected an item from the list, you
can press enter to use the associated address($07d5). Next, you will
need to enter a value. It doesn't need to be large(in fact, it probably
shouldn't be; abnormally high numbers can cause some games to misbehave).
I suggest a value of 2. After this, you should get a prompt that looks like
this:
</p>
<pre>
Add cheat "Infinite lives" for address $07d5 with value 002?(Y/N)[N]:
</pre>
<p>
Answer "Y". You now have infinite lives.
</p>
<hr width="100%">
<a name="tips"><h2>Tips</h2></a>
<p>
Games store player information in many different ways. For example,
if you have "3" lives in Super Wacky Dodgeball 1989, the game might store
it in memory as 2, 3, or 4, or perhaps a different number all together.
Also, say that you have 69 life points out of 200 in Mole Mashers. The
game might store how many life points you have, or how much damage you have
taken. Relative value searches are very valuable because you probably
don't know the way that the game stores its player data.
</p>
<p>
Some games, especially RPGs, deal with individual numbers greater than
8-bits in size. Most that I've seen seem to store the multiple-byte data
least significant byte(lower byte of number) first in memory, though
conceivably, it could be stored most significant byte first, or the component
bytes of the number could be non-contiguous, though the latter is very unlikely.
For example, say I have 5304 experience points in Boring Quest for the
Overused Plot Device. To split the number into two eight bit decimal numbers,
take 5304 %(modulus) 256. This will give a number that is the lower 8 bits.
Next, take 5304 / 256. The integral component of your answer will be the
upper 8 bits(or the next 8 bits, if the number is or can be larger than 16
bits) of 5304. Now you will need to search for these numbers. Fortunately,
most(all?) RPGs seem to store large numbers exactly as they are shown in the
game.
</p>
</body>
</html>

341
documentation/copying Normal file
View File

@ -0,0 +1,341 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

67
documentation/faq Normal file
View File

@ -0,0 +1,67 @@
FCE Ultra General User's FAQ
preliminary version
Last updated on: Friday 13th, 2003
------------------
Q: Why do some games make a popping sound(Rad Racer 2, Final Fantasy 3)?
A: These games do a very crude drum imitation by causing a large jump in
the output level for a short period of time via the register at $4011.
The analog filters on a real NES and Famicom make it sound rather decent.
I have not completely emulated these filters. Enabling high-quality
sound emulation will also make these pseudo-drums sound better. See
the next question for more information.
Q: Why do some games' digitized sounds sound too loud?
Why do the drums in Crystalis and other games sound fuzzy?
A: The NES' digital to analog converter is faulty, in that it does not output
sound linearly. This effect is most noteable when a games messes with
register $4011, which is added with the triangle wave channel and the noise
channel outputs. When $4011 is set to a large value, the volume
of the triangle wave channel and the noise channel drop significantly. More
Importantly, when digitized sounds are being played and the digitized sample
stream is at a high value, less changes will be noticeable. In other words,
the byte sequence "00 01 00" would be much more audible than the sequence
"7e 7f 7e". This non-linearity is only emulated when high-quality sound
emulation is enabled.
Q: Why doesn't the NSF <insert name here> work(correctly) on FCE Ultra?
A: Some NSF rips are bad. Some read from addresses that are not specified
in the NSF specifications, expecting certain values to be returned.
Others execute undocumented instructions that have no affect on
less-accurate software NSF players, but will cause problems on NSF players
that emulate these instructions. Also, the playback rate specified
in the NSF header is currently ignored, though I haven't encountered
any problems in doing this.
Q: Why doesn't the game <insert name here> work(correctly) on FCE Ultra?
A: Many factors can make a game not work on FCE Ultra:
- If the ROM image is in the iNES format(typically files that have
the extension "nes"), its header may be incorrect. This
incorrectness may also be because of garbage in the
header. Certain utilities used to put text in the reserved
bytes of the iNES header, then those reserved bytes were
later assigned functions. FCE Ultra recognizes and
automatically removes(from the ROM image in RAM, not on the
storage medium) SOME header junk.
If the game has graphical errors while scrolling, chances are
the mirroring is set incorrectly in the header.
You can try to edit the header with a utility(in the NES
utilities section at http://zophar.net ) or a hex editor.
- The on-cart hardware the game uses may not be emulated
correctly.
- Limitations of the ROM image format may prevent a game from
being emulated correctly without special code to recognize that
game. This occurs quite often with many Koei MMC5(iNES mapper 5)
and MMC1(iNES mapper 1) games in the iNES format. FCE Ultra identifies
and emulates some of these games based on the ROM CRC32 value.
- The ROM image may be encrypted. The author of SMYNES seems to
have done this intentionally to block other emulators from
playing "SMYNES only" games.

128
documentation/fceu-sdl.6 Normal file
View File

@ -0,0 +1,128 @@
.\" t Hey, EMACS: -*- nroff -*-
.\" First parameter, NAME, should be all caps
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
.\" other parameters are allowed: see man(7), man(1)
.TH FCEU-SDL 6 "March 27, 2003"
.\" Please adjust this date whenever revising the manpage.
.\"
.\" Some roff macros, for reference:
.\" .nh disable hyphenation
.\" .hy enable hyphenation
.\" .ad l left justify
.\" .ad b justify to both left and right margins
.\" .nf disable filling
.\" .fi enable filling
.\" .br insert line break
.\" .sp <n> insert n+1 empty lines
.\" for manpage-specific macros, see man(7)
.SH NAME
fceu-sdl \- An emulator for the original (8-bit) Nintendo game console.
.SH SYNOPSIS
.B fceu-sdl
.RI [ options ]
"filename"
.SH DESCRIPTION
.B FCE Ultra
is an emulator for the original (8-bit) Nintendo Entertainment System (NES).
It has a robust color palette rendering engine that is fully customizable,
along with excellent sound and joystick support, and even includes TCP/IP based
network playing for head-to-head gaming with your friends!
.PP
The emulator is available in two versions for Linux (there are also versions
for DOS and Windows): an SDL-based version, and one based on SVGALIB. The two
versions are essentially the same \-\- use the SVGALIB version for the Linux
console, and the SDL version for playing in X.
.SH OPTIONS
These are some of the more frequently used options in FCE Ultra SDL. Run
fceu-sdl without any options for a larger list, or see the README for the full
list.
.TP
.B \-fs {0|1}
Toggle full-screen mode.
.TP
.B \-xres <res>
Set the Horizontal resolution for full-screen mode.
.TP
.B \-yres <res>
Set the Vertical resolution for full-screen mode.
.TP
.B \-gg
Enable Game Genie emulation support.
.TP
.B \-cpalette <file>
Use the custom palette in <file>.
.TP
.B \-soundvol <percentge>
Sets the sound volume to the given percentage.
.TP
.B \-sound <rate>
Set the sound playback sample rate (0 == off).
.TP
.B \-connect <server>
Connect as a client (player 2) to <server> for TCP/IP network play.
.TP
.B \-server
Be a host server (player 1) for a TCP/IP network game.
.TP
.B \-netport <portnum>
Use <portnum> for TCP/IP network play. Default is 4046.
.SH KEYBOARD COMMANDS
.B FCE Ultra
has a number of commands available within the emulator.
It also includes default keyboard bindings when emulating game pads or power pads.
.SS Gamepad Keyboard Bindings
.TS
center box;
cb | cb, c | ci.
NES Gamepad Keyboard
=
Arrows Cursor Arrows
A Left ALT
B Left CTRL
Select TAB
Start ENTER
.TE
.SS Other Commands
.PP
.TP 15
.BI <F2>
Activate cheat interface.
.TP 15
.BI <F4>
Toggle full-screen mode.
.TP 15
.BI <F5>
Save game state into current slot (set using number keys).
.TP 15
.BI <F7>
Restore game state from current slot (set using number keys).
.TP 15
.BI <F9>
Save screen snapshot.
.TP 15
.BI <F10>
Reset NES.
.TP 15
.BI <F11>
Toggle power to NES.
.TP 15
.BR <F12> ", " <ESC>
Quit
.B FCE Ultra.
.SH SEE ALSO
.BR fceu-svga (6)
.TP
.I /usr/share/doc/fceu-doc/readme-linux.txt.gz
The
.B FCE Ultra README
for linux, available from the
.I fceu-doc
Debian package.
.TP
.I http://fceultra.sourceforge.net/
The
.B FCE Ultra
project homepage.
.SH AUTHOR
This manual page was written by Joe Nahmias <joe@nahmias.net>,
for the Debian GNU/Linux system (but may be used by others).

135
documentation/fceu-svga.6 Normal file
View File

@ -0,0 +1,135 @@
.\" t Hey, EMACS: -*- nroff -*-
.\" First parameter, NAME, should be all caps
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
.\" other parameters are allowed: see man(7), man(1)
.TH FCEU-SVGA 6 "March 27, 2003"
.\" Please adjust this date whenever revising the manpage.
.\"
.\" Some roff macros, for reference:
.\" .nh disable hyphenation
.\" .hy enable hyphenation
.\" .ad l left justify
.\" .ad b justify to both left and right margins
.\" .nf disable filling
.\" .fi enable filling
.\" .br insert line break
.\" .sp <n> insert n+1 empty lines
.\" for manpage-specific macros, see man(7)
.SH NAME
fceu-svga \- An emulator for the original (8-bit) Nintendo game console.
.SH SYNOPSIS
.B fceu-svga
.RI [ options ]
"filename"
.SH DESCRIPTION
.B FCE Ultra
is an emulator for the original (8-bit) Nintendo Entertainment System (NES).
It has a robust color palette rendering engine that is fully customizable,
along with excellent sound and joystick support, and even includes TCP/IP based
network playing for head-to-head gaming with your friends!
.PP
The emulator is available in two versions for Linux (there are also versions
for DOS and Windows): an SDL-based version, and one based on SVGALIB. The two
versions are essentially the same \-\- use the SVGALIB version for the Linux
console, and the SDL version for playing in X.
.SH OPTIONS
These are some of the more frequently used options in FCE Ultra SVGALIB. Run
fceu-svga without any options for a larger list, or see the README for the full
list.
.TP
.BI \-gg
Enable Game Genie emulation support.
.TP
.BI "\-cpalette " <file>
Use the custom palette in <file>.
.TP
.BI "\-soundvol " <percentage>
Sets the sound volume to the given percentage.
.TP
.BI "\-sound " <rate>
Set the sound playback sample rate (0 == off).
.TP
.BI "\-connect " <server>
Connect as a client (player 2) to <server> for TCP/IP network play.
.TP
.BI \-server
Be a host server (player 1) for a TCP/IP network game.
.TP
.BI "\-netport " <portnum>
Use <portnum> for TCP/IP network play.
.SH KEYBOARD COMMANDS
.B FCE Ultra
has a number of commands available within the emulator.
It also includes default keyboard bindings when emulating game pads or power pads.
.SS Gamepad Keyboard Bindings
.TS
center box;
cb | cb, c | ci.
NES Gamepad Keyboard
=
Arrows Cursor Arrows
A Left ALT
B Left CTRL
Select TAB
Start ENTER
.TE
.SS Other Commands
.PP
.TP 15
.BI <F2>
Activate cheat interface.
.TP 15
.BI <F3>
Lock virtual console.
.TP 15
.BI <F4>
Unlock virtual console.
.TP 15
.BI <F5>
Save game state into current slot (set using number keys).
.TP 15
.BI <F7>
Restore game state from current slot (set using number keys).
.TP 15
.BI <F9>
Save screen snapshot.
.TP 15
.BI <F10>
Reset NES.
.TP 15
.BI <F11>
Toggle power to NES.
.TP 15
.BR <F12> ", " <ESC>
Quit
.B FCE Ultra.
.SH NOTES
This program needs permisison to the video hardware, this normally requires
root access. One way to accomplish this is by making
.BI "fceu-svga " suid-root.
An easy way to do this is using the
.BR dpkg-statoverride
command. For example, as root on a
.I Debian
system run:
.PP
.B dpkg-statoverride --update --add root games 04755 /usr/games/fceu-svga
.PP
This will ensure that the setuid bit is preserved across package upgrades.
.SH SEE ALSO
.BR dpkg-statoverride "(8), " fceu-sdl (6)
.TP
.I /usr/share/doc/fceu-doc/readme-linux.txt.gz
The
.B FCE Ultra README
for linux, available from the
.I fceu-doc
Debian package.
.TP
.I http://fceultra.sourceforge.net/
The
.B FCE Ultra
project homepage.
.SH AUTHOR
This manual page was written by Joe Nahmias <joe@nahmias.net>,
for the Debian GNU/Linux system (but may be used by others).

702
documentation/fceultra.html Normal file
View File

@ -0,0 +1,702 @@
<html>
<head>
<title>FCE Ultra General Documentation</title>
</head>
<body>
<center><h1>FCE Ultra General Documentation</h1></center>
<center><i>Last updated November 12, 2003<br />Valid as of FCE Ultra 0.97</i></center>
<p>
<b>Table of Contents:</b>
<ul>
<li /><a href="#intro">Introduction</a>
<ul>
<li/><a href="#intro-history">History of FCE Ultra</a>
</ul>
<li /><a href="#features">Core Features</a>
<ul>
<li /><a href="#features-cpu">CPU</a>
<li /><a href="#features-ppu">PPU</a>
<ul>
<li /><a href="#features-ppu-palettes">Palettes</a>
</ul>
<li /><a href="#features-sound">Sound</a>
<li /><a href="#features-input">Input</a>
<ul>
<li /><a href="#features-input-zapper">Zapper</a>
</ul>
<li /><a href="#features-expansion">File Formats/Expansion Hardware</a>
<ul>
<li /><a href="#features-expansion-ines">iNES Format</a>
<li /><a href="#features-expansion-unif">UNIF</a>
<li /><a href="#features-expansion-fds">Famicom Disk System</a>
<li /><a href="#features-expansion-genie">Game Genie</a>
<li /><a href="#features-expansion-vs">VS Unisystem</a>
</ul>
<li /><a href="#features-ips">Automatic IPS Patching</a>
</ul>
<li /><a href="#using">Using FCE Ultra</a>
<ul>
<li /><a href="#using-keys">Key Assignments</a>
<ul />
<li /><a href="#using-keys-vs">VS Unisystem</a>
<li /><a href="#using-keys-fds">Famicom Disk System</a>
<li /><a href="#using-keys-gamepad">Game Pad</a>
<li /><a href="#using-keys-powerpad">Power Pad</a>
<li /><a href="#using-keys-fkb">Family Keyboard</a>
</ul>
<li /><a href="#using-cli">Command-line Ports</a>
<ul>
<li /><a href="#using-cli-sdl">SDL</a>
<li /><a href="#using-cli-dos">DOS</a>
<li /><a href="#using-cli-svgalib">Linux SVGAlib</a>
</ul>
<li /><a href="#using-win">MS Windows Port</a>
</ul>
<li /><a href="cheat.html">Cheat Guide</a>
<li /><a href="netplay.html">Network Play</a>
<li /><a href="#credits">Credits</a>
</ul>
</p>
<hr width="100%">
<a name="intro"><h2>Introduction</h2></a>
<p>
FCE Ultra is an NTSC and PAL Famicom/NES emulator for various
platforms. It is based upon Bero's original FCE source code. Current
features include good PPU, CPU, pAPU, expansion chip, and joystick
emulation. Also a feature unique to this emulator(at the current
time) is authentic Game Genie emulation. Save states and snapshot
features also have been implemented.
</p>
<p>
This document has been arranged to keep user interface details and emulation
details as separate as possible, though this has not been accomplished entirely.
</p>
<p>
In several places references are made to the "base directory". If you
are running a port on a UN*X-like system(Linux/*BSD/Mac OSX/SunOS/etc.), the
base directory is "~/.fceultra", or in other words,
"your home directory plus .fceultra". For all other ports(including DOS and
MS Windows), the base directory is the directory that the executable is in.
</p>
<a name="intro-history"><h3>History of FCE Ultra</h3></a>
<p>
<i>This section is a work-in-progress. Some details may be incorrect.</i><br/>
Bero originally wrote a NES emulator that was referred to as <a href="http://www.geocities.co.jp/Playtown/2004/fce.htm">FCE</a>.
This name was apparently meant only to serve as a temporary name, but its usage remained. Xodnizel originally ported it to
Linux SVGAlib, and made a few improvements. This code base was abandoned,
and work began anew, under DOS, with the original FCE source code.
At the end of November, 1998, FCE Ultra Beta 1 was released.
</p>
<p>
FCE Ultra remained DOS-only until version 0.18, when it was ported to Linux
SVGAlib, and released as a staticly-linked executable. The first MS Windows
port was released as version 0.25.
</p>
<p>
The source code of 0.40 was released on November 12, 2000. It retained
the simple license of FCE for a long time, which stated that " This software is freeware.you can use it non-commercially."
Almost two years later, in June 2002, 0.80 was released, and FCE Ultra was relicensed under the GNU GPL.
</p>
<hr width="100%">
<a name="features"><h2>Core Features</h2></a>
<a name="features-cpu"><h3>CPU</h3></a>
<p>
All official instructions of the NES' CPU, the 2A03, which is compatible(mostly)
with the 6502, are emulated. "Unofficial" instructions are also emulated,
though probably not as accurately as the more well-defined official instructions.
</p>
<hr width="90%">
<a name="features-ppu"><h3>PPU</h3></a>
<p>
</p>
<a name="features-ppu-palettes"><h4>Palettes</h4></a>
<p>
FCE Ultra has many palette features, including loading a custom palette
to replace the default NES palette. The palette from an NTSC NES can
also be generated on-the-fly.
</p>
<p>
First, a note on on the format of external palettes; Palette files are expected to contain 64 8-bit RGB triplets(each in
that order; red comes first in the triplet in the file, then green,
then blue). Each 8-bit value represents brightness for that particular
color. 0 is minimum, 255 is maximum.
</p>
<p>
Palettes can be set on a per-game basis. To do this, put a palette
file in the "gameinfo" directory with the same base filename
as the game you wish to associate with and add the extension "pal".
Examples:
<pre>
File name: Palette file name:
BigBad.nes BigBad.pal
BigBad.zip BigBad.pal
BigBad.Better.nes BigBad.Better.pal
</pre>
</p>
<p>
With so many ways to choose a palette, figuring out which one will
be active may be difficult. Here's a list of what palettes will
be used, in order from highest priority to least priority(if a
condition doesn't exist for a higher priority palette, the emulator
will continue down its list of palettes).
<ul>
<li />NSF Palette(for NSFs only)
<li />Palette loaded from the "gameinfo" directory.
<li />NTSC Color Emulation(only for NTSC NES games).
<li />VS Unisystem palette(if the game is a VS Unisystem game and a palette is available).
<li />Custom global palette.
<li />Default NES palette.
</ul>
</p>
<p>
<hr width="90%">
<a name="features-sound"><h3>Sound</h3></a>
<p>
All 5 internal sound channels are supported(2x rectangle, triangle, noise,
and DMC/PCM). Sound channels are emulated with CPU instruction granularity.
There are two sound quality options. Low-quality sound, the default sound
quality option, generates sound data at 16x the playback rate and averages
those samples together to 1 sample. This method works fairly well and
is reasonably fast, but there is still some aliasing and sound distortion.
All sample rates between 8192Hz and 96000Hz are supported.
</p><p>
The high-quality sound emulation is much more CPU intensive, but the
quality is worth it, if your machine is fast enough. Sound data is
generated at the NES' CPU clock rate(about 1.8MHz for the NTSC NES), and
then resampled to the output sample rate. Custom-designed 483rd order
Parks-McClellan algorithm filter coefficients are used. Supported playback rates are
44100Hz, 48000Hz, and 96000Hz. The following filter statistics apply
for NTSC emulation.
<p>
<table border>
<tr><th>Rate:</th><th>Passband Upper Bound(Hz):</th><th>Passband ripple(dB):</th><th>Transition bandwidth(Hz):</th><th>Stopband attenuation(dB):</th></tr>
<tr><td>44100</td><td>11982.5</td><td>0.20</td><td>10067.5</td><td>66.4</td></tr>
<tr><td>48000</td><td>13932.5</td><td>0.10</td><td>10067.5</td><td>60.0</td></tr>
<tr><td>96000</td><td>30000.0</td><td>0.01</td><td>18000.0</td><td>103.0</td></tr>
</table>
</p>
<p>
In addition to improved tonal quality, the high-quality sound emulation
also simulates deficiencies present in the NES' D/A converter, which
causes the sound output to be non-linear(for example, 16+16 might create
an output equivalent to 25). Emulation of this is necessary for the
DMC/PCM sound channel to sound correct. For example, without this feature,
the drums in "Crystalis" are excessively fuzzy.
</p>
<p>
The "highest" sound quality mode is similar to the normal high-quality mode,
but the filters are of a higher order(1024 coefficients). Ripple is
reduced, the upper bound of the passband is higher, and the stopband
attenuation is slightly higher. The highest-quality mode filter coefficients
were created using "gmeteor". The parameters used to create these filters
can be found in the source code distribution.
</p>
<p>
Besides the 5 internal NES sound channels, FCE Ultra emulates the extra
sound capabilities of the Konami VRCVI, Konami VRCVII, Namco 106, Nintendo MMC5, and the Sunsoft FME-07 chips. The extra sound
channel in the Famicom Disk System is also emulated, but the support for
its FM capabilities is limited.
</p>
<hr width="90%">
<a name="features-input"><h3>Input</h3></a>
<p>
FCE Ultra emulates the standard NES gamepad, the Four-Score multiplayer
adapter, the Zapper, the Power Pad, and the Arkanoid controller. The
Famicom version of the Arkanoid controller, the "Space Shadow" gun, the
Famicom 4-player adapter, and the Family Keyboard are also emulated.
</p>
<a name="features-input-zapper"><h4>Zapper</h4></a>
<p>
Most Zapper NES games expect the Zapper to be plugged into port 2.
and most VS Unisystem games expect the Zapper to be plugged
into port 1.
</p><p>
The left mouse button is the emulated trigger button for the
Zapper. The right mouse button is also emulated as the trigger,
but as long as you have the right mouse button held down, no color
detection will take place, which is effectively like pulling the
trigger while the Zapper is pointed away from the television screen.
Note that you must hold the right button down for a short
time to have the desired effect.
</p>
<hr width="90%">
<a name="features-expansion"><h3>File Formats/Expansion Hardware</h3></a>
<p>
FCE Ultra supports the iNES, FDS(raw and with a header), UNIF, and NSF file
formats. FDS ROM images in the iNES format are not supported; it would
be silly to do so and storing them in that format is nonsensical.
</p>
<p>
FCE Ultra supports loading ROM/disk images from some types of compressed files.
FCE Ultra can load data from both PKZIP-format files and
gzip-format files. Only the "deflate" algorithm is supported, but
this is the most widely used algorithm for these formats.
A compressed FDS disk image will only be saved back to disk if it
uses the gzip format.
</p>
<p>
All files in a PKZIP format archive will be scanned for the
followings extensions: .nes, .fds, .nsf, .unf, .nez, .unif
The first compressed file to have one of these extensions will be
loaded. If no compressed file has one of these extensions, the
first compressed file will be loaded.
</p>
<a name="features-expansion-ines"><h4>iNES Format</h4></a>
<p>
The battery-backed RAM, vertical/horizontal mirroring, four-screen
name table layout, and 8-bit mapper number capabilities of the iNES
format are supported. The 512-byte trainer capability is also supported,
but it is deprecated. Common header corruption conditions are cleaned(let's
go on a DiskDude hunt), though not all conditions can be automatically
detected and fixed. In addition, a few common header inaccuracies for
games are also corrected(detected by CRC32 value). Note that these
fixes are not written back to the storage medium.
</p>
<p>
Support for the recent VS System bit and "number of 8kB RAM banks"
is not implemented. Too many iNES headers are corrupt where this new data
is stored, causing problems for those games.
</p>
<p>
The following table lists iNES-format "mappers" supported well in FCE Ultra.
</p><p>
<table width="100%" border>
<tr><th>Number:</th><th>Description:</th><th>Game Examples:</th></tr>
<tr><td>0</td><td>No bankswitching</td><td>Donkey Kong, Mario Bros</td></tr>
<tr><td>1</td><td>Nintendo MMC1</td><td>MegaMan 2, Final Fantasy</td></tr>
<tr><td>2</td><td>Simple 16KB PROM Switch(UNROM)</td><td>MegaMan, Archon, 1944</td></tr>
<tr><td>3</td><td>Simple 8KB VROM Switch(CNROM)</td><td>Spy Hunter, Gradius</td></tr>
<tr><td>4</td><td>Nintendo MMC3</td><td>Super Mario Bros. 3, Recca, Final Fantasy 3</td></tr>
<tr><td>5</td><td>Nintendo MMC5</td><td>Castlevania 3, Just Breed, Bandit Kings of Ancient China</td></tr>
<tr><td>6</td><td>FFE F4 Series(hacked, bootleg)</td><td></td></tr>
<tr><td>7</td><td>AOROM</td><td>Battle Toads, Time Lord</td></tr>
<tr><td>8</td><td>FFE F3 Series(hacked, bootleg)</td><td></td></tr>
<tr><td>9</td><td>Nintendo MMC2</td><td>Punchout!</td></tr>
<tr><td>10</td><td>Nintendo MMC4</td><td>Fire Emblem, Fire Emblem Gaiden</td></tr>
<tr><td>11</td><td>Color Dreams</td><td>Crystal Mines, Bible Adventures</td></tr>
<tr><td>12</td><td>??</td><td>Dragon Ball Z 5 ("bootleg" original)</td></tr>
<tr><td>13</td><td>CPROM</td><td>Videomation</td></tr>
<tr><td>15</td><td>Multi-cart(bootleg)</td><td>100-in-1: Contra Function 16</td></tr>
<tr><td>16</td><td>Bandai ??</td><td>Dragon Ball Z, SD Gundam Gaiden</td></tr>
<tr><td>17</td><td>FFE F8 Series(hacked, bootleg)</td><td></td></tr>
<tr><td>18</td><td>Jaleco SS806</td><td>Pizza Pop, Plasma Ball</td></tr>
<tr><td>19</td><td>Namco 106</td><td>Splatter House, Mappy Kids</td></tr>
<tr><td>21</td><td>Konami VRC4 2A</td><td>WaiWai World 2, Ganbare Goemon Gaiden 2</td></tr>
<tr><td>22</td><td>Konami VRC4 1B</td><td>Twinbee 3</td></tr>
<tr><td>23</td><td>Konami VRC2B</td><td>WaiWai World, Crisis Force</td></tr>
<tr><td>24</td><td>Konami VRC6</td><td>Akumajou Densetsu</td></tr>
<tr><td>25<td>Konami VRC4</td><td>Gradius 2, Bio Miracle:Boku tte Upa</td></tr>
<tr><td>26</td><td>Konami VRC6 A0-A1 Swap</td><td>Esper Dream 2, Madara</td></tr>
<tr><td>32</td><td>IREM G-101</td><td>Image Fight 2, Perman</td></tr>
<tr><td>33</td><td>Taito TC0190/TC0350</td><td>Don Doko Don</td></tr>
<tr><td>34</td><td>NINA-001 and BNROM</td><td>Impossible Mission 2, Deadly Towers, Bug Honey</td></tr>
<tr><td>40</td><td>(bootleg)</td><td>Super Mario Bros. 2</td></tr>
<tr><td>41</td><td>Caltron 6-in-1</td><td>Caltron 6-in-1</td></tr>
<tr><td>42</td><td>(bootleg)</td><td>Mario Baby</td></tr>
<tr><td>44</td><td>Multi-cart(bootleg)</td><td>Super HiK 7 in 1</td></tr>
<tr><td>45</td><td>Multi-cart(bootleg)</td><td>Super 1000000 in 1</td></tr>
<tr><td>46</td><td>Game Station</td><td>Rumble Station</td></tr>
<tr><td>47</td><td>NES-QJ</td><td>Nintendo World Cup/Super Spike V-Ball</td></tr>
<tr><td>48</td><td>Taito TC190V</td><td>Flintstones</td></tr>
<tr><td>49</td><td>Multi-cart(bootleg)</td><td>Super HiK 4 in 1</td></tr>
<tr><td>50</td><td>(bootleg)</td><td>Super Mario Bros. 2</td></tr>
<tr><td>51</td><td>Multi-cart(bootleg)</td><td>11 in 1 Ball Games</td></tr>
<tr><td>52</td><td>Multi-cart(bootleg)</td><td>Mario Party 7 in 1</td></tr>
<tr><td>57</td><td>Multi-cart(bootleg)</td><td>Game Star GK-54</td></tr>
<tr><td>60</td><td>Multi-cart(bootleg)</td><td>4 in 1(Reset-selected)</td></tr>
<tr><td>61</td><td>Multi-cart(bootleg)</td><td>20 in 1</td></tr>
<tr><td>62</td><td>Multi-cart(bootleg)</td><td>Super 700 in 1</td></tr>
<tr><td>64</td><td>Tengen RAMBO 1</td><td>Klax, Rolling Thunder, Skull and Crossbones</td></tr>
<tr><td>65</td><td>IREM H-3001</td><td>Daiku no Gensan 2</td></tr>
<tr><td>66</td><td>GNROM</td><td>SMB/Duck Hunt</td></tr>
<tr><td>67</td><td>Sunsoft ??</td><td>Fantasy Zone 2</td></tr>
<tr><td>68</td><td>Sunsoft ??</td><td>After Burner 2, Nantetta Baseball</td></tr>
<tr><td>69</td><td>Sunsoft FME-7</td><td>Batman: Return of the Joker, Hebereke</td>
<tr><td>70</td><td>??</td><td>Kamen Rider Club</td></tr>
<tr><td>71</td><td>Camerica</td><td>Fire Hawk, Linus Spacehead</td></tr>
<tr><td>72</td><td>Jaleco ??</td><td>Pinball Quest</td></tr>
<tr><td>73</td><td>Konami VRC3</td><td>Salamander</td></tr>
<tr><td>74</td><td>Taiwanese MMC3 CHR ROM w/ VRAM</td><td>Super Robot Wars 2</td></tr>
<tr><td>75</td><td>Jaleco SS8805/Konami VRC1</td><td>Tetsuwan Atom, King Kong 2</td></tr>
<tr><td>76</td><td>Namco 109</td><td>Megami Tensei</td></tr>
<tr><td>77</td><td>IREM ??</td><td>Napoleon Senki</td></tr>
<tr><td>78</td><td>Irem 74HC161/32</td><td>Holy Diver</td></tr>
<tr><td>79</td><td>NINA-06/NINA-03</td><td>F15 City War, Krazy Kreatures, Tiles of Fate</td></tr>
<tr><td>80</td><td>Taito X-005</td><td>Minelvation Saga</td></tr>
<tr><td>82</td><td>Taito ??</td><td>Kyuukyoku Harikiri Stadium - Heisei Gannen Ban</td><tr/>
<tr><td>83</td><td>Multi-cart(bootleg)</td><td>Dragon Ball Party</td></tr>
<tr><td>85</td><td>Konami VRC7</td><td>Lagrange Point</td></tr>
<tr><td>86</td><td>Jaleco ??</td><td>More Pro Baseball</td></tr>
<tr><td>87</td><td>??</td><td>Argus</td></tr>
<tr><td>88</td><td>Namco 118</td><td>Dragon Spirit</td></tr>
<tr><td>89</td><td>Sunsoft ??</td><td>Mito Koumon</td></tr>
<tr><td>90</td><td>??</td><td>Super Mario World</td></tr>
<tr><td>91</td><td>??</td><td>Mari Street Fighter 3 Turbo</td></tr>
<tr><td>92</td><td>Jaleco ??</td><td>MOERO Pro Soccer</td></tr>
<tr><td>93</td><td>??</td><td>Fantasy Zone</td></tr>
<tr><td>94</td><td>??</td><td>Senjou no Ookami</td></tr>
<tr><td>95</td><td>Namco ??</td><td>Dragon Buster</td></tr>
<tr><td>96</td><td>Bandai ??</td><td>Oeka Kids</td></tr>
<tr><td>97</td><td>??</td><td>Kaiketsu Yanchamaru</td></tr>
<tr><td>99</td><td>VS System 8KB VROM Switch</td><td>VS SMB, VS Excite Bike</td></tr>
<tr><td>105</td><td>NES-EVENT</td><td>Nintendo World Championships</td></tr>
<tr><td>107</td><td>??</td><td>Magic Dragon</td></tr>
<tr><td>112</td><td>Asder</td><td>Sango Fighter, Hwang Di</td></tr>
<tr><td>113</td><td>MB-91</td><td>Deathbots</td></tr>
<tr><td>114</td><td>??</td><td>The Lion King</td></tr>
<tr><td>115</td><td>??</td><td>Yuu Yuu Hakusho Final</td></tr>
<tr><td>117</td><td>??</td><td>San Guo Zhi 4</td></tr>
<tr><td>118</td><td>MMC3-TLSROM/TKSROM Board</td><td>Ys 3, Goal! 2, NES Play Action Football</td></tr>
<tr><td>119</td><td>MMC3-TQROM Board</td><td>High Speed, Pin*Bot</td></tr>
<tr><td>140</td><td>Jaleco ??</td><td>Bio Senshi Dan</td></tr>
<tr><td>144</td><td>??</td><td>Death Race</td></tr>
<tr><td>151</td><td>Konami VS System Expansion</td><td>VS The Goonies, VS Gradius</td></tr>
<tr><td>152</td><td>??</td><td>Arkanoid 2, Saint Seiya Ougon Densetsu</td></tr>
<tr><td>153</td><td>Bandai ??</td><td>Famicom Jump 2</td></tr>
<tr><td>154</td><td>Namco ??</td><td>Devil Man</td></tr>
<tr><td>155</td><td>MMC1 w/o normal WRAM disable</td><td>The Money Game, Tatakae!! Rahmen Man</td></tr>
<tr><td>156</td><td>??</td><td>(TBA)</td></tr>
<tr><td>158</td><td nowrap>RAMBO 1 Derivative</td><td>Alien Syndrome</td></tr>
<tr><td>180</td><td>??</td><td>Crazy Climber</td></tr>
<tr><td>182</td><td>??</td><td>Super Donkey Kong</td></tr>
<tr><td>184</td><td>??</td><td>Wing of Madoola, The</td></tr>
<tr><td>189</td><td>??</td><td>Thunder Warrior, Street Fighter 2 (Yoko)</td></tr>
<tr><td>193</td><td>Mega Soft</td><td>Fighting Hero</td></tr>
<tr><td>200</td><td>Multi-cart(bootleg)</td><td>1200-in-1</td></tr>
<tr><td>201</td><td>Multi-cart(bootleg)</td><td>21-in-1</td></tr>
<tr><td>202</td><td>Multi-cart(bootleg)</td><td>150 in 1</td></tr>
<tr><td>203</td><td>Multi-cart(bootleg)</td><td>35 in 1</td></tr>
<tr><td>225</td><td>Multi-cart(bootleg)</td><td>58-in-1/110-in-1/52 Games</td></tr>
<tr><td>226</td><td>Multi-cart(bootleg)</td><td>76-in-1</td></tr>
<tr><td>227</td><td>Multi-cart(bootleg)</td><td>1200-in-1</td></tr>
<tr><td>228</td><td>Action 52</td><td>Action 52, Cheetahmen 2</td></tr>
<tr><td>229</td><td>Multi-cart(bootleg)</td><td>31-in-1</td></tr>
<tr><td>230</td><td>Multi-cart(bootleg)</td><td>22 Games</td></tr>
<tr><td>231</td><td>Multi-cart(bootleg)</td><td>20-in-1</td></tr>
<tr><td>232</td><td>BIC-48</td><td>Quattro Arcade, Quattro Sports</td></tr>
<tr><td>234</td><td>Multi-cart ??</td><td>Maxi-15</td></tr>
<tr><td>235</td><td>Multi-cart(bootleg)</td><td>Golden Game 150 in 1</td></tr>
<tr><td>240</td><td>??</td><td>Gen Ke Le Zhuan, Shen Huo Le Zhuan</td></tr>
<tr><td>242</td><td>??</td><td>Wai Xing Zhan Shi</td></tr>
<tr><td>244</td><td>??</td><td>Decathalon</td></tr>
<tr><td>246</td><td>??</td><td>Fong Shen Ban</td></tr>
<tr><td>248</td><td>??</td><td>Bao Qing Tian</td></tr>
<tr><td>249</td><td>Waixing ??</td><td>??</td></tr>
<tr><td>250</td><td>??</td><td>Time Diver Avenger</td></tr>
</table>
</p>
<p>
<a name="features-expansion-unif"><h4>UNIF</h4></a>
</p>
<p>
FCE Ultra supports the following UNIF boards. The prefixes HVC-, NES-, BTL-, and BMC- are omitted, since they are currently ignored in FCE Ultra's UNIF loader.
</p>
<p>
<table border width="100%">
<tr><th colspan="2">Group:</th></tr>
<tr><th>Name:</th><th>Game Examples:</th></tr>
<tr><th colspan="2">Bootleg:</th></tr>
<tr><td>MARIO1-MALEE2</td><td>Super Mario Bros. Malee 2</td></tr>
<tr><td>NovelDiamond9999999in1</td><td>Novel Diamond 999999 in 1</td></tr>
<tr><td>Super24in1SC03</td><td>Super 24 in 1</td></tr>
<tr><td>Supervision16in1</td><td>Supervision 16-in-1</td></tr>
<tr><th colspan="2">Unlicensed:</th></tr>
<tr><td>Sachen-8259A</td><td>Super Cartridge Version 1</td></tr>
<tr><td>Sachen-8259B</td><td>Silver Eagle</td></tr>
<tr><td>Sachen-74LS374N</td><td>Auto Upturn</td></tr>
<tr><td>SA-016-1M</td><td>Master Chu and the Drunkard Hu</td></tr>
<tr><td>SA-72007</td><td>Sidewinder</td></tr>
<tr><td>SA-72008</td><td>Jovial Race</td></tr>
<tr><td>SA-0036</td><td>Mahjong 16</td></tr>
<tr><td>SA-0037</td><td>Mahjong Trap</td></tr>
<tr><td>TC-U01-1.5M</td><td>Challenge of the Dragon</td></tr>
<tr><td>8237</td><td>Pocahontas Part 2</td></tr>
<tr><th colspan="2">MMC1:</th></tr>
<tr><td>SAROM</td><td>Dragon Warrior</td></tr>
<tr><td>SBROM</td><td>Dance Aerobics</td></tr>
<tr><td>SCROM</td><td>Orb 3D</td></tr>
<tr><td>SEROM</td><td>Boulderdash</td></tr>
<tr><td>SGROM</td><td>Defender of the Crown</td></tr>
<tr><td>SKROM</td><td>Dungeon Magic</td></tr>
<tr><td>SLROM</td><td>Castlevania 2</td></tr>
<tr><td>SL1ROM</td><td>Sky Shark</td></tr>
<tr><td>SNROM</td><td>Shingen the Ruler</td></tr>
<tr><td>SOROM</td><td>Nobunaga's Ambition</td></tr>
<tr><th colspan="2">MMC3:</th></tr>
<tr><td>TFROM</td><td>Legacy of the Wizard</td></tr>
<tr><td>TGROM</td><td>Megaman 4</td></tr>
<tr><td>TKROM</td><td>Kirby's Adventure</td></tr>
<tr><td>TKSROM</td><td>Ys 3</td></tr>
<tr><td>TLROM</td><td>Super Spike V'Ball</td></tr>
<tr><td>TLSROM</td><td>Goal! 2</td></tr>
<tr><td>TR1ROM</td><td>Gauntlet</td></tr>
<tr><td>TQROM</td><td>Pinbot</td></tr>
<tr><td>TSROM</td><td>Super Mario Bros. 3</td></tr>
<tr><td>TVROM</td><td>Rad Racer 2</td></tr>
<tr><th colspan="2">MMC5:</th></tr>
<tr><td>EKROM</td><td>Gemfire</td></tr>
<tr><td>ELROM</td><td>Castlevania 3</td></tr>
<tr><td>ETROM</td><td>Nobunaga's Ambition 2</td></tr>
<tr><td>EWROM</td><td>Romance of the Three Kingdoms 2</td></tr>
<tr><th colspan="2">MMC6:</th></tr>
<tr><td>HKROM</td><td>Star Tropics</td></tr>
<tr><th colspan="2">Nintendo Discrete Logic:</th></tr>
<tr><td>CNROM</td><td>Gotcha</td></tr>
<tr><td>CPROM</td><td>Videomation</td></tr>
<tr><td>GNROM</td><td>Super Mario Bros./Duck Hunt</td></tr>
<tr><td>MHROM</td><td></td></tr>
<tr><td>NROM-128</td><td>Mario Bros.</td></tr>
<tr><td>NROM-256</td><td>Super Mario Bros.</td></tr>
<tr><td>RROM-128</td><td></td></tr>
<tr><td>UNROM</td><td>Megaman</td></tr>
</table>
</p>
<a name="features-expansion-fds"><h4>Famicom Disk System</h4></a>
<p>
You will need the FDS BIOS ROM image in the base FCE Ultra directory.
It must be named "disksys.rom". FCE Ultra will not load FDS games
without this file.
</p>
Two types of FDS disk images are supported: disk images with the
FWNES-style header, and disk images with no header. The number
of sides on headerless disk images is calculated by the total file
size, so don't put extraneous data at the end of the file.
<p>
You should make backups of all of your FDS games you use with FCE
Ultra. This is because FCE Ultra will write the disk image back to
the storage medium, and the disk image in RAM might have been corrupted
because of inaccurate emulation(this case is not likely to occur, but
it could occur).
</p>
<a name="features-expansion-genie"><h4>Game Genie</h4></a>
<p>
The Game Genie ROM image is loaded from the file "gg.rom" in the
base directory the first time Game Genie emulation is enabled and
a ROM image is loaded since the time FCE Ultra has run.
</p><p>
The ROM image may either be the 24592 byte iNES-format image, or
the 4352 raw ROM image.
</p><p>
Remember that enabling/disabling Game Genie emulation will not take
effect until a new game is loaded(this statement shouldn't concern
any of the "run once" command-line driven ports).
</p>
<a name="features-expansion-vs"><h4>VS Unisystem</h4></a>
<p>
FCE Ultra currently only supports VS Unisystem ROM images in the
iNES format. DIP switches and coin insertion are both emulated.
The following games are supported, and have palettes provided(though not
necessarily 100% accurate or complete):
<ul>
<li />Dr. Mario
<li />VS Castlevania
<li />VS Duck Hunt
<li />VS Excitebike
<li />VS Gradius
<li />VS Golf
<li />VS Hogan's Alley
<li />VS Ice Climber
<li />VS Pinball
<li />VS Platoon
<li />VS RBI Baseball
<li />VS Sky Kid
<li />VS Slalom
<li />VS Star Luster
<li />VS Stroke and Match Golf
<li />VS Super Mario Bros
<li />VS Tetris
<li />VS The Goonies
</ul>
</p>
<a name="features-ips"><h3>Automatic IPS Patching</h3></a>
<p>
Place the IPS file in the same directory as the file to load,
and name it filename.ips.
</p>
<pre>
Examples: Boat.nes - Boat.nes.ips
Boat.zip - Boat.zip.ips
Boat.nes.gz - Boat.nes.gz.ips
Boat - Boat.ips
</pre>
<p>
Some operating systems and environments will hide file extensions.
Keep this in mind if you are having trouble.
</p>
<p>
Patching is supported for all supported formats(iNES, FDS, UNIF, and
NSF), but it will probably only be useful for the iNES format. It
can be used with the FDS format, but be warned that it will permanently
patch your disk image, as the disk image is written back to disk
when the game is unloaded(unless the disk image is in a zip file, in
which case it isn't written back to disk). UNIF files can't be
patched well with the IPS format because they are chunk-based with no
fixed offsets.
</p>
<hr width="100%">
<a name="using"><h2>Using FCE Ultra</h2></a>
<p>
</p>
<a name="using-keys"><h3>Key Assignments</h3></a>
<p>
<table border>
<tr><th>Key:</th><th>Action:</th></tr>
<tr><td>F5</td><td>Save state.</td></tr>
<tr><td>F7</td><td>Load state.</td></tr>
<tr><td>0-9</td><td>Select save state slot.</td></tr>
<tr><td>F9</td><td>Save screen snapshot.</td></tr>
<tr><td>F10</td><td>Reset.</td></tr>
<tr><td>F11</td><td>Hard reset(toggle power switch).</td></tr>
<tr><td>F12</td><td>Exit.</td></tr>
</table>
</p>
<a name="using-keys-vs"><h4>VS Unisystem</h4></a>
<p>
<table border>
<tr><th>Key:</th><th>Action:</th></tr>
<tr><td>F8</td><td>Insert coin.</td></tr>
<tr><td>F6</td><td>Show/Hide dip switches.</td></tr>
<tr><td>1-8</td><td>Toggle dip switches(when dip switches are shown).</td></tr>
</table>
</p>
<a name="using-keys-fds"><h4>Famicom Disk System</h4></a>
<p>
<table border>
<tr><th>Key:</th><th>Action:</th></tr>
<tr><td>F6</td><td>Select disk and disk side.</td></tr>
<tr><td>F8</td><td>Eject or Insert disk.</td></tr>
</table>
</p>
<a name="using-keys-gamepad"><h4>Game Pad</h4></a>
<p>
<table border>
<tr><th>Key:</th><th nowrap>Button on Emulated Gamepad:</th></tr>
<tr><td>Left Control</td><td>B</td></tr>
<tr><td>Left Alt</td><td>A</td></tr>
<tr><td>Enter/Return</td><td>Start</td></tr>
<tr><td>Tab</td><td>Select</td></tr>
<tr><td>Cursor Down</td><td>Down</td></tr>
<tr><td>Cursor Up</td><td>Up</td></tr>
<tr><td>Cursor Left</td><td>Left</td></tr>
<tr><td>Cursor Right</td><td>Right</td></tr>
</table>
</p>
<a name="using-keys-powerpad"><h4>Power Pad</h4></a>
<p>
<table border>
<tr><th colspan="4">Side B</th></tr>
<tr><td width="25%">O</td><td width="25%">P</td>
<td width="25%">[</td><td width="25%">]</td></tr>
<tr><td>K</td><td>L</td><td>;</td><td>'</td></tr>
<tr><td>M</td><td>,</td><td>.</td><td>/</td></tr>
</table>
<br />
<table border>
<tr><th colspan="4">Side A</th></tr>
<tr><td width="25%"></td><td width="25%">P</td>
<td width="25%">[</td><td width="25%"></td></tr>
<tr><td>K</td><td>L</td><td>;</td><td>'</td></tr>
<tr><td></td><td>,</td><td>.</td><td></td></tr>
</table>
</p>
<a name="using-keys-fkb"><h4>Family Keyboard</h4></a>
<p>
All emulated keys are mapped to the closest open key on the PC
keyboard, with a few exceptions. The emulated "@" key is
mapped to the "`"(grave) key, and the emulated "kana" key
is mapped to the "Insert" key(in the 3x2 key block above the
cursor keys).
</p>
<p>
To enable or disable Family Keyboard input, press the "Scroll Lock" key.
When Family Keyboard input is enabled, FCE Ultra will also attempt
to prevent any key presses from being passed to the GUI or system.
</p>
<a name="using-cli"><h3>Command-line</h3></a>
<p>
FCE Ultra supports arguments passed on the command line. <b>The MS Windows
port only allows for a file name to be specified, however.</b> Arguments
are taken in the form of "-parameter value". Some arguments are valueless.
Arguments that have both a parameter and a value will be saved in the
configuration file, with the exception being the network-play arguments.
All ports other than the MS Windows one recognize the following parameters:
</p>
<p>
<table border>
<tr><th>Argument:</th><th>Value Type:</th><th>Default value:</th><th>Description:</th></tr>
<tr><td>-cpalette x</td><td>string</td><td>0</td><td>Load a custom global palette from file "x". Specifying "0" will cause FCE Ultra to stop using the custom global palette.</td></tr>
<tr><td>-ntsccol x</td><td>boolean</td><td>0</td><td>If value is true, enable automatic generation and use of an NTSC NES' colors.</td></tr>
<tr><td>-pal x</td><td>boolean</td><td>0</td><td>If value is true, emulate a PAL NES. Otherwise emulate an NTSC NES.</td></tr>
<tr><td>-sound x</td><td>integer</td><td>48000(44100 for DOS)</td><td>Sets the playback rate. If it is zero, sound emulation(and output) is disabled.</td></tr>
<tr><td>-soundvol x</td><td>integer</td><td>100</td><td>Sound volume.</td></tr>
<tr><td>-soundq x</td><td>boolean</td><td>0</td><td>If value is true, enable high-quality sound emulation.</td></tr>
<tr><td>-input1 x <br /><i>and</i><br />-input2 x</td><td>string</td><td>gamepad</td><td>Select input device for input port 1 or 2. Valid strings are "none", "gamepad", "zapper", "powerpada", and "powerpadb".</td></tr>
<tr><td>-fcexp x</td><td>string</td><td>none</td><td>Select Famicom expansion port device. Valid strings are "none", "shadow", "arkanoid", "4player", and "fkb".</td></tr>
<tr><td>-nofs x</td><td>boolean</td><td>0</td><td>If value is true, disable four-score emulation.</td></tr>
<tr><td>-gg</td><td>boolean</td><td>0</td><td>Enable Game Genie emulation.</td></tr>
<tr><td>-subase x</td><td>boolean</td><td>0</td><td>If value is true, Save extra game data files under the base directory.</td></tr>
<tr><td nowrap>-snapname x</td><td>boolean</td><td>0</td><td>If value is true, use an alternate naming scheme(file base and numeric) for screen snapshots.</td></tr>
<tr><td nowrap>-nothrottle x</td><td>boolean</td><td>0</td><td>If value is true, disable the speed throttling that is used when sound emulation is disabled.</td></tr>
<tr><td>-clipsides x</td><td>boolean</td><td>0</td><td>If value is true, clip leftmost and rightmost 8 columns of pixels of the video output.</td></tr>
<tr><td>-slstart x</td><td>integer</td><td>8</td>
<td>First scanline to be rendered in NTSC emulation mode(when PAL emulation is disabled).</td></tr>
<tr><td>-slend x</td><td>integer</td><td>231</td>
<td>Last scanline to be rendered in NTSC emulation mode.</td></tr>
<tr><td nowrap>-slstartp x</td><td>integer</td><td>0</td>
<td>First scanline to be rendered in PAL emulation mode.</td></tr>
<tr><td>-slendp x</td><td>integer</td><td>239</td>
<td>Last scanline to be rendered in PAL emulation mode.</td></tr>
</table>
</p>
<a name="using-cli-sdl"><h4>SDL</h4></a>
<p>
<table border>
<tr><th>Argument:</th><th>Value Type:</th><th>Default value:</th><th>Description:</th></tr>
<tr><td>-joyx y</td><td>integer,integer</td><td>0</td><td>Enable joystick #y for virtual joystick #x(both starting at "1").</td></tr>
<tr><td>-stretchx/-stretchy x</td><td>boolean</td><td>0/0</td><td>Stretch to fill surface on x or y axis(fullscreen, only with OpenGL).</td></tr>
<tr><td>-fs</td><td>boolean</td><td>0</td><td>Full screen mode.</td></tr>
</table>
</p>
<a name="using-cli-dos"><h4>DOS</h4></a>
<p>
<table border>
</table>
</p>
<a name="using-cli-dos"><h4>Linux SVGAlib</h4></a>
<p>
<table border>
</table>
</p>
<hr width="100%">
<a name="credits"><h2>Credits</h2></a>
<p>
<table border width="100%">
<tr><th>Name:</th><th>Contribution(s):</th></tr>
<tr><td>\Firebug\</td><td>High-level mapper information.</td></tr>
<tr><td>Bero</td><td>Original FCE source code.</td></tr>
<tr><td>Brad Taylor</td><td>NES sound information.</td></tr>
<tr><td>Chris Hickman</td><td>Archaic Ruins.</td></tr>
<tr><td>Donald Moore</td><td>DC PasoFami NES packs.</td></tr>
<tr><td>Fredrik Olson</td><td>NES four-player adapter information.</td></tr>
<tr><td>Gilles Vollant</td><td>PKZIP file loading functions.</td></tr>
<tr><td>goroh</td><td>Various documents.</td></tr>
<tr><td>Info-ZIP</td><td><a href="http://www.gzip.org/zlib/">ZLIB</a></td></tr>
<tr><td>Jeremy Chadwick</td><td>General NES information.</td></tr>
<tr><td>Justin Smith</td><td>Good stuff.</td></tr>
<tr><td>Kevin Horton</td><td>Low-level NES information and sound information.</td></tr>
<tr><td>Ki</td><td>Various technical information.</td></tr>
<tr><td>Mark Knibbs</td><td>Various NES information.</td></tr>
<tr><td>Marat Fayzullin</td><td>General NES information.</td></tr>
<tr><td>Matthew Conte</td><td>Sound information.</td></tr>
<tr><td>nori</td><td>FDS sound information.</td></tr>
<tr><td>Quietust</td><td>VRC7 sound translation code by The Quietust (quietust at ircN dort org).</td></tr>
<tr><td>R. Hoelscher</td><td>Famicom four-player adapter information.</td></tr>
<tr><td>Rob Mocca</td><td>DC PasoFami NES packs, testing.</td></tr>
<tr><td>Sean Whalen</td><td>Node 99</td></tr>
<tr><td>Tatsuyuki Satoh</td><td>OPL2 emulator used for the VRCVII sound emulation.</td></tr>
<tr><td>TheRedEye</td><td>ROM images, testing.</td></tr>
</table>
</p>
</body>
</html>

153
documentation/fcs.txt Normal file
View File

@ -0,0 +1,153 @@
FCE Ultra Save State Format
Updated: Mar 9, 2003
---------------------------------------
FCE Ultra's save state format is now designed to be as forward and backwards
compatible as possible. This is achieved through the (over)use of chunks.
All multiple-byte variables are stored LSB(least significant byte)-first.
Data types:
(u)int8 - (un)signed 8 bit variable(also referred to as "byte")
(u)int16 - (un)signed 16 bit variable
(u)int32 - (un)signed 32 bit variable
-- Main File Header:
The main file header is 16-bytes in length. The first three bytes contain
the string "FCS". The next byte contains the version of FCE Ultra that saved
this save state. This document only applies to version "53"(.53) and higher.
After the version byte, the size of the entire file in bytes(minus the 16 byte
main file header) is stored. The rest of the header is currently unused
and should be nulled out. Example of relevant parts:
FCS <uint8 version> <uint32 totalsize>
-- Section Chunks:
Sections chunk headers are 5-bytes in length. The first byte defines what
section it is, the next four bytes define the total size of the section
(including the section chunk header).
<uint8 section> <uint32 size>
Section definitions:
1 - "CPU"
2 - "CPUC"
3 - "PPU"
4 - "CTLR"
5 - "SND"
16 - "EXTRA"
-- Subsection Chunks
Subsection chunks are stored within section chunks. They contain the actual
state data. Each subsection chunk is composed of an 8-byte header and the data.
The header contains a description(a name) and the size of the data contained
in the chunk:
<uint8 description[4]> <uint32 size>
The name is a four-byte string. It does not need to be null-terminated.
If the string is less than four bytes in length, the remaining unused bytes
must be null.
-- Subsection Chunk Description Definitions
Note that not all subsection chunk description definitions listed below
are guaranteed to be in the section chunk. It's just a list of what CAN
be in a section chunk. This especially applies to the "EXTRA" subsection.
---- Section "CPU"
Name: Type: Description:
PC uint16 Program Counter
A uint8 Accumulator
P uint8 Processor status register
X uint8 X register
Y uint8 Y register
S uint8 Stack pointer
RAM uint8[0x800] 2KB work RAM
---- Section "CPUC" (emulator specific)
Name: Type: Description:
JAMM uint8 Non-zero value if CPU in a "jammed" state
IRQL uint8 Non-zero value if IRQs are to be generated constantly
ICoa int32 Temporary cycle counter
ICou int32 Cycle counter
---- Section "PPU"
Name: Type: Description:
NTAR uint8[0x800] 2 KB of name/attribute table RAM
PRAM uint8[32] 32 bytes of palette index RAM
SPRA uint8[0x100] 256 bytes of sprite RAM
PPU uint8[4] Last values written to $2000 and $2001, the PPU
status register, and the last value written to
$2003.
XOFF uint8 Tile X-offset.
VTOG uint8 Toggle used by $2005 and $2006.
RADD uint16 PPU Address Register(address written to/read from
when $2007 is accessed).
TADD uint16 PPU Address Register
VBUF uint8 VRAM Read Buffer
PGEN uint8 PPU "general" latch. See Ki's document.
---- Section "CTLR" (somewhat emulator specific)
Name: Type: Description:
J1RB uint8 Bit to be returned when first joystick is read.
J2RB uint8 Bit to be returned when second joystick is read.
---- Section "SND" (somewhat emulator specific)
NREG uint16 Noise LFSR.
P17 uint8 Last byte written to $4017.
PBIN uint8 DMC bit index.
PAIN uint32 DMC address index(from $8000).
PSIN uint32 DMC length counter(how many bytes left
to fetch).
<to be finished>
---- Section "EXTRA" (varying emulator specificness)
For iNES-format games(incomplete, and doesn't apply to every game):
Name: Type: Description:
WRAM uint8[0x2000] 8KB of WRAM at $6000-$7fff
MEXR uint8[0x8000] (very emulator specific)
CHRR uint8[0x2000] 8KB of CHR RAM at $0000-$1fff(in PPU address space).
EXNR uint8[0x800] Extra 2KB of name/attribute table RAM.
MPBY uint8[32] (very emulator specific)
MIRR uint8 Current mirroring:
0 = "Horizontal"
1 = "Vertical"
$10 = Mirror from $2000
$11 = Mirror from $2400
IRQC uint32 Generic IRQ counter
IQL1 uint32 Generic IRQ latch
IQL2 uint32 Generic IRQ latch
IRQA uint8 Generic IRQ on/off register.
PBL uint8[4] List of 4 8KB ROM banks paged in at $8000-$FFFF
CBL uint8[8] List of 8 1KB VROM banks page in at $0000-$1FFF(PPU).
For FDS games(incomplete):
Name: Type: Description:
DDT<x> uint8[65500] Disk data for side x(0-3).
FDSR uint8[0x8000] 32 KB of work RAM
CHRR uint8[0x2000] 8 KB of CHR RAM
IRQC uint32 IRQ counter
IQL1 uint32 IRQ latch
IRQA uint8 IRQ on/off.
WAVE uint8[64] Carrier waveform data.
MWAV uint8[32] Modulator waveform data.
AMPL uint8[2] Amplitude data.

289
documentation/porting.txt Normal file
View File

@ -0,0 +1,289 @@
FCE Ultra Porting Guide
Updated: October 4, 2003
*Incomplete*
***Driver-supplied functions:
These functions will only be called after the driver code calls
FCEUI_LoadGame() or FCEUI_Emulate().
void FCEUD_Update(uint8 *XBuf, int32 *Buffer, int Count);
Called by FCE Ultra on every emulated frame. This function should
perform the following three things(in any order):
1.
Update the data pointed to by the pointers passed to
FCEUI_SetInput() and FCEUI_SetInputFC().
2.
Copy contents of XBuf over to video memory(or whatever needs to be
done to make the contents of XBuf visible on screen).
Each line is 256 pixels(and bytes) in width, and there can be 240
lines. The pitch for each line is 272 bytes.
XBuf will be 0 if the symbol FRAMESKIP is defined and this frame
was skipped.
3.
Write the contents of "Buffer" to the sound device. "Count" is the
number of samples to write. Only the lower 16-bits of each
sample are used, so each 32-bit sample in "Buffer" can be converted to
signed 16-bit by dropping the upper 16 bits.
When sound was disabled for the frame, "Count" will be 0.
void FCEUD_SetPalette(uint8 index, uint8 r, uint8 g, uint8 b);
Set palette entry "index" to specified RGB values(value: min=0, max=255).
void FCEUD_GetPalette(uint8 index, uint8 *r, uint8 *g, uint8 *b);
Get palette entry "index" data.
void FCEUD_PrintError(char *s);
Print/Display an error message string pointed to by "s".
void FCEUD_Message(char *s);
Display a status message string.
int FCEUD_NetworkConnect(void);
Initialize a network connection. Return 0 if an error occurs.
int FCEUD_GetDataFromClients(uint8 *data);
/* Sends 5 bytes of data to all clients. */
int FCEUD_SendDataToClients(uint8 *data);
/* Sends 1 byte of data to server, and maybe a command. */
int FCEUD_SendDataToServer(uint8 v, uint8 cmd);
/* Gets 5 bytes of data from the server. This function must block. */
int FCEUD_GetDataFromServer(uint8 *data);
void FCEUD_NetworkClose(void);
Close the network connection.
***FCE Ultra functions(called by the driver code):
The FCEUI_* functions may only be called before FCEUI_Emulate() is
called or after it returns and before it is called again, or after the
following functions are called and before they return:
FCEUD_Update();
Calling the FCEUI_* functions at any other time may result in
undefined behavior.
void FCEUI_SetInput(int port, int type, void *ptr, int attrib);
"port" can be either 0 or 1, and corresponds to the physical
ports on the front of a NES.
"type" may be:
SI_NONE - No input on this port.
SI_GAMEPAD - Standard NES gamepad
SI_ZAPPER - "Zapper" light gun.
SI_POWERPAD - Power-pad mat.
SI_ARKANOID - Arkanoid controller.
void FCEUI_SetInputFC(int type, void *ptr, int attrib);
Special Famicom devices.
"type" may be:
SIFC_NONE - No input here.
SIFC_ARKANOID - Arkanoid controller.
SIFC_SHADOW - "Space Shadow" gun.
SIFC_4PLAYER - Famicom 4-player adapter
SIFC_FKB - Family Keyboard
void FCEUI_DisableFourScore(int s);
Disables four-score emulation if s is nonzero.
void FCEUI_SetSnapName(int a);
0 to order screen snapshots numerically(0.png), 1 to order them file
base-numerically(smb3-0.png).
void FCEUI_DisableSpriteLimitation(int a);
Disables the 8-sprite-per-scanline limitation of the NES if "a"
is nonzero. The default behavior is the limitation is enabled.
void FCEUI_SaveExtraDataUnderBase(int a);
If "a" is nonzero, save extra non-volatile game data(battery-backed
RAM) under FCE Ultra's base directory. Otherwise, the behavior is
to save it under the same directory the game is located in(this is
the default behavior).
FCEUGI *FCEUI_LoadGame(char *name);
Loads a new file. "name" is the full path of the file to load.
Returns 0 on failure, or a pointer to data type "FCEUGI":
See file "git.h" for more details on this structure.
int FCEUI_Initialize(void);
Allocates and initializes memory. Should only be called once, before
any calls to other FCEU functions.
void FCEUI_SetBaseDirectory(void);
Specifies the base FCE Ultra directory. This should be called
immediately after FCEUI_Initialize() and any time afterwards.
void FCEUI_SetDirOverride(int which, char *n);
FCEUIOD_CHEATS - Cheats
FCEUIOD_MISC - Miscellaneous stuff(custom game palettes)
FCEUIOD_NV - Non-volatile game data(battery backed RAM)
FCEUIOD_SNAPS - Screen snapshots
FCEUIOD_STATE - Save states
void FCEUI_Emulate(void);
Enters the emulation loop. This loop will be exited when FCEUI_CloseGame()
is called. This function obviously shouldn't be called if FCEUI_LoadGame()
wasn't called or FCEUI_CloseGame() was called after FCEUI_LoadGame().
void FCEUI_CloseGame(void);
Closes the loaded game and frees all memory used to load it.
Also causes FCEUI_Emulate() to return.
void FCEUI_ResetNES(void);
void FCEUI_PowerNES(void);
void FCEUI_SetRenderedLines(int ntscf, int ntscl, int palf, int pall);
Sets the first(minimum is 0) and last(NOT the last scanline plus one;
maximum is 239) scanlines of background data to draw, for both NTSC
emulation mode and PAL emulation mode.
Defaults are as if this function were called with the variables set
up as follows:
ntscf=8, ntscl=231, palf=0, pall=239
void FCEUI_SetNetworkPlay(int type);
Sets status of network play according to "type". If type is 0,
then network play is disabled. If type is 1, then we are server.
If type is 2, then we are a client.
void FCEUI_SelectState(int w);
Selects the state "slot" to save to and load from.
void FCEUI_SaveState(void);
Saves the current virtual NES state from the "slot" selected by
FCEUI_SelectState().
void FCEUI_LoadState(void);
Loads the current virtual NES state from the "slot" selected by
FCEUI_SelectState().
void FCEUI_SaveSnapshot(void);
Saves a screen snapshot.
void FCEUI_DispMessage(char *msg);
Displays a short, one-line message using FCE Ultra's built-in
functions and ASCII font data.
int32 FCEUI_GetDesiredFPS(void);
Returns the desired FPS based on whether NTSC or PAL emulation is
enabled, shifted left by 24 bits(this is necessary because the real
FPS value is not a whole integer). This function should only be
necessary if sound emulation is disabled.
int FCEUI_GetCurrentVidSystem(int *slstart, int *slend);
Convenience function(not strictly necessary, but reduces excessive code
duplication); returns currently emulated video system
(0=NTSC, 1=PAL). It will also set the variables pointed to by slstart
and slend to the first and last scanlines to be rendered, respectively,
if slstart and slend are not 0.
void FCEUI_GetNTSCTH(int *tint, int *hue);
void FCEUI_SetNTSCTH(int n, int tint, int hue);
int FCEUI_AddCheat(char *name, uint16 addr, uint8 val);
Adds a RAM cheat with the specified name to patch the address "addr"
with the value "val".
int FCEUI_DelCheat(uint32 which);
Deletes the specified(by number) cheat.
void FCEUI_ListCheats(void (*callb)(char *name, uint16 a, uint8 v));
Causes FCE Ultra to go through the list of all cheats loaded for
the current game and call callb() for each cheat with the cheat
information.
int FCEUI_GetCheat(uint32 which, char **name, int32 *a, int32 *v, int *s);
Gets information on the cheat referenced by "which".
int FCEUI_SetCheat(uint32 which, char *name, int32 a, int32 v, int s);
Sets information for the cheat referenced by "which".
void FCEUI_CheatSearchBegin(void);
Begins the cheat search process. Current RAM values are copied
to a buffer to later be processed by the other cheat search functions.
void FCEUI_CheatSearchEnd(int type, int v1, int v2);
Searches the buffer using the search method specified by "type"
and the parameters "v1" and "v2".
int32 FCEUI_CheatSearchGetCount(void);
Returns the number of matches from the cheat search.
void FCEUI_CheatSearchGet(void (*callb)(uint16 a, int last, int current));
void FCEUI_CheatSearchGetRange(int first, int last, void (*callb)(uint16 a, int last, int current));
Like FCEUI_CheatSearchGet(), but you can specify the first and last
matches to get.
void FCEUI_CheatSearchShowExcluded(void);
Undos any exclusions of valid addresses done by FCEUI_CheatSearchEnd().
void FCEUI_CheatSearchSetCurrentAsOriginal(void);
Copies the current values in RAM into the cheat search buffer.
void FCEUI_MemDump(uint16 a, int32 len, void (*callb)(uint16 a, uint8 v));
Callback to dump memory.
void FCEUI_DumpMem(char *fname, uint32 start, uint32 end);
Dump memory to filename fname.
void FCEUI_MemPoke(uint16 a, uint8 v, int hl);
Write a byte to specified address. Set hl to 1 to attempt to store
it to ROM("high-level" write).
void FCEUI_NMI(void);
Triggers(queues) an NMI.
void FCEUI_IRQ(void);
Triggers(queues) an IRQ.
void FCEUI_Disassemble(uint16 a, int (*callb)(uint16 a, char *s));
Text disassembly.
void FCEUI_GetIVectors(uint16 *reset, uint16 *irq, uint16 *nmi);
Get current interrupt vectors.
***Recognized defined symbols:
The following defined symbols affect the way FCE Ultra is compiled:
C80x86
- Include 80x86 inline assembly in AT&T syntax, if available. Also
use special 80x86-specific C constructs if the compiler is compatible.
FRAMESKIP
- Include frame skipping code.
NETWORK
- Include network play code.
FPS
- Compile code that prints out a number when FCE Ultra exits
that represents the average fps.
ZLIB
- Compile support for compressed PKZIP-style files AND gzip compressed
files. "unzip.c" will need to be compiled and linked in by you if
this is defined(it's in the zlib subdirectory).
LSB_FIRST
- Compile code to expect that variables that are greater than 8 bits
in size are stored Least Significant Byte First in memory.
PSS_STYLE x
- Sets the path separator style to the integer 'x'. Valid styles are:
1: Only "/" - For UNIX platforms.
2: Both "/" and "\" - For Windows and MSDOS platforms.
3: Only "\" - For ???.
4: Only ":" - For Apple IIs ^_^.

View File

@ -0,0 +1,90 @@
FCE Ultra 0.91+ network play protocol.
Description v 0.0.1
--------------------------------------
In FCE Ultra, all data is sent to the server, and then the server
distributes, every emulated frame(60hz on NTSC), the collated data to the
clients.
The server should not block when it is receiving UDP data from the clients.
If no UDP data is available, then just go on.
The clients MUST block until the input data packet comes on every emulated
frame.
Packets from the server to the client are sent out over both TCP and UDP.
Duplicate packets should be discarded. Out-of-order packets can either
be cached, or discarded(what I recommend, as caching code gets a little
complex and wouldn't yield any benefit from what I've observed).
In the case of client->server UDP communications, the server should just use
the data from the UDP packet that has the highest packet number count, and
the server should then set its internal incoming packet counter to that
number(to prevent out-of-order packets from totally screwing up user input).
The "magic number"(used with UDP packets) is meant to reduce the chance of a hostile remote host
from disrupting the network play, without resorting to using extreme amounts
of network bandwidth. The server generates the magic number, and it is best
if the magic number is as random as possible.
UDP packets received with an incorrect magic number should be discarded, of
course.
Initialization, server->client:
uint32 Local UDP port(what the server is listening on).
uint32 Player number(0-3) for client.
uint32 Magic number(for UDP).
Initialization, client->server
uint32 Local UDP port(that the client is listening on).
Structure of UDP packet data:
uint32 CRC32 - Includes magic number, packet counter, and
data. For reference, CRC32 is calculated
with the zlib function crc32().
uint32 Magic number
uint32 Packet counter(linear, starts at 0).
uint8[variable] Data.
Structure of tcp packet data:
uint32 Packet counter(" ").
uint8[variable] Data.
Data format of server->client communications:
uint8[4] Controller data
uint8 Command byte. 0 if no command. Otherwise(in decimal):
1 Select FDS disk side.
2 Insert/eject FDS disk.
10 Toggle VS Unisystem dip switch editing.
11 ... 18 Toggle VS Unisystem dip switches.
19 Insert VS Unisystem coin.
30 Reset NES.
31 Power toggle NES.
40 Save state(not implemented correctly).
41 Load state(not implemented correctly).
42 ... 50 Select save state slot(minus 42).
Special message communications occurs if the "Packet counter" is
0xFFFFFFFF(only with TCP):
uint32 Length of text data, minus the null character(the null
character is sent, though).
uint8[variable] Text data. Convert all characters <32 to space, and
then display the text message(it's one line) as is.
Structure of client->server communication:
uint8 Controller data(for this client).
Over tcp channel, a text message can be sent. It is one line,
null terminated(remember the data and parse it and display it and
distribute it to the clients once the null byte is received).
Maximum size of message(including the null byte) should be 256 bytes.

32
documentation/readme Normal file
View File

@ -0,0 +1,32 @@
FCE Ultra was developed with gcc and GNU make in mind. MSVC will probably
compile the Windows source code with a few modifications, but you'll still
need to make a project file.
Several pre-made makefiles are provided:
Makefile.beos - BeOS(with SDL and SDL_net)
Makefile.unixsdl - UN*X(FreeBSD/Linux/etc, with SDL)
Makefile.linuxvga - Linux(with svgalib)
Makefile.dos - (MS/PC/DR) DOS
Makefile.win - MS Windows 9x/Me/Xp/2000/etc.(with DirectX).
Makefile.macosx - Mac OS X(with SDL)
If you want to use Makefile.beos or Makefile.unixsdl for a cpu type other
than 80x86, you will need to remove "-DC80x86" from the defines line
and also remove "-DLSB_FIRST" if your target cpu uses MSB first ordering.
You'll also need to remove/change "-mcpu=xxx".
For the Windows port, I use MINGW32. http://www.mingw.org
To compile the DOS port, you'll need to download the DJGPP package.
Any version of gcc >=2.95.3 should compile the code without changes, BUT
the DJGPP versions of gcc have some problems:
gcc 2.95.3 sometimes breaks when compiling the code. gcc 3.0.2 seems to
produce bad code for sound.c. So, I recommend using gcc 3.0.4 for
compiling the DOS version.
Modifying the "-mcpu=i686" string in the makefiles, to optimize more effectively
for your cpu type, is a good idea.
Always do a "make -f Makefile.<platform> clean" before compiling for a
different platform or if you update via cvs and if you have stale object
files lying around.

View File

@ -0,0 +1,97 @@
This is an email posted to nesdev by Ki a while back. I have removed one
line at the end regarding the B flag of the cpu(the information was
incorrect, which Ki noted in a later email).
--------------------------------------------------------------------------------
By reading Brad's NESSOUND document, we know that there is a
"frame counter" in the NES/FC APU. I would like to post
some more on this.
The frame counter is reset upon any write to $4017. It is
reset at system power-on as well, but is NOT reset upon
system reset.
Thanks to Samus Aran, we now know the exact period of the
PPU's single frame. In another words, we are now sure that
the NMI occurs on every 29780 2/3 CPU cycles.
However, the APU's single frame is NOT 29780 2/3 CPU cycles.
What I mean by "APU's single frame" here is that it is the
number of CPU cycles taken between the frame IRQs.
The APU's single frame seems to be:
1789772.727... / 60 = 29829 6/11 [CPU CYCLE]
Below is a simple diagram which shows the difference
in periods of the PPU's single frame and the APU's.
RESET 29780 2/3 CPU CYCLES NMI
PPU |------------------------------------------|
| 29829 6/11 CPU CYCLES IRQ
APU |----------|----------|----------|----------|
Note that if you write $00 to $4017 on every NMI, the frame
IRQ would NEVER go off even if it is enabled. This is because
the the period of NMI is slightly shorter than the period of
the frame IRQ. This causes the frame counter to be reset
before the frame IRQ goes off.
When you write zero to bit 7 of $4017, the frame counter will
be reset, and the first sound update will be done after 7457 CPU
cycles (i.e. 29829/4). 2nd update will be done 7457 after that,
same goes for 3rd update and 4th update, but the frame IRQ occurs
on 4th update, resetting the frame counter as well.
When you write 1 to bit 7 of $4017, the frame counter will be
reset, but the first sound update will occur at the same time.
2nd, 3rd, and 4th update will be done after 7457, 14914, 22371
CPU cycles after the first update respectively, but the 5th
update will be 14914 cycles after the 4th update. This causes
sound output to last 1.25 times longer than that of bit 7 = 0.
$4017W:
o when the MSB of $4017 is 0:
bit7=0
|---------|---------|---------|---------|---------|---------|----
1st 2nd 3rd 4th 5th(1st) 6th(2nd)
o when the MSB of $4017 is 1:
bit7=1
|---------|---------|---------|-------------------|---------|----
1st 2nd 3rd 4th 5th(1st) 6th(2nd)
On 1st, 3rd, 5th, ... updates, the envelope decay and the
linear counter are updated.
On 2nd, 4th, 6th, ... updates, the envelope decay, the
linear counter, the length counter, and the frequency sweep
are updated.
----
The original info was provided by goroh, and verified by me.
However, it could still be wrong. Please tell me if you
find anything wrong.
----
(Correction from my last posting)
I have checked once again and it turned out that the frame IRQ
was NOT disabled upon system reset. What actually prevented the
frame IRQ to occur after system reset was, in fact, the I flag.
I checked this flag shortly after system reset (right after stack
pointer was initialized), and the flag was 1, although I never
executed "sei" after reset. Therefore the I flag of the PR2A03G
is 1 on system reset.
Thanks Matthew Conte and Samus Aran for pointing out the
inaccuracy.

View File

@ -0,0 +1,235 @@
Delta modulation channel tutorial 1.0
Written by Brad Taylor
Last updated: August 20th, 2000.
All results were obtained by studying prior information available (from
nestech 1.00, and postings on NESDev from miscellanious people), and through
a series of experiments conducted by me. Results aquired by individuals
prior to my reverse-engineering have been double checked, and final results
have been confirmed. Credit is due to those individual(s) who contributed
any information in regards to the DMC.
Description
-----------
The delta modulation channel (DMC) is a complex digital network of counters
and registers used to produce analog sound. It's primary function is to play
"samples" from memory, and have an internal counter connected to a digital
to analog converter (DAC) updated accordingly. The channel is able to be
assigned a pointer to a chunk of memory to be played. At timed intervals,
the DMC will halt the 2A03 (NES's CPU) for 1 clock cycle to retrieve the
sample to pe played. This method of playback will be refered to here on as
direct memory access (DMA). Another method of playback known as pulse code
modulation (PCM) is available by the channel, which requires the constant
updating of one of the DMC's memory-mapped registers.
Registers
---------
The DMC has 5 registers assigned to it. They are as follows:
$4010: play mode and DMA frequency
$4011: delta counter
$4012: play code's starting address
$4013: length of play code
$4015: DMC/IRQ status
Note that $4015 is the only R/W register. All others are write only (attempt
to read them will most likely result in a returned 040H, due to heavy
capacitance on the NES's data bus).
$4010 - Play mode and DMA frequency
-----------------------------------
This register is used to control the frequency of the DMA fetches, and to
control the playback mode.
Bits
----
6-7 this is the playback mode.
00 - play DMC sample until length counter reaches 0 (see $4013)
x1 - loop the DMC sample (x = immaterial)
10 - play DMC sample until length counter reaches 0, then generate a CPU
IRQ
Looping (playback mode "x1") will have the chunk of memory played over and
over, until the channel is disabled (via $4015). In this case, after the
length counter reaches 0, it will be reloaded with the calculated length
value of $4013.
If playback mode "10" is chosen, an interrupt will be dispached when the
length counter reaches 0 (after the sample is done playing). There are 2
ways to acknowledge the DMC's interrupt request upon recieving it. The first
is a write to this register ($4010), with the MSB (bit 7) cleared (0). The
second is any write to $4015 (see the $4015 register description for more
details).
If playback mode "00" is chosen, the sample plays until the length counter
reaches 0. No interrupt is generated.
5-4 appear to be unused
3-0 this is the DMC frequency control. Valid values are from 0 - F. The
value of this register determines how many CPU clocks to wait before the DMA
will fetch another byte from memory. The # of clocks to wait -1 is initially
loaded into an internal 12-bit down counter. The down counter is then
decremented at the frequency of the CPU (1.79MHz). The channel fetches the
next DMC sample byte when the count reaches 0, and then reloads the count.
This process repeats until the channel is disabled by $4015, or when the
length counter has reached 0 (if not in the looping playback mode). The
exact number of CPU clock cycles is as follows:
value CPU
written clocks octave scale
------- ------ ------ -----
F 1B0 8 C
E 240 7 G
D 2A0 7 E
C 350 7 C
B 400 6 A
A 470 6 G
9 500 6 F
8 5F0 6 D
7 6B0 6 C
6 710 5 B
5 7F0 5 A
4 8F0 5 G
3 A00 5 F
2 AA0 5 E
1 BE0 5 D
0 D60 5 C
The octave and scale values shown represent the DMC DMA clock cycle rate
equivelant. These values are merely shown for the music enthusiast
programmer, who is more familiar with notes than clock cycles.
Every fetched byte is loaded into a internal 8-bit shift register. The shift
register is then clocked at 8x the DMA frequency (which means that the CPU
clock count would be 1/8th that of the DMA clock count), or shifted at +3
the octave of the DMA (same scale). The data shifted out of the register is
in serial form, and the least significant bit (LSB, or bit 0) of the fetched
byte is the first one to be shifted out (then bit 1, bit 2, etc.).
The bits shifted out are then fed to the UP/DOWN control pin of the internal
delta counter, which will effectively have the counter increment it's
retained value by one on "1" bit samples, and decrement it's value by one on
"0" bit samples. This counter is clocked at the same frequency of the shift
register's.
The counter is only 6 bits in size, and has it's 6 outputs tied to the 6 MSB
inputs of a 7 bit DAC. The analog output of the DAC is then what you hear
being played by the DMC.
Wrap around counting is not allowed on this counter. Instead, a "clipping"
behaviour is exhibited. If the internal value of the counter has reached 0,
and the next bit sample is a 0 (instructing a decrement), the counter will
take no action. Likewise, if the counter's value is currently at -1
(111111B, or 03FH), and the bit sample to be played is a 1, the counter will
not increment.
$4011 - Delta counter load register
-----------------------------------
bits
----
7 appears to be unused
1-6 the load inputs of the internal delta counter
0 LSB of the DAC
A write to this register effectively loads the internal delta counter with a
6 bit value, but can be used for 7 bit PCM playback. Bit 0 is connected
directly to the LSB (bit 0) of the DAC, and has no effect on the internal
delta counter. Bit 7 appears to be unused.
This register can be used to output direct 7-bit digital PCM data to the
DMC's audio output. To use this register for PCM playback, the programmer
would be responsible for making sure that this register is updated at a
constant rate. The rate is completely user-definable. For the regular CD
quality 44100 Hz playback sample rate, this register would have to be
written to approximately every 40 CPU cycles (assuming the 2A03 is running @
1.79 MHz).
$4012 - DMA address load register
----------------------------
This register contains the initial address where the DMC is to fetch samples
from memory for playback. The effective address value is $4012 shl 6 or
0C000H. This register is connected to the load pins of the internal DMA
address pointer register (counter). The counter is incremented after every
DMA byte fetch. The counter is 15 bits in size, and has addresses wrap
around from $FFFF to $8000 (not $C000, as you might have guessed). The DMA
address pointer register is reloaded with the initial calculated address,
when the DMC is activated from an inactive state, or when the length counter
has arrived at terminal count (count=0), if in the looping playback mode.
$4013 - DMA length register
---------------------------
This register contains the length of the chunk of memory to be played by the
DMC, and it's size is measured in bytes. The value of $4013 shl 4 is loaded
into a 12 bit internal down counter, dubbed the length counter. The length
counter is decremented after every DMA fetch, and when it arrives at 0, the
DMC will take action(s) based on the 2 MSB of $4010. This counter will be
loaded with the current calculated address value of $4013 when the DMC is
activated from an inactive state. Because the value that is loaded by the
length counter is $4013 shl 4, this effectively produces a calculated byte
sample length of $4013 shl 4 + 1 (i.e. if $4013=0, sample length is 1 byte
long; if $4013=FF, sample length is $FF1 bytes long).
$4015 - DMC status
------------------
This contains the current status of the DMC channel. There are 2 read bits,
and 1 write bit.
bits
----
7(R) DMC's IRQ status (1=CPU IRQ being caused by DMC)
4(R) DMC is currently enabled (playing a stream of samples)
4(W) enable/disable DMC (1=start/continue playing a sample;0=stop playing)
When an IRQ goes off inside the 2A03, Bit 7 of $4015 can tell the interrupt
handler if it was caused by the DMC hardware or not. This bit will be set
(1) if the DMC is responsible for the IRQ. Of course, if your program has no
other IRQ-generating hardware going while it's using the DMC, then reading
this register is not neccessary upon IRQ generation. Note that reading this
register will NOT clear bit 7 (meaning that the DMC's IRQ will still NOT be
acknowledged). Also note that if the 2 MSB of $4010 were set to 10, no IRQ
will be generated, and bit 7 will always be 0.
Upon generation of a IRQ, to let the DMC know that the software has
acknowledged the /IRQ (and to reset the DMC's internal IRQ flag), any write
out to $4015 will reset the flag, or a write out to $4010 with the MSB set
to 0 will do. These practices should be performed inside the IRQ handler
routine. To replay the same sample that just finished, all you need to do is
just write a 1 out to bit 4 of $4015.
Bit 4 of $4015 reports the real-time status of the DMC. A returned value of
1 denotes that the channel is currently playing a stream of samples. A
returned value of 0 indicates that the channel is inactive. If the
programmer needed to know when a stream of samples was finished playing, but
didn't want to use the IRQ generation feature of the DMC, then polling this
bit would be a valid option.
Writing a value to $4015's 4th bit has the effect of enabling the channel
(start, or continue playing a stream of samples), or disabling the channel
(stop all DMC activity). Note that writing a 1 to this bit while the channel
is currently enabled, will have no effect on counters or registers internal
to the DMC.
The conditions that control the time the DMC will stay enabled are
determined by the 2 MSB of $4010, and register $4013 (if applicable).
System Reset
------------
On system reset, all 7 used bits of $4011 are reset to 0, the IRQ flag is
cleared (disabled), and the channel is disabled. All other registers will
remain unmodified.

View File

@ -0,0 +1,551 @@
*******************************************
*2A03 sound channel hardware documentation*
*******************************************
Brad Taylor (big_time_software@hotmail.com)
4th release: February 19th, 2K3
All results were obtained by studying prior information available (from nestech 1.00, and postings on NESDev from miscellanious people), and through a series of experiments conducted by me. Results acquired by individuals prior to my reverse-engineering have been double checked, and final results have been confirmed. Credit is due to those individual(s) who contributed miscellanious information in regards to NES sound channel hardware. Such individuals are:
Goroh
Memblers
FluBBa
Izumi
Chibi-Tech
Quietust
SnowBro
Kentaro Ishihara (Ki) is responsible for posting (on the NESdev mailing list) differrences in the 2 square wave channels, including the operation of 2A03 hardware publically undocumented (until now) such as the frame IRQ counter, and it's ties with sound hardware. Goroh had originally discovered some of this information, and Ki confirmed it.
A special thanks goes out to Matthew Conte, for his expertise on pseudo-random number generation (amoung other things), which allowed for the full reverse engineering of the NES's noise channel to take place. Without his help, I would still be trying to find a needle in a haystack, as far as the noise's method of pseudo-random number generation goes. Additionally, his previous findings / reverse engineering work on the NES's sound hardware really got the ball of NES sound emulation rolling. If it weren't for Matt's original work, this document wouldn't exist.
****************
* Introduction *
****************
The 2A03 (NES's integrated CPU) has 4 internal channels to it that have the ability to generate semi-analog sound, for musical playback purposes. These channels are 2 square wave channels, one triangle wave channel, and a noise generation channel. This document will go into full detail on every aspect of the operation and timing of the mentioned sound channels.
*******************
* Channel details *
*******************
Each channel has different characteristics to it that make up it's operation.
The square channel(s) have the ability to generate a square wave frequency in the range of 54.6 Hz to 12.4 KHz. It's key features are frequency sweep abilities, and output duty cycle adjustment.
The triangle wave channel has the ability to generate an output triangle wave with a resolution of 4-bits (16 steps), in the range of 27.3 Hz to 55.9 KHz. The key features this channel has is it's analog triangle wave output, and it's linear counter, which can be set to automatically disable the channel's sound after a certain period of time has gone by.
The noise channel is used for producing random frequencys, which results in a "noisey" sounding output. Output frequencys can range anywhere from 29.3 Hz to 447 KHz. It's key feature is it's pseudo- random number generator, which generates the random output frequencys heard by the channel.
*****************
* Frame counter *
*****************
The 2A03 has an internal frame counter. The purpose of it is to generate the various low frequency signals (60, 120, 240 Hz, and 48, 96, 192 Hz) required to clock several of the sound hardware's counters. It also has the ability to generate IRQ's.
The smallest unit of timing the frame counter operates around is 240Hz; all other frequencies are generated by multiples of this base frequency. A clock divider of 14915 (clocked at twice the CPU speed) is used to get 240Hz (this was the actual measured ratio).
+---------------+
|$4017 operation|
+---------------+
Writes to register $4017 control operation of both the clock divider, and the frame counter.
- Any write to $4017 resets both the frame counter, and the clock divider. Sometimes, games will write to this register in order to synchronize the sound hardware's internal timing, to the sound routine's timing (usually tied into the NMI code). The frame IRQ is slightly longer than the PPU's, so you can see why games would desire this syncronization.
- bit 7 of $4017 controls the frame counter's divide rate. Every time the counter cycles (reaches terminal count (0)), a frame IRQ will be generated, if enabled by clearing bit 6 of $4017. $4015.6 holds the status of the frame counter IRQ; it will be set if the frame counter is responsible for the interrupt.
$4017.7 divider frame IRQ freq.
------- ------- ---------------
0 4 60
1 5 48
On 2A03 reset, both bits of $4017 (6 & 7) will be cleared, enabling frame IRQ's off the hop. The reason why the existence of frame IRQ's are generally unknown is because the 6502's maskable interrupt is disabled on reset, and this blocks out the frame IRQ's. Most games don't use any IRQ-generating hardware in general, therefore they don't bother enabling maskable interrupts.
Note that the IRQ line will be held down by the frame counter until it is acknowledged (by reading $4015). Before this, the 6502 will generate an IRQ *every* time interrupts are enabled (either by CLI or RTI), since the IRQ design on the 6502 is level-triggered, and not edge. If you've written a program that does not read $4015 in the IRQ handler, and you execute CLI, the processor will immediately go into a infinite IRQ call-return loop.
+-----------------------+
|Frame counter operation|
+-----------------------+
Depending on the status of $4017.7, the frame counter will follow 2 different count sequences. These sequences determine when sound hardware counters will be clocked. The sequences are initialized immediately following any write to $4017.
$4017.7 sequence
------- --------
0 4, 0,1,2,3, 0,1,2,3,..., etc.
1 0,1,2,3,4, 0,1,2,3,4,..., etc.
During count sequences 0..3, the linear (triangle) and envelope decay (square & noise) counters recieve a clock for each count. This means that both these counters are clocked once immediately after $4017.7 is written with a value of 1.
Count sequences 1 & 3 clock (update) the frequency sweep (square), and length (all channels) counters. Even though the length counter's smallest unit of time counting is a frame, it seems that it is actually being clocked twice per frame. That said, you can consider the length counters to contain an extra stage to divide this clock signal by 2.
No aforementioned sound hardware counters are clocked on count sequence #4. You should now see how this causes the 96, and 192 Hz signals to be generated when $4017.7=1.
The rest of the document will describe the operation of the sound channels using the $4017.7=0 frequencies (60, 120, and 240 Hz). For $4017.7=1 operation, replace those frequencies with 48, 96, and 192 Hz (respectively).
************************
* Sound hardware delay *
************************
After resetting the 2A03, the first time any sound channel(s) length counter contains a non-zero value (channel is enabled), there will be a 2048 CPU clock cycle delay before any of the sound hardware is clocked. After the 2K clock cycles go by, the NES sound hardware will be clocked normally. This phenomenon only occurs prior to a system reset, and only occurs during the first 2048 CPU clocks after the activation of any of the 4 basic sound channels.
The information in regards to this delay is only provided to keep this document accurate with all information that is currently known about the 2A03's sound hardware. I haven't done much tests on the behaviour of this delay (mainly because I don't care, as I view it as a inconvenience anyway), so this information should be taken with a grain of salt.
************************
* Register Assignments *
************************
The sound hardware internal to the 2A03 has been designated these special memory addresses in the CPU's memory map.
$4000-$4003 Square wave 1
$4004-$4007 Square wave 2 (identical to the first, except for upward frequency sweeps (see "sweep unit" section))
$4008-$400B Triangle
$400C-$400F Noise
$4015 Channel enable / length/frame counter status
$4017 frame counter control
Note that $4015 (and $4017, but is unrelated to sound hardware) are the only R/W registers. All others are write only (attempt to read them will most likely return the last byte on the bus (usually 040H), due to heavy capacitance on the NES's data bus). Reading a "write only" register, will have no effect on the specific register, or channel.
Every sound channel has 4 registers affiliated with it. The description of the register sets are as follows:
+----------------+
| Register set 1 |
+----------------+
$4000(sq1)/$4004(sq2)/$400C(noise) bits
---------------------------------------
0-3 volume / envelope decay rate
4 envelope decay disable
5 length counter clock disable / envelope decay looping enable
6-7 duty cycle type (unused on noise channel)
$4008(tri) bits
---------------
0-6 linear counter load register
7 length counter clock disable / linear counter start
+----------------+
| Register set 2 |
+----------------+
$4001(sq1)/$4005(sq2) bits
--------------------------
0-2 right shift amount
3 decrease / increase (1/0) wavelength
4-6 sweep update rate
7 sweep enable
$4009(tri)/$400D(noise) bits
----------------------------
0-7 unused
+----------------+
| Register set 3 |
+----------------+
$4002(sq1)/$4006(sq2)/$400A(Tri) bits
-------------------------------------
0-7 8 LSB of wavelength
$400E(noise) bits
-----------------
0-3 playback sample rate
4-6 unused
7 random number type generation
+----------------+
| Register set 4 |
+----------------+
$4003(sq1)/$4007(sq2)/$400B(tri)/$400F(noise) bits
--------------------------------------------------
0-2 3 MS bits of wavelength (unused on noise channel)
3-7 length counter load register
+--------------------------------+
| length counter status register |
+--------------------------------+
$4015(read)
-----------
0 square wave channel 1
1 square wave channel 2
2 triangle wave channel
3 noise channel
4 DMC (see "DMC.TXT" for details)
5-6 unused
7 IRQ status of DMC (see "DMC.TXT" for details)
+-------------------------+
| channel enable register |
+-------------------------+
$4015(write)
------------
0 square wave channel 1
1 square wave channel 2
2 triangle wave channel
3 noise channel
4 DMC channel (see "DMC.TXT" for details)
5-7 unused
************************
* Channel architecture *
************************
This section will describe the internal components making up each individual channel. Each component will then be described in full detail.
Device Triangle Noise Square
------ -------- ------ ------
triangle step generator X
linear counter X
programmable timer X X X
length counter X X X
4-bit DAC X X X
volume/envelope decay unit X X
sweep unit X
duty cycle generator X
wavelength converter X
random number generator X
+-------------------------+
| Triangle step generator |
+-------------------------+
This is a 5-bit, single direction counter, and it is only used in the triangle channel. Each of the 4 LSB outputs of the counter lead to one input on a corresponding mutually exclusive XNOR gate. The 4 XNOR gates have been strobed together, which results in the inverted representation of the 4 LSB of the counter appearing on the outputs of the gates when the strobe is 0, and a non-inverting action taking place when the strobe is 1. The strobe is naturally connected to the MSB of the counter, which effectively produces on the output of the XNOR gates a count sequence which reflects the scenario of a near- ideal triangle step generator (D,E,F,F,E,D,...,2,1,0,0,1,2,...). At this point, the outputs of the XNOR gates will be fed into the input of a 4-bit DAC.
This 5-bit counter will be halted whenever the Triangle channel's length or linear counter contains a count of 0. This results in a "latching" behaviour; the counter will NOT be reset to any definite state.
On system reset, this counter is loaded with 0.
The counter's clock input is connected directly to the terminal count output pin of the 11-bit programmable timer in the triangle channel. As a result of the 5-bit triangle step generator, the output triangle wave frequency will be 32 times less than the frequency of the triangle channel's programmable timer is set to generate.
+----------------+
| Linear counter |
+----------------+
The linear counter is only found in the triangle channel. It is a 7-bit presettable down counter, with a decoded output condition of 0 available (not exactly the same as terminal count). Here's the bit assignments:
$4008 bits
----------
0-6 bits 0-6 of the linear counter load register (NOT the linear counter itself)
7 linear counter start
The counter is clocked at 240 Hz (1/4 framerate), and the calculated length in frames is 0.25*N, where N is the 7-bit loaded value. The counter is always being clocked, except when 0 appears on the output of the counter. At this point, the linear counter & triangle step counter clocks signals are disabled, which results in both counters latching their current state (the linear counter will stay at 0, and the triangle step counter will stop, and the channel will be silenced due to this).
The linear counter has 2 modes: load, and count. When the linear counter is in load mode, it essentially becomes transparent (i.e. whatever value is currently in, or being written to $4008, will appear on the output of the counter). Because of this, no count action can occur in load mode. When the mode changes from load to count, the counter will now latch the value currently in it, and start counting down from there. In the count mode, the current value of $4008 is ignored by the counter (but still retained in $4008). Described below is how the mode of the linear counter is set:
Writes to $400B
---------------
cur mode
--- ----
1 load
0 load (on next linear counter clock), count
Cur is the current state of the MSB of $4008.
Writes to $4008
---------------
old new mode
--- --- ----
0 X count
1 0 no change (during the CPU write cycle), count
1 1 no change
Old and new represent the state(s) of the MSB of $4008. Old is the value being replaced in the MSB of $4008 on the write, and new is the value replacing the old one.
"no change" indicates that the mode of the linear counter will not change from the last.
Note that writes to $400B when $4008.7=0 only loads the linear counter with the value in $4008 on the next *linear* counter clock (and NOT at the end of the CPU write cycle). This is a correction from older versions of this doc.
+--------------------+
| Programmable timer |
+--------------------+
The programmable timer is a 11-bit presettable down counter, and is found in the square, triangle, and noise channel(s). The bit assignments are as follows:
$4002(sq1)/$4006(sq2)/$400A(Tri) bits
-------------------------------------
0-7 represent bits 0-7 of the 11-bit wavelength
$4003(sq1)/$4007(sq2)/$400B(Tri) bits
-------------------------------------
0-2 represent bits 8-A of the 11-bit wavelength
Note that on the noise channel, the 11 bits are not available directly. See the wavelength converter section, for more details.
The counter has automatic syncronous reloading upon terminal count (count=0), therefore the counter will count for N+1 (N is the 11-bit loaded value) clock cycles before arriving at terminal count, and reloading. This counter will typically be clocked at the 2A03's internal 6502 speed (1.79 MHz), and produces an output frequency of 1.79 MHz/(N+1). The terminal count's output spike length is typically no longer than half a CPU clock. The TC signal will then be fed to the appropriate device for the particular sound channel (for square, this terminal count spike will lead to the duty cycle generator. For the triangle, the spike will be fed to the triangle step generator. For noise, this signal will go to the random number generator unit).
+----------------+
| Length counter |
+----------------+
The length counter is found in all sound channels. It is essentially a 7-bit down counter, and is conditionally clocked at a frequency of 60 Hz.
When the length counter arrives at a count of 0, the counter will be stopped (stay on 0), and the appropriate channel will be silenced.
The length counter clock disable bit, found in all the channels, can also be used to halt the count sequence of the length counter for the appropriate channel, by writing a 1 out to it. A 0 condition will permit counting (unless of course, the counter's current count = 0). Location(s) of the length counter clock disable bit:
$4000(sq1)/$4004(sq2)/$400C(noise) bits
---------------------------------------
5 length counter clock disable
$4008(tri) bits
---------------
7 length counter clock disable
To load the length counter with a specified count, a write must be made out to the length register. Location(s) of the length register:
$4003(sq1)/$4007(sq2)/$400B(tri)/$400F(noise) bits
--------------------------------------------------
3-7 length
The 5-bit length value written, determines what 7-bit value the length counter will start counting from. A conversion table here will show how the values are translated.
+-----------------------+
| bit3=0 |
+-------+---------------+
| |frames |
|bits +-------+-------+
|4-6 |bit7=0 |bit7=1 |
+-------+-------+-------+
|0 |05 |06 |
|1 |0A |0C |
|2 |14 |18 |
|3 |28 |30 |
|4 |50 |60 |
|5 |1E |24 |
|6 |07 |08 |
|7 |0D |10 |
+-------+-------+-------+
+---------------+
| bit3=1 |
+-------+-------+
|bits | |
|4-7 |frames |
+-------+-------+
|0 |7F |
|1 |01 |
|2 |02 |
|3 |03 |
|4 |04 |
|5 |05 |
|6 |06 |
|7 |07 |
|8 |08 |
|9 |09 |
|A |0A |
|B |0B |
|C |0C |
|D |0D |
|E |0E |
|F |0F |
+-------+-------+
The length counter's real-time status for each channel can be attained. A 0 is returned for a zero count status in the length counter (channel's sound is disabled), and 1 for a non-zero status. Here's the bit description of the length counter status register:
$4015(read)
-----------
0 length counter status of square wave channel 1
1 length counter status of square wave channel 2
2 length counter status of triangle wave channel
3 length counter status of noise channel
4 length counter status of DMC (see "DMC.TXT" for details)
5 unknown
6 frame IRQ status
7 IRQ status of DMC (see "DMC.TXT" for details)
Writing a 0 to the channel enable register will force the length counters to always contain a count equal to 0, which renders that specific channel disabled (as if it doesn't exist). Writing a 1 to the channel enable register disables the forced length counter value of 0, but will not change the count itself (it will still be whatever it was prior to the writing of 1).
Bit description of the channel enable register:
$4015(write)
------------
0 enable square wave channel 1
1 enable square wave channel 2
2 enable triangle wave channel
3 enable noise channel
4 enable DMC channel (see "DMC.TXT" for details)
5-7 unknown
Note that all 5 used bits in this register will be set to 0 upon system reset.
+-----------+
| 4-bit DAC |
+-----------+
This is just a standard 4-bit DAC with 16 steps of output voltage resolution, and is used by all 4 sound channels. On the 2A03, square wave 1 & 2 are mixed together, and are available via pin 1. Triangle & noise are available on pin 2.
These analog outputs require a negative current source, to attain linear symmetry on the various output voltage levels generated by the channel(s) (moreover, to get the sound to be audible). Instead of current sources, the NES uses external 100 ohm pull-down resistors. This results in the output waveforms having some linear asymmetry (i.e., as the desired output voltage increases on a linear scale, the actual outputted voltage increases less and less each step).
The side effect of this is that the DMC's 7-bit DAC port ($4011) is able to indirectly control the volume (somewhat) of both triangle & noise channels. While I have not measured the voltage asymmetery, others on the NESdev messageboards have posted their findings. The conclusion is that when $4011 is 0, triangle & noise volume outputs are at maximum. When $4011 = 7F, the triangle & noise channel outputs operate at only 57% total volume.
The odd thing is that a few games actually take advantage of this "volume" feature, and write values to $4011 in order to regulate the amplitude of the triangle wave channel's output.
+------------------------------+
| Volume / envelope decay unit |
+------------------------------+
The volume / envelope decay hardware is found only in the square wave and noise channels.
$4000(sq1)/$4004(sq2)/$400C(noise)
----------------------------------
0-3 volume / envelope decay rate
4 envelope decay disable
5 envelope decay looping enable
When the envelope decay disable bit (bit 4) is set (1), the current volume value (bits 0-3) is sent directly to the channel's DAC. However, depending on certain conditions, this 4-bit volume value will be ignored, and a value of 0 will be sent to the DAC instead. This means that while the channel is enabled (producing sound), the output of the channel (what you'll hear from the DAC) will either be the 4-bit volume value, or 0. This also means that a 4-bit volume value of 0 will result in no audible sound. These conditions are as follows:
- When hardware in the channel wants to disable it's sound output (like the length counter, or sweep unit (square channels only)).
- On the negative portion of the output frequency signal coming from the duty cycle / random number generator hardware (square wave channel / noise channel).
When the envelope decay disable bit is cleared, bits 0-3 now control the envelope decay rate, and an internal 4-bit down counter (hereon the envelope decay counter) now controls the channel's volume level. "Envelope decay" is used to describe the action of the channel's audio output volume starting from a certain value, and decreasing by 1 at a fixed (linear) rate (which produces a "fade-out" sounding effect). This fixed decrement rate is controlled by the envelope decay rate (bits 0-3). The calculated decrement rate is 240Hz/(N+1), where N is any value between $0-$F.
When the channel's envelope decay counter reaches a value of 0, depending on the status of the envelope decay looping enable bit (bit 5, which is shared with the length counter's clock disable bit), 2 different things will happen:
bit 5 action
----- ------
0 The envelope decay count will stay at 0 (channel silenced).
1 The envelope decay count will wrap-around to $F (upon the next clock cycle). The envelope decay counter will then continue to count down normally.
Only a write out to $4003/$4007/$400F will reset the current envelope decay counter to a known state (to $F, the maximum volume level) for the appropriate channel's envelope decay hardware. Otherwise, the envelope decay counter is always counting down (by 1) at the frequency currently contained in the volume / envelope decay rate bits (even when envelope decays are disabled (setting bit 4)), except when the envelope decay counter contains a value of 0, and envelope decay looping (bit 5) is disabled (0).
+------------+
| Sweep unit |
+------------+
The sweep unit is only found in the square wave channels. The controls for the sweep unit have been mapped in at $4001 for square 1, and $4005 for square 2.
The controls
------------
Bit 7 when this bit is set (1), sweeping is active. This results in real-time increasing or decreasing of the the current wavelength value (the audible frequency will decrease or increase, respectively). The wavelength value in $4002/3 ($4006/7) is constantly read & updated by the sweep. Modifying the contents of $4002/3 will be immediately audible, and will result in the sweep now starting from this new wavelength value.
Bits 6-4 These 3 bits represent the sweep refresh rate, or the frequency at which $4002/3 is updated with the new calculated wavelength. The refresh rate frequency is 120Hz/(N+1), where N is the value written, between 0 and 7.
Bit 3 This bit controls the sweep mode. When this bit is set (1), sweeps will decrease the current wavelength value, as a 0 will increase the current wavelength.
Bits 2-0 These bits control the right shift amount of the new calculated sweep update wavelength. Code that shows how the sweep unit calculates a new sweep wavelength is as follows:
bit 3
-----
0 New = Wavelength + (Wavelength >> N)
1 New = Wavelength - (Wavelength >> N) (minus an additional 1, if using square wave channel 1)
where N is the the shift right value, between 0-7.
Note that in decrease mode, for subtracting the 2 values:
1's compliment (NOT) is being used for square wave channel 1
2's compliment (NEG) is being used for square wave channel 2
This information is currently the only known difference between the 2 square wave channels.
On each sweep refresh clock, the Wavelength register will be updated with the New value, but only if all 3 of these conditions are met:
- bit 7 is set (sweeping enabled)
- the shift value (which is N in the formula) does not equal to 0
- the channel's length counter contains a non-zero value
Notes
-----
There are certain conditions that will cause the sweep unit to silence the channel, and halt the sweep refresh clock (which effectively stops sweep action, if any). Note that these conditions pertain regardless of any sweep refresh rate values, or if sweeping is enabled/disabled (via bit 7).
- an 11-bit wavelength value less than $008 will cause this condition
- if the sweep unit is currently set to increase mode, the New calculated wavelength value will always be tested to see if a carry (bit $B) was generated or not (if sweeping is enabled, this carry will be examined before the Wavelength register is updated) from the shift addition calculation. If carry equals 1, the channel is silenced, and sweep action is halted.
+----------------------+
| Duty cycle generator |
+----------------------+
The duty cycle generator takes the fequency produced from the 11-bit programmable timer, and uses a 4 bit counter to produce 4 types of duty cycles. The output frequency is then 1/16 that of the programmable timer. The duty cycle hardware is only found in the square wave channels. The bit assignments are as follows:
$4000(sq1)/$4004(sq2)
---------------------
6-7 Duty cycle type
duty (positive/negative)
val in clock cycles
--- ---------------
00 2/14
01 4/12
10 8/ 8
11 12/ 4
Where val represents bits 6-7 of $4000/$4004.
This counter is reset when the length counter of the same channel is written to (via $4003/$4007).
The output frequency at this point will now be fed to the volume/envelope decay hardware.
+----------------------+
| Wavelength converter |
+----------------------+
The wavelength converter is only used in the noise channel. It is used to convert a given 4-bit value to an 11-bit wavelength, which then is sent to the noise's own programmable timer. Here is the bit descriptions:
$400E bits
----------
0-3 The 4-bit value to be converted
Below is a conversion chart that shows what 4-bit value will represent the 11-bit wavelength to be fed to the channel's programmable timer:
value octave scale CPU clock cycles (11-bit wavelength+1)
----- ------ ----- --------------------------------------
0 15 A 002
1 14 A 004
2 13 A 008
3 12 A 010
4 11 A 020
5 11 D 030
6 10 A 040
7 10 F 050
8 10 C 065
9 9 A 07F
A 9 D 0BE
B 8 A 0FE
C 8 D 17D
D 7 A 1FC
E 6 A 3F9
F 5 A 7F2
Octave and scale information is provided for the music enthusiast programmer who is more familiar with notes than clock cycles.
+-------------------------+
| Random number generator |
+-------------------------+
The noise channel has a 1-bit pseudo-random number generator. It's based on a 15-bit shift register, and an exclusive or gate. The generator can produce two types of random number sequences: long, and short. The long sequence generates 32,767-bit long number patterns. The short sequence generates 93-bit long number patterns. The 93-bit mode will generally produce higher sounding playback frequencys on the channel. Here is the bit that controls the mode:
$400E bits
----------
7 mode
If mode=0, then 32,767-bit long number sequences will be produced (32K mode), otherwise 93-bit long number sequences will be produced (93-bit mode).
The following diagram shows where the XOR taps are taken off the shift register to produce the 1-bit pseudo-random number sequences for each mode.
mode <-----
---- EDCBA9876543210
32K **
93-bit * *
The current result of the XOR will be transferred into bit position 0 of the SR, upon the next shift cycle. The 1-bit random number output is taken from pin E, is inverted, then is sent to the volume/envelope decay hardware for the noise channel. The shift register is shifted upon recieving 2 clock pulses from the programmable timer (the shift frequency will be half that of the frequency from the programmable timer (one octave lower)).
On system reset, this shift register is loaded with a value of 1.
RP2A03E quirk
-------------
I have been informed that revisions of the 2A03 before "F" actually lacked support for the 93-bit looped noise playback mode. While the Famicom's 2A03 went through 4 revisions (E..H), I think that only one was ever used for the front loading NES: "G". Other differences between 2A03 revisions are unknown.
EOF

View File

@ -0,0 +1,697 @@
The NES sound channel guide 1.8
Written by Brad Taylor.
btmine@hotmail.com
Last updated: July 27th, 2000.
All results were obtained by studying prior information available (from
nestech 1.00, and postings on NESDev from miscellanious people), and through
a series of experiments conducted by me. Results acquired by individuals
prior to my reverse-engineering have been double checked, and final results
have been confirmed. Credit is due to those individual(s) who contributed
any information in regards to the the miscellanious sound channels wihtin
the NES.
A special thanks goes out to Matthew Conte, for his expertise on
pseudo-random number generation (amoung other things), which allowed for the
full reverse engineering of the NES's noise channel to take place. Without
his help, I would still be trying to find a needle in a haystack, as far as
the noise's method of pseudo-random number generation goes. Additionally,
his previous findings / reverse engineering work on the NES's sound hardware
really got the ball of NES sound emulation rolling. If it weren't for Matt's
original work, this document wouldn't exist.
Thanks to Kentaro Ishihara, for his excellent work on finding the difference
in upward frequency sweep between the 2 square wave channels.
****************
* Introduction *
****************
The 2A03 (NES's integrated CPU) has 4 internal channels to it that have the
ability to generate semi-analog sound, for musical playback purposes. These
channels are 2 square wave channels, one triangle wave channel, and a noise
generation channel. This document will go into full detail on every aspect
of the operation and timing of the mentioned sound channels.
*******************
* Channel details *
*******************
Each channel has different characteristics to it that make up it's
operation.
The square channel(s) have the ability to generate a square wave frequency
in the range of 54.6 Hz to 12.4 KHz. It's key features are frequency sweep
abilities, and output duty cycle adjustment.
The triangle wave channel has the ability to generate an output triangle
wave with a resolution of 4-bits (16 steps), in the range of 27.3 Hz to 55.9
KHz. The key features this channel has is it's analog triangle wave output,
and it's linear counter, which can be set to automatically disable the
channel's sound after a certain period of time has gone by.
The noise channel is used for producing random frequencys, which results in
a "noisey" sounding output. Output frequencys can range anywhere from 29.3
Hz to 447 KHz. It's key feature is it's pseudo- random number generator,
which generates the random output frequencys heard by the channel.
*****************
* Frame counter *
*****************
The 2A03 has an internal frame counter. It has the ability to generate 60 Hz
(1/1 framerate), 120 Hz (1/2 framerate), and 240 Hz (1/4 framerate) signals,
used by some of the sound hardware. The 1/4 framerate is calculated by
taking twice the CPU clock speed (3579545.454545 Hz), and dividing it by
14915 (i.e., the divide-by-14915 counter is decremented on the rising AND
falling edge of the CPU's clock signal).
************************
* Sound hardware delay *
************************
After resetting the 2A03, the first time any sound channel(s) length counter
contains a non-zero value (channel is enabled), there will be a 2048 CPU
clock cycle delay before any of the sound hardware is clocked. After the 2K
clock cycles go by, the NES sound hardware will be clocked normally. This
phenomenon only occurs prior to a system reset, and only occurs during the
first 2048 CPU clocks for any sound channel prior to a sound channel being
enabled.
The information in regards to this delay is only provided to keep this
entire document persistently accurate on the 2A03's sound hardware, but may
not be 100% accurate in itself. I haven't done much tests on the behaviour
of this delay (mainly because I don't care, as I view it as a inconvenience
anyway), so that's why I believe there could be some inaccuracies.
************************
* Register Assignments *
************************
The sound hardware internal to the 2A03 has been designated these special
memory addresses in the CPU's memory map.
$4000-$4003 Square wave 1
$4004-$4007 Square wave 2 (identical to the first, except for upward
frequency sweeps (see "sweep unit" section))
$4008-$400B Triangle
$400C-$400F Noise
$4015 Channel enable / length counter status
Note that $4015 is the only R/W register. All others are write only (attempt
to read them will most likely result in a returned 040H, due to heavy
capacitance on the NES's data bus). Reading a "write only" register, will
have no effect on the specific register, or channel.
Every sound channel has 4 registers affiliated with it. The description of
the register sets are as follows:
+----------------+
| Register set 1 |
+----------------+
$4000(sq1)/$4004(sq2)/$400C(noise) bits
---------------------------------------
0-3 volume / envelope decay rate
4 envelope decay disable
5 length counter clock disable / envelope decay looping enable
6-7 duty cycle type (unused on noise channel)
$4008(tri) bits
---------------
0-6 linear counter load register
7 length counter clock disable / linear counter start
+----------------+
| Register set 2 |
+----------------+
$4001(sq1)/$4005(sq2) bits
--------------------------
0-2 right shift amount
3 decrease / increase (1/0) wavelength
4-6 sweep update rate
7 sweep enable
$4009(tri)/$400D(noise) bits
----------------------------
0-7 unused
+----------------+
| Register set 3 |
+----------------+
$4002(sq1)/$4006(sq2)/$400A(Tri) bits
-------------------------------------
0-7 8 LSB of wavelength
$400E(noise) bits
-----------------
0-3 playback sample rate
4-6 unused
7 random number type generation
+----------------+
| Register set 4 |
+----------------+
$4003(sq1)/$4007(sq2)/$400B(tri)/$400F(noise) bits
--------------------------------------------------
0-2 3 MS bits of wavelength (unused on noise channel)
3-7 length counter load register
+--------------------------------+
| length counter status register |
+--------------------------------+
$4015(read)
-----------
0 square wave channel 1
1 square wave channel 2
2 triangle wave channel
3 noise channel
4 DMC (see "DMC.TXT" for details)
5-6 unused
7 IRQ status of DMC (see "DMC.TXT" for details)
+-------------------------+
| channel enable register |
+-------------------------+
$4015(write)
------------
0 square wave channel 1
1 square wave channel 2
2 triangle wave channel
3 noise channel
4 DMC channel (see "DMC.TXT" for details)
5-7 unused
************************
* Channel architecture *
************************
This section will describe the internal components making up each individual
channel. Each component will then be described in full detail.
Device Triangle Noise Square
------ -------- ------ ------
triangle step generator X
linear counter X
programmable timer X X X
length counter X X X
4-bit DAC X X X
volume/envelope decay unit X X
sweep unit X
duty cycle generator X
wavelength converter X
random number generator X
+-------------------------+
| Triangle step generator |
+-------------------------+
This is a 5-bit, single direction counter, and it is only used in the
triangle channel. Each of the 4 LSB outputs of the counter lead to one input
on a corresponding mutually exclusive XNOR gate. The 4 XNOR gates have been
strobed together, which results in the inverted representation of the 4 LSB
of the counter appearing on the outputs of the gates when the strobe is 0,
and a non-inverting action taking place when the strobe is 1. The strobe is
naturally connected to the MSB of the counter, which effectively produces on
the output of the XNOR gates a count sequence which reflects the scenario of
a near- ideal triangle step generator (D,E,F,F,E,D,...,2,1,0,0,1,2,...). At
this point, the outputs of the XNOR gates will be fed into the input of a
4-bit DAC.
This 5-bit counter will be halted whenever the Triangle channel's length or
linear counter contains a count of 0. This results in a "latching"
behaviour; the counter will NOT be reset to any definite state.
On system reset, this counter is loaded with 0.
The counter's clock input is connected directly to the terminal count output
pin of the 11-bit programmable timer in the triangle channel. As a result of
the 5-bit triangle step generator, the output triangle wave frequency will
be 32 times less than the frequency of the triangle channel's programmable
timer is set to generate.
+----------------+
| Linear counter |
+----------------+
The linear counter is only found in the triangle channel. It is a 7-bit
presettable down counter, with a decoded output condition of 0 available
(not exactly the same as terminal count). Here's the bit assignments:
$4008 bits
----------
0-6 bits 0-6 of the linear counter load register (NOT the linear counter
itself)
7 linear counter start
The counter is clocked at 240 Hz (1/4 framerate), and the calculated length
in frames is 0.25*N, where N is the 7-bit loaded value. The counter is
always being clocked, except when 0 appears on the output of the counter. At
this point, the linear counter & triangle step counter clocks signals are
disabled, which results in both counters latching their current state (the
linear counter will stay at 0, and the triangle step counter will stop, and
the channel will be silenced due to this).
The linear counter has 2 modes: load, and count. When the linear counter is
in load mode, it essentially becomes transparent (i.e. whatever value is
currently in, or being written to $4008, will appear on the output of the
counter). Because of this, no count action can occur in load mode. When the
mode changes from load to count, the counter will now latch the value
currently in it, and start counting down from there. In the count mode, the
current value of $4008 is ignored by the counter (but still retained in
$4008). Described below is how the mode of the linear counter is set:
Writes to $400B
---------------
cur mode
--- ----
1 load
0 load (during the write cycle), count
Cur is the current state of the MSB of $4008.
Writes to $4008
---------------
old new mode
--- --- ----
0 X count
1 0 no change (during the write cycle), count
1 1 no change
Old and new represent the state(s) of the MSB of $4008. Old is the value
being replaced in the MSB of $4008 on the write, and new is the value
replacing the old one.
"no change" indicates that the mode of the linear counter will not change
from the last.
+--------------------+
| Programmable timer |
+--------------------+
The programmable timer is a 11-bit presettable down counter, and is found in
the square, triangle, and noise channel(s). The bit assignments are as
follows:
$4002(sq1)/$4006(sq2)/$400A(Tri) bits
-------------------------------------
0-7 represent bits 0-7 of the 11-bit wavelength
$4003(sq1)/$4007(sq2)/$400B(Tri) bits
-------------------------------------
0-2 represent bits 8-A of the 11-bit wavelength
Note that on the noise channel, the 11 bits are not available directly. See
the wavelength converter section, for more details.
The counter has automatic syncronous reloading upon terminal count
(count=0), therefore the counter will count for N+1 (N is the 11-bit loaded
value) clock cycles before arriving at terminal count, and reloading. This
counter will typically be clocked at the 2A03's internal 6502 speed (1.79
MHz), and produces an output frequency of 1.79 MHz/(N+1). The terminal
count's output spike length is typically no longer than half a CPU clock.
The TC signal will then be fed to the appropriate device for the particular
sound channel (for square, this terminal count spike will lead to the duty
cycle generator. For the triangle, the spike will be fed to the triangle
step generator. For noise, this signal will go to the random number
generator unit).
+----------------+
| Length counter |
+----------------+
The length counter is found in all sound channels. It is essentially a 7-bit
down counter, and is conditionally clocked at a frequency of 60 Hz.
When the length counter arrives at a count of 0, the counter will be stopped
(stay on 0), and the appropriate channel will be silenced.
The length counter clock disable bit, found in all the channels, can also be
used to halt the count sequence of the length counter for the appropriate
channel, by writing a 1 out to it. A 0 condition will permit counting
(unless of course, the counter's current count = 0). Location(s) of the
length counter clock disable bit:
$4000(sq1)/$4004(sq2)/$400C(noise) bits
---------------------------------------
5 length counter clock disable
$4008(tri) bits
---------------
7 length counter clock disable
To load the length counter with a specified count, a write must be made out
to the length register. Location(s) of the length register:
$4003(sq1)/$4007(sq2)/$400B(tri)/$400F(noise) bits
--------------------------------------------------
3-7 length
The 5-bit length value written, determines what 7-bit value the length
counter will start counting from. A conversion table here will show how the
values are translated.
+-----------------------+
| bit3=0 |
+-------+---------------+
| |frames |
|bits +-------+-------+
|4-6 |bit7=0 |bit7=1 |
+-------+-------+-------+
|0 |05 |06 |
|1 |0A |0C |
|2 |14 |18 |
|3 |28 |30 |
|4 |50 |60 |
|5 |1E |24 |
|6 |07 |08 |
|7 |0E |10 |
+-------+-------+-------+
+---------------+
| bit3=1 |
+-------+-------+
|bits | |
|4-7 |frames |
+-------+-------+
|0 |7F |
|1 |01 |
|2 |02 |
|3 |03 |
|4 |04 |
|5 |05 |
|6 |06 |
|7 |07 |
|8 |08 |
|9 |09 |
|A |0A |
|B |0B |
|C |0C |
|D |0D |
|E |0E |
|F |0F |
+-------+-------+
The length counter's real-time status for each channel can be attained. A 0
is returned for a zero count status in the length counter (channel's sound
is disabled), and 1 for a non-zero status. Here's the bit description of the
length counter status register:
$4015(read)
-----------
0 length counter status of square wave channel 1
1 length counter status of square wave channel 2
2 length counter status of triangle wave channel
3 length counter status of noise channel
4 length counter status of DMC (see "DMC.TXT" for details)
5-6 unused
7 IRQ status of DMC (see "DMC.TXT" for details)
Writing a 0 to the channel enable register will force the length counters to
always contain a count equal to 0, which renders that specific channel
disabled (as if it doesn't exist). Writing a 1 to the channel enable
register disables the forced length counter value of 0, but will not change
the count itself (it will still be whatever it was prior to the writing of
1).
Bit description of the channel enable register:
$4015(write)
------------
0 enable square wave channel 1
1 enable square wave channel 2
2 enable triangle wave channel
3 enable noise channel
4 enable DMC channel (see "DMC.TXT" for details)
5-7 unused
Note that all 5 used bits in this register will be set to 0 upon system
reset.
+-----------+
| 4-bit DAC |
+-----------+
This is just a standard 4-bit DAC with 16 steps of output voltage
resolution, and is used by all 4 sound channels.
On the 2A03, square wave 1 & 2 are mixed together, and are available via pin
1. Triangle & noise are available on pin 2. These analog outputs require a
negative current source, to attain linear symmetry on the various output
voltage levels generated by the channel(s) (moreover, to get the sound to be
audible). Since the NES just uses external 100 ohm pull-down resistors, this
results in the output waveforms being of very small amplitude, but with
minimal linearity asymmetry.
+------------------------------+
| Volume / envelope decay unit |
+------------------------------+
The volume / envelope decay hardware is found only in the square wave and
noise channels.
$4000(sq1)/$4004(sq2)/$400C(noise)
----------------------------------
0-3 volume / envelope decay rate
4 envelope decay disable
5 envelope decay looping enable
When the envelope decay disable bit (bit 4) is set (1), the current volume
value (bits 0-3) is sent directly to the channel's DAC. However, depending
on certain conditions, this 4-bit volume value will be ignored, and a value
of 0 will be sent to the DAC instead. This means that while the channel is
enabled (producing sound), the output of the channel (what you'll hear from
the DAC) will either be the 4-bit volume value, or 0. This also means that a
4-bit volume value of 0 will result in no audible sound. These conditions
are as follows:
- When hardware in the channel wants to disable it's sound output (like the
length counter, or sweep unit (square channels only)).
- On the negative portion of the output frequency signal coming from the
duty cycle / random number generator hardware (square wave channel / noise
channel).
When the envelope decay disable bit is cleared, bits 0-3 now control the
envelope decay rate, and an internal 4-bit down counter (hereon the envelope
decay counter) now controls the channel's volume level. "Envelope decay" is
used to describe the action of the channel's audio output volume starting
from a certain value, and decreasing by 1 at a fixed (linear) rate (which
produces a "fade-out" sounding effect). This fixed decrement rate is
controlled by the envelope decay rate (bits 0-3). The calculated decrement
rate is 240Hz/(N+1), where N is any value between $0-$F.
When the channel's envelope decay counter reaches a value of 0, depending on
the status of the envelope decay looping enable bit (bit 5, which is shared
with the length counter's clock disable bit), 2 different things will
happen:
bit 5 action
----- ------
0 The envelope decay count will stay at 0 (channel silenced).
1 The envelope decay count will wrap-around to $F (upon the next clock
cycle). The envelope decay counter will then continue to count down
normally.
Only a write out to $4003/$4007/$400F will reset the current envelope decay
counter to a known state (to $F, the maximum volume level) for the
appropriate channel's envelope decay hardware. Otherwise, the envelope decay
counter is always counting down (by 1) at the frequency currently contained
in the volume / envelope decay rate bits (even when envelope decays are
disabled (setting bit 4)), except when the envelope decay counter contains a
value of 0, and envelope decay looping (bit 5) is disabled (0).
+------------+
| Sweep unit |
+------------+
The sweep unit is only found in the square wave channels. The controls for
the sweep unit have been mapped in at $4001 for square 1, and $4005 for
square 2.
The controls
------------
Bit 7 when this bit is set (1), sweeping is active. This results in
real-time increasing or decreasing of the the current wavelength value (the
audible frequency will decrease or increase, respectively). The wavelength
value in $4002/3 ($4006/7) is constantly read & updated by the sweep.
Modifying the contents of $4002/3 will be immediately audible, and will
result in the sweep now starting from this new wavelength value.
Bits 6-4 These 3 bits represent the sweep refresh rate, or the frequency at
which $4002/3 is updated with the new calculated wavelength. The refresh
rate frequency is 120Hz/(N+1), where N is the value written, between 0 and
7.
Bit 3 This bit controls the sweep mode. When this bit is set (1), sweeps
will decrease the current wavelength value, as a 0 will increase the current
wavelength.
Bits 2-0 These bits control the right shift amount of the new calculated
sweep update wavelength. Code that shows how the sweep unit calculates a new
sweep wavelength is as follows:
bit 3
-----
0 New = Wavelength + (Wavelength >> N)
1 New = Wavelength - (Wavelength >> N) (minus an additional 1, if using
square wave channel 1)
where N is the the shift right value, between 0-7.
Note that in decrease mode, for subtracting the 2 values:
1's compliment (NOT) is being used for square wave channel 1
2's compliment (NEG) is being used for square wave channel 2
This information is currently the only known difference between the 2 square
wave channels.
On each sweep refresh clock, the Wavelength register will be updated with
the New value, but only if all 3 of these conditions are met:
- bit 7 is set (sweeping enabled)
- the shift value (which is N in the formula) does not equal to 0
- the channel's length counter contains a non-zero value
Notes
-----
There are certain conditions that will cause the sweep unit to silence the
channel, and halt the sweep refresh clock (which effectively stops sweep
action, if any). Note that these conditions pertain regardless of any sweep
refresh rate values, or if sweeping is enabled/disabled (via bit 7).
- an 11-bit wavelength value less than $008 will cause this condition
- if the sweep unit is currently set to increase mode, the New calculated
wavelength value will always be tested to see if a carry (bit $B) was
generated or not (if sweeping is enabled, this carry will be examined before
the Wavelength register is updated) from the shift addition calculation. If
carry equals 1, the channel is silenced, and sweep action is halted.
+----------------------+
| Duty cycle generator |
+----------------------+
The duty cycle generator takes the fequency produced from the 11-bit
programmable timer, and uses a 4 bit counter to produce 4 types of duty
cycles. The output frequency is then 1/16 that of the programmable timer.
The duty cycle hardware is only found in the square wave channels. The bit
assignments are as follows:
$4000(sq1)/$4004(sq2)
---------------------
6-7 Duty cycle type
duty (positive/negative)
val in clock cycles
--- ---------------
00 2/14
01 4/12
10 8/ 8
11 12/ 4
Where val represents bits 6-7 of $4000/$4004.
The output frequency at this point will now be fed to the volume/envelope
decay hardware.
+----------------------+
| Wavelength converter |
+----------------------+
The wavelength converter is only used in the noise channel. It is used to
convert a given 4-bit value to an 11-bit wavelength, which then is sent to
the noise's own programmable timer. Here is the bit descriptions:
$400E bits
----------
0-3 The 4-bit value to be converted
Below is a conversion chart that shows what 4-bit value will represent the
11-bit wavelength to be fed to the channel's programmable timer:
value octave scale CPU clock cycles (11-bit wavelength+1)
----- ------ ----- --------------------------------------
0 15 A 002
1 14 A 004
2 13 A 008
3 12 A 010
4 11 A 020
5 11 D 030
6 10 A 040
7 10 F 050
8 10 C 065
9 9 A 07F
A 9 D 0BE
B 8 A 0FE
C 8 D 17D
D 7 A 1FC
E 6 A 3F9
F 5 A 7F2
Octave and scale information is provided for the music enthusiast programmer
who is more familiar with notes than clock cycles.
+-------------------------+
| Random number generator |
+-------------------------+
The noise channel has a 1-bit pseudo-random number generator. It's based on
a 15-bit shift register, and an exclusive or gate. The generator can produce
two types of random number sequences: long, and short. The long sequence
generates 32,767-bit long number patterns. The short sequence generates
93-bit long number patterns. The 93-bit mode will generally produce higher
sounding playback frequencys on the channel. Here is the bit that controls
the mode:
$400E bits
----------
7 mode
If mode=0, then 32,767-bit long number sequences will be produced (32K
mode), otherwise 93-bit long number sequences will be produced (93-bit
mode).
The following diagram shows where the XOR taps are taken off the shift
register to produce the 1-bit pseudo-random number sequences for each mode.
mode <-----
---- EDCBA9876543210
32K **
93-bit * *
The current result of the XOR will be transferred into bit position 0 of the
SR, upon the next shift cycle. The 1-bit random number output is taken from
pin E, is inverted, then is sent to the volume/envelope decay hardware for
the noise channel. The shift register is shifted upon recieving 2 clock
pulses from the programmable timer (the shift frequency will be half that of
the frequency from the programmable timer (one octave lower)).
On system reset, this shift register is loaded with a value of 1.

View File

@ -0,0 +1,250 @@
========= mmc5 infomation ==========
date 1998/05/31
by goroh
translated May 31, 1998 by Sgt. Bowhack
mail goroh_kun@geocities.co.jp
5000,5004 ch1,ch2 Pulse Control
bit CCwevvvv
CC Duty Cycle (Positive vs. Negative)
#0:87.5% #1:75.0% #2:50.0% #3:25.0%
w Waveform Hold (e.g. Looping)
0: Off 1: On
e Envelope Select
0: Varied 1: Fixed
< e=0 >
vvvv Playback Rate
#0<-fast<--->-slow--> #15
< e=1 >
vvvv Output Volume
5002,5006 ch1,ch2 frequency L
bit ffffffff
5003,5007 ch1,ch2 frequency H
bit tttttfff
ttttt sound occurence time
Objective is to remove the continuous changing of frequency for
square wave setup and do the same to the main part of the square wave
of studying the main part of the famicom. (?- Sgt. Bowhack)
5010 ch3 synthetic voice business channel
bit -------O
O wave output 0:Off 1:On
5011 ch4 synthetic voice business channel 2
bit vvvvvvvv
vvvvvvvv wave size
5015 sound output channel
bit ------BA
A: ch1 output 1:enable 0:disable
B: ch2 output 1:enable 0:disable
5100 PRG-page size Setting
bit ------SS
SS PRG-page size
0: 32k 1:16k 2,3:8k
* Reset is misled the first times for about 8k (?- SB)
5101 CHR-page size Setting
bit ------SS
SS CHR-page size
0:8k 1:4k 2:2k 3:1k
5102 W BBR-RAM Write Protect 1
bit ------AA
5103 W BBR-RAM Write Protect 2
bit ------BB
(AA,BB) = (2,1) permitted to write to BBR-RAM only when crowded
*Reset write around becomes prohibited when crowded
5104 Grafix Mode Setting
$5c00-$5fff decides how it should be used
bit ------MM
#00:Enable only Split Mode
#01:Enable Split Mode & ExGrafix Mode
#02:ExRAM Mode
#03:ExRAM Mode & Write Protect
Consideration
MMC5 has 2 graphic mode extensions that allow more than 256 characters
on one standard game screen. It uses Split Mode so it can display the
specified CHR-page and scroll position seperate from ExGrafix Mode to
be able to choose a palette, and the other divides it vertically.
5105 W NameTable Setting
bit ddccbbaa
aa: Select VRAM at 0x2000-0x23ff
bb: Select VRAM at 0x2400-0x27ff
cc: Select VRAM at 0x2800-0x2bff
dd: Select VRAM at 0x2c00-0x2fff
#0:use VRAM 0x000-0x3ff
#1:use VRAM 0x400-0x7ff
#2:use ExVRAM 0x000-0x3ff
#3:use ExNameTable(Fill Mode)
Consideration
The name table can designate 4 kinds of this resister and be a useful
special quality for this because painting and smashing it with a
character that there is 1 sheet for the remaining sheets can generally
be used. (?-SB)
5106 W Fill Mode Setting 1
bit vvvvvvvv
Fill chr-table
For whether it paints or smashes it at any non-designated character
5107 W Fill Mode Setting 2
bit ------pp
Whether or not it uses any non-designated palettes
5113 RAM-page for $6000-$7FFF
bit -----p--
5114-5117 Program Bank switch
< page_size=32k >
$5117 [8]-[F] bit pppppp--
< page_size=16k >
$5115 [8]-[B] bit ppppppp-
$5117 [C]-[F] bit ppppppp-
< page_size=8k >
$5114 [8][9] bit pppppppp
$5115 [A][B] bit pppppppp
$5116 [C][D] bit pppppppp
$5117* [E][F] bit pppppppp
*Reset is around early, Last Page misled
5120-512b Charactor Bank switch
< page_size=8k >
$5120-$5127 switch to mode A
$5128-$512b switch to mode B
$5127 [0]-[7] modeA
$512b [0]-[7] modeB
< page_size=4k >
$5120-$5127 switch to mode A
$5128-$512b switch to mode B
$5123 [0]-[3] modeA
$5127 [4]-[7] modeA
$512b [0]-[3],[4]-[7] modeB
< page_size=2k >
$5120-$5127 switch to mode A
$5128-$512b switch to mode B
$5121 [0]-[1] modeA
$5123 [2]-[3] modeA
$5125 [4]-[5] modeA
$5127 [6]-[7] modeA
$5129 [0]-[1],[4]-[5] modeB
$512b [2]-[3],[6]-[7] modeB
< page_size=1k >
$5120-$5127 switch to mode A
$5128-$512b switch to mode B
$5120 [0] modeA
$5121 [1] modeA
$5122 [2] modeA
$5123 [3] modeA
$5124 [4] modeA
$5125 [5] modeA
$5126 [6] modeA
$5127 [7] modeA
$5128 [0],[4] modeB
$5129 [1],[5] modeB
$512a [2],[6] modeB
$512b [3],[7] modeB
Consideration
MMC5 has mode A ,mode B and 2 kinds of CHR-page memory resistors.
They can be used for refreshing it. (?-SB)
5130 ???
analyzing it...
5200 W Split Mode Control 1
bit Ec-vvvvv
For the E function 0:don't use 1:use
c boundary's side is for using Split Mode extension of graphics
0: left side 1: right side
vvvvv left boundary is designated with the char. # to count places
Sample.
5200 <- #00
(not?) used yet
5200 <- #82
Used for SplitMode GFX extension from left 1-2 character
5200 <- #c2
Used for SplitMode GFX extension from the right side 3 chars.
5200 <- #c0
Used for SplitMode GFX extension on the whole screen
5200 <- #d0
Used for SplitMode GFX extension on the right side of the screen
5200 <- #90
Used for SplitMode GFX extension on the left side of the screen
5201 W SplitMode setup for SplitMode Ext. GFX use 1
$2005 determines the vertical movement; it can also delay ext. gfx's
vert. movement if necessary. It's written 2 times in bulk in the same
way as it would slip off a grade in $2005 (??-SB)
5202 W SplitMode setup for SplitMode Ext. GFX use 2
bit --pppppp
uses vertical division of ext. gfx CHR-page designation
index_size=4k(0x1000byte)
In case it uses a character 0x4000-0x4fff for the ext. gfx in question
$5202 <- 4
5203 W scanline break point
For scanline # that it splits and wants to make it designate it in bulk
5204 WR IRQ enable/disable
W bit I-------
I 1:IRQ Enable 0:IRQ Disable
R bit I-------
I 1:Scanline Hit 0:Scanline not Hit
$5203 is designated as scanline when arrived.
5205 WR mult input/output
5206 WR mult input/output
($5205in)*($5206in) = $5205,$5206out
5c00-5fbf ext. gfx business VRAM
shows an attribute of every position character
<ExGrafix Mode>
bit PPpppppp
PP: use character palette number
pppppp: use background CHR-PAGE number index=4k
#0-#3F are designations, $0000-$3FFF is CHR-data's range
Use for extension gfx
<Split Mode>
SplitMode uses a Name Table for extension gfx use.
bit pppppppp
pppppppp: use for background char. number designation
<ExRAM Mode>
Used for Extension RAM
5fc0-5fff
<ExGrafix Mode>
(not?) used yet
<Split Mode>
SplitMode uses gfx's Attribute Table extension.
PPU uses $23c0-$23ff in the same way as the Attribute Table
<ExRAM Mode>
Used for Extension RAM
Consideration
5c00-5fff has 3 uses.
Split Mode and ExGrafix Mode's VBlank is written so as to become
crowded, it writes a 0 and becomes crowded.
Every mode tries to go around ExRAM mode including reading but it
writes it, is effective in bulk and #5c-#5f is the output at times
where it is effective.

View File

@ -0,0 +1,112 @@
SMB2j Revision "A". Mapper #50 Info
-----------------------------------
12.09.2000
V2.00
Mapper info by The Mad Dumper
---
This mapper has been assigned the number 50. (that's 50 decimal)
Wow, another SMB2j cart! This one is alot different than the last one I
worked on. It has a single 128K PRG ROM and VRAM. The other SMB2j had
64K PRG and 8K CHR. Not much more to say other than this has one very
fucked up mapper circuit on it!
---
The hardware:
It consists of 6 TTL chips (74163, 74157, 74139, 7400, 7474, and a 4020),
1 8K RAM chip for the VRAM, and 1 128K 28 pin ROM. There is some
"M^2L" logic on the board (Mickey-Mouse Logic). It is a 3 input OR gate
made out of 3 diodes and a resistor.
Also, they swapped D3 and D6, as well as A1 and A3. Why this was done, I
have no idea. It sure mussed up my REing efforts! I desoldered and read
the ROM out through the EPROM programmer as a check and was not happy to
find the data seemingly corrupt!
After converting the ROM image over via some QBasic, it matched up great.
You do not have to swap the addresses or data bytes; I have done this
already in the released .NES ROM.
---
There are two registers on this cartridge. They are located in the 4000h-
5FFFh block.
Funny addresses are decoded for the register selection; presumably so they
did not interfere with the FDS or NES registers.
A15 ... A0
-------------------
010x xxxQ x01x xxxx
x = Don't Care
0 = must be 0
1 = must be 1
Q = register selection bit. 0 = ROM Page; 1 = IRQ Timer Enable
-
ROM Page Register:
------------------
Accessed when the address lines are in the above state. An example address
would be 4020h. 4021h, 4022h, ... 403Fh, 40A0h, 40A1h, ... are all mirrors
of this register. Writing here stores the desired bank #.
7 bit 0
---------
xxxx DCBA
These 4 bits are shown below in the ROM memory map. Note that they are
somewhat "scrambled". The value of this register is unknown at powerup.
-
IRQ Timer.
7 bit 0
---------
xxxx xxxI
The IRQ Timer register controls the state of the IRQ timer. Writing here
will turn it on or off. Turning the IRQ timer off resets it to 0. Writing
a 1 here will turn the timer on, while writing a 0 will turn it off.
The timer is composed of a binary ripple counter. After 4096 M2 cycles,
/IRQ is pulled low. This is about 36 scanlines. The idea behind the timer
is to split the screen for the score bar at the top. You start it at the
beginning of the VBI routine, and then after 36 scanlines, it sends the IRQ
which clears the timer, and resets the scroll registers. The value of this
register is unknown at powerup.
---
ROM Memory Map:
Address Range | Bank bits: 3210
-------------------------------
6000h-7FFFh 1111
8000h-9FFFh 1000
A000h-BFFFh 1001
C000h-DFFFh DACB -- Selectable page
E000h-FFFFh 1011
The ROM is composed of 16 8K banks. The 4 bank bits are shown above. Bit 3
is the MSB while bit 0 is the LSB. 6000h-7FFFh is set to 1111b, or bank 0fh.
All banks are FIXED except the bank at C000h-DFFFh. Only it can be changed.

View File

@ -0,0 +1,18 @@
Unknown:
Alien Syndrome 128KiB/128KiB
Super Sprint
MIMIC 1:
Fantasy Zone 64KiB/64KiB
Toobin' 128KiB/64KiB
Vindicators 64KiB/32KiB
RAMBO 1(board looks like it can take 256KiB PRG/256KiB CHR max):
Klax 64KiB PRG/64KiB CHR
Road Runner 64KiB PRG/128KiB CHR
Rolling Thunder 128KiB PRG/128KiB CHR
Skull and Crossbones 128KiB PRG/64KiB CHR

View File

@ -0,0 +1,388 @@
VRCVI CHIP INFO
----- ---- ----
By:
Kevin Horton
khorton@iquest.net
The RENES Project:
Reverse-engineering
the world.
V1.01 08/31/99 teeny fix
V1.00 08/31/99 Complete Version
VRCVI (VRC6) (48 pin standard 600mil wide DIP)
------------
This chip is used in such games as Konami's CV3j and Madara. It's unique
because it has some extra sound channels on it that get piped through the
Famicom (note this is a fami-only chip and you will not find one in any
NES game). "VI" of "VRCVI" is "6" for the roman numeral challenged.
This chip generates its audio via a 6 bit R2R ladder. This is contained
inside a 9 pin resistor network like so:
3K 3K 3K 3K 3K 2K
/------*-\/\/-*-\/\/-*-\/\/-*-\/\/-*-\/\/-*------*-\/\/-\
| | | | | | | | |
\ \ \ \ \ \ \ | |
/ / / / / / / | |
\ 6K \ 6K \ 6K \ 6K \ 6K \ 6K \ 6K | |
/ / / / / / / | |
| | | | | | | | |
O O O O O O O O O
GND D0 D1 D2 D3 D4 D5 Aud In Aud Out
Legend:
-------
(s) means this pin connects to the System
(r) this only connects to the ROM
(w) this is a SRAM/WRAM connection only
AUD : these pass to the resistor network
CHR : these connect to the CHR ROM and/or fami's CHR pins
PRG : these connect to the PRG ROM and/or fami's PRG pins
WRAM : this hooks to the WRAM
CIRAM : the RAM chip which is on the fami board
.----\/----.
GND - |01 48| - +5V
AUD D1 - |02 47| - AUD D0
AUD D3 - |03 46| - AUD D2
AUD D5 - |04 45| - AUD D4
(s) PRG A12 - |05 44| - PRG A16 (r)
(s) PRG A14 - |06 43| - PRG A13 (s)
(s) M2 - |07 42| - PRG A17 (r)
(r) PRG A14 - |08 41| - PRG A15 (r)
*1 (s) PRG A1 - |09 40| - PRG A13 (r)
*1 (s) PRG A0 - |10 39| - PRG D7 (s)
(s) PRG D0 - |11 38| - PRG D6 (s)
(s) PRG D1 - |12 37| - PRG D5 (s)
(s) PRG D2 - |13 36| - PRG D4 (s)
(r) PRG /CE - |14 35| - PRG D3 (s)
(s) R/W - |15 34| - PRG /CE (s)
*2 (w) WRAM /CE - |16 33| - /IRQ (s)
(r) CHR /CE - |17 32| - CIRAM /CE (s)
(s) CHR /RD - |18 31| - CHR A10 (s)
(s) CHR /A13 - |19 30| - CHR A11 (s)
(r) CHR A16 - |20 29| - CHR A12 (s)
(r) CHR A15 - |21 28| - CHR A17 (r)
(r) CHR A12 - |22 27| - CHR A14 (r)
(r) CHR A11 - |23 26| - CHR A13 (r)
GND - |24 25| - CHR A10 (r)
| |
`----------'
VRCVI
*1: On some VRCVI carts, these are reversed. This affects some registers.
*2: This passes through a small pulse shaping network consisting of a
resistor, diode, and cap.
Registers: (sound related only)
----------
regs 9000-9002 are for pulse channel #1
regs a000-a002 are for pulse channel #2
regs b000-b002 are for the phase accumulator channel (sawtooth)
(bits listed 7 through 0)
9000h: GDDDVVVV
D - Duty Cycle bits:
000 - 1/16th ( 6.25%)
001 - 2/16ths (12.50%)
010 - 3/16ths (18.75%)
011 - 4/16ths (25.00%)
100 - 5/16ths (31.25%)
101 - 6/16ths (37.50%)
110 - 7/16ths (43.75%)
111 - 8/16ths (50.00%)
V - Volume bits. 0000b is silence, 1111b is loudest. Volume is
linear. When in "normal" mode (see G bit), this acts as a general
volume control register. When in "digitized" mode, these act as a
4 bit sample input.
G - Gate bit. 0=normal operation, 1=digitized. In digi operation,
registers 9001h and 9002h are totally disabled. Only bits 0-3 of
9000h are used. Whatever binary word is present here is passed on
as a 4 bit digitized output.
9002h: FFFFFFFF
F - Lower 8 bits of frequency data
9003h: X---FFFF
X - Channel disable. 0=channel disabled, 1=channel enabled.
F - Upper 4 bits of frequency data
A000h-A002h are identical in operation to 9000h-9002h. One note: this chip
will mix both digitized outputs (if the G bits are both set) into one
added output. (see in-depth chip operation below)
B000h: --PPPPPP
P - Phase accumulator input bits
B001h: FFFFFFFF
F - Lower 8 bits of frequency data
B002h: X---FFFF
X - Channel disable. 0=channel disabled, 1=channel enabled.
F - Upper 4 bits of frequency data
How the sounds are formed:
--------------------------
This chip is pretty cool. It outputs a 6 bit binary word for the sound
which is passed through a DAC and finally to the NES/Fami. Because of this,
the sound can be emulated *very* close to the original.
I used my scope to figure all this out as well as my meter and logic probe
so it should be 100% accurate.
Block diagrams of the VRCVI: (as reverse engineered by me)
----------------------------
| F bits | | D bits| | V bits |
| (12) | | (3) | | (4) |
\___________/ \_______/ \________/
+-----+ +----------------+ +-----------+ +------------+
| | | | | | | |--\
| OSC |-->|Divider (12 bit)|-->| Duty Cycle|-->| AND array |(4)> chan out
|(M2) | | | | Generator | | |--/
+-----+ +----------------+ +-----------+ +------------+
^ ^
| |
| |
G X
One Pulse channel (both are identical)
--------------------------------------
How it works: The oscillator (in the NES, the clock arrives on the M2 line
and is about 1.78Mhz) generates our starting frequency. This is passed
first into a divide by 1 to 4096 divider to generate a base frequency.
This is then passed to the duty cycle generator. The duty cycle generator
generates the desired duty cycle for the output waveform. The "D" input
controls the duty cycle generator's duty cycle. If the "G" bit is
a "1", it forces the output of the duty cycle generator to a "1" also. If
the "X" bit is "0", it forces the output of the duty cycle generator to "0",
which effectively disables the channel. Note that this input has precidence
over the "G" bit.
The AND array is just that- an array of 4 AND gates. If the output of
the duty cycle generator is a "0", then the "chan out" outputs will all be
forced to "0". If the output of the duty cycle generator is a "1", then
the chan out outputs will follow the V bit inputs.
Note that the output of this generator is a 4 bit binary word.
---
| F bits | | P bits|
| (12) | | (6) |
\___________/ \_______/
+-----+ +----------------+ +-----------+
| | | | | |--\
| OSC |-->|Divider (12 bit)|-->| Phase |(5)> chan out
|(M2) | | | |Accumulator|--/
+-----+ +----------------+ +-----------+
^
|
|
X
The Sawtooth (ramp) channel
---------------------------
This one is pretty similar to the above when it comes to frequency selection.
The output frequency will be the same relative to the square wave channels.
OK, the tough part will be explaining the phase accumulator. :-) What it is
is just an adder tied to a latch. Every clock it adds a constant to the
latch. In the case of the VRCVI, what you do is this:
The ramp is generated over the course of 7 evenly spaced cycles, generated
from the divider. Every clock from the divider causes the phase accumulator
to add once. So... let's say we have 03h in the P bits. Every 7 cycles
the phase accumulator (which is 8 bits) is reset to 00h.
cycle: accumulator: chan out: notes:
-----------------------------------------
0 00h 00h On the first cycle, the acc. is reset to 0
1 03h 00h We add 3 to 0 to get 3
2 06h 00h We add 3 to 3 to get 6
3 09h 01h
4 0ch 01h
5 0fh 01h
6 12h 02h
7 00h 00h Reset the acc. back to 0 and do it again
This will look like so: (as viewed on an oscilloscope)
- - - 2
--- --- --- 1
--- --- --- 0
|
012345601234560123456-+
Note: if you enter a value that is too large (i.e. 30h) the accumulator
*WILL WRAP*. Yes, this doesn't sound very good at all and you no longer
have a sawtooth. ;-)
The upper 5 bits of said accumulator are run to the "chan out" outputs.
The lower 3 bits are not run anywhere.
"X" disables the phase accumulator and forces all outputs to "0".
Note that the output of this generator is a 5 bit word.
---
Now that the actual sound generation is out of the way, here's how the
channels are combined into the final 6 bit binary output:
+---------+
| Pulse |
|Generator|
| #1 | Final 6 Bit
+---------+ Output
| (4) | / \
\ / | (6) |
+---------+ +---------+ +---------+
| 4 Bit |--\ | 5 Bit | /--|Sawtooth |
| Binary |(5)>| Binary |<(5)|Generator|
| Adder |--/ | Adder | \--| |
+---------+ +---------+ +---------+
/ \
| (4) |
+---------+
| Pulse |
|Generator|
| #2 |
+---------+
Channel Combining
-----------------
The three channels are finally added together through a series of adders
to produce the final output word. The two pulse chans are most likely added
first since they are 4 bit words, and that 5 bit result is most likely
added to the sawtooth's output. (The actual adding order is not known,
but I can make a *very* good guess. The above illustrated way uses the least
amount of transistors). In the end it does not matter the order in which
the words are added; the final word will always be the same.
The final 6 bit output word is run through an "R2R" resistor ladder which
turns the digital bits into a 64 level analog representation. The ladder
is binarally weighted and works like the DAC on your soundcard. :-)
(so take heart emulator authours: just run the finished 6 bit word to
your soundcard and it will sound right ;-).
Frequency Generation:
---------------------
The chip generates all its output frequencies based on M2, which is
colourburst divided by two (1789772.7272Hz). This signal is passed
directly into the programmable dividers (the 12 bit frequency regs).
Squares:
--------
These take the output of the programmable divider and then run it through
the duty cycle generator, which in the process of generating the duty cycle,
divides the frequency by 16.
To calculate output frequency:
1789772.7272
Fout = ----------------
(freq_in+1) * 16
This will tell you the exact frequency in Hz. (note that the * 16 is to
compensate for the divide by 16 duty cycle generator.)
Saw:
----
This is similar to the above, however the duty cycle generator is replaced
with a phase accumulator which divides the output frequency by 14.
To calculate output frequency:
1789772.7272
Fout = ----------------
(freq_in+1) * 14
This will tell you the exact frequency in Hz. (note that the * 14 is to
compensate for the phase accumulator.)
So how accurate is this info, anyways?
--------------------------------------
I believe the info to be 100% accurate. I have extensively tested the
output of the actual VRCVI chip to this spec and everything fits perfectly.
I did this by using a register dump and a QBASIC program I wrote which
takes the register dump and produces a WAV file. All frequency and
duty cycle measurements were taken with a Fluke 83 multimeter, and all
waveform data was culled from my oscilloscope measuring the real chip.
---EOF---

View File

@ -0,0 +1,321 @@
VRCVII CHIP INFO
------ ---- ----
By:
Kevin Horton
khorton@iquest.net
The RENES Project:
Reverse-engineering
the world.
V0.10 11/05/99 Document started, pinned out chip and audio thingy
V0.20 11/10/99 Added very, very, very preliminary register findings
v1.00 11/14/99 First release version of this doc
VRCVII (VRC7) (48 pin standard 600mil wide DIP)
-------------
This chip is used in only one Konami game that I know of- Lagrange Point.
I heard rumours it was used in another game, so if someone could provide
info and/or a ROM image, that would help immensely. It handles ROM
bankswitching as well as sound generation. The sound generation is done
using FM synthesis, so the music sounds like "Adlib" OPL2 music. Due to
extra sound, this is a Famicom-only chip like its cousin the
VRCVI. (See the VRCVI doc for more info)
"VII" of "VRCVII" is "7" for the roman numeral challenged.
This chip appears to generate all of its audio internally, which is then
fed to a small hybrid (aka "black blob") that does the audio interfacing
to the Famicom proper. It is physically a small ceramic substrate with
an 8 pin SMD chip on it (probably an op-amp), and what appears to be three
0805 sized SMD chip parts (capacitors most likely, since resistors can be
formed on the substrate). The whole works is then coated with a black
dipped epoxy coating, and the smooth side (opposite the parts) is then
marked with an identifying part number and the pin 1 dot.
Here's the pinout for it:
Front Side (parts facing away)
+-----------------+ 1- Audio in from Famicom
| 054002 | 2- Audio out to Famicom
|@ | 3,7 - Ground
+-----------------+ 4-6 - NC
| | | | | | | | | 8- Audio from VRCVII
1 2 3 4 5 6 7 8 9 9- +5V
Legend:
-------
(s) means this pin connects to the System
(r) this only connects to the ROM
(w) this is a SRAM/WRAM connection only
PRG : these connect to the PRG ROM and/or fami's PRG pins
WRAM : this hooks to the WRAM
Note: There is a 3.58Mhz ceramic resonator connected to the "X1" and "X2"
pins. it is the three-pin style with internal caps tied to the third pin
which is grounded.
Chip is physically marked: "VRV VII 053982"
.----\/----.
*1 (RAM&s) CHR /OE - |01 48| - NC
*1 (RAM&s) CHR /CE - |02 47| - M2 (s)
GND - |03 46| - /CE WRAM (w)
(s) R/W - |04 45| - PRG /A15 (s) (aka /CE)
(s) /IRQ - |05 44| - PRG ROM /CE (r)
(s) CIRAM A11 - |06 43| - Audio Out
(s) PD0 - |07 42| - +5V
(s) PD1 - |08 41| - NC
(s) PD2 - |09 40| - NC
(s) PD3 - |10 39| - NC
(s) PD4 - |11 38| - NC
(s) PD5 - |12 37| - NC
(s) PD6 - |13 36| - CHR RAM A12
(s) PD7 - |14 35| - CHR RAM A11
+5V - |15 34| - CHR RAM A10
(s) PRG A5 - |16 33| - CHR A12 (s)
Crystal X2 - |17 32| - CHR A11 (s)
Crystal X1 - |18 31| - CHR A10 (s)
(s) PRG A4 - |19 30| - +5V
(r) PRG ROM A13 - |20 29| - PRG A14 (s)
(r) PRG ROM A14 - |21 28| - PRG A13 (s)
(r) PRG ROM A15 - |22 27| - PRG A12 (s)
(r) PRG ROM A16 - |23 26| - PRG ROM A18 (r)
GND - |24 25| - PRG ROM A17 (r)
| |
`----------'
VRCVII
*1: these connect to both the CHR RAM's pins and the card edge.
Note: the NC pins 37-41 most likely for CHR ROM bankswitching. Since this
cart uses CHR RAM these obviously weren't used ;-)
Registers: (sound related only)
----------
All sound registers are accessed through only two physical registers.
9010:
-----
This is the index register. You write the desired register number here.
9030:
-----
This is the data register. Data written here is stored in the register
pointed to by the above index register.
There are 6 channels, each containing three registers, and 8 custom
instrument control registers.
Sound Registers:
----------------
00h - 07h : Custom instrument registers. See below for info.
---
10h - 15h : ffffffff
f: Lower 8 bits of frequency
---
20h - 25h : ???tooof
f: Upper bit of frequency
o: Octave Select
t: Channel trigger.
?: Dunno what these do yet (No audible effect)
---
30h - 35h : iiiivvvv
i: Instrument number
v: Volume
Instrument numbers 01h-0fh are fixed and cannot be changed.
Instrument number 00h is the "programmable" one.
To program the custom instrument, you load registers 00h-07h with the
desired parameters for it. All channels set to instrument 00h will
then use this instrument. Note that you can only program one custom
instrument at a time.
How do the frequency registers work?
------------------------------------
To generate a tone, you must select an octave and a frequency value. The
frequency values stay the same for say, the note "C", while the octave
bits determine which octave "C" lies in. This makes your note lookup table
quite small.
o = 000 is octave 0
o = 001 is octave 1
.
.
.
o = 111 is octave 7
49722*freqval
F = -------------
2^(19-octave)
Where:
F = output frequency in Hz
freqval = frequency register value
octave = desired octave (starting at 0)
Custom Instrument Registers (00-07)
-----------------------------------
Note: I will not provide too extensive documentation of the instrument
registers since their functions are identical to those of the OPL2 chip,
commonly found on Adlib/Soundblaster/compatible cards, and there is alot
of information out on how to program these. I will use terminology
similar to that found in said documents. My VRC7 "emulator" test program
I wrote simply re-arranged and tweaked the register writes to correspond
with the OPL2 registers.
Here's a link to a good document about this chip:
http://www.ccms.net/~aomit/oplx/
The tremolo depth is set to 4.3db and the vibrato depth is set to 14 cent
(in reguards to OPL2 settings; to achieve this you would write 0C0h to
OPL register 0BDh). All operator connections are fixed in FM mode. (Where
Modulator modulates the Carrier).
---
00 (Modulator) - tvskmmmm
01 (Carrier)
t: Tremolo Enable
v: Vibrato Enable
s: Sustain Enable
k: KSR
m: Multiplier
---
02 - kkoooooo
k: Key Scale Level
o: Output Level
---
03 - ---qweee
-: Not used: Write 0's
q: Carrier Waveform
w: Modulator Waveform
Note: There are only two waveforms available. Sine and rectified sine (only
the positive cycle of the sine; negative cycle "chopped off".)
e: Feedback Control
---
04 (Modulator) - aaaadddd
05 (Carrier)
a: Attack
d: Decay
---
06 (Modulator) - ssssrrrr
07 (Carrier)
s: Sustain
r: Release
Register Settings for the 15 fixed instruments.
-----------------------------------------------
*CAUTION*CAUTION*CAUTION*CAUTION*CAUTION*CAUTION*CAUTION*CAUTION*CAUTION*
C C
A These instruments are not 100% correct! There is no way to extract A
U the register settings from the chip short of an electron microscope. U
T I have "tuned" these instruments best I could, though I know a couple T
I are not exactly right. Use them at your own perl! If someone wants I
O to waste all day tuning a new set, please let me know what you get. O
N N
*CAUTION*CAUTION*CAUTION*CAUTION*CAUTION*CAUTION*CAUTION*CAUTION*CAUTION*
Register
--------
00 01 02 03 04 05 06 07
-----------------------
0 | -- -- -- -- -- -- -- --
1 | 05 03 10 06 74 A1 13 F4
2 | 05 01 16 00 F9 A2 15 F5
3 | 01 41 11 00 A0 A0 83 95
4 | 01 41 17 00 60 F0 83 95
5 | 24 41 1F 00 50 B0 94 94
6 | 05 01 0B 04 65 A0 54 95
7 | 11 41 0E 04 70 C7 13 10
Instrument 8 | 02 44 16 06 E0 E0 31 35
---------- 9 | 48 22 22 07 50 A1 A5 F4
A | 05 A1 18 00 A2 A2 F5 F5
B | 07 81 2B 05 A5 A5 03 03
C | 01 41 08 08 A0 A0 83 95
D | 21 61 12 00 93 92 74 75
E | 21 62 21 00 84 85 34 15
F | 21 62 0E 00 A1 A0 34 15
So how accurate is this info, anyways?
--------------------------------------
I believe the info to be 100% accurate. The pinout was generated with the
help of a multimeter and my Super-8 with both an NES cart and the Fami cart
plugged in. (this allows me to measure between my known "control" board
with the unknown "experimental" board to generate the connections).
Register info was gleaned via writing test code and listening and capturing
the resultant audio stream for analysis.
---EOF---

View File

@ -0,0 +1,336 @@
NES Music Format Spec
---------------------
By: Kevin Horton khorton@iquest.net
NOTE:
-----
Remember that I am very willing to add stuff and update this spec. If
you find a new sound chip or other change let me know and I will get back
with you. E-mail to the above address.
V1.61 - 06/27/2000 Updated spec a bit
V1.60 - 06/01/2000 Updated Sunsoft, MMC5, and Namco chip information
V1.50 - 05/28/2000 Updated FDS, added Sunsoft and Namco chips
V1.32 - 11/27/1999 Added MMC5 register locations
V1.30 - 11/14/1999 Added MMC5 audio bit, added some register info
V1.20 - 09/12/1999 VRC and FDS prelim sound info added
V1.00 - 05/11/1999 First official NSF specification file
This file encompasses a way to transfer NES music data in a small, easy to
use format.
The basic idea is one rips the music/sound code from an NES game and prepends
a small header to the data.
A program of some form (6502/sound emulator) then takes the data and loads
it into the proper place into the 6502's address space, then inits and plays
the tune.
Here's an overview of the header:
offset # of bytes Function
----------------------------
0000 5 STRING "NESM",01Ah ; denotes an NES sound format file
0005 1 BYTE Version number (currently 01h)
0006 1 BYTE Total songs (1=1 song, 2=2 songs, etc)
0007 1 BYTE Starting song (1= 1st song, 2=2nd song, etc)
0008 2 WORD (lo/hi) load address of data (8000-FFFF)
000a 2 WORD (lo/hi) init address of data (8000-FFFF)
000c 2 WORD (lo/hi) play address of data (8000-FFFF)
000e 32 STRING The name of the song, null terminated
002e 32 STRING The artist, if known, null terminated
004e 32 STRING The Copyright holder, null terminated
006e 2 WORD (lo/hi) speed, in 1/1000000th sec ticks, NTSC (see text)
0070 8 BYTE Bankswitch Init Values (see text, and FDS section)
0078 2 WORD (lo/hi) speed, in 1/1000000th sec ticks, PAL (see text)
007a 1 BYTE PAL/NTSC bits:
bit 0: if clear, this is an NTSC tune
bit 0: if set, this is a PAL tune
bit 1: if set, this is a dual PAL/NTSC tune
bits 2-7: not used. they *must* be 0
007b 1 BYTE Extra Sound Chip Support
bit 0: if set, this song uses VRCVI
bit 1: if set, this song uses VRCVII
bit 2: if set, this song uses FDS Sound
bit 3: if set, this song uses MMC5 audio
bit 4: if set, this song uses Namco 106
bit 5: if set, this song uses Sunsoft FME-07
bits 6,7: future expansion: they *must* be 0
007c 4 ---- 4 extra bytes for expansion (must be 00h)
0080 nnn ---- The music program/data follows
This may look somewhat familiar; if so that's because this is somewhat
sorta of based on the PSID file format for C64 music/sound.
Loading a tune into RAM
-----------------------
If offsets 0070h to 0077h have 00h in them, then bankswitching is *not*
used. If one or more bytes are something other than 00h then bankswitching
is used. If bankswitching is used then the load address is still used,
but you now use (ADDRESS AND 0FFFh) to determine where on the first bank
to load the data.
Each bank is 4K in size, and that means there are 8 of them for the
entire 08000h-0ffffh range in the 6502's address space. You determine where
in memory the data goes by setting bytes 070h thru 077h in the file.
These determine the inital bank values that will be used, and hence where
the data will be loaded into the address space.
Here's an example:
METROID.NSF will be used for the following explaination.
The file is set up like so: (starting at 070h in the file)
0070: 05 05 05 05 05 05 05 05 - 00 00 00 00 00 00 00 00
0080: ... music data goes here...
Since 0070h-0077h are something other than 00h, then we know that this
tune uses bankswitching. The load address for the data is specified as
08000h. We take this AND 0fffh and get 0000h, so we will load data in
at byte 0 of bank 0, since data is loaded into the banks sequentially
starting from bank 0 up until the music data is fully loaded.
Metroid has 6 4K banks in it, numbered 0 through 5. The 6502's address
space has 8 4K bankswitchable blocks on it, starting at 08000h-08fffh,
09000h-09fffh, 0a000h-0afffh ... 0f000h-0ffffh. Each one of these is 4K in
size, and the current bank is controlled by writes to 05ff8h thru 05fffh,
one byte per bank. So, 05ff8h controls the 08000h-08fffh range, 05ff9h
controls the 09000h-09fffh range, etc. up to 05fffh which controls the
0f000h-0ffffh range. When the song is loaded into RAM, it is loaded into
the banks and not the 6502's address space. Once this is done, then the
bank control registers are written to set up the inital bank values.
To do this, the value at 0070h in the file is written to 05ff8h, 0071h
is written to 05ff9h, etc. all the way to 0077h is written to 05fffh.
This is should be done before every call to the init routine.
If the tune was not bankswitched, then it is simply loaded in at the
specified load address, until EOF
Initalizing a tune
------------------
This is pretty simple. Load the desired song # into the accumulator,
minus 1 and set the X register to specify PAL (X=1) or NTSC (X=0).
If this is a single standard tune (i.e. PAL *or* NTSC but not both)
then the X register contents should not matter. Once the song # and
optional PAL/NTSC standard are loaded, simply call the INIT address.
Once init is done, it should perform an RTS.
Playing a tune
--------------
Once the tune has been initalized, it can now be played. To do this,
simply call the play address several times a second. How many times
per second is determined by offsets 006eh and 006fh in the file.
These bytes denote the speed of playback in 1/1000000ths of a second.
For the "usual" 60Hz playback rate, set this to 411ah.
To generate a differing playback rate, use this formula:
1000000
PBRATE= ---------
speed
Where PBRATE is the value you stick into 006e/006fh in the file, and
speed is the desired speed in hertz.
"Proper" way to load the tune
-----------------------------
1) If the tune is bankswitched, go to #3.
2) Load the data into the 6502's address space starting at the specified
load address. Go to #4.
3) Load the data into a RAM area, starting at (start_address AND 0fffh).
4) Tune load is done.
"Proper" way to init a tune
---------------------------
1) Clear all RAM at 0000h-07ffh.
2) Clear all RAM at 6000h-7fffh.
3) Init the sound registers by writing 00h to 04000-0400Fh, 10h to 4010h,
and 00h to 4011h-4013h.
4) Set volume register 04015h to 00fh.
5) If this is a banked tune, load the bank values from the header into
5ff8-5fffh.
6) Set the accumulator and X registers for the desired song.
7) Call the music init routine.
"Proper" way to play a tune
---------------------------
1) Call the play address of the music at periodic intervals determined
by the speed words. Which word to use is determined by which mode
you are in- PAL or NTSC.
Sound Chip Support
------------------
Byte 007bh of the file stores the sound chip flags. If a particular flag
is set, those sound registers should be enabled. If the flag is clear,
then those registers should be disabled.
* VRCVI Uses registers 9000-9002, A000-A002, and B000-B002, write only.
Caveats: 1) The above registers are *write only* and must not disrupt music
code that happens to be stored there.
2) Major caveat: The A0 and A1 lines are flipped on a few games!!
If you rip the music and it sounds all funny, flip around
the xxx1 and xxx2 register pairs. (i.e. 9001 and 9002) 9000
and 9003 can be left untouched. I decided to do this since it
would make things easier all around, and this means you only
will have to change the music code in a very few places (6).
Esper2 and Madara will need this change, while Castlevania 3j
will not for instance.
3) See my VRCVI.TXT doc for a complete register description.
* VRCVII Uses registers 9010 and 9030, write only.
Caveats: 1) Same caveat as #1, above.
2) See my VRCVII.TXT doc for a complete register description.
* FDS Sound uses registers from 4040 through 4092.
Caveats: 1) 6000-DFFF is assumed to be RAM, since 6000-DFFF is RAM on the
FDS. E000-FFFF is usually not included in FDS games because
it is the BIOS ROM. However, it can be used on FDS rips to help
the ripper (for modified play/init addresses).
2) Bankswitching operates slightly different on FDS tunes.
5FF6 and 5FF7 control the banks 6000-6FFF and 7000-7FFF
respectively. NSF header offsets 76h and 77h correspond to
*both* 6000-7FFF *AND* E000-FFFF. Keep this in mind!
* MMC5 Sound Uses registers 5000-5015, write only as well as 5205 and 5206,
and 5C00-5FF5
Caveats: 1) Generating a proper doc file. Be patient.
2) 5205 and 5206 are a hardware 8*8 multiplier. The idea being
you write your two bytes to be multiplied into 5205 and 5206
and after doing so, you read the result back out. Still working
on what exactly triggers it (I think a write to either 5205
or 5206 triggers the multiply).
3) 5C00-5FF5 should be RAM to emulate EXRAM while in MMC5 mode.
Note: Thanks to Mamiya for the EXRAM info.
* Namco 106 Sound Uses registers 4800 and F800.
This works similar to VRC7. 4800 is the "data" port which is
readable and writable, while F800 is the "address" port and is
writable only.
The address is 7 bits plus a "mode" bit. Bit 7 controls
address auto-incrementing. If bit 7 is set, the address will
auto-increment after a byte of data is read or written from/to
4800.
$40 ffffffff f:frequency L
$42 ffffffff f:frequency M
$44 ---sssff f:frequency H s:tone length (8-s)*4 in 4bit-samples
$46 tttttttt t:tone address(4bit-address,$41 means high-4bits of $20)
$47 -cccvvvv v:linear volume 1+c:number of channels in use($7F only)
$40-47:ch1 $48-4F:ch2 ... $78-7F:ch8
ch2-ch8 same to ch1
$00-3F(8ch)...77(1ch) hhhhllll tone data
h:odd address data(signed 4bit)
l:even address data(signed 4bit)
real frequency = (f * NES_BASECYCLES) / (40000h * (c+1) * (8-s)*4 * 45)
NES_BASECYCLES 21477270(Hz)
Note: Very Special thanks to Mamiya for this information!
* Sunsoft FME-07 Sound uses registers C000 and E000
This is similar to the common AY 3-8910 sound chip that is
used on tons of arcade machines, and in the Intellivision.
C000 is the address port
E000 is the data port
Both are write-only, and behave like the AY 3-8910.
Note: Special thanks to Mamiya for this information as well
Caveats
-------
1) The starting song number and maximum song numbers start counting at
1, while the init address of the tune starts counting at 0. To
"fix", simply pass the desired song number minus 1 to the init
routine.
2) The NTSC speed word is used *only* for NTSC tunes, or dual PAL/NTSC tunes.
The PAL speed word is used *only* for PAL tunes, or dual PAL/NTSC tunes.
3) The length of the text in the name, artist, and copyright fields must
be 31 characters or less! There has to be at least a single NULL byte
(00h) after the text, between fields.
4) If a field is not known (name, artist, copyright) then the field must
contain the string "<?>" (without quotes).
5) There should be 8K of RAM present at 6000-7FFFh. MMC5 tunes need RAM at
5C00-5FF7 to emulate its EXRAM. 8000-FFFF Should be read-only (not
writable) after a tune has loaded. The only time this area should be
writable is if an FDS tune is being played.
6) Do not assume the state of *anything* on entry to the init routine
except A and X. Y can be anything, as can the flags.
7) Do not assume the state of *anything* on entry to the play routine either.
Flags, X, A, and Y could be at any state. I've fixed about 10 tunes
because of this problem and the problem, above.
8) The stack sits at 1FFh and grows down. Make sure the tune does not
attempt to use 1F0h-1FFh for variables. (Armed Dragon Villigust did and
I had to relocate its RAM usage to 2xx)
9) Variables should sit in the 0000h-07FFh area *only*. If the tune writes
outside this range, say 1400h this is bad and should be relocated.
(Terminator 3 did this and I relocated it to 04xx).
That's it!

View File

@ -0,0 +1,296 @@
*******************************
*NTSC 2C02 technical operation*
*******************************
Brad Taylor (big_time_software@hotmail.com)
1st release: Sept 25th, Y2K
2nd release: Jan 27th, 2K3
3rd release: Feb 4th, 2K3
4th release: Feb 19th, 2K3
This document describes the low-level operation and technical details of the 2C02, the NES's PPU. In general, it contains important information in regards to PPU timing, which no NES coder/emulator author should be without. This document assumes that you already understand the basics of how the PPU works, like how the playfield/object images are generated, and the behaviour of scroll/address counters during playfield rendering.
Alot of the concepts behind how the PPU works described here have been extracted from Nintendo's patent documentation (U.S.#4,824,106). With block diagrams of the PPU's architecture (and even some schematics), these papers will definetely aid in the comprehension of this complex device.
Since the first release, this document has been given a major overhaul. Most sections of the document have been reworked, and new information has been added just about everywhere. If you've read the old version of this document before, I recommend that you read this new one in it's entirity; there's new information even in sections which may look like they haven't changed much.
Topics discussed hereon are as follows.
- Video signal generation
- PPU base timing
- Miscellanious PPU info
- PPU memory access cycles
- Frame rendering details
- Scanline rendering details
- In-range object evaluation
- Details of playfield render pipeline
- Details of object pattern fetch & render
- Extra cycle frames
- The MMC3's scanline counter
- PPU pixel priority quirk
- Graphical enhancements
+-------+
|History|
+-------+
On the weekend of Sept. 25th, Y2K, I setup an experiment with my NTSC NES MB & my PC so's I could RE the PPU's timing. What I did was (using a PC interface) analyse the changes that occur on the PPU's address and data pins on every rising & falling edge of the PPU's clock. I was not planning on removing the PPU from the motherboard (yet), so basically I just kept everything intact (minus the stuff I added onto the MB so I could monitor the PPU's signals), and popped in a game, so that it would initialize the PPU for me (I used DK classics, since it was only taking somthing like 4 frames before it was turning on the background/sprites).
The only change I made was taking out the 21 MHz clock generator circuitry. To replace the clock signal, I connected a port controlled latch to the NES's main clock line instead. Now, by writing a 0 or a 1 out to an PC ISA port of my choice (I was using $104), I was able to control the 21 MHz clockline of the NES. After I would create a rise or a fall on the NES's clock line, I would then read in the data that appeared on the PPU's address and data pins, which included monitoring what PPU registers the game read/wrote to (& the data that was read/written).
+-----------------------+
|Video signal generation|
+-----------------------+
A 21.48 MHz clock signal is fed into the PPU. This is the NES's main clock line, which is shared by the CPU.
Inside the PPU, the 21.48 MHz signal is used to clock a three-stage Johnson counter. The complimentery outputs of both master and slave portions of each stage are used to form 12 mutually exclusive output phases- all 3.58 MHz each (the NTSC colorburst). These 12 different phases form the basis of all color generation for the PPU's composite video output.
Naturally, when the user programs the lower 4-bits of a palette register, they are essentially selecting any 1 of 12 phases to be routed to the PPU's video out pin (this corresponds to chrominance (tint/hue) video information) when the appropriate pixel indexes it. Other chrominance combinations (0 & 13) are simply hardwired to a 1 or 0 to generate grayscale pixels.
Bits 4 & 5 of a palette entry selects 1 of 4 linear DC voltage offsets to apply to the selected chrominance signal (this corresponds to luminance (brightness) video information) for a pixel.
Chrominance values 14 & 15 yield a black pixel color, regardless of any luminance value setting.
Luminance value 0, mixed with chrominance value 13 yield a "blacker than black" pixel color. This super black pixel has an output voltage level close to the vertical/horizontal syncronization pulses. Because of this, some video monitors will display warped/distorted screens for games which use this color for black (Game Genie is the best example of this). Essentially what is happening is the video monitor's horizontal timing is compromised by what it thinks are extra syncronization pulses in the scanline. This is not damaging to the monitors which are effected by it, but use of the super black color should be avoided, due to the graphical distortion it causes.
The amplitude of the selected chrominance signal (via the 4 lower bits of a palette register) remain constant regardless of bits 4 or 5. Thus it is not possible to adjust the saturation level of a particular color.
+---------------+
|PPU base timing|
+---------------+
Other than the 3-stage Johnson counter, the 21.48 MHz signal is not used directly by any other PPU hardware. Instead, the signal is divided by 4 to get 5.37 MHz, and is used as the smallest unit of timing in the PPU. All following references to PPU clock cycle (abbr. "cc") timing in this document will be in respect to this timing base, unless otherwise indicated.
- Pixels are rendered at the same rate as the base PPU clock. In other words, 1 clock cycle= 1 pixel.
- 341 PPU cc's make up the time of a typical scanline (or 341/3 CPU cc's).
- One frame consists of 262 scanlines. This equals 341*262 PPU cc's per frame (divide by 3 for # of CPU cc's).
+------------------------+
|PPU memory access cycles|
+------------------------+
All PPU memory access cycles are 2 clocks long, and can be made back-to-back (typically done during rendering). Here's how the access breaks down:
At the beginning of the access cycle, PPU address lines 8..13 are updated with the target address. This data remains here until the next time an access cycle occurs.
The lower 8-bits of the PPU address lines are multiplexed with the data bus, to reduce the PPU's pin count. On the first clock cycle of the access, A0..A7 are put on the PPU's data bus, and the ALE (address latch enable) line is activated for the first half of the cycle. This loads the lower 8-bit address into an external 8-bit transparent latch strobed by ALE (74LS373 is used).
On the second clock cycle, the /RD (or /WR) line is activated, and stays active for the entire cycle. Appropriate data is driven onto the bus during this time.
+----------------------+
|Miscellanious PPU info|
+----------------------+
- Sprite DMA is 1536 clock cycles long (512 CPU cc's). 256 individual transfers are made from CPU memory to a temp register inside the CPU, then from the CPU's temp reg, to $2004.
- The PPU makes NO external access to the PPU bus, unless the playfield or objects are enabled during a scanline outside vblank. This means that the PPU's address and data busses are dead while in this state.
- palette RAM is accessed internally during playfield rendering (i.e., the palette address/data is never put on the PPU bus during this time). Additionally, when the programmer accesses palette RAM via $2006/7, the palette address accessed actually does show up on the PPU address bus, but the PPU's /RD & /WR flags are not activated. This is required; to prevent writing over name table data falling under the approprite mirrored area (since the name table RAM's address decoder simply consists of an inverter connected to the A13 line- effectively decoding all addresses in $2000-$3FFF).
- the VINT impulse (NMI) and bit $2002.7 are set simultaniously. Reading $2002 will reset bit 7, but it seems that the VINT flag goes down on it's own. Because of this, when the PPU generates a VINT, it doesn't require any acknowledgement whatsoever; it will continue firing off VINTs, regardless of inservice to $2002. The only way to stop VINTs is to clear $2000.7.
- Because the PPU cannot make a read from PPU memory immediately upon request (via $2007), there is an internal buffer, which acts as a 1-stage data pipeline. As a read is requested, the contents of the read buffer are returned to the NES's CPU. After this, at the PPU's earliest convience (according to PPU read cycle timings), the PPU will fetch the requested data from the PPU memory, and throw it in the read buffer. Writes to PPU mem via $2007 are pipelined as well, but it is unknown to me if the PPU uses this same buffer (this could be easily tested by writing somthing to $2007, and seeing if the same value is returned immediately after reading).
+-----------------------+
|Frame rendering details|
+-----------------------+
The following describes the PPU's status during all 262 scanlines of a frame. Any scanlines where work is done (like image rendering), consists of the steps which will be described in the next section.
0..19: Starting at the instant the VINT flag is pulled down (when a NMI is generated), 20 scanlines make up the period of time on the PPU which I like to call the VINT period. During this time, the PPU makes no access to it's external memory (i.e. name / pattern tables, etc.).
20: After 20 scanlines worth of time go by (since the VINT flag was set), the PPU starts to render scanlines. This first scanline is a dummy one; although it will access it's external memory in the same sequence it would for drawing a valid scanline, no on-screen pixels are rendered during this time, making the fetched background data immaterial. Both horizontal *and* vertical scroll counters are updated (presumably) at cc offset 256 in this scanline. Other than that, the operation of this scanline is identical to any other. The primary reason this scanline exists is to start the object render pipeline, since it takes 256 cc's worth of time to determine which objects are in range or not for any particular scanline.
21..260: after rendering 1 dummy scanline, the PPU starts to render the actual data to be displayed on the screen. This is done for 240 scanlines, of course.
261: after the very last rendered scanline finishes, the PPU does nothing for 1 scanline (i.e. the programmer gets screwed out of perfectly good VINT time). When this scanline finishes, the VINT flag is set, and the process of drawing lines starts all over again.
+--------------------------+
|Scanline rendering details|
+--------------------------+
Naturally, the PPU will fetch data from name, attribute, and pattern tables during a scanline to produce an image on the screen. This section details the PPU's doings during this time.
As explained before, external PPU memory can be accessed every 2 cc's. With 341 cc's per scanline, this gives the PPU enough time to make 170 memory accesses per scanline (and it uses all of them!). After the 170th fetch, the PPU does nothing for 1 clock cycle. Remember that a single pixel is rendered every clock cycle.
Memory fetch phase 1 thru 128
-----------------------------
1. Name table byte
2. Attribute table byte
3. Pattern table bitmap #0
4. Pattern table bitmap #1
This process is repeated 32 times (32 tiles in a scanline).
This is when the PPU retrieves the appropriate data from PPU memory for rendering the playfield. The first playfield tile fetched here is actually the 3rd to be drawn on the screen (the playfield data for the first 2 tiles to be rendered on this scanline are fetched at the end of the scanline prior to this one).
All valid on-screen pixel data arrives at the PPU's video out pin during this time (256 clocks). For determining the precise delay between when a tile's bitmap fetch phase starts (the whole 4 memory fetches), and when the first pixel of that tile's bitmap data hits the video out pin, the formula is (16-n) clock cycles, where n is the fine horizontal scroll offset (0..7 pixels). This information is relivant for understanding the exact timing operation of the "object 0 collision" flag.
Note that the PPU fetches an attribute table byte for every 8 sequential horizontal pixels it draws. This essentially limits the PPU's color area (the area of pixels which are forced to use the same 3-color palette) to only 8 horizontally sequential pixels.
It is also during this time that the PPU evaluates the "Y coordinate" entries of all 64 objects in object attribute RAM (OAM), to see if the objects are within range (to be drawn on the screen) for the *next* scanline (this is why Y-coordinate entries in the OAM must be programmed to a value 1 less than the scanline the object is to appear on). Each evaluation (presumably) takes 4 clock cycles, for a total of 256 (which is why it's done during on-screen pixel rendering).
In-range object evaluation
--------------------------
An 8-bit comparator is used to calculate the 9-bit difference between the current scanline (minus 21), and each Y-coordinate (plus 1) of every object entry in the OAM. Objects are considered in range if the comparator produces a difference in the range of 0..7 (if $2000.5 currently = 0), or 0..15 (if $2000.5 currently = 1).
(Note that a 9-bit comparison result is generated. This means that setting object scanline coordinates for ranges -1..-15 are actually interpreted as ranges 241..255. For this reason, objects with these ranges will never be considered to be part of any on-screen scanline range, and will not allow smooth object scrolling off the top of the screen.)
Tile index (8 bits), X-coordinate (8 bits), & attribute information (4 bits; vertical inversion is excluded) from the in-range OAM element, plus the associated 4-bit result of the range comparison accumulate in a part of the PPU called the "sprite temporary memory". Logical inversion is applied to the loaded 4-bit range comparison result, if the object's vertical inversion attribute bit is set.
Since object range evaluations occur sequentially through the OAM (starting from entry 0 to 63), the sprite temporary memory always fills in order from the highest priority in-range object, to lower ones. A 4-bit "in-range" counter is used to determine the number of found objects on the scanline (from 0 up to 8), and serves as an index pointer for placement of found object data into the 8-element sprite temporary memory. The counter is reset at the beginning of the object evaluation phase, and is post-incremented everytime an object is found in-range. This occurs until the counter equals 8, when found object data after this is discarded, and a flag (bit 5 of $2002) is raised, indicating that it is going to be dropping objects for the next scanline.
An additional memory bit associated with the sprite temporary memory is used to indicate that the primary object (#0) was found to be in range. This will be used later on to detect primary object-to-playfield pixel collisions.
Playfield render pipeline details
---------------------------------
As pattern table & palette select data is fetched, it is loaded into internal latches (the palette select data is selected from the fetched byte via a 2-bit 1-of-4 selector).
At the start of a new tile fetch phase (every 8 cc's), both latched pattern table bitmaps are loaded into the upper 8-bits of 2- 16-bit shift registers (which both shift right every clock cycle). The palette select data is also transfered into another latch during this time (which feeds the serial inputs of 2 8-bit right shift registers shifted every clock). The pixel data is fed into these extra shift registers in order to implement fine horizontal scrolling, since the periods when the PPU fetch tile data is fixed.
A single bit from each shift register is selected, to form the valid 4-bit playfield pixel for the current clock cycle. The bit selection offset is based on the fine horizontal scroll value (this selects bit positions 0..7 for all 4 shift registers). The selected 4-bit pixel data will then be fed into the multiplexer (described later) to be mixed with object data.
Memory fetch phase 129 thru 160
-------------------------------
1. Garbage name table byte
2. Garbage name table byte
3. Pattern table bitmap #0 for applicable object (for next scanline)
4. Pattern table bitmap #1 for applicable object (for next scanline)
This process is repeated 8 times.
This is the period of time when the PPU retrieves the appropriate pattern table data for the objects to be drawn on the *next* scanline. When less than 8 objects exist on the next scanline (as the in-range object evaluation counter indicates), dummy pattern table fetches take place for the remaining fetches. Internally, the fetched dummy-data is discarded, and replaced with completely transparent bitmap patterns).
Although the fetched name table data is thrown away, and the name table address is somewhat unpredictable, the address does seem to relate to the first name table tile to be fetched for the next scanline. This would seem to imply that PPU cc #256 is when the PPU's scroll/address counters have their horizontal scroll values automatically updated.
It should also be noted that because this fetch is required for objects on the next scanline, it is neccessary for a garbage scanline to exist prior to the very first scanline to be actually rendered, so that object attribute RAM entries can be evaluated, and the appropriate bitmap data retrieved.
As far as the wasted fetch phases here, well, what can I say. Either Nintendo's engineers were VERY lazy, and didn't want to add the small amount of extra circuitry to the PPU so that 16 object fetches could take place per scanline, or Nintendo couldn't spot the extra memory required to implement 16 object scanlines. Thing is though- between the object attribute mem, sprite temporary & buffer mem, and palette mem, that's already 2406 bits of RAM; I don't think it would've killed them to just add the 408 bits it would've took for an extra 8 objects, which would've made games with horrible OAM cycling (Double Dragon 2 w/ 2 players) look half-decent (hell, with 16 object scanlines, games would hardly even need OAM cycling).
Details of object pattern fetch & render
----------------------------------------
Where the PPU fetches pattern table data for an individual object is conditioned on the contents of the sprite temporary memory element, and $2000.5. If $2000.5 = 0, the tile index data is used as usual, and $2000.3 selects the pattern table to use. If $2000.5 = 1, the MSB of the range result value become the LSB of the indexed tile, and the LSB of the tile index value determines pattern table selection. The lower 3 bits of the range result value are always used as the fine vertical offset into the selected pattern.
Horizontal inversion (bit order reversing) is applied to fetched bitmaps, if indicated in the sprite temporary memory element.
The fetched pattern table data (which is 2 bytes), plus the associated 3 attribute bits (palette select & priority), and the x coordinate byte in sprite temporary memory are then loaded into a part of the PPU called the "sprite buffer memory" (the primary object present bit is also copied). This memory area again, is large enough to hold the contents for 8 sprites.
The composition of one sprite buffer element here is: 2 8-bit shift registers (the fetched pattern table data is loaded in here, where it will be serialized at the appropriate time), a 3-bit latch (which holds the color & priority data for an object), and an 8-bit down counter (this is where the x coordinate is loaded).
The counter is decremented every time the PPU renders a pixel (the first 256 cc's of a scanline; see "Memory fetch phase 1 thru 128" above). When the counter equals 0, the pattern table data in the shift registers will start to serialize (1 shift per clock). Before this time, or 8 clocks after, consider the outputs of the serializers for each stage to be 0 (transparency).
The streams of all 8 object serializers are prioritized, and ultimately only one stream (with palette select & priority information) is selected for output to the multiplexer (where object & playfield pixels are prioritized).
The data for the first sprite buffer entry (including the primary object present flag) has the first chance to enter the multiplexer, if it's output pixel is non-transparent (non-zero). Otherwise, priority is passed to the next serializer in the sprite buffer memory, and the test for non-transparency is made again (the primary object present status will always be passed to the multiplexer as false in this case). This is done until the last (8th) stage is reached, when the object data is passed through unconditionally. Keep in mind that this whole process occurs every clock cycle (hardware is used to determine priority instantly).
The multiplexer does 2 things: determines primary object collisions, and decides which pixel data to pass through to index the palette RAM- either the playfield's or the object's.
Primary object collisions occur when a non-transparent playfield pixel coincides with a non-transparent object pixel, while the primary object present status entering the multiplexer for the current clock cycle is true. This causes a flip-flop ($2002.6) to be set, and remains set (presumably) some time after the VINT occurence (prehaps up until scanline 20?).
The decision for selecting the data to pass through to the palette index is made rather easilly. The condition to use object (opposed to playfield) data is:
(OBJpri=foreground OR PFpixel=xparent) AND OBJpixel<>xparent
Since the PPU has 2 palettes; one for objects, and one for playfield, the appropriate palette will be selected depending on which pixel data is passed through.
After the palette look-up, the operation of events follows the aforementioned steps in the "video signal generation" section.
Memory fetch phase 161 thru 168
-------------------------------
1. Name table byte
2. Attribute table byte
3. Pattern table bitmap #0 (for next scanline)
4. Pattern table bitmap #1 (for next scanline)
This process is repeated 2 times.
It is during this time that the PPU fetches the appliciable playfield data for the first and second tiles to be rendered on the screen for the *next* scanline. These fetches initialize the internal playfield pixel pipelines (2- 16-bit shift registers) with valid bitmap data. The rest of tiles (3..32) are fetched at the beginning of the following scanline.
Memory fetch phase 169 thru 170
-------------------------------
1. Name table byte
2. Name table byte
I'm unclear of the reason why this particular access to memory is made. The name table address that is accessed 2 times in a row here, is also the same nametable address that points to the 3rd tile to be rendered on the screen (or basically, the first name table address that will be accessed when the PPU is fetching playfield data on the next scanline).
After memory access 170
-----------------------
The PPU simply rests for 1 cycle here (or the equivelant of half a memory access cycle) before repeating the whole pixel/scanline rendering process.
+------------------+
|Extra cycle frames|
+------------------+
Scanline 20 is the only scanline that has variable length. On every odd frame, this scanline is only 340 cycles (the dead cycle at the end is removed). This is done to cause a shift in the NTSC colorburst phase.
You see, a 3.58 MHz signal, the NTSC colorburst, is required to be modulated into a luminance carrying signal in order for color to be generated on an NTSC monitor. Since the PPU's video out consists of basically square waves (as opposed to sine waves, which would be preferred), it takes an entire colorburst cycle (1/3.58 MHz) for an NTSC monitor to identify the color of a PPU pixel accurately.
But now you remember that the PPU renders pixels at 5.37 MHz- 1.5x the rate of the colorburst. This means that if a single pixel resides on a scanline with a color different to those surrounding it, the pixel will probably be misrepresented on the screen, sometimes appearing faintly.
Well, to somewhat fix this problem, they added this extra pixel into every odd frame (shifting the colorburst phase over a bit), and changing the way the monitor interprets isolated colored pixels each frame. This is why when you play games with detailed background graphics, the background seems to flicker a bit. Once you start scrolling the screen however, it seems as if some pixels become invisible; this is how stationary PPU images would look without this cycle removed from odd frames.
Certain scroll rates expose this NTSC PPU color caveat regardless of the toggling phase shift. Some of Zelda 2's dungeon backgrounds are a good place to see this effect.
+---------------------------+
|The MMC3's scanline counter|
+---------------------------+
As most people know, the MMC3 bases it's scanline counter on PPU address line A13 (which is why IRQ's can be fired off manually by toggling A13 a bunch of times via $2006). What's not common knowledge is the number of times A13 is expected to toggle in a scanline (although if you've been paying close attention to the doc here, you should already know ;)
A13 was probably used for the IRQ counter (as opposed to using the PPU's /READ line) because this address line already needed to be connected to the MMC for bankswitching purposes (so in other words, to reduce the MMC3's pin count by 1). They also probably used this method of counting (as opposed to a CPU cycle counter) since A13 cycles (0 -> 1) exactly 42 times per scanline, whereas the CPU count of cycles per scanline is not an exact integer (113.67). Having said that, I guess Nintendo wanted to provide an "easy-to-use" method of generating special image effects, without making programmers have to figure out how many clock cycles to program an IRQ counter with (a pretty lame excuse for not providing an IRQ counter with CPU clock cycle precision (which would have been more useful and versatile)).
Regardless of any values PPU registers are programmed with, A13 will operate in a predictable fashion during image rendering (and if you understand how PPU addressing works, you should understand that A13 is the *only* address line with fixed behaviour during image rendering).
+------------------------+
|PPU pixel priority quirk|
+------------------------+
Object data is prioritized between itself, then prioritized between the playfield. There are some odd side effects to this scheme of rendering, however. For instance, imagine a low priority object pixel with foreground priority, a high priority object pixel with background priority, and a playfield pixel all coinciding (all non-transparent).
Ideally, the playfield is considered to be the middle layer between background and foreground priority objects. This means that the playfield pixel should hide the background priority object pixel (regardless of object priority), and the foreground priority object should appear atop the PF pixel.
However, because of the way the PPU renders (as just described), OBJ priority is evaluated first, and therefore the background object pixel wins, which means that you'll only be seeing the PF pixel after this mess.
A good game to demonstrate this behaviour is Megaman 2. Go into airman's stage. First, jump into the energy bar, just to confirm that megaman's sprite is of a higher priority than the energy bar's. Now, get to the second half of the stage, where the clouds cover the energy bar. The energy bar will be ontop of the clouds, but megaman will be behind them. Now, look what happens when you jump into the energy bar here... you see the clouds where megaman underlaps the energy bar.
+----------------------+
|Graphical enhancements|
+----------------------+
Since an NES cartridge has access to the PPU bus, any number of on-cart hardware schemes can be used to enhance the graphic capabilities of the NES. After all, the PPU's playfield pipeline is very simple: it fetches 272 playfield pixels per scanline (as 34*2 byte fetches, in real-time), and outputs 256 of them to the screen (with the 0..7 pixel offset determined by the fine X scroll register), along with object data combined with it.
Essentially, you can bypass the PPU's simple scrolling system, implement a custom one on your cart (fetching bitmap data in your own fashion), and feed the PPU bitmap data in your own order.
The possibilities of this are endless (like sporting multiple playfields, or even playfield rotation/scaling), but of course what it comes down to is the amount of cartridge hardware required.
Generally, playfield rotation/scaling can be done quite easily- it only requires a few sets of 16-bit registers and adders (the 16 bits are broken up into 8.8 fixed point values). But this kind of implementation is more suited for an integrated circuit, since this would require dozens of discrete logic chips.
Multiple playfields are another thing which could be easily done. The caveat here is that pixel pipelines (i.e., shift registers) and a multiplexer would have to be implemented on the cart (not to mention exclusive name table RAM) in order to process the playfield bitmaps from multiple sources. The access to the CHR-ROM/RAM would also have to increased- but as it stands, the CHR-ROM/RAM bandwidth is 1.34 MHz, a rather low frequency. With a memory device capable of a 10.74 MHz bandwith, you could have 8 playfields to work with. Generally, this would be very useful for displaying multiple huge objects on the screen- without ever having to worry about annoying flicker.
The only restriction to doing any of this is that:
- every 8 sequential horizontal pixels sent to the PPU must share the same palette select value. Because of this, hardware would have to be implemented to decide which palette select value to feed the PPU between 8 horizontally sequential pixels, if they do not all share the same palette select value. The on-screen results of this may not be too flattering sometimes, but this is a small price to pay to do some neat graphical tricks on the NES.
-only the playfield palette can be used. As usual, this pretty much limits your randomly accessable colors to about 12+1.
It's a damn shame that Nintendo never created a MMC which would enhance graphics on the NES in useful ways as mentioned above. The MMC5 was the only device that came close, and it's only selling features were the single-tile color area, and the vertical split screen mode (which I don't think any game ever used). Considering the amount of pins (100) the MMC5 had, and number of gates they put in it just for the EXRAM (which was 1K bytes), they could've put some really useful graphics hardware inside there instead.
Prehaps the infamous Color Dreams "Hellraiser" cart was the closest the NES ever came to seeing such sophisticated graphics. The cart was never released, but from what I've read, it was going to use some sort of frame buffer, and a Z80 CPU to do the graphical rendering. It had been rumored that the game had 3D graphics (or at least 2.5D) in it. If so (and the game was actually good), prehaps it would have raised a few eyebrows in the industry, and inspired Nintendo to develop a new MMC chip with similar capabilities, in order to keep the NES in it's profit margin for another few years (and allow it to compete somewhat with the more advanced systems of the time).
EOF

View File

@ -0,0 +1,63 @@
Subject: [nesdev] the skinny on nes scrolling
Date: Tue, 13 Apr 1999 16:42:00 -0600
From: loopy <zxcvzxcv@netzero.net>
Reply-To: nesdev@onelist.com
To: nesdev@onelist.com
From: loopy <zxcvzxcv@netzero.net>
---------
the current information on background scrolling is sufficient for most games;
however, there are a few that require a more complete understanding.
here are the related registers:
(v) vram address, a.k.a. 2006 which we all know and love. (16 bits)
(t) another temp vram address (16 bits)
(you can really call them 15 bits, the last isn't used)
(x) tile X offset (3 bits)
the ppu uses the vram address for both reading/writing to vram thru 2007,
and for fetching nametable data to draw the background. as it's drawing the
background, it updates the address to point to the nametable data currently
being drawn. bits 0-11 hold the nametable address (-$2000). bits 12-14 are
the tile Y offset.
---------
stuff that affects register contents:
(sorry for the shorthand logic but i think it's easier to see this way)
2000 write:
t:0000110000000000=d:00000011
2005 first write:
t:0000000000011111=d:11111000
x=d:00000111
2005 second write:
t:0000001111100000=d:11111000
t:0111000000000000=d:00000111
2006 first write:
t:0011111100000000=d:00111111
t:1100000000000000=0
2006 second write:
t:0000000011111111=d:11111111
v=t
scanline start (if background and sprites are enabled):
v:0000010000011111=t:0000010000011111
frame start (line 0) (if background and sprites are enabled):
v=t
note! 2005 and 2006 share the toggle that selects between first/second
writes. reading 2002 will clear it.
note! all of this info agrees with the tests i've run on a real nes. BUT
if there's something you don't agree with, please let me know so i can verify
it.
________________________________________________________
NetZero - We believe in a FREE Internet. Shouldn't you?
Get your FREE Internet Access and Email at
http://www.netzero.net/download.html
------------------------------------------------------------------------
New hobbies? New curiosities? New enthusiasms?
http://www.ONElist.com
Sign up for a new e-mail list today!

View File

@ -0,0 +1,33 @@
Subject: [nesdev] Re: the skinny on nes scrolling
Date: Tue, 13 Apr 1999 17:48:54 -0600
From: loopy <zxcvzxcv@netzero.net>
Reply-To: nesdev@onelist.com
To: nesdev@onelist.com
From: loopy <zxcvzxcv@netzero.net>
(more notes on ppu logic)
you can think of bits 0,1,2,3,4 of the vram address as the "x scroll"(*8)
that the ppu increments as it draws. as it wraps from 31 to 0, bit 10 is
switched. you should see how this causes horizontal wrapping between name
tables (0,1) and (2,3).
you can think of bits 5,6,7,8,9 as the "y scroll"(*8). this functions
slightly different from the X. it wraps to 0 and bit 11 is switched when
it's incremented from _29_ instead of 31. there are some odd side effects
from this.. if you manually set the value above 29 (from either 2005 or
2006), the wrapping from 29 obviously won't happen, and attrib data will be
used as name table data. the "y scroll" still wraps to 0 from 31, but
without switching bit 11. this explains why writing 240+ to 'Y' in 2005
appeared as a negative scroll value.
________________________________________________________
NetZero - We believe in a FREE Internet. Shouldn't you?
Get your FREE Internet Access and Email at
http://www.netzero.net/download.html
------------------------------------------------------------------------
Looking for a new hobby? Want to make a new friend?
http://www.ONElist.com
Come join one of the 115,000 e-mail communities at ONElist!

View File

@ -0,0 +1,6 @@
Many(possibly all) of these documents contain flaws or are incomplete, so
don't pull out your hair if there are inconsistencies between the documents,
what's in FCE Ultra, and what you observe. That's not to say that FCE Ultra
doesn't have its share of (emulation) flaws, though...
For many more NES-related documents, try http://nesdev.parodius.com

View File

@ -0,0 +1,2 @@
Sound information is in the "cpu" subdirectory, due to the intimate
relationship between the sound circuitry and the cpu.

73
documentation/todo Normal file
View File

@ -0,0 +1,73 @@
The following games are broken to some extent:
Crystalis: Mostly working, but the screen jumps around during
dialogue. It apparently resets the MMC3 IRQ counter
mid-scanline. It'll require low-level PPU and MMC3
IRQ counter emulation to function properly.
Kyoro Chan Land: Expects a sprite hit to happen, but it has sprite 0 over
transparent background.
*** First, things that are not on the TODO list(Don't bug me about these
things if you're an idiot. I don't like listening to idiots.
If you are not an idiot, and you can make decent arguments for why
these should be on the TODO list, then you can bug me.).
*** General Features:
Remappable command keys(to multiple keys on the keyboard and a joystick).
Fix possible UNIF crashes(if no PRGx or CHRx chunks exist, it may crash,
due to changes made in 0.92).
Windows Port:
Support for command-line options(so that one crazy guy will quit bugging
me).
SDL Port:
Make the code better.
Add a GTK+ interface using GLADE, interfacing with the emulator via a
stdio interface.
Update(FIX) the SDL_net netplay code to work with the new netplay code.
Figure out a good way to add "turbo" button support and then do it.
Make default svgalib video mode a non-tweaked VGA mode.
Finish the software video blitting "library", add support for 2xsai, eagle,
interpolation, etc. effects.
*** Emulation:
***IMPORTANT***
If anyone ever cares to implement movie recording/playback, we must figure
out what to do with some unsaved variables, like timestamp and timestampbase.
These variables are abused in the sound emulation code, and modifying them
in certain ways elsewhere can cause crashes.
***IMPORTANT***
Implement cart-based expansion devices, and interfaces for them(dip switches
and that Datach barcode reader, and maybe others).
Fix DPCM playback and IRQ at end of playback.
Fix some 6502 emulation bugs(undocumented opcodes might not be implemented
correctly and I'm not sure if the IRQ flag latency is implemented correctly).
Implement more dummy CPU reads when in debug mode.
Fix MMC3 IRQ emulation.
Figure out correct timing for when the PPU refresh address register is
updated by the PPU(for the next scanline).
Sound frame count stuff on PAL games(is it correct?).
Fix FDS sound emulation.
Fix NMI timing and D7 of 2002 setting timing. Fixing this might require
a small hack. Also be aware that this might break Battletoads, particularly
during the second level.
Fix Zapper emulation(Chiller still doesn't always work correctly).

7
rename-cpp Normal file
View File

@ -0,0 +1,7 @@
seek=.c$
replace=.cpp
for X in *.c
do
fname=$(echo $X | sed -e "s/$seek/$replace/")
svn move $X $fname
done

17
src/.gitignore vendored Normal file
View File

@ -0,0 +1,17 @@
# A simulation of Subversion default ignores, generated by reposurgeon.
*.o
*.lo
*.la
*.al
*.libs
*.so
*.so.[0-9]*
*.a
*.pyc
*.pyo
*.rej
*~
*.#*
.*.swp
.DS_store
# Simulated Subversion default ignores end here

76
src/SConscript Normal file
View File

@ -0,0 +1,76 @@
file_list = Split("""
cart.cpp
cheat.cpp
crc32.cpp
config.cpp
debug.cpp
endian.cpp
fceu.cpp
fds.cpp
file.cpp
filter.cpp
general.cpp
ines.cpp
input.cpp
md5.cpp
memory.cpp
netplay.cpp
nsf.cpp
palette.cpp
ppu.cpp
sound.cpp
state.cpp
unif.cpp
video.cpp
vsuni.cpp
wave.cpp
x6502.cpp
movie.cpp
unzip.c""")
Export('file_list')
SConscript(Split("""
boards/SConscript
input/SConscript
fir/SConscript
mappers/SConscript
drivers/common/SConscript
drivers/pc/SConscript
"""))
#palettes/SConscript
Import('file_list')
# XXX path separator fixed right now
opts = Options()
opts.Add('PSS_STYLE', 'Path separator style', 1)
opts.Add('LSB_FIRST', 'Least significant byte first?', 1)
env = Environment(options = opts,
CPPDEFINES={'PSS_STYLE' : '${PSS_STYLE}',
'LSB_FIRST' : '${LSB_FIRST}'})
# use sdl-config to get the cflags and libpath
import os;
sdl_cflags_pipe = os.popen("sdl-config --cflags");
sdl_cflags = sdl_cflags_pipe.read();
sdl_cflags = sdl_cflags.rstrip(os.linesep);
sdl_cflags_pipe.close();
sdl_libpath = [];
sdl_libs = [];
sdl_libflags_pipe = os.popen("sdl-config --libs");
sdl_libflags = sdl_libflags_pipe.read();
for flag in sdl_libflags.split(' '):
if flag.find("-L") == 0:
sdl_libpath.append(flag.strip("-L"));
else:
sdl_libs.append(flag.strip("-l"));
sdl_libflags_pipe.close();
# add zlib
libs = sdl_libs;
libs.append('z');
env.Program('fceu', file_list, CCFLAGS=sdl_cflags, LIBS=libs, LIBPATH=sdl_libpath)

27
src/attic/fceustr.cpp Normal file
View File

@ -0,0 +1,27 @@
#include <string.h>
#include <stdlib.h>
#include "types.h"
#include "fceustr.h"
/* Creates a fceustr from a C-style string. */
fceustr *fceustr_create(const char *str)
{
fceustr *ret;
ret=malloc(sizeof(fceustr));
ret->data=malloc(strlen(str)+1);
strcpy(ret->data,str);
ret->len=strlen(str);
return(ret);
}
void fceustr_destroy(fceustr *str)
{
if(str->data) free(str->data);
free(str);
}

4
src/attic/fceustr.h Normal file
View File

@ -0,0 +1,4 @@
typedef struct {
uint8 *data;
uint32 len; /* Not including extra NULL character. */
} fceustr;

Binary file not shown.

72
src/attic/pc/dface.h Normal file
View File

@ -0,0 +1,72 @@
#ifdef __cplusplus
extern "C" {
#endif
#include "../common/args.h"
#include "../common/config.h"
#include "input.h"
extern FCEUGI *CurGame;
extern CFGSTRUCT DriverConfig[];
extern ARGPSTRUCT DriverArgs[];
extern char *DriverUsage;
void DoDriverArgs(void);
uint8 *GetBaseDirectory(void);
int InitSound(FCEUGI *gi);
void WriteSound(int32 *Buffer, int Count);
int KillSound(void);
uint32 GetMaxSound(void);
uint32 GetWriteSound(void);
void SilenceSound(int s); /* DOS and SDL */
int InitMouse(void);
void KillMouse(void);
void GetMouseData(uint32 *MouseData);
int InitJoysticks(void);
int KillJoysticks(void);
uint32 *GetJSOr(void);
int InitKeyboard(void);
int UpdateKeyboard(void);
char *GetKeyboard(void);
void KillKeyboard(void);
int InitVideo(FCEUGI *gi);
int KillVideo(void);
void BlitScreen(uint8 *XBuf);
void LockConsole(void);
void UnlockConsole(void);
void ToggleFS(); /* SDL */
int LoadGame(const char *path);
int CloseGame(void);
int GUI_Init(int argc, char **argv, int (*dofunc)(void));
int GUI_Idle(void);
int GUI_Update(void);
void GUI_Hide(int);
void GUI_RequestExit(void);
int GUI_SetVideo(int fullscreen, int width, int height);
char *GUI_GetKeyboard(void);
void GUI_GetMouseState(uint32 *b, int *x, int *y);
void UpdatePhysicalInput(void);
int DTestButton(ButtConfig *bc);
int DWaitButton(const uint8 *text, ButtConfig *bc, int wb);
int ButtonConfigBegin(void);
void ButtonConfigEnd(void);
void Giggles(int);
void DoFun(void);
int FCEUD_NetworkConnect(void);
#ifdef __cplusplus
}
#endif

200
src/attic/pc/dos-joystick.c Normal file
View File

@ -0,0 +1,200 @@
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2002 Xodnizel
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <dpmi.h>
#include <sys/farptr.h>
#include <go32.h>
#include <pc.h>
#include "dos.h"
#include "dos-joystick.h"
#define JOY_A 1
#define JOY_B 2
#define JOY_SELECT 4
#define JOY_START 8
#define JOY_UP 0x10
#define JOY_DOWN 0x20
#define JOY_LEFT 0x40
#define JOY_RIGHT 0x80
int joy=0;
int joyBMap[6];
static int32 joybuttons=0;
static uint32 joyx=0;
static uint32 joyy=0;
static uint32 joyxcenter;
static uint32 joyycenter;
static void ConfigJoystick(void);
volatile int soundjoyer=0;
volatile int soundjoyeron=0;
/* Crude method to detect joystick. */
static int DetectJoystick(void)
{
uint8 b;
outportb(0x201,0);
b=inportb(0x201);
sleep(1);
if((inportb(0x201)&3)==(b&3))
return 0;
else
return 1;
}
void UpdateJoyData(void)
{
uint32 xc,yc;
joybuttons=((inportb(0x201)&0xF0)^0xF0)>>4;
xc=yc=0;
{
outportb(0x201,0);
for(;;)
{
uint8 b;
b=inportb(0x201);
if(!(b&3))
break;
if(b&1) xc++;
if(b&2) yc++;
}
}
joyx=xc;
joyy=yc;
}
uint32 GetJSOr(void)
{
int y;
unsigned long ret;
static int rtoggle=0;
ret=0;
rtoggle^=1;
if(!soundo)
UpdateJoyData();
for(y=0;y<6;y++)
if((y>=4 && rtoggle) || y<4)
if(joybuttons&joyBMap[y]) ret|=(1<<y&3)<<((joy-1)<<3);
if(joyx<=joyxcenter*.25) ret|=JOY_LEFT<<((joy-1)<<3);
else if(joyx>=joyxcenter*1.75) ret|=JOY_RIGHT<<((joy-1)<<3);
if(joyy<=joyycenter*.25) ret|=JOY_UP<<((joy-1)<<3);
else if(joyy>=joyycenter*1.75) ret|=JOY_DOWN<<((joy-1)<<3);
return ret;
}
int InitJoysticks(void)
{
if(!joy) return(0);
if(!DetectJoystick())
{
printf("Joystick not detected!\n");
joy=0;
return 0;
}
if(soundo)
{
soundjoyeron=1;
while(!soundjoyer);
}
else
UpdateJoyData();
joyxcenter=joyx;
joyycenter=joyy;
if(!(joyBMap[0]|joyBMap[1]|joyBMap[2]|joyBMap[3]))
ConfigJoystick();
return(1);
}
static void BConfig(int b)
{
int c=0;
uint32 st=time(0);
while(time(0)< (st+4) )
{
if(!soundo)
UpdateJoyData();
if(joybuttons) c=joybuttons;
else if(c && !joybuttons)
{
joyBMap[b]=c;
break;
}
}
}
void KillJoysticks(void)
{
}
static void ConfigJoystick(void)
{
static char *genb="** Press button for ";
printf("\n\n Joystick button configuration:\n\n");
printf(" Push and release the button to map to the virtual joystick.\n");
printf(" If you do not wish to assign a button, wait a few seconds\n");
printf(" and the configuration will continue.\n\n");
printf(" Press enter to continue...\n");
getchar();
printf("%s\"Select\".\n",genb);
BConfig(2);
printf("%s\"Start\".\n",genb);
BConfig(3);
printf("%s\"B\".\n",genb);
BConfig(1);
printf("%s\"A\".\n",genb);
BConfig(0);
printf("%s\"Rapid fire B\".\n",genb);
BConfig(5);
printf("%s\"Rapid fire A\".\n",genb);
BConfig(4);
}

View File

@ -0,0 +1,27 @@
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2002 Xodnizel
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
void UpdateJoyData(void);
uint32 GetJSOr(void);
int InitJoysticks(void);
/* Variables to save in config file. */
extern int joy;
extern int joyBMap[6];

131
src/attic/pc/dos-keyboard.c Normal file
View File

@ -0,0 +1,131 @@
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2002 Xodnizel
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <pc.h>
#include <dpmi.h>
#include <go32.h>
#include "keyscan.h"
static unsigned char lastsc;
static char keybuf[256];
int newk;
/* Read scan code from port $60 */
/* Acknowledge interrupt( output $20 to port $20) */
static void ihandler(_go32_dpmi_registers *r)
{
unsigned char scode=inp(0x60); /* Get scan code. */
if(scode!=0xE0)
{
int offs=0;
/* I'm only interested in preserving the independent status of the
right ALT and CONTROL keys.
*/
if(lastsc==0xE0)
if((scode&0x7F)==SCAN_LEFTALT || (scode&0x7F)==SCAN_LEFTCONTROL)
offs=0x80;
keybuf[(scode&0x7f)|offs]=((scode&0x80)^0x80);
newk++;
}
lastsc=scode;
outp(0x20,0x20); /* Acknowledge interrupt. */
}
static _go32_dpmi_seginfo KBIBack,KBIBackRM;
static _go32_dpmi_seginfo KBI,KBIRM;
static _go32_dpmi_registers KBIRMRegs;
static int initdone=0;
int InitKeyboard(void)
{
/* I'll assume that the keyboard is in the correct scancode mode(translated
mode 2, I think).
*/
newk=0;
memset(keybuf,0,sizeof(keybuf));
KBIRM.pm_offset=KBI.pm_offset=(int)ihandler;
KBIRM.pm_selector=KBI.pm_selector=_my_cs();
_go32_dpmi_get_real_mode_interrupt_vector(9,&KBIBackRM);
_go32_dpmi_allocate_real_mode_callback_iret(&KBIRM, &KBIRMRegs);
_go32_dpmi_set_real_mode_interrupt_vector(9,&KBIRM);
_go32_dpmi_get_protected_mode_interrupt_vector(9,&KBIBack);
_go32_dpmi_allocate_iret_wrapper(&KBI);
_go32_dpmi_set_protected_mode_interrupt_vector(9,&KBI);
lastsc=0;
initdone=1;
return(1);
}
void KillKeyboard(void)
{
if(initdone)
{
_go32_dpmi_set_protected_mode_interrupt_vector(9,&KBIBack);
_go32_dpmi_free_iret_wrapper(&KBI);
_go32_dpmi_set_real_mode_interrupt_vector(9,&KBIBackRM);
_go32_dpmi_free_real_mode_callback(&KBIRM);
initdone=0;
}
}
/* In FCE Ultra, it doesn't matter if the key states change
in the middle of the keyboard handling code. If you want
to use this code elsewhere, you may want to memcpy() keybuf
to another buffer and return that when GetKeyboard() is
called.
*/
char *GetKeyboard(void)
{
return keybuf;
}
/* Returns 1 on new scan codes generated, 0 on no new scan codes. */
int UpdateKeyboard(void)
{
int t=newk;
if(t)
{
asm volatile(
"subl %%eax,_newk\n\t"
:
: "a" (t)
);
if(keybuf[SCAN_LEFTCONTROL] && keybuf[SCAN_C])
raise(SIGINT);
return(1);
}
return(0);
}

80
src/attic/pc/dos-mouse.c Normal file
View File

@ -0,0 +1,80 @@
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2002 Xodnizel
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <dpmi.h>
#include <string.h>
#include "dos.h"
int InitMouse(void)
{
__dpmi_regs regs;
memset(&regs,0,sizeof(regs));
regs.x.ax=0;
__dpmi_int(0x33,&regs);
if(regs.x.ax!=0xFFFF)
return(0);
memset(&regs,0,sizeof(regs));
regs.x.ax=0x7;
regs.x.cx=0; // Min X
regs.x.dx=260; // Max X
__dpmi_int(0x33,&regs);
memset(&regs,0,sizeof(regs));
regs.x.ax=0x8;
regs.x.cx=0; // Min Y
regs.x.dx=260; // Max Y
__dpmi_int(0x33,&regs);
memset(&regs,0,sizeof(regs));
regs.x.ax=0xF;
regs.x.cx=8; // Mickey X
regs.x.dx=8; // Mickey Y
__dpmi_int(0x33,&regs);
memset(&regs,0,sizeof(regs));
regs.x.ax=0x2;
__dpmi_int(0x33,&regs);
return(1);
}
uint32 GetMouseData(uint32 *x, uint32 *y)
{
if(FCEUI_IsMovieActive()<0)
return;
__dpmi_regs regs;
memset(&regs,0,sizeof(regs));
regs.x.ax=0x3;
__dpmi_int(0x33,&regs);
*x=regs.x.cx;
*y=regs.x.dx;
return(regs.x.bx&3);
}
void KillMouse(void)
{
}

567
src/attic/pc/dos-sound.c Normal file
View File

@ -0,0 +1,567 @@
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2002 Xodnizel
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/farptr.h>
#include <pc.h>
#include <dos.h>
#include <dpmi.h>
#include <go32.h>
#include <ctype.h>
#include "dos.h"
#include "dos-sound.h"
#include "dos-joystick.h"
static void SBIRQHandler(_go32_dpmi_registers *r);
static uint32 LMBuffer; /* Address of low memory DMA playback buffer. */
static int LMSelector;
static uint8 *WaveBuffer;
static unsigned int IVector, SBIRQ, SBDMA, SBDMA16, SBPort;
static int DSPV,hsmode;
static int format;
static int frags, fragsize, fragtotal;
static volatile int WritePtr, ReadPtr;
static volatile int hbusy;
static volatile int whichbuf;
static uint8 PICMask;
/* Protected mode interrupt vector info. */
static _go32_dpmi_seginfo SBIH,SBIHOld;
/* Real mode interrupt vector info. */
static _go32_dpmi_seginfo SBIHRM,SBIHRMOld;
static _go32_dpmi_registers SBIHRMRegs;
static int WriteDSP(uint8 V)
{
int x;
for(x=65536;x;x--)
{
if(!(inportb(SBPort+0xC)&0x80))
{
outportb(SBPort+0xC,V);
return(1);
}
}
return(0);
}
static int ReadDSP(uint8 *V)
{
int x;
for(x=65536;x;x--) /* Should be more than enough time... */
{
if(inportb(SBPort+0xE)&0x80)
{
*V=inportb(SBPort+0xA);
return(1);
}
}
return(0);
}
static int SetVectors(void)
{
SBIH.pm_offset=SBIHRM.pm_offset=(int)SBIRQHandler;
SBIH.pm_selector=SBIHRM.pm_selector=_my_cs();
/* Get and set real mode interrupt vector. */
_go32_dpmi_get_real_mode_interrupt_vector(IVector,&SBIHRMOld);
_go32_dpmi_allocate_real_mode_callback_iret(&SBIHRM, &SBIHRMRegs);
_go32_dpmi_set_real_mode_interrupt_vector(IVector,&SBIHRM);
/* Get and set protected mode interrupt vector. */
_go32_dpmi_get_protected_mode_interrupt_vector(IVector,&SBIHOld);
_go32_dpmi_allocate_iret_wrapper(&SBIH);
_go32_dpmi_set_protected_mode_interrupt_vector(IVector,&SBIH);
return(1);
}
static void ResetVectors(void)
{
_go32_dpmi_set_protected_mode_interrupt_vector(IVector,&SBIHOld);
_go32_dpmi_free_iret_wrapper(&SBIH);
_go32_dpmi_set_real_mode_interrupt_vector(IVector,&SBIHRMOld);
_go32_dpmi_free_real_mode_callback(&SBIHRM);
}
int GetBLASTER(void)
{
int check=0;
char *s;
if(!(s=getenv("BLASTER")))
{
puts(" Error getting BLASTER environment variable.");
return(0);
}
while(*s)
{
switch(toupper(*s))
{
case 'A': check|=(sscanf(s+1,"%x",&SBPort)==1)?1:0;break;
case 'I': check|=(sscanf(s+1,"%d",&SBIRQ)==1)?2:0;break;
case 'D': check|=(sscanf(s+1,"%d",&SBDMA)==1)?4:0;break;
case 'H': check|=(sscanf(s+1,"%d",&SBDMA16)==1)?8:0;break;
}
s++;
}
if((check^7)&7 || SBDMA>=4 || (SBDMA16<=4 && check&8) || SBIRQ>15)
{
puts(" Invalid or incomplete BLASTER environment variable.");
return(0);
}
if(!(check&8))
format=0;
return(1);
}
static int ResetDSP(void)
{
uint8 b;
outportb(SBPort+0x6,0x1);
delay(10);
outportb(SBPort+0x6,0x0);
delay(10);
if(ReadDSP(&b))
if(b==0xAA)
return(1);
return(0);
}
static int GetDSPVersion(void)
{
int ret;
uint8 t;
if(!WriteDSP(0xE1))
return(0);
if(!ReadDSP(&t))
return(0);
ret=t<<8;
if(!ReadDSP(&t))
return(0);
ret|=t;
return(ret);
}
static void KillDMABuffer(void)
{
__dpmi_free_dos_memory(LMSelector);
}
static int MakeDMABuffer(void)
{
uint32 size;
int32 tmp;
size=fragsize*2; /* Two buffers in the DMA buffer. */
size<<=format; /* Twice the size for 16-bit than for 8-bit. */
size<<=1; /* Double the size in case the first 2 buffers
cross a 64KB or 128KB page boundary.
*/
size=(size+15)>>4; /* Convert to paragraphs */
if((tmp=__dpmi_allocate_dos_memory(size,&LMSelector))<0)
return(0);
LMBuffer=tmp<<=4;
if(format) /* Check for and fix 128KB page boundary crossing. */
{
if((LMBuffer&0x20000) != ((LMBuffer+fragsize*2*2-1)&0x20000))
LMBuffer+=fragsize*2*2;
}
else /* Check for and fix 64KB page boundary crossing. */
{
if((LMBuffer&0x10000) != ((LMBuffer+fragsize*2-1)&0x10000))
LMBuffer+=fragsize*2;
}
DOSMemSet(LMBuffer, format?0:128, (fragsize*2)<<format);
return(1);
}
static void ProgramDMA(void)
{
static int PPorts[8]={0x87,0x83,0x81,0x82,0,0x8b,0x89,0x8a};
uint32 tmp;
if(format)
{
outportb(0xd4,(SBDMA16&0x3)|0x4);
outportb(0xd8,0x0);
outportb(0xd6,(SBDMA16&0x3)|0x58);
tmp=((SBDMA16&3)<<2)+0xC2;
}
else
{
outportb(0xA,SBDMA|0x4);
outportb(0xC,0x0);
outportb(0xB,SBDMA|0x58);
tmp=(SBDMA<<1)+1;
}
/* Size of entire buffer. */
outportb(tmp,(fragsize*2-1));
outportb(tmp,(fragsize*2-1)>>8);
/* Page of buffer. */
outportb(PPorts[format?SBDMA16:SBDMA],LMBuffer>>16);
/* Offset of buffer within page. */
if(format)
tmp=((SBDMA16&3)<<2)+0xc0;
else
tmp=SBDMA<<1;
outportb(tmp,(LMBuffer>>format));
outportb(tmp,(LMBuffer>>(8+format)));
}
int InitSB(int Rate, int bittage)
{
hsmode=hbusy=0;
whichbuf=1;
puts("Initializing Sound Blaster...");
format=bittage?1:0;
frags=8;
if(Rate<=11025)
fragsize=1<<5;
else if(Rate<=22050)
fragsize=1<<6;
else
fragsize=1<<7;
fragtotal=frags*fragsize;
WaveBuffer=malloc(fragtotal<<format);
if(format)
memset(WaveBuffer,0,fragtotal*2);
else
memset(WaveBuffer,128,fragtotal);
WritePtr=ReadPtr=0;
if((Rate<8192) || (Rate>65535))
{
printf(" Unsupported playback rate: %d samples per second\n",Rate);
return(0);
}
if(!GetBLASTER())
return(0);
/* Disable IRQ line in PIC0 or PIC1 */
if(SBIRQ>7)
{
PICMask=inportb(0xA1);
outportb(0xA1,PICMask|(1<<(SBIRQ&7)));
}
else
{
PICMask=inportb(0x21);
outportb(0x21,PICMask|(1<<SBIRQ));
}
if(!ResetDSP())
{
puts(" Error resetting the DSP.");
return(0);
}
if(!(DSPV=GetDSPVersion()))
{
puts(" Error getting the DSP version.");
return(0);
}
printf(" DSP Version: %d.%d\n",DSPV>>8,DSPV&0xFF);
if(DSPV<0x201)
{
printf(" DSP version number is too low.\n");
return(0);
}
if(DSPV<0x400)
format=0;
if(!MakeDMABuffer())
{
puts(" Error creating low-memory DMA buffer.");
return(0);
}
if(SBIRQ>7) IVector=SBIRQ+0x68;
else IVector=SBIRQ+0x8;
if(!SetVectors())
{
puts(" Error setting interrupt vectors.");
KillDMABuffer();
return(0);
}
/* Reenable IRQ line. */
if(SBIRQ>7)
outportb(0xA1,PICMask&(~(1<<(SBIRQ&7))));
else
outportb(0x21,PICMask&(~(1<<SBIRQ)));
ProgramDMA();
/* Note that the speaker must always be turned on before the mode transfer
byte is sent to the DSP if we're going into high-speed mode, since
a real Sound Blaster(at least my SBPro) won't accept DSP commands(except
for the reset "command") after it goes into high-speed mode.
*/
WriteDSP(0xD1); // Turn on DAC speaker
if(DSPV>=0x400)
{
WriteDSP(0x41); // Set sampling rate
WriteDSP(Rate>>8); // High byte
WriteDSP(Rate&0xFF); // Low byte
if(!format)
{
WriteDSP(0xC6); // 8-bit output
WriteDSP(0x00); // 8-bit mono unsigned PCM
}
else
{
WriteDSP(0xB6); // 16-bit output
WriteDSP(0x10); // 16-bit mono signed PCM
}
WriteDSP((fragsize-1)&0xFF);// Low byte of size
WriteDSP((fragsize-1)>>8); // High byte of size
}
else
{
int tc,command;
if(Rate>22050)
{
tc=(65536-(256000000/Rate))>>8;
Rate=256000000/(65536-(tc<<8));
command=0x90; // High-speed auto-initialize DMA mode transfer
hsmode=1;
}
else
{
tc=256-(1000000/Rate);
Rate=1000000/(256-tc);
command=0x1c; // Auto-initialize DMA mode transfer
}
WriteDSP(0x40); // Set DSP time constant
WriteDSP(tc); // time constant
WriteDSP(0x48); // Set DSP block transfer size
WriteDSP((fragsize-1)&0xFF);
WriteDSP((fragsize-1)>>8);
WriteDSP(command);
}
/* Enable DMA */
if(format)
outportb(0xd4,SBDMA16&3);
else
outportb(0xa,SBDMA);
printf(" %d hz, %d-bit\n",Rate,8<<format);
return(Rate);
}
extern volatile int soundjoyer;
extern volatile int soundjoyeron;
static int ssilence=0;
static void SBIRQHandler(_go32_dpmi_registers *r)
{
uint32 *src;
uint32 dest;
int32 x;
if(format)
{
uint8 status;
outportb(SBPort+4,0x82);
status=inportb(SBPort+5);
if(status&2)
inportb(SBPort+0x0F);
}
else
inportb(SBPort+0x0E);
#ifdef OLD
{
uint8 status;
outportb(SBPort+4,0x82);
status=inportb(SBPort+5);
if(status&1)
inportb(SBPort+0x0E);
else if(status&2)
inportb(SBPort+0x0F);
else
return; // Mysterious interrupt source! *eerie music*
}
#endif
if(hbusy)
{
outportb(0x20,0x20);
if(SBIRQ>=8)
outportb(0xA0,0x20);
whichbuf^=1;
return;
}
hbusy=1;
{
/* This code seems to fail on many SB emulators. Bah.
SCREW SB EMULATORS. ^_^ */
uint32 count;
uint32 block;
uint32 port;
if(format)
port=((SBDMA16&3)*4)+0xc2;
else
port=(SBDMA*2)+1;
count=inportb(port);
count|=inportb(port)<<8;
if(count>=fragsize)
block=1;
else
block=0;
dest=LMBuffer+((block*fragsize)<<format);
#ifdef MOO
dest=LMBuffer+((whichbuf*fragsize)<<format);
whichbuf^=1;
#endif
}
_farsetsel(_dos_ds);
src=(uint32 *)(WaveBuffer+(ReadPtr<<format));
if(ssilence)
{
uint32 sby;
if(format) sby=0; /* 16-bit silence. */
else sby=0x80808080; /* 8-bit silence. */
for(x=(fragsize<<format)>>2;x;x--,dest+=4)
{
_farnspokel(dest,sby);
}
}
else
{
for(x=(fragsize<<format)>>2;x;x--,dest+=4,src++)
{
_farnspokel(dest,*src);
}
ReadPtr=(ReadPtr+fragsize)&(fragtotal-1);
}
if(soundjoyeron)
{
static int coot=0;
if(!coot)
{
UpdateJoyData();
soundjoyer=1;
}
coot=(coot+1)&3;
}
hbusy=0;
outportb(0x20,0x20);
if(SBIRQ>=8)
outportb(0xA0,0x20);
}
void SilenceSound(int s)
{
ssilence=s;
}
void WriteSBSound(int32 *Buffer, int Count, int NoBlocking)
{
int x;
if(!format)
{
for(x=0;x<Count;x++)
{
while(WritePtr==ReadPtr)
if(NoBlocking)
return;
WaveBuffer[WritePtr]=(uint8)((Buffer[x])>>8)^128;
WritePtr=(WritePtr+1)&(fragtotal-1);
}
}
else // 16 bit
{
for(x=0;x<Count;x++)
{
while(WritePtr==ReadPtr)
if(NoBlocking)
return;
((int16 *)WaveBuffer)[WritePtr]=Buffer[x];
WritePtr=(WritePtr+1)&(fragtotal-1);
}
}
}
void KillSB(void)
{
if(hsmode)
ResetDSP(); /* High-speed mode requires a DSP reset. */
else
WriteDSP(format?0xD9:0xDA); /* Exit auto-init DMA transfer mode. */
WriteDSP(0xD3); /* Turn speaker off. */
outportb((SBIRQ>7)?0xA1:0x21,PICMask|(1<<(SBIRQ&7)));
ResetVectors();
outportb((SBIRQ>7)?0xA1:0x21,PICMask);
KillDMABuffer();
}

26
src/attic/pc/dos-sound.h Normal file
View File

@ -0,0 +1,26 @@
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2002 Xodnizel
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
int InitSB(int Rate, int bittage);
void KillSB(void);
void WriteSBSound(int32 *Buffer, int Count, int NoBlocking);
void SilenceSound(int s);

246
src/attic/pc/dos-video.c Normal file
View File

@ -0,0 +1,246 @@
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 1998 \Firebug\
* Copyright (C) 2002 Xodnizel
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdio.h>
#include <string.h>
#include <dpmi.h>
#include <sys/farptr.h>
#include <go32.h>
#include <pc.h>
#include "dos.h"
#include "dos-video.h"
#define TEXT 3
#define G320x200x256 0x13
static void vga_waitretrace(void)
{
while(inp(0x3da)&0x8);
while(!(inp(0x3da)&0x8));
}
static void vga_setmode(int mode)
{
__dpmi_regs regs;
memset(&regs,0,sizeof(regs));
regs.x.ax=mode;
__dpmi_int(0x10,&regs);
}
void vga_setpalette(int i, int r, int g, int b)
{
outp(0x3c8,i);
outp(0x3c9,r);
outp(0x3c9,g);
outp(0x3c9,b);
}
int FCEUDvmode=1;
static int vidready=0;
/* Part of the VGA low-level mass register setting code derived from
code by \Firebug\.
*/
#include "vgatweak.c"
void SetBorder(void)
{
inportb(0x3da);
outportb(0x3c0,(0x11|0x20));
outportb(0x3c0,0x80);
}
void TweakVGA(int VGAMode)
{
int I;
vga_waitretrace();
outportb(0x3C8,0x00);
for(I=0;I<768;I++) outportb(0x3C9,0x00);
outportb(0x3D4,0x11);
I=inportb(0x3D5)&0x7F;
outportb(0x3D4,0x11);
outportb(0x3D5,I);
switch(VGAMode)
{
case 1: for(I=0;I<25;I++) VGAPortSet(v256x240[I]);break;
case 2: for(I=0;I<25;I++) VGAPortSet(v256x256[I]);break;
case 3: for(I=0;I<25;I++) VGAPortSet(v256x256S[I]);break;
case 6: for(I=0;I<25;I++) VGAPortSet(v256x224S[I]);break;
case 8: for(I=0;I<25;I++) VGAPortSet(v256x224_103[I]);break;
default: break;
}
outportb(0x3da,0);
}
static uint8 palettedbr[256],palettedbg[256],palettedbb[256];
static void FlushPalette(void)
{
int x;
for(x=0;x<256;x++)
{
int z=x;
vga_setpalette(z,palettedbr[x]>>2,palettedbg[x]>>2,palettedbb[x]>>2);
}
}
void FCEUD_SetPalette(uint8 index, uint8 r, uint8 g, uint8 b)
{
palettedbr[index]=r;
palettedbg[index]=g;
palettedbb[index]=b;
if(vidready)
{
vga_setpalette(index,r>>2,g>>2,b>>2);
}
}
void FCEUD_GetPalette(uint8 i, uint8 *r, uint8 *g, uint8 *b)
{
*r=palettedbr[i];
*g=palettedbg[i];
*b=palettedbb[i];
}
static uint32 ScreenLoc;
int InitVideo(void)
{
vidready=0;
switch(FCEUDvmode)
{
default:
case 1:
case 2:
case 3:
case 6:
case 8:
vga_setmode(G320x200x256);
vidready|=1;
ScreenLoc=0xa0000;
TweakVGA(FCEUDvmode);
SetBorder();
DOSMemSet(ScreenLoc, 128, 256*256);
break;
}
vidready|=2;
FlushPalette();
return 1;
}
void KillVideo(void)
{
if(vidready)
{
vga_setmode(TEXT);
vidready=0;
}
}
void LockConsole(void){}
void UnlockConsole(void){}
void BlitScreen(uint8 *XBuf)
{
uint32 dest;
int tlines;
if(eoptions&4 && !NoWaiting)
vga_waitretrace();
tlines=erendline-srendline+1;
dest=ScreenLoc;
switch(FCEUDvmode)
{
case 1:dest+=(((240-tlines)>>1)<<8);break;
case 2:
case 3:dest+=(((256-tlines)>>1)<<8);break;
case 4:
case 5:dest+=(((240-tlines)>>1)*640+((640-512)>>1));break;
case 8:
case 6:if(tlines>224) tlines=224;dest+=(((224-tlines)>>1)<<8);break;
}
XBuf+=(srendline<<8)+(srendline<<4);
_farsetsel(_dos_ds);
if(eoptions&DO_CLIPSIDES)
{
asm volatile(
"agoop1:\n\t"
"movl $30,%%eax\n\t"
"agoop2:\n\t"
"movl (%%esi),%%edx\n\t"
"movl 4(%%esi),%%ecx\n\t"
".byte 0x64 \n\t"
"movl %%edx,(%%edi)\n\t"
".byte 0x64 \n\t"
"movl %%ecx,4(%%edi)\n\t"
"addl $8,%%esi\n\t"
"addl $8,%%edi\n\t"
"decl %%eax\n\t"
"jne agoop2\n\t"
"addl $32,%%esi\n\t"
"addl $16,%%edi\n\t"
"decb %%bl\n\t"
"jne agoop1\n\t"
:
: "S" (XBuf+8), "D" (dest+8), "b" (tlines)
: "%eax","%cc","%edx","%ecx" );
}
else
{
asm volatile(
"goop1:\n\t"
"movl $32,%%eax\n\t"
"goop2:\n\t"
"movl (%%esi),%%edx\n\t"
"movl 4(%%esi),%%ecx\n\t"
".byte 0x64 \n\t"
"movl %%edx,(%%edi)\n\t"
".byte 0x64 \n\t"
"movl %%ecx,4(%%edi)\n\t"
"addl $8,%%esi\n\t"
"addl $8,%%edi\n\t"
"decl %%eax\n\t"
"jne goop2\n\t"
"addl $16,%%esi\n\t"
"decb %%bl\n\t"
"jne goop1\n\t"
:
: "S" (XBuf), "D" (dest), "b" (tlines)
: "%eax","%cc","%edx","%ecx" );
}
}

22
src/attic/pc/dos-video.h Normal file
View File

@ -0,0 +1,22 @@
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2002 Xodnizel
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
extern int FCEUDvmode;

128
src/attic/pc/dos.c Normal file
View File

@ -0,0 +1,128 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <crt0.h>
#include <sys/farptr.h>
#include <go32.h>
#include "dos.h"
#include "dos-joystick.h"
#include "dos-video.h"
#include "dos-sound.h"
#include "../common/args.h"
#include "../common/config.h"
/* _CRT0_FLAG_LOCK_MEMORY might not always result in all memory being locked.
Bummer. I'll add code to explicitly lock the data touched by the sound
interrupt handler(and the handler itself), if necessary(though that might
be tricky...). I'll also to cover the data the keyboard
interrupt handler touches.
*/
int _crt0_startup_flags = _CRT0_FLAG_FILL_SBRK_MEMORY | _CRT0_FLAG_LOCK_MEMORY | _CRT0_FLAG_USE_DOS_SLASHES;
static int f8bit=0;
int soundo=44100;
int doptions=0;
CFGSTRUCT DriverConfig[]={
NAC("sound",soundo),
AC(doptions),
AC(f8bit),
AC(FCEUDvmode),
NACA("joybmap",joyBMap),
AC(joy),
ENDCFGSTRUCT
};
char *DriverUsage=
"-vmode x Select video mode(all are 8 bpp).\n\
1 = 256x240 6 = 256x224(with scanlines)\n\
2 = 256x256 8 = 256x224\n\
3 = 256x256(with scanlines)\n\
-vsync x Wait for the screen's vertical retrace before updating the\n\
screen. Refer to the documentation for caveats.\n\
0 = Disabled.\n\
1 = Enabled.\n\
-sound x Sound.\n\
0 = Disabled.\n\
Otherwise, x = playback rate.\n\
-f8bit x Force 8-bit sound.\n\
0 = Disabled.\n\
1 = Enabled.";
ARGPSTRUCT DriverArgs[]={
{"-vmode",0,&FCEUDvmode,0},
{"-sound",0,&soundo,0},
{"-f8bit",0,&f8bit,0},
{"-vsync",0,&doptions,DO_VSYNC},
{0,0,0,0}
};
void DoDriverArgs(void)
{
if(!joy) memset(joyBMap,0,sizeof(joyBMap));
}
int InitSound(void)
{
if(soundo)
{
if(soundo==1)
soundo=44100;
soundo=InitSB(soundo,f8bit?0:1);
FCEUI_Sound(soundo);
}
return(soundo);
}
void WriteSound(int32 *Buffer, int Count, int NoWaiting)
{
WriteSBSound(Buffer,Count,NoWaiting);
}
void KillSound(void)
{
if(soundo)
KillSB();
}
void DOSMemSet(uint32 A, uint8 V, uint32 count)
{
uint32 x;
_farsetsel(_dos_ds);
for(x=0;x<count;x++)
_farnspokeb(A+x,V);
}
static char *arg0;
uint8 *GetBaseDirectory(void)
{
int x=0;
uint8 *ret = 0;
if(arg0)
for(x=strlen(arg0);x>=0;x--)
{
if(arg0[x]=='/' || arg0[x]=='\\')
{
ret = malloc(x + 1);
strncpy(ret,arg0,x);
break;
}
}
if(!ret) { x=0; ret = malloc(1); }
BaseDirectory[x]=0;
}
int main(int argc, char *argv[])
{
puts("\nStarting FCE Ultra "VERSION_STRING"...\n");
arg0=argv[0];
return(CLImain(argc,argv));
}

27
src/attic/pc/dos.h Normal file
View File

@ -0,0 +1,27 @@
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2002 Xodnizel
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "main.h"
#include "dface.h"
#include "input.h"
void DOSMemSet(uint32 A, uint8 V, uint32 count);
#define DO_VSYNC 1

720
src/attic/pc/input.c Normal file
View File

@ -0,0 +1,720 @@
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2002 Xodnizel
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <string.h>
#include "main.h"
#include "dface.h"
#include "input.h"
#include "../common/cheat.h"
/* UsrInputType[] is user-specified. InputType[] is current
(game loading can override user settings)
*/
int UsrInputType[3]={SI_GAMEPAD,SI_GAMEPAD,SIFC_NONE};
int InputType[3]={0,0,0};
static int cspec=0;
int gametype=0;
/* Necessary for proper GUI functioning(configuring when a game isn't loaded). */
void InputUserActiveFix(void)
{
int x;
for(x=0;x<3;x++) InputType[x]=UsrInputType[x];
}
void ParseGIInput(FCEUGI *gi)
{
gametype=gi->type;
InputType[0]=UsrInputType[0];
InputType[1]=UsrInputType[1];
InputType[2]=UsrInputType[2];
if(gi->input[0]>=0)
InputType[0]=gi->input[0];
if(gi->input[1]>=0)
InputType[1]=gi->input[1];
if(gi->inputfc>=0)
InputType[2]=gi->inputfc;
cspec = gi->cspecial;
#ifdef EXTGUI
Giggles(gi->cspecial);
#endif
}
static uint8 QuizKingData=0;
static uint8 HyperShotData=0;
static uint32 MahjongData=0;
static uint32 FTrainerData=0;
static uint8 TopRiderData=0;
static uint8 BWorldData[1+13+1];
static void UpdateFKB(void);
static void UpdateGamepad(void);
static void UpdateQuizKing(void);
static void UpdateHyperShot(void);
static void UpdateMahjong(void);
static void UpdateFTrainer(void);
static void UpdateTopRider(void);
static uint32 JSreturn=0;
int NoWaiting=1;
#ifndef EXTGUI
static void DoCheatSeq(void)
{
#if defined(DOS) || defined(SDL)
SilenceSound(1);
#endif
KillKeyboard();
KillVideo();
DoConsoleCheatConfig();
InitVideo(CurGame);
InitKeyboard();
#if defined(DOS) || defined(SDL)
SilenceSound(0);
#endif
}
#endif
#include "keyscan.h"
static char *keys=0;
static int DIPS=0;
static uint8 keyonce[MKK_COUNT];
#define KEY(__a) keys[MKK(__a)]
static int _keyonly(int a)
{
if(keys[a])
{
if(!keyonce[a])
{
keyonce[a]=1;
return(1);
}
}
else
keyonce[a]=0;
return(0);
}
#define keyonly(__a) _keyonly(MKK(__a))
static int cidisabled=0;
static void KeyboardCommands(void)
{
int is_shift, is_alt;
keys=GetKeyboard();
if(InputType[2]==SIFC_FKB)
{
if(keyonly(SCROLLLOCK))
{
cidisabled^=1;
FCEUI_DispMessage("Family Keyboard %sabled.",cidisabled?"en":"dis");
}
#ifdef SDL
SDL_WM_GrabInput(cidisabled?SDL_GRAB_ON:SDL_GRAB_OFF);
#endif
if(cidisabled) return;
}
is_shift = KEY(LEFTSHIFT) | KEY(RIGHTSHIFT);
is_alt = KEY(LEFTALT) | KEY(RIGHTALT);
if(keyonly(F4))
{
if(is_shift) FCEUI_SetRenderDisable(-1, 2);
else FCEUI_SetRenderDisable(2, -1);
}
#ifdef SDL
if(keyonly(ENTER) && is_alt) ToggleFS();
#endif
NoWaiting&=~1;
if(KEY(GRAVE))
NoWaiting|=1;
if(gametype==GIT_FDS)
{
if(keyonly(F6)) FCEUI_FDSSelect();
if(keyonly(F8)) FCEUI_FDSInsert();
}
if(keyonly(F9)) FCEUI_SaveSnapshot();
if(gametype!=GIT_NSF)
{
#ifndef EXTGUI
if(keyonly(F2)) DoCheatSeq();
#endif
if(keyonly(F5))
{
if(is_shift)
FCEUI_SaveMovie(NULL,0,NULL);
else
FCEUI_SaveState(NULL);
}
if(keyonly(F7))
{
if(is_shift)
FCEUI_LoadMovie(NULL,0);
else
FCEUI_LoadState(NULL);
}
}
if(keyonly(F1)) FCEUI_ToggleTileView();
if(keyonly(MINUS)) DecreaseEmulationSpeed();
if(keyonly(EQUAL)) IncreaseEmulationSpeed();
if(keyonly(BACKSPACE)) FCEUI_MovieToggleFrameDisplay();
if(keyonly(BACKSLASH)) FCEUI_ToggleEmulationPause();
if(keyonly(RIGHTCONTROL)) FCEUI_FrameAdvance();
if(keyonly(F10)) FCEUI_ResetNES();
if(keyonly(F11)) FCEUI_PowerNES();
#ifdef EXTGUI
if(keyonly(F3)) GUI_Hide(-1);
if(KEY(F12)) GUI_RequestExit();
if(KEY(ESCAPE)) CloseGame();
#else
if(KEY(F12) || KEY(ESCAPE)) CloseGame();
#endif
if(gametype==GIT_VSUNI)
{
if(keyonly(F8)) FCEUI_VSUniCoin();
if(keyonly(F6))
{
DIPS^=1;
FCEUI_VSUniToggleDIPView();
}
if(!(DIPS&1)) goto DIPSless;
if(keyonly(1)) FCEUI_VSUniToggleDIP(0);
if(keyonly(2)) FCEUI_VSUniToggleDIP(1);
if(keyonly(3)) FCEUI_VSUniToggleDIP(2);
if(keyonly(4)) FCEUI_VSUniToggleDIP(3);
if(keyonly(5)) FCEUI_VSUniToggleDIP(4);
if(keyonly(6)) FCEUI_VSUniToggleDIP(5);
if(keyonly(7)) FCEUI_VSUniToggleDIP(6);
if(keyonly(8)) FCEUI_VSUniToggleDIP(7);
}
else
{
static uint8 bbuf[32];
static int bbuft;
static int barcoder = 0;
if(keyonly(H)) FCEUI_NTSCSELHUE();
if(keyonly(T)) FCEUI_NTSCSELTINT();
if(KEY(KP_MINUS) || KEY(MINUS)) FCEUI_NTSCDEC();
if(KEY(KP_PLUS) || KEY(EQUAL)) FCEUI_NTSCINC();
if((InputType[2] == SIFC_BWORLD) || (cspec == SIS_DATACH))
{
if(keyonly(F8))
{
barcoder ^= 1;
if(!barcoder)
{
if(InputType[2] == SIFC_BWORLD)
{
strcpy(&BWorldData[1],bbuf);
BWorldData[0]=1;
}
else
FCEUI_DatachSet(bbuf);
FCEUI_DispMessage("Barcode Entered");
}
else { bbuft = 0; FCEUI_DispMessage("Enter Barcode");}
}
} else barcoder = 0;
#define SSM(x) \
{ if(barcoder) { if(bbuft < 13) {bbuf[bbuft++] = '0' + x; bbuf[bbuft] = 0;} FCEUI_DispMessage("Barcode: %s",bbuf);} \
else { \
if(is_shift) FCEUI_SelectMovie(x,1); \
else FCEUI_SelectState(x,1); \
} }
DIPSless:
if(keyonly(0)) SSM(0);
if(keyonly(1)) SSM(1);
if(keyonly(2)) SSM(2);
if(keyonly(3)) SSM(3);
if(keyonly(4)) SSM(4);
if(keyonly(5)) SSM(5);
if(keyonly(6)) SSM(6);
if(keyonly(7)) SSM(7);
if(keyonly(8)) SSM(8);
if(keyonly(9)) SSM(9);
#undef SSM
}
}
#define MK(x) {{BUTTC_KEYBOARD},{0},{MKK(x)},1}
#define MK2(x1,x2) {{BUTTC_KEYBOARD},{0},{MKK(x1),MKK(x2)},2}
#define MKZ() {{0},{0},{0},0}
#define GPZ() {MKZ(), MKZ(), MKZ(), MKZ()}
ButtConfig GamePadConfig[4][10]={
/* Gamepad 1 */
{
MK(KP3), MK(KP2), MK(TAB), MK(ENTER), MK(W),MK(Z),
MK(A), MK(S), MKZ(), MKZ()
},
/* Gamepad 2 */
GPZ(),
/* Gamepad 3 */
GPZ(),
/* Gamepad 4 */
GPZ()
};
static void UpdateGamepad(void)
{
if(FCEUI_IsMovieActive()<0)
return;
static int rapid=0;
uint32 JS=0;
int x;
int wg;
rapid^=1;
for(wg=0;wg<4;wg++)
{
for(x=0;x<8;x++)
if(DTestButton(&GamePadConfig[wg][x]))
JS|=(1<<x)<<(wg<<3);
if(rapid)
for(x=0;x<2;x++)
if(DTestButton(&GamePadConfig[wg][8+x]))
JS|=(1<<x)<<(wg<<3);
}
// for(x=0;x<32;x+=8) /* Now, test to see if anything weird(up+down at same time)
// is happening, and correct */
// {
// if((JS & (0xC0<<x) ) == (0xC0<<x) ) JS&=~(0xC0<<x);
// if((JS & (0x30<<x) ) == (0x30<<x) ) JS&=~(0x30<<x);
// }
JSreturn=JS;
}
ButtConfig powerpadsc[2][12]={
{
MK(O),MK(P),MK(BRACKET_LEFT),
MK(BRACKET_RIGHT),MK(K),MK(L),MK(SEMICOLON),
MK(APOSTROPHE),
MK(M),MK(COMMA),MK(PERIOD),MK(SLASH)
},
{
MK(O),MK(P),MK(BRACKET_LEFT),
MK(BRACKET_RIGHT),MK(K),MK(L),MK(SEMICOLON),
MK(APOSTROPHE),
MK(M),MK(COMMA),MK(PERIOD),MK(SLASH)
}
};
static uint32 powerpadbuf[2]={0,0};
static uint32 UpdatePPadData(int w)
{
if(FCEUI_IsMovieActive()<0)
return;
uint32 r=0;
ButtConfig *ppadtsc=powerpadsc[w];
int x;
for(x=0;x<12;x++)
if(DTestButton(&ppadtsc[x])) r|=1<<x;
return r;
}
static uint32 MouseData[3]={0,0,0};
static uint8 fkbkeys[0x48];
void FCEUD_UpdateInput(void)
{
int x;
int t=0;
UpdatePhysicalInput();
KeyboardCommands();
for(x=0;x<2;x++)
switch(InputType[x])
{
case SI_GAMEPAD:t|=1;break;
case SI_ARKANOID:t|=2;break;
case SI_ZAPPER:t|=2;break;
case SI_POWERPADA:
case SI_POWERPADB:powerpadbuf[x]=UpdatePPadData(x);break;
}
switch(InputType[2])
{
case SIFC_ARKANOID:t|=2;break;
case SIFC_SHADOW:t|=2;break;
case SIFC_FKB:if(cidisabled) UpdateFKB();break;
case SIFC_HYPERSHOT: UpdateHyperShot();break;
case SIFC_MAHJONG: UpdateMahjong();break;
case SIFC_QUIZKING: UpdateQuizKing();break;
case SIFC_FTRAINERB:
case SIFC_FTRAINERA: UpdateFTrainer();break;
case SIFC_TOPRIDER: UpdateTopRider();break;
case SIFC_OEKAKIDS:t|=2;break;
}
if(t&1)
UpdateGamepad();
if(t&2)
GetMouseData(MouseData);
}
void InitOtherInput(void)
{
void *InputDPtr;
int t;
int x;
int attrib;
for(t=0,x=0;x<2;x++)
{
attrib=0;
InputDPtr=0;
switch(InputType[x])
{
case SI_POWERPADA:
case SI_POWERPADB:InputDPtr=&powerpadbuf[x];break;
case SI_GAMEPAD:InputDPtr=&JSreturn;break;
case SI_ARKANOID:InputDPtr=MouseData;t|=1;break;
case SI_ZAPPER:InputDPtr=MouseData;
t|=1;
attrib=1;
break;
}
FCEUI_SetInput(x,InputType[x],InputDPtr,attrib);
}
attrib=0;
InputDPtr=0;
switch(InputType[2])
{
case SIFC_SHADOW:InputDPtr=MouseData;t|=1;attrib=1;break;
case SIFC_OEKAKIDS:InputDPtr=MouseData;t|=1;attrib=1;break;
case SIFC_ARKANOID:InputDPtr=MouseData;t|=1;break;
case SIFC_FKB:InputDPtr=fkbkeys;break;
case SIFC_HYPERSHOT:InputDPtr=&HyperShotData;break;
case SIFC_MAHJONG:InputDPtr=&MahjongData;break;
case SIFC_QUIZKING:InputDPtr=&QuizKingData;break;
case SIFC_TOPRIDER:InputDPtr=&TopRiderData;break;
case SIFC_BWORLD:InputDPtr=BWorldData;break;
case SIFC_FTRAINERA:
case SIFC_FTRAINERB:InputDPtr=&FTrainerData;break;
}
FCEUI_SetInputFC(InputType[2],InputDPtr,attrib);
FCEUI_DisableFourScore(eoptions&EO_NOFOURSCORE);
if(t)
InitMouse();
}
ButtConfig fkbmap[0x48]=
{
MK(F1),MK(F2),MK(F3),MK(F4),MK(F5),MK(F6),MK(F7),MK(F8),
MK(1),MK(2),MK(3),MK(4),MK(5),MK(6),MK(7),MK(8),MK(9),MK(0),
MK(MINUS),MK(EQUAL),MK(BACKSLASH),MK(BACKSPACE),
MK(ESCAPE),MK(Q),MK(W),MK(E),MK(R),MK(T),MK(Y),MK(U),MK(I),MK(O),
MK(P),MK(GRAVE),MK(BRACKET_LEFT),MK(ENTER),
MK(LEFTCONTROL),MK(A),MK(S),MK(D),MK(F),MK(G),MK(H),MK(J),MK(K),
MK(L),MK(SEMICOLON),MK(APOSTROPHE),MK(BRACKET_RIGHT),MK(INSERT),
MK(LEFTSHIFT),MK(Z),MK(X),MK(C),MK(V),MK(B),MK(N),MK(M),MK(COMMA),
MK(PERIOD),MK(SLASH),MK(RIGHTALT),MK(RIGHTSHIFT),MK(LEFTALT),MK(SPACE),
MK(DELETE),MK(END),MK(PAGEDOWN),
MK(CURSORUP),MK(CURSORLEFT),MK(CURSORRIGHT),MK(CURSORDOWN)
};
static void UpdateFKB(void)
{
int x;
for(x=0;x<0x48;x++)
{
fkbkeys[x]=0;
if(DTestButton(&fkbmap[x]))
fkbkeys[x]=1;
}
}
static ButtConfig HyperShotButtons[4]=
{
MK(Q),MK(W),MK(E),MK(R)
};
static void UpdateHyperShot(void)
{
int x;
HyperShotData=0;
for(x=0;x<0x4;x++)
{
if(DTestButton(&HyperShotButtons[x]))
HyperShotData|=1<<x;
}
}
static ButtConfig MahjongButtons[21]=
{
MK(Q),MK(W),MK(E),MK(R),MK(T),
MK(A),MK(S),MK(D),MK(F),MK(G),MK(H),MK(J),MK(K),MK(L),
MK(Z),MK(X),MK(C),MK(V),MK(B),MK(N),MK(M)
};
static void UpdateMahjong(void)
{
int x;
MahjongData=0;
for(x=0;x<21;x++)
{
if(DTestButton(&MahjongButtons[x]))
MahjongData|=1<<x;
}
}
ButtConfig QuizKingButtons[6]=
{
MK(Q),MK(W),MK(E),MK(R),MK(T),MK(Y)
};
static void UpdateQuizKing(void)
{
int x;
QuizKingData=0;
for(x=0;x<6;x++)
{
if(DTestButton(&QuizKingButtons[x]))
QuizKingData|=1<<x;
}
}
ButtConfig TopRiderButtons[8]=
{
MK(Q),MK(W),MK(E),MK(R),MK(T),MK(Y),MK(U),MK(I)
};
static void UpdateTopRider(void)
{
int x;
TopRiderData=0;
for(x=0;x<8;x++)
if(DTestButton(&TopRiderButtons[x]))
TopRiderData|=1<<x;
}
ButtConfig FTrainerButtons[12]=
{
MK(O),MK(P),MK(BRACKET_LEFT),
MK(BRACKET_RIGHT),MK(K),MK(L),MK(SEMICOLON),
MK(APOSTROPHE),
MK(M),MK(COMMA),MK(PERIOD),MK(SLASH)
};
static void UpdateFTrainer(void)
{
int x;
FTrainerData=0;
for(x=0;x<12;x++)
{
if(DTestButton(&FTrainerButtons[x]))
FTrainerData|=1<<x;
}
}
static void subcon(char *text, ButtConfig *bc)
{
uint8 buf[256];
int wc;
for(wc=0;wc<MAXBUTTCONFIG;wc++)
{
sprintf(buf,"%s (%d)",text,wc+1);
DWaitButton(buf,bc,wc);
if(wc && bc->ButtType[wc]==bc->ButtType[wc-1] && bc->DeviceNum[wc]==bc->DeviceNum[wc-1] &&
bc->ButtonNum[wc]==bc->ButtonNum[wc-1])
break;
}
bc->NumC=wc;
}
void ConfigDevice(int which, int arg)
{
uint8 buf[256];
int x;
ButtonConfigBegin();
switch(which)
{
case FCFGD_QUIZKING:
for(x=0;x<6;x++)
{
sprintf(buf,"Quiz King Buzzer #%d", x+1);
subcon(buf,&QuizKingButtons[x]);
}
break;
case FCFGD_HYPERSHOT:
for(x=0;x<4;x++)
{
sprintf(buf,"Hyper Shot %d: %s",((x&2)>>1)+1,(x&1)?"JUMP":"RUN");
subcon(buf,&HyperShotButtons[x]);
}
break;
case FCFGD_POWERPAD:
for(x=0;x<12;x++)
{
sprintf(buf,"PowerPad %d: %d", (arg&1)+1,x+11);
subcon(buf,&powerpadsc[arg&1][x]);
}
break;
case FCFGD_GAMEPAD:
{
char *str[10]={"A","B","SELECT","START","UP","DOWN","LEFT","RIGHT","Rapid A","Rapid B"};
for(x=0;x<10;x++)
{
sprintf(buf,"GamePad #%d: %s",arg+1,str[x]);
subcon(buf,&GamePadConfig[arg][x]);
}
}
break;
}
ButtonConfigEnd();
}
CFGSTRUCT InputConfig[]={
ACA(UsrInputType),
AC(powerpadsc),
AC(QuizKingButtons),
AC(FTrainerButtons),
AC(HyperShotButtons),
AC(MahjongButtons),
AC(GamePadConfig),
AC(fkbmap),
ENDCFGSTRUCT
};
static void InputCfg(char *text)
{
if(!strncasecmp(text,"gamepad",strlen("gamepad")))
{
ConfigDevice(FCFGD_GAMEPAD,(text[strlen("gamepad")]-'1')&3);
}
else if(!strncasecmp(text,"powerpad",strlen("powerpad")))
{
ConfigDevice(FCFGD_POWERPAD,(text[strlen("powerpad")]-'1')&1);
}
else if(!strcasecmp(text,"hypershot"))
ConfigDevice(FCFGD_HYPERSHOT,0);
else if(!strcasecmp(text,"quizking"))
ConfigDevice(FCFGD_QUIZKING,0);
}
static void FCExp(char *text)
{
static char *fccortab[11]={"none","arkanoid","shadow","4player","fkb","hypershot",
"mahjong","quizking","ftrainera","ftrainerb","oekakids"};
static int fccortabi[11]={SIFC_NONE,SIFC_ARKANOID,SIFC_SHADOW,
SIFC_4PLAYER,SIFC_FKB,SIFC_HYPERSHOT,SIFC_MAHJONG,SIFC_QUIZKING,
SIFC_FTRAINERA,SIFC_FTRAINERB,SIFC_OEKAKIDS};
int y;
for(y=0;y<11;y++)
if(!strcmp(fccortab[y],text))
UsrInputType[2]=fccortabi[y];
}
static char *cortab[6]={"none","gamepad","zapper","powerpada","powerpadb","arkanoid"};
static int cortabi[6]={SI_NONE,SI_GAMEPAD,
SI_ZAPPER,SI_POWERPADA,SI_POWERPADB,SI_ARKANOID};
static void Input1(char *text)
{
int y;
for(y=0;y<6;y++)
if(!strcmp(cortab[y],text))
UsrInputType[0]=cortabi[y];
}
static void Input2(char *text)
{
int y;
for(y=0;y<6;y++)
if(!strcmp(cortab[y],text))
UsrInputType[1]=cortabi[y];
}
ARGPSTRUCT InputArgs[]={
{"-inputcfg",0,(void *)InputCfg,0x2000},
{"-fcexp",0,(void *)FCExp,0x2000},
{"-input1",0,(void *)Input1,0x2000},
{"-input2",0,(void *)Input2,0x2000},
{0,0,0,0}
};

38
src/attic/pc/input.h Normal file
View File

@ -0,0 +1,38 @@
#ifndef _aosdfjk02fmasf
#define _aosdfjk02fmasf
#define MAXBUTTCONFIG 4
typedef struct {
uint8 ButtType[MAXBUTTCONFIG];
uint8 DeviceNum[MAXBUTTCONFIG];
uint16 ButtonNum[MAXBUTTCONFIG];
uint32 NumC;
//uint64 DeviceID[MAXBUTTCONFIG]; /* TODO */
} ButtConfig;
extern CFGSTRUCT InputConfig[];
extern ARGPSTRUCT InputArgs[];
void ParseGIInput(FCEUGI *GI);
#define BUTTC_KEYBOARD 0x00
#define BUTTC_JOYSTICK 0x01
#define BUTTC_MOUSE 0x02
#define FCFGD_GAMEPAD 1
#define FCFGD_POWERPAD 2
#define FCFGD_HYPERSHOT 3
#define FCFGD_QUIZKING 4
void InitOtherInput(void);
void InputUserActiveFix(void);
#ifdef EXTGUI
extern ButtConfig GamePadConfig[4][10];
extern ButtConfig powerpadsc[2][12];
extern ButtConfig QuizKingButtons[6];
extern ButtConfig FTrainerButtons[12];
#endif
void IncreaseEmulationSpeed(void);
void DecreaseEmulationSpeed(void);
void FCEUD_UpdateInput(void);
#endif

161
src/attic/pc/keyscan.h Normal file
View File

@ -0,0 +1,161 @@
#ifdef SDL
#include <SDL.h>
#define SDLK_A SDLK_a
#define SDLK_B SDLK_b
#define SDLK_C SDLK_c
#define SDLK_D SDLK_d
#define SDLK_E SDLK_e
#define SDLK_F SDLK_f
#define SDLK_G SDLK_g
#define SDLK_H SDLK_h
#define SDLK_I SDLK_i
#define SDLK_J SDLK_j
#define SDLK_K SDLK_k
#define SDLK_L SDLK_l
#define SDLK_M SDLK_m
#define SDLK_N SDLK_n
#define SDLK_O SDLK_o
#define SDLK_P SDLK_p
#define SDLK_Q SDLK_q
#define SDLK_R SDLK_r
#define SDLK_S SDLK_s
#define SDLK_T SDLK_t
#define SDLK_U SDLK_u
#define SDLK_V SDLK_v
#define SDLK_W SDLK_w
#define SDLK_X SDLK_x
#define SDLK_Y SDLK_y
#define SDLK_Z SDLK_z
#define SDLK_LEFTCONTROL SDLK_LCTRL
#define SDLK_RIGHTCONTROL SDLK_RCTRL
#define SDLK_LEFTALT SDLK_LALT
#define SDLK_RIGHTALT SDLK_RALT
#define SDLK_LEFTSHIFT SDLK_LSHIFT
#define SDLK_RIGHTSHIFT SDLK_RSHIFT
#define SDLK_CURSORDOWN SDLK_DOWN
#define SDLK_CURSORUP SDLK_UP
#define SDLK_CURSORLEFT SDLK_LEFT
#define SDLK_CURSORRIGHT SDLK_RIGHT
#define SDLK_ENTER SDLK_RETURN
#define SDLK_EQUAL SDLK_EQUALS
#define SDLK_APOSTROPHE SDLK_QUOTE
#define SDLK_BRACKET_LEFT SDLK_LEFTBRACKET
#define SDLK_BRACKET_RIGHT SDLK_RIGHTBRACKET
#define SDLK_SCROLLLOCK SDLK_SCROLLOCK /* I guess the SDL people don't like lots of Ls... */
#define SDLK_GRAVE SDLK_BACKQUOTE
#define MKK(k) SDLK_##k
#define MKK_COUNT (SDLK_LAST+1)
#elif DOS
#define SCAN_GRAVE 0x29
#define SCAN_1 0x02
#define SCAN_2 0x03
#define SCAN_3 0x04
#define SCAN_4 0x05
#define SCAN_5 0x06
#define SCAN_6 0x07
#define SCAN_7 0x08
#define SCAN_8 0x09
#define SCAN_9 0x0A
#define SCAN_0 0x0B
#define SCAN_MINUS 0x0C
#define SCAN_EQUAL 0x0D
#define SCAN_BACKSLASH 0x2B
#define SCAN_BACKSPACE 0x0E
#define SCAN_TAB 0x0F
#define SCAN_Q 0x10
#define SCAN_W 0x11
#define SCAN_E 0x12
#define SCAN_R 0x13
#define SCAN_T 0x14
#define SCAN_Y 0x15
#define SCAN_U 0x16
#define SCAN_I 0x17
#define SCAN_O 0x18
#define SCAN_P 0x19
#define SCAN_BRACKET_LEFT 0x1A
#define SCAN_BRACKET_RIGHT 0x1B
#define SCAN_LOWBACKSLASH 0x2B
#define SCAN_CAPSLOCK 0x3A
#define SCAN_A 0x1E
#define SCAN_S 0x1F
#define SCAN_D 0x20
#define SCAN_F 0x21
#define SCAN_G 0x22
#define SCAN_H 0x23
#define SCAN_J 0x24
#define SCAN_K 0x25
#define SCAN_L 0x26
#define SCAN_SEMICOLON 0x27
#define SCAN_APOSTROPHE 0x28
#define SCAN_ENTER 0x1C
#define SCAN_LEFTSHIFT 0x2A
#define SCAN_Z 0x2C
#define SCAN_X 0x2D
#define SCAN_C 0x2E
#define SCAN_V 0x2F
#define SCAN_B 0x30
#define SCAN_N 0x31
#define SCAN_M 0x32
#define SCAN_COMMA 0x33
#define SCAN_PERIOD 0x34
#define SCAN_SLASH 0x35
#define SCAN_RIGHTSHIFT 0x36
#define SCAN_LEFTCONTROL 0x1D
#define SCAN_LEFTALT 0x38
#define SCAN_SPACE 0x39
/* Extended keys. */
#define SCAN_RIGHTALT (0x38|0x80)
#define SCAN_RIGHTCONTROL (0x1D|0x80)
#define SCAN_BL_INSERT (0x52|0x80)
#define SCAN_BL_DELETE (0x53|0x80)
#define SCAN_BL_CURSORLEFT (0x4B|0x80)
#define SCAN_BL_HOME (0x47|0x80)
#define SCAN_BL_END (0x4F|0x80)
#define SCAN_BL_CURSORUP (0x48|0x80)
#define SCAN_BL_CURSORDOWN (0x50|0x80)
#define SCAN_BL_PAGEUP (0x49|0x80)
#define SCAN_BL_PAGEDOWN (0x51|0x80)
#define SCAN_BL_CURSORRIGHT (0x4D|0x80)
#define SCAN_SCROLLLOCK 0x46
/* Keys often found in the key pad area. */
#define SCAN_NUMLOCK 0x45
#define SCAN_HOME 0x47
#define SCAN_CURSORLEFT 0x4B
#define SCAN_END 0x4F
#define SCAN_SLASH 0x35
#define SCAN_CURSORUP 0x48
#define SCAN_CENTER 0x4C
#define SCAN_CURSORDOWN 0x50
#define SCAN_INSERT 0x52
#define SCAN_PAUSE 0xC5
#define SCAN_ASTERISK 0x37
#define SCAN_PAGEUP 0x49
#define SCAN_CURSORRIGHT 0x4D
#define SCAN_PAGEDOWN 0x51
#define SCAN_DELETE 0x53
#define SCAN_KP_MINUS 0x4A
#define SCAN_KP_PLUS 0x4E
#define SCAN_KP_ENTER 0x1C
#define SCAN_ESCAPE 0x01
#define SCAN_F1 0x3B
#define SCAN_F2 0x3C
#define SCAN_F3 0x3D
#define SCAN_F4 0x3E
#define SCAN_F5 0x3F
#define SCAN_F6 0x40
#define SCAN_F7 0x41
#define SCAN_F8 0x42
#define SCAN_F9 0x43
#define SCAN_F10 0x44
#define SCAN_F11 0x57
#define SCAN_F12 0x58
#define MK_COUNT 256
#define MK(k) SCAN_##k
#endif

553
src/attic/pc/main.c Normal file
View File

@ -0,0 +1,553 @@
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2002 Xodnizel
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include "main.h"
#include "throttle.h"
#include "../common/cheat.h"
#include "input.h"
#include "dface.h"
extern int32 fps_scale;
int CloseGame(void);
static char *soundrecfn=0; /* File name of sound recording. */
static int ntsccol=0,ntschue=0,ntsctint=0;
int soundvol=100;
long soundq=0;
int _sound=1;
long soundrate=48000;
#ifdef WIN32
long soundbufsize=52;
#else
long soundbufsize=24;
#endif
#ifdef FRAMESKIP
static int frameskip=0;
#endif
static int inited=0;
static int isloaded=0; // Is game loaded?
int srendlinev[2]={8,0};
int erendlinev[2]={231,239};
static uint8 *DrBaseDirectory;
int eoptions=0;
static void DriverKill(void);
static int DriverInitialize(FCEUGI *gi);
int gametype;
FCEUGI *CurGame=NULL;
static void ParseGI(FCEUGI *gi)
{
ParseGIInput(gi);
gametype=gi->type;
}
#ifndef EXTGUI
void FCEUD_PrintError(char *s)
{
puts(s);
}
void FCEUD_Message(char *s)
{
fputs(s,stdout);
}
#endif
static char *cpalette=0;
static void LoadCPalette(void)
{
uint8 tmpp[192];
FILE *fp;
if(!(fp=FCEUD_UTF8fopen(cpalette,"rb")))
{
printf(" Error loading custom palette from file: %s\n",cpalette);
return;
}
fread(tmpp,1,192,fp);
FCEUI_SetPaletteArray(tmpp);
fclose(fp);
}
#ifdef EXTGUI
extern CFGSTRUCT GUIConfig;
#endif
static CFGSTRUCT fceuconfig[]={
AC(soundrate),
AC(soundq),
AC(_sound),
AC(soundvol),
AC(soundbufsize),
ACS(cpalette),
AC(ntsctint),
AC(ntschue),
AC(ntsccol),
AC(eoptions),
ACA(srendlinev),
ACA(erendlinev),
ADDCFGSTRUCT(InputConfig),
ADDCFGSTRUCT(DriverConfig),
#ifdef EXTGUI
ADDCFGSTRUCT(GUIConfig),
#endif
ENDCFGSTRUCT
};
static void SaveConfig(void)
{
char tdir[2048];
sprintf(tdir,"%s"PSS"fceu98.cfg",DrBaseDirectory);
FCEUI_GetNTSCTH(&ntsctint, &ntschue);
SaveFCEUConfig(tdir,fceuconfig);
}
static void LoadConfig(void)
{
char tdir[2048];
sprintf(tdir,"%s"PSS"fceu98.cfg",DrBaseDirectory);
FCEUI_GetNTSCTH(&ntsctint, &ntschue); /* Get default settings for if
no config file exists. */
LoadFCEUConfig(tdir,fceuconfig);
InputUserActiveFix();
}
static void CreateDirs(void)
{
char *subs[7]={"fcs","fcm","snaps","gameinfo","sav","cheats","movie"};
char tdir[2048];
int x;
#ifdef WIN32
mkdir(DrBaseDirectory);
for(x=0;x<6;x++)
{
sprintf(tdir,"%s"PSS"%s",DrBaseDirectory,subs[x]);
mkdir(tdir);
}
#else
mkdir(DrBaseDirectory,S_IRWXU);
for(x=0;x<6;x++)
{
sprintf(tdir,"%s"PSS"%s",DrBaseDirectory,subs[x]);
mkdir(tdir,S_IRWXU);
}
#endif
}
#ifndef WIN32
static void SetSignals(void (*t)(int))
{
int sigs[11]={SIGINT,SIGTERM,SIGHUP,SIGPIPE,SIGSEGV,SIGFPE,SIGKILL,SIGALRM,SIGABRT,SIGUSR1,SIGUSR2};
int x;
for(x=0;x<11;x++)
signal(sigs[x],t);
}
static void CloseStuff(int signum)
{
DriverKill();
printf("\nSignal %d has been caught and dealt with...\n",signum);
switch(signum)
{
case SIGINT:printf("How DARE you interrupt me!\n");break;
case SIGTERM:printf("MUST TERMINATE ALL HUMANS\n");break;
case SIGHUP:printf("Reach out and hang-up on someone.\n");break;
case SIGPIPE:printf("The pipe has broken! Better watch out for floods...\n");break;
case SIGSEGV:printf("Iyeeeeeeeee!!! A segmentation fault has occurred. Have a fluffy day.\n");break;
/* So much SIGBUS evil. */
#ifdef SIGBUS
#if(SIGBUS!=SIGSEGV)
case SIGBUS:printf("I told you to be nice to the driver.\n");break;
#endif
#endif
case SIGFPE:printf("Those darn floating points. Ne'er know when they'll bite!\n");break;
case SIGALRM:printf("Don't throw your clock at the meowing cats!\n");break;
case SIGABRT:printf("Abort, Retry, Ignore, Fail?\n");break;
case SIGUSR1:
case SIGUSR2:printf("Killing your processes is not nice.\n");break;
}
exit(1);
}
#endif
static void DoArgs(int argc, char *argv[])
{
int x;
static ARGPSTRUCT FCEUArgs[]={
{"-soundbufsize",0,&soundbufsize,0},
{"-soundrate",0,&soundrate,0},
{"-soundq",0,&soundq,0},
#ifdef FRAMESKIP
{"-frameskip",0,&frameskip,0},
#endif
{"-sound",0,&_sound,0},
{"-soundvol",0,&soundvol,0},
{"-cpalette",0,&cpalette,0x4001},
{"-soundrecord",0,&soundrecfn,0x4001},
{"-ntsccol",0,&ntsccol,0},
{"-pal",0,&eoptions,0x8000|EO_PAL},
{"-lowpass",0,&eoptions,0x8000|EO_LOWPASS},
{"-gg",0,&eoptions,0x8000|EO_GAMEGENIE},
{"-no8lim",0,&eoptions,0x8001},
{"-snapname",0,&eoptions,0x8000|EO_SNAPNAME},
{"-nofs",0,&eoptions,0x8000|EO_NOFOURSCORE},
{"-clipsides",0,&eoptions,0x8000|EO_CLIPSIDES},
{"-nothrottle",0,&eoptions,0x8000|EO_NOTHROTTLE},
{"-slstart",0,&srendlinev[0],0},{"-slend",0,&erendlinev[0],0},
{"-slstartp",0,&srendlinev[1],0},{"-slendp",0,&erendlinev[1],0},
{0,(int *)InputArgs,0,0},
{0,(int *)DriverArgs,0,0},
{0,0,0,0}
};
ParseArguments(argc, argv, FCEUArgs);
if(cpalette)
{
if(cpalette[0]=='0')
if(cpalette[1]==0)
{
free(cpalette);
cpalette=0;
}
}
FCEUI_SetVidSystem((eoptions&EO_PAL)?1:0);
FCEUI_SetGameGenie((eoptions&EO_GAMEGENIE)?1:0);
FCEUI_SetLowPass((eoptions&EO_LOWPASS)?1:0);
FCEUI_DisableSpriteLimitation(eoptions&1);
FCEUI_SetSnapName(eoptions&EO_SNAPNAME);
for(x=0;x<2;x++)
{
if(srendlinev[x]<0 || srendlinev[x]>239) srendlinev[x]=0;
if(erendlinev[x]<srendlinev[x] || erendlinev[x]>239) erendlinev[x]=239;
}
FCEUI_SetRenderedLines(srendlinev[0],erendlinev[0],srendlinev[1],erendlinev[1]);
DoDriverArgs();
}
#include "usage.h"
/* Loads a game, given a full path/filename. The driver code must be
initialized after the game is loaded, because the emulator code
provides data necessary for the driver code(number of scanlines to
render, what virtual input devices to use, etc.).
*/
int LoadGame(const char *path)
{
FCEUGI *tmp;
CloseGame();
if(!(tmp=FCEUI_LoadGame(path,1)))
return 0;
CurGame=tmp;
ParseGI(tmp);
RefreshThrottleFPS();
if(!DriverInitialize(tmp))
return(0);
if(soundrecfn)
{
if(!FCEUI_BeginWaveRecord(soundrecfn))
{
free(soundrecfn);
soundrecfn=0;
}
}
isloaded=1;
#ifdef EXTGUI
if(eoptions&EO_AUTOHIDE) GUI_Hide(1);
#endif
FCEUD_NetworkConnect();
return 1;
}
/* Closes a game. Frees memory, and deinitializes the drivers. */
int CloseGame(void)
{
if(!isloaded) return(0);
FCEUI_CloseGame();
DriverKill();
isloaded=0;
CurGame=0;
if(soundrecfn)
FCEUI_EndWaveRecord();
#ifdef EXTGUI
GUI_Hide(0);
#endif
InputUserActiveFix();
return(1);
}
void FCEUD_Update(uint8 *XBuf, int32 *Buffer, int Count);
void DoFun(void)
{
uint8 *gfx;
int32 *sound;
int32 ssize;
static int fskipc=0;
static int opause=0;
#ifdef FRAMESKIP
fskipc=(fskipc+1)%(frameskip+1);
#endif
if(NoWaiting) {gfx=0;}
FCEUI_Emulate(&gfx, &sound, &ssize, fskipc);
FCEUD_Update(gfx, sound, ssize);
if(opause!=FCEUI_EmulationPaused())
{
opause=FCEUI_EmulationPaused();
SilenceSound(opause);
}
}
int CLImain(int argc, char *argv[])
{
int ret;
if(!(ret=FCEUI_Initialize()))
return(0);
DrBaseDirectory=GetBaseDirectory();
FCEUI_SetBaseDirectory(DrBaseDirectory);
CreateDirs();
#ifdef EXTGUI
if(argc==2 && !strcmp(argv[1],"-help")) // I hope no one has a game named "-help" :b
#else
if(argc<=1)
#endif
{
ShowUsage(argv[0]);
return(0);
}
LoadConfig();
DoArgs(argc-2,&argv[1]);
FCEUI_SetNTSCTH(ntsccol, ntsctint, ntschue);
if(cpalette)
LoadCPalette();
/* All the config files and arguments are parsed now. */
#ifdef EXTGUI
return(1);
#else
if(!LoadGame(argv[argc-1]))
{
DriverKill();
return(0);
}
while(CurGame)
DoFun();
#if(0)
{
int x;
for(x=1;x<argc;x++)
{ LoadGame(argv[x]); while(CurGame) DoFun(); }
}
#endif
CloseGame();
SaveConfig();
FCEUI_Kill();
#endif
return(1);
}
static int DriverInitialize(FCEUGI *gi)
{
#ifndef WIN32
SetSignals(CloseStuff);
#endif
/* Initialize video before all else, due to some wacko dependencies
in the SexyAL code(DirectSound) that need to be fixed.
*/
if(!InitVideo(gi)) return 0;
inited|=4;
if(InitSound(gi))
inited|=1;
if(InitJoysticks())
inited|=2;
if(!InitKeyboard()) return 0;
inited|=8;
InitOtherInput();
return 1;
}
static void DriverKill(void)
{
SaveConfig();
#ifndef WIN32
SetSignals(SIG_IGN);
#endif
if(inited&2)
KillJoysticks();
if(inited&8)
KillKeyboard();
if(inited&4)
KillVideo();
if(inited&1)
KillSound();
if(inited&16)
KillMouse();
inited=0;
}
void FCEUD_Update(uint8 *XBuf, int32 *Buffer, int Count)
{
#ifdef NETWORK
extern int FCEUDnetplay;
#endif
int ocount = Count;
// apply frame scaling to Count
Count = (Count<<8)/fps_scale;
if(Count)
{
int32 can=GetWriteSound();
static int uflow=0;
int32 tmpcan;
// don't underflow when scaling fps
if(can >= GetMaxSound() && fps_scale<=256) uflow=1; /* Go into massive underflow mode. */
if(can > Count) can=Count;
else uflow=0;
WriteSound(Buffer,can);
//if(uflow) puts("Underflow");
tmpcan = GetWriteSound();
// don't underflow when scaling fps
if(fps_scale>256 || ((tmpcan < Count*0.90) && !uflow))
{
if(XBuf && (inited&4) && !(NoWaiting & 2))
BlitScreen(XBuf);
Buffer+=can;
Count-=can;
if(Count)
{
if(NoWaiting)
{
can=GetWriteSound();
if(Count>can) Count=can;
WriteSound(Buffer,Count);
}
else
{
while(Count>0)
{
WriteSound(Buffer,(Count<ocount) ? Count : ocount);
Count -= ocount;
}
}
}
} //else puts("Skipped");
#ifdef NETWORK
else if(!NoWaiting && FCEUDnetplay && (uflow || tmpcan >= (Count * 1.8)))
{
if(Count > tmpcan) Count=tmpcan;
while(tmpcan > 0)
{
// printf("Overwrite: %d\n", (Count <= tmpcan)?Count : tmpcan);
WriteSound(Buffer, (Count <= tmpcan)?Count : tmpcan);
tmpcan -= Count;
}
}
#endif
}
else
{
if(!NoWaiting && (!(eoptions&EO_NOTHROTTLE) || FCEUI_EmulationPaused()))
SpeedThrottle();
if(XBuf && (inited&4))
{
BlitScreen(XBuf);
}
}
FCEUD_UpdateInput();
//if(!Count && !NoWaiting && !(eoptions&EO_NOTHROTTLE))
// SpeedThrottle();
//if(XBuf && (inited&4))
//{
// BlitScreen(XBuf);
//}
//if(Count)
// WriteSound(Buffer,Count,NoWaiting);
//FCEUD_UpdateInput();
}
/* Maybe ifndef WXWINDOWS would be better? ^_^ */
#ifndef EXTGUI
FILE *FCEUD_UTF8fopen(const char *fn, const char *mode)
{
return(fopen(fn,mode));
}
#endif

55
src/attic/pc/main.h Normal file
View File

@ -0,0 +1,55 @@
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2002 Xodnizel
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifdef __cplusplus
extern "C" {
#endif
#include "../../driver.h"
#include "../common/config.h"
#include "../common/args.h"
extern int eoptions;
#define EO_NO8LIM 1
#define EO_SUBASE 2
#define EO_CLIPSIDES 8
#define EO_SNAPNAME 16
#define EO_NOFOURSCORE 32
#define EO_NOTHROTTLE 64
#define EO_GAMEGENIE 128
#define EO_PAL 256
#define EO_LOWPASS 512
#define EO_AUTOHIDE 1024
extern int srendlinev[2],erendlinev[2];
extern int NoWaiting;
extern int soundvol;
extern long soundq;
extern int _sound;
extern long soundrate;
extern long soundbufsize;
int CLImain(int argc, char *argv[]);
#ifdef __cplusplus
}
#endif

134
src/attic/pc/sdl-icon.h Normal file
View File

@ -0,0 +1,134 @@
static const struct {
unsigned int width;
unsigned int height;
unsigned int bytes_per_pixel; /* 3:RGB, 4:RGBA */
uint8 pixel_data[32 * 32 * 3 + 1];
} fceu_playicon = {
32, 32, 3,
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\\\223\373\\\223\373\\\223\373\\\223"
"\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373"
"\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\"
"\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223"
"\373\\\223\373\\\223\373\0\0\0\0\0\0\0\0\0\\\223\373\\\223\373\267\317\373"
"\246\304\373\260\312\373\222\267\373\246\304\373\\\223\373\\\223\373\\\223"
"\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373"
"\267\317\373\246\304\373\260\312\373\210\261\373\246\304\373\\\223\373\\"
"\223\373\210\261\373\222\267\373\267\317\373\241\301\373\\\223\373\\\223"
"\373\0\0\0\0\0\0\\\223\373\\\223\373\227\272\373\227\272\373\227\272\373"
"\227\272\373\227\272\373\227\272\373\\\223\373\\\223\373z`sr\242\373\227"
"\272\373\227\272\373\\\223\373\\\223\373\\\223\373\\\223\373\213\261\373"
"z\247\373\213\261\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\"
"\223\373\\\223\373\\\223\373\\\223\373\0\0\0\0\0\0\\\223\373\\\223\373\\"
"\223\373\\\223\373\313i=\315Z\40\315Z\40\315Z\40\315Z\40\315Z\40\315Z\40"
"\315Z\40\315Z\40\315Z\40\315Z\40\315Z\40\315Z\40\315Z\40\315Z\40\315Z\40"
"\315Z\40\315Z\40\315Z\40\315Z\40\315Z\40\257T*\\\223\373\\\223\373\\\223"
"\373\\\223\373\0\0\0\0\0\0\\\223\373\\\223\373\\\223\373\\\223\373\315Z\40"
"\355\235\203\355\235\203\341\232\205\341\232\205\365\255\233\355\235\203"
"\355\235\203\350\250\232\365\255\233\355\235\203\307L\14\307L\14\307L\14"
"\307L\14\307L\14\307L\14\307L\14\307L\14\307L\14\307L\14\256B\12\\\223\373"
"\\\223\373\\\223\373\\\223\373\0\0\0\0\0\0\\\223\373\\\223\373\\\223\373"
"\\\223\373\315Z\40\273zc\233l_\325\225\205\325\225\205\325\225\205\273\212"
"\201\325\225\205\237qd\325\225\205\317\231\216\307L\14\307L\14\307L\14\307"
"L\14\307L\14\307L\14\307L\14\307L\14\307L\14\307L\14\256B\12\\\223\373\\"
"\223\373\\\223\373\\\223\373\0\0\0\0\0\0\\\223\373\\\223\373\\\223\373\\"
"\223\373\315Z\40\344\232\203\301\215\201\344\232\203\273\212\201\325\225"
"\205\2114\10\344\232\203\350\250\232\325\225\205\333\230\205\307L\14\307"
"L\14\307L\14\307L\14\307L\14\307L\14\307L\14\307L\14\307L\14\307L\14\256"
"B\12\\\223\373\\\223\373\\\223\373\\\223\373\0\0\0\0\0\0\\\223\373\\\223"
"\373\\\223\373\\\223\373\315Z\40\2259\11}0\10\2259\11}0\10}0\10\307L\14\225"
"9\11d&\6}0\10}0\10\307L\14\307L\14\307L\14\307L\14\307L\14\307L\14\307L\14"
"\307L\14\307L\14\307L\14\256B\12\\\223\373\\\223\373\\\223\373\\\223\373"
"\0\0\0\0\0\0\\\223\373\\\223\373\\\223\373\\\223\373\315Z\40\355\235\203"
"\355\233\201\355\235\203\355\235\203\355\235\203\365\255\233\355\235\203"
"\341\232\205\355\235\203\355\235\203\307L\14\365\255\233\355\235\203\365"
"\255\233\355\235\203\355\235\203\355\235\203\355\235\203\355\235\203\307"
"L\14\256B\12\\\223\373\\\223\373\\\223\373\\\223\373\0\0\0\0\0\0\\\223\373"
"\\\223\373\\\223\373\\\223\373\315Z\40\325\225\205\311\221\204\311\221\204"
"\325\225\205\311\221\204\325\225\205\311\221\204\325\225\205\325\225\205"
"\311\221\204\307L\14\325\225\205\311\221\204\325\225\205\311\221\204\325"
"\225\205\311\221\204\325\225\205\311\221\204\307L\14\256B\12\\\223\373\\"
"\223\373\\\223\373\\\223\373\0\0\0\0\0\0\\\223\373\\\223\373\\\223\373\\"
"\223\373\315Z\40\325\225\205\325\225\205\325\225\205\325\225\205\325\225"
"\205\325\225\205\317\231\216\325\225\205\325\225\205\325\225\205\307L\14"
"\325\225\205\317\231\216\325\225\205\317\231\216\325\225\205\325\225\205"
"\273zc\241n_\307L\14\256B\12\\\223\373\\\223\373\\\223\373\\\223\373\0\0"
"\0\0\0\0\\\223\373\\\223\373\\\223\373\\\223\373\315Z\40\325\225\205\325"
"\225\205\325\225\205\365\255\233\333\243\231\325\225\205\333\230\205\325"
"\225\205\325\225\205\325\225\205\307L\14\325\225\205\317\223\204\325\225"
"\205\333\230\205\325\225\205\325\225\205\333\230\205\317\223\204\307L\14"
"\256B\12\\\223\373\\\223\373\\\223\373\\\223\373\0\0\0\0\0\0\\\223\373\\"
"\223\373\\\223\373\\\223\373\315Z\40\325\225\205\325\225\205\325\225\205"
"\325\225\205\311\221\204\325\225\205\325\225\205\325\225\205\344\232\203"
"\273\212\201\307L\14\325\225\205\273\212\201\325\225\205\325\225\205\344"
"\232\203\273\212\201\344\232\203\273\212\201\341\232\205\256B\12\\\223\373"
"\\\223\373\\\223\373\\\223\373\0\0\0\0\0\0\\\223\373\\\223\373\\\223\373"
"\\\223\373\257T*d&\6d&\6d&\6d&\6d&\6d&\6d&\6d&\6}0\10d&\6\256B\12d&\6p+\7"
"d&\6d&\6}0\10d&\6}0\10d&\6d&\6\223@\26\\\223\373\\\223\373\\\223\373\\\223"
"\373\0\0\0\0\0\0\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223"
"\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\234\243"
"\335\213\237\344\237\244\333\251\247\326\237\244\333\\\223\373\267\252\317"
"\222\241\341\267\252\317\210\237\346\241\245\332\267\252\317\246\246\327"
"\246\246\327\\\223\373\\\223\373\\\223\373\\\223\373\0\0\0\0\0\0\\\223\373"
"\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\"
"\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223"
"\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373"
"\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\0"
"\0\0\0\0\0\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\"
"\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223"
"\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373"
"\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\"
"\223\373\\\223\373\0\0\0\0\0\0\\\223\373\\\223\373\\\223\373\\\223\373\\"
"\223\373\\\223\373\\\223\373\\\223\373\257hT\\\223\373\213\261\373\\\223"
"\373\241\301\373\210\261\373\246\304\373\222\267\373\241\301\373\260\312"
"\373\\\223\373\237\277\373\246\304\373\267\317\373\241\301\373\\\223\373"
"\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\0\0\0\0\0\0"
"\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\"
"\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223"
"\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373"
"\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\"
"\223\373\0\0\0\0\0\0\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\"
"\223\373\\\223\373\\\223\373\\\223\373\\\223\373\246\304\373\\\223\373\241"
"\301\373\210\261\373\246\304\373\222\267\373\241\301\373\260\312\373\\\223"
"\373\237\277\373\246\304\373\267\317\373\241\301\373\\\223\373\\\223\373"
"\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\0\0\0\0\0\0\\\223\373"
"\\\223\373\\\223\373D\203\270D\203\270\\\223\373\\\223\373\\\223\373\\\223"
"\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373"
"\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\"
"\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\0\0"
"\0\0\0\0\\\223\373\\\223\373(\211n\0\247\0\0n\0(\211n\\\223\373\\\223\373"
"\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\"
"\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223"
"\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373"
"\0\0\0\0\0\0\\\223\373(\211n\0\247\0\0\247\0\0\247\0\0\247\0(\211n\\\223"
"\373\\\223\373\\\223\373\\\223\373\210\261\373\246\304\373\241\301\373z\247"
"\373\\\223\373\227\272\373\227\272\373\227\272\373\227\272\373\227\272\373"
"\227\272\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373"
"\\\223\373\\\223\373\0\0\0\0\0\0(\211n\0\247\0\0n\0\0\247\0\\\215\12fs\16"
"\0\247\0(\211n\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373"
"\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\"
"\223\373\\\223\373Z\231Z[\230SZ\231Z[\230SZ\231Z[\230S\\\223\373\0\0\0\0"
"\0\0\0\247\0\0\247\0\0\247\0\0\247\0vy\7p|\7\0\247\0\0\247\0(\211n\\\223"
"\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373"
"\\\223\373\\\223\373\\\223\373\\\223\373\\\223\373b\237L\200\317\20\200\317"
"\20\200\317\20\200\317\20\200\317\20\200\317\20[\223H\0\0\0\0\0\0\230P.\323"
"d0\230P.\323d0\221M&\313`%\230P.\323d0\230P.\323d0\230P.\323d0\230P.\323"
"d0\230P.\323d0\230P.\323d0\230P.\323d0\230P.\323d0\230P.\323d0\230P.\323"
"d0\230P.\323d0\230P.\323d0\0\0\0\0\0\0\0\0\0\237N(\225E\35\237N(\225E\35"
"\237N(\225E\35\237N(\225E\35\237N(\225E\35\237N(\225E\35\237N(\225E\35\237"
"N(\225E\35\237N(\225E\35\237N(\225E\35\237N(\225E\35\237N(\225E\35\237N("
"\225E\35\237N(\225E\35\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",
};

View File

@ -0,0 +1,99 @@
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2002 Xodnizel
* Copyright (C) 2002 Paul Kuliniewicz
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* PK: SDL joystick input stuff */
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include "sdl.h"
#define MAX_JOYSTICKS 32
static SDL_Joystick *Joysticks[MAX_JOYSTICKS] = {NULL};
int DTestButtonJoy(ButtConfig *bc)
{
int x;
for(x=0;x<bc->NumC;x++)
{
if(bc->ButtonNum[x]&0x8000) /* Axis "button" */
{
int pos;
pos = SDL_JoystickGetAxis(Joysticks[bc->DeviceNum[x]], bc->ButtonNum[x]&16383);
if ((bc->ButtonNum[x]&0x4000) && pos <= -16383)
return(1);
else if (!(bc->ButtonNum[x]&0x4000) && pos >= 16363)
return(1);
}
else if(bc->ButtonNum[x]&0x2000) /* Hat "button" */
{
if( SDL_JoystickGetHat(Joysticks[bc->DeviceNum[x]],(bc->ButtonNum[x]>>8)&0x1F) & (bc->ButtonNum[x]&0xFF))
return(1);
}
else
if(SDL_JoystickGetButton(Joysticks[bc->DeviceNum[x]], bc->ButtonNum[x] ))
return(1);
}
return(0);
}
static int jinited=0;
/* Cleanup opened joysticks. */
int KillJoysticks (void)
{
int n; /* joystick index */
if(!jinited) return(0);
for (n = 0; n < MAX_JOYSTICKS; n++)
{
if (Joysticks[n] != 0)
SDL_JoystickClose(Joysticks[n]);
Joysticks[n]=0;
}
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
return(1);
}
/* Initialize joysticks. */
int InitJoysticks (void)
{
int n; /* joystick index */
int total;
SDL_InitSubSystem(SDL_INIT_JOYSTICK);
total=SDL_NumJoysticks();
if(total>MAX_JOYSTICKS) total=MAX_JOYSTICKS;
for (n = 0; n < total; n++)
{
/* Open the joystick under SDL. */
Joysticks[n] = SDL_JoystickOpen(n);
//printf("Could not open joystick %d: %s.\n",
//joy[n] - 1, SDL_GetError());
continue;
}
jinited=1;
return(1);
}

224
src/attic/pc/sdl-netplay.c Normal file
View File

@ -0,0 +1,224 @@
#include "sdl.h"
#include <SDL/SDL_net.h>
#include "sdl-netplay.h"
char *ServerHost;
static int LocalPortTCP=0xFCE;
static int LocalPortUDP=0xFCE;
static int RemotePortTCP=0xFCE;
static int RemotePortUDP; /* Not configurable, figured out during handshake. */
static TCPsocket Socket;
static UDPsocket UDPSocket;
static SDLNet_SocketSet set;
static void en32(uint8 *buf, uint32 morp)
{
buf[0]=morp;
buf[1]=morp>>8;
buf[2]=morp>>16;
buf[3]=morp>>24;
}
static uint32 de32(uint8 *morp)
{
return(morp[0]|(morp[1]<<8)|(morp[2]<<16)|(morp[3]<<24));
}
int FCEUD_NetworkConnect(void)
{
IPaddress rip;
SDLNet_Init();
if(netplay==1) /* Be a server. */
{
TCPsocket tmp;
Uint16 p=LocalPortUDP;
SDLNet_ResolveHost(&rip,NULL,LocalPortTCP);
UDPSocket=SDLNet_UDP_Open(&p);
tmp=SDLNet_TCP_Open(&rip);
Socket=SDLNet_TCP_Accept(&tmp);
memcpy(&rip,SDLNet_TCP_GetPeerAddress(Socket),sizeof(IPaddress));
{
uint8 buf[12];
uint32 player=1;
magic=SDL_GetTicks();
SDLNet_Write32(buf,uport);
SDLNet_Write32(buf+4,1);
SDLNet_Write32(buf+8,magic);
SDLNet_TCP_Send(Socket, buf, 12);
/* Get the UDP port the client is waiting for data on. */
SDLNet_TCP_Recv(Socket, buf, 2);
RemotePortUDP=de32(buf);
}
}
else /* Be a client */
{
SDLNet_ResolveHost(&rip,ServerHost,RemotePortTCP);
Socket=SDLNet_TCP_Open(&rip);
{
Uint16 p=LocalPortUDP;
uint8 buf[12];
UDPSocket=SDLNet_UDP_Open(&p);
/* Now, tell the server what local UDP port it should send to. */
en32(buf,p);
SDLNet_TCP_Send(Socket, buf, 4);
/* Get the UDP port from the server we should send data to. */
SDLNet_TCP_Recv(Socket, buf, 12);
RemotePortUDP=de32(buf);
magic=de32(buf+8);
}
set=SDLNet_AllocSocketSet(1);
SDLNet_TCP_AddSocket(set,TCPSocket);
SDLNet_UDP_AddSocket(set,UDPSocket);
} // End client connect code.
rip.port=RemotePortUDP;
SDLNet_UDP_Bind(UDPSocket, 0, &rip);
}
static int CheckUDP(uint8 *packet, int32 len, int32 alt)
{
uint32 crc;
uint32 repcrc;
crc=FCEUI_CRC32(0,packet+4,len+8);
repcrc=de32(packet);
if(crc!=repcrc) return(0); /* CRC32 mismatch, bad packet. */
packet+=4;
if(de32(packet)!=magic) /* Magic number mismatch, bad or spoofed packet. */
return(0);
packet+=4;
if(alt)
{
if(de32(packet)<incounter) /* Time warped packet. */
return(0);
}
else
if(de32(packet)!=incounter) /* Time warped packet. */
return(0);
return(1);
}
/* Be careful where these MakeXXX() functions are used. */
static uint8 *MakeUDP(uint8 *data, int32 len)
{
/* UDP packet data header is 12 bytes in length. */
static uint8 buf[12+32]; // arbitrary 32.
en32(buf+4,magic);
en32(buf+8,outcounter);
memcpy(buf+12,data,len);
en32(buf,FCEUI_CRC32(0,buf+4,8+len));
return(buf);
}
static uint8 *MakeTCP(uint8 *data, int32 len)
{
/* TCP packet data header is 4 bytes in length. */
static uint8 buf[4+32]; // arbitrary 32.
en32(buf,outcounter);
memcpy(buf+4,data,len);
return(buf);
}
#define UDPHEADSIZE 12
#define TCPHEADSIZE 4
void FCEUD_NetworkClose(void)
{
SDLNet_Quit();
}
/* 1 byte to server */
int FCEUD_SendDataToServer(uint8 v,uint8 cmd)
{
UDPpacket upack;
upack.channel=0;
upack.data=MakeUDP(data,1);
upack.len=upack.maxlen=UDPHEADSIZE+1;
upack.status=0;
SDLNet_UDP_Send(UDPSocket,0,&upack);
outcounter++;
return(1);
}
void FCEUD_SendDataToClients(uint8 *data)
{
UDPpacket upack;
SDLNet_TCP_Send(Socket,MakeTCP(data,5),TCPHEADSIZE+5);
upack.channel=0;
upack.data=MakeUDP(data,5);
upack.len=upack.maxlen=UDPHEADSIZE+5;
upack.status=0;
SDLNet_UDP_Send(UDPSocket,0,&upack);
outcounter++;
return(1);
}
int FCEUD_GetDataFromServer(uint8 *data)
{
uint8 buf[128];
NoWaiting&=~2;
while(SDLNet_CheckSockets(set,1)==0)
{
// do something here.
}
if(SDLNet_SocketReady(Socket))
{
SDLNet_TCP_Recv
if(de32(buf)==incounter) /* New packet, keep. */
{
unsigned long beefie;
memcpy(data,buf+TCPHEADSIZE,5);
incounter++;
if(!ioctl(Socket,FIONREAD,&beefie))
if(beefie)
NoWaiting|=2;
return(1);
}
}
if(SDLNet_SocketReady(UDPSocket)
{
}
}

View File

241
src/attic/pc/sdl-opengl.c Normal file
View File

@ -0,0 +1,241 @@
#define GL_GLEXT_LEGACY
#ifdef APPLEOPENGL
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#include <OpenGL/glext.h>
#else
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glext.h>
#endif
#include <string.h>
#include <stdlib.h>
#include "sdl.h"
#include "sdl-opengl.h"
#include "../common/vidblit.h"
#ifndef APIENTRY
#define APIENTRY
#endif
static GLuint textures[2]={0,0}; // Normal image, scanline overlay.
static int left,right,top,bottom; // right and bottom are not inclusive.
static int scanlines;
static void *HiBuffer;
void APIENTRY (*p_glBindTexture)(GLenum target,GLuint texture);
void APIENTRY (*p_glColorTableEXT)(GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const GLvoid *table);
void APIENTRY (*p_glTexImage2D)( GLenum target, GLint level,
GLint internalFormat,
GLsizei width, GLsizei height, GLint border,
GLenum format, GLenum type,
const GLvoid *pixels );
void APIENTRY (*p_glBegin)(GLenum mode);
void APIENTRY (*p_glVertex2f)(GLfloat x, GLfloat y);
void APIENTRY (*p_glTexCoord2f)(GLfloat s, GLfloat t);
void APIENTRY (*p_glEnd)(void);
void APIENTRY (*p_glEnable)(GLenum cap);
void APIENTRY (*p_glBlendFunc)(GLenum sfactor, GLenum dfactor);
const GLubyte* APIENTRY (*p_glGetString)(GLenum name);
void APIENTRY (*p_glViewport)(GLint x, GLint y,GLsizei width, GLsizei height);
void APIENTRY (*p_glGenTextures)(GLsizei n, GLuint *textures);
void APIENTRY (*p_glDeleteTextures)(GLsizei n,const GLuint *textures);
void APIENTRY (*p_glTexParameteri)(GLenum target, GLenum pname, GLint param);
void APIENTRY (*p_glClearColor)(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);
void APIENTRY (*p_glLoadIdentity)(void);
void APIENTRY (*p_glClear)(GLbitfield mask);
void APIENTRY (*p_glMatrixMode)(GLenum mode);
void APIENTRY (*p_glDisable)(GLenum cap);
void SetOpenGLPalette(uint8 *data)
{
if(!HiBuffer)
{
p_glBindTexture(GL_TEXTURE_2D, textures[0]);
p_glColorTableEXT(GL_TEXTURE_2D,GL_RGB,256,GL_RGBA,GL_UNSIGNED_BYTE,data);
}
else
SetPaletteBlitToHigh((uint8*)data);
}
void BlitOpenGL(uint8 *buf)
{
p_glBindTexture(GL_TEXTURE_2D, textures[0]);
if(HiBuffer)
{
static int xo=0;
xo=(xo+1)&3;
Blit8ToHigh(buf,(uint8*)HiBuffer,256,240,256*4,1,1);
if(!xo)
p_glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA8,256,256, 0, GL_RGBA,GL_UNSIGNED_BYTE,
HiBuffer);
}
else
{
//glPixelStorei(GL_UNPACK_ROW_LENGTH, 256);
p_glTexImage2D(GL_TEXTURE_2D, 0, GL_COLOR_INDEX8_EXT, 256, 256, 0,
GL_COLOR_INDEX,GL_UNSIGNED_BYTE,buf);
}
p_glBegin(GL_QUADS);
p_glTexCoord2f(1.0f*left/256, 1.0f*bottom/256); // Bottom left of our picture.
p_glVertex2f(-1.0f, -1.0f); // Bottom left of target.
p_glTexCoord2f(1.0f*right/256, 1.0f*bottom/256); // Bottom right of our picture.
p_glVertex2f( 1.0f, -1.0f); // Bottom right of target.
p_glTexCoord2f(1.0f*right/256, 1.0f*top/256); // Top right of our picture.
p_glVertex2f( 1.0f, 1.0f); // Top right of target.
p_glTexCoord2f(1.0f*left/256, 1.0f*top/256); // Top left of our picture.
p_glVertex2f(-1.0f, 1.0f); // Top left of target.
p_glEnd();
//glDisable(GL_BLEND);
if(scanlines)
{
p_glEnable(GL_BLEND);
p_glBindTexture(GL_TEXTURE_2D, textures[1]);
p_glBlendFunc(GL_DST_COLOR, GL_SRC_ALPHA);
p_glBegin(GL_QUADS);
p_glTexCoord2f(1.0f*left/256, 1.0f*bottom/256); // Bottom left of our picture.
p_glVertex2f(-1.0f, -1.0f); // Bottom left of target.
p_glTexCoord2f(1.0f*right/256, 1.0f*bottom/256); // Bottom right of our picture.
p_glVertex2f( 1.0f, -1.0f); // Bottom right of target.
p_glTexCoord2f(1.0f*right/256, 1.0f*top/256); // Top right of our picture.
p_glVertex2f( 1.0f, 1.0f); // Top right of target.
p_glTexCoord2f(1.0f*left/256, 1.0f*top/256); // Top left of our picture.
p_glVertex2f(-1.0f, 1.0f); // Top left of target.
p_glEnd();
p_glDisable(GL_BLEND);
}
SDL_GL_SwapBuffers();
}
void KillOpenGL(void)
{
if(textures[0])
p_glDeleteTextures(2, &textures[0]);
textures[0]=0;
if(HiBuffer)
{
free(HiBuffer);
HiBuffer=0;
}
}
/* Rectangle, left, right(not inclusive), top, bottom(not inclusive). */
int InitOpenGL(int l, int r, int t, int b, double xscale,double yscale, int efx, int ipolate,
int stretchx, int stretchy, SDL_Surface *screen)
{
const char *extensions;
#define LFG(x) if(!(p_##x = SDL_GL_GetProcAddress(#x))) return(0);
#define LFGN(x) p_##x = SDL_GL_GetProcAddress(#x)
LFG(glBindTexture);
LFGN(glColorTableEXT);
LFG(glTexImage2D);
LFG(glBegin);
LFG(glVertex2f);
LFG(glTexCoord2f);
LFG(glEnd);
LFG(glEnable);
LFG(glBlendFunc);
LFG(glGetString);
LFG(glViewport);
LFG(glGenTextures);
LFG(glDeleteTextures);
LFG(glTexParameteri);
LFG(glClearColor);
LFG(glLoadIdentity);
LFG(glClear);
LFG(glMatrixMode);
LFG(glDisable);
left=l;
right=r;
top=t;
bottom=b;
HiBuffer=0;
extensions=(const char*)p_glGetString(GL_EXTENSIONS);
if((efx&2) || !extensions || !p_glColorTableEXT || !strstr(extensions,"GL_EXT_paletted_texture"))
{
if(!(efx&2)) // Don't want to print out a warning message in this case...
FCEU_printf("Paletted texture extension not found. Using slower texture format...");
HiBuffer=malloc(4*256*256);
memset(HiBuffer,0x00,4*256*256);
#ifndef LSB_FIRST
InitBlitToHigh(4,0xFF000000,0xFF0000,0xFF00,efx&2,0);
#else
InitBlitToHigh(4,0xFF,0xFF00,0xFF0000,efx&2,0);
#endif
}
{
int rw=(r-l)*xscale;
int rh=(b-t)*yscale;
int sx=(screen->w-rw)/2; // Start x
int sy=(screen->h-rh)/2; // Start y
if(stretchx) { sx=0; rw=screen->w; }
if(stretchy) { sy=0; rh=screen->h; }
p_glViewport(sx, sy, rw, rh);
}
p_glGenTextures(2, &textures[0]);
scanlines=0;
if(efx&1)
{
uint8 *buf;
int x,y;
scanlines=1;
p_glBindTexture(GL_TEXTURE_2D, textures[1]);
p_glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,ipolate?GL_LINEAR:GL_NEAREST);
p_glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,ipolate?GL_LINEAR:GL_NEAREST);
buf=(uint8*)malloc(256*(256*2)*4);
for(y=0;y<(256*2);y++)
for(x=0;x<256;x++)
{
buf[y*256*4+x*4]=0;
buf[y*256*4+x*4+1]=0;
buf[y*256*4+x*4+2]=0;
buf[y*256*4+x*4+3]=(y&1)?0x00:0xFF; //?0xa0:0xFF; // <-- Pretty
//buf[y*256+x]=(y&1)?0x00:0xFF;
}
p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 256, (scanlines==2)?256*4:512, 0,
GL_RGBA,GL_UNSIGNED_BYTE,buf);
free(buf);
}
p_glBindTexture(GL_TEXTURE_2D, textures[0]);
p_glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,ipolate?GL_LINEAR:GL_NEAREST);
p_glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,ipolate?GL_LINEAR:GL_NEAREST);
p_glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP);
p_glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);
p_glEnable(GL_TEXTURE_2D);
p_glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // Background color to black.
p_glMatrixMode(GL_MODELVIEW);
p_glClear(GL_COLOR_BUFFER_BIT);
p_glLoadIdentity();
return(1);
}

View File

@ -0,0 +1,6 @@
void SetOpenGLPalette(uint8 *data);
void BlitOpenGL(uint8 *buf);
void KillOpenGL(void);
int InitOpenGL(int l, int r, int t, int b, double xscale,double yscale, int efx, int ipolate,
int stretchx, int stretchy, SDL_Surface *screen);

373
src/attic/pc/sdl-sound.c Normal file
View File

@ -0,0 +1,373 @@
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2002 Xodnizel
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "sdl.h"
#ifdef USE_SEXYAL
#include "../sexyal/sexyal.h"
static SexyAL *Interface;
static SexyAL_device *Output;
static SexyAL_format format;
static SexyAL_buffering buffering;
uint32 GetMaxSound(void)
{
return(buffering.totalsize);
}
uint32 GetWriteSound(void)
{
return(Output->CanWrite(Output));
}
void WriteSound(int32 *Buffer, int Count)
{
//printf("%d\n",Output->CanWrite(Output));
Output->Write(Output, Buffer, Count);
}
int InitSound(FCEUGI *gi)
{
if(!_sound) return(0);
memset(&format,0,sizeof(format));
memset(&buffering,0,sizeof(buffering));
FCEUI_SetSoundVolume(soundvol);
FCEUI_SetSoundQuality(soundq);
Interface=SexyAL_Init(0);
format.sampformat=SEXYAL_FMT_PCMS32S16;
format.channels=gi->soundchan?gi->soundchan:1;
format.rate=gi->soundrate?gi->soundrate:soundrate;
buffering.fragcount=buffering.fragsize=0;
buffering.ms=soundbufsize;
FCEUI_printf("\nInitializing sound...");
if(!(Output=Interface->Open(Interface,SEXYAL_ID_UNUSED,&format,&buffering)))
{
FCEUD_PrintError("Error opening a sound device.");
Interface->Destroy(Interface);
Interface=0;
return(0);
}
if(soundq && format.rate!=48000 && format.rate!=44100 && format.rate!=96000)
{
FCEUD_PrintError("Set sound playback rate neither 44100, 48000, nor 96000, but needs to be when in high-quality sound mode.");
KillSound();
return(0);
}
if(format.rate<8192 || format.rate > 96000)
{
FCEUD_PrintError("Set rate is out of range [8192-96000]");
KillSound();
return(0);
}
FCEUI_printf("\n Bits: %u\n Rate: %u\n Channels: %u\n Byte order: CPU %s\n Buffer size: %u sample frames(%f ms)\n",(format.sampformat>>4)*8,format.rate,format.channels,format.byteorder?"Reversed":"Native",buffering.totalsize,(double)buffering.totalsize*1000/format.rate);
format.sampformat=SEXYAL_FMT_PCMS32S16;
format.channels=gi->soundchan?gi->soundchan:1;
format.byteorder=0;
//format.rate=gi->soundrate?gi->soundrate:soundrate;
Output->SetConvert(Output,&format);
FCEUI_Sound(format.rate);
return(1);
}
void SilenceSound(int n)
{
}
int KillSound(void)
{
FCEUI_Sound(0);
if(Output)
Output->Close(Output);
if(Interface)
Interface->Destroy(Interface);
Interface=0;
if(!Output) return(0);
Output=0;
return(1);
}
#elif USE_JACKACK /* Use JACK Audio Connection Kit */
#include <jack/jack.h>
#include <jack/ringbuffer.h>
static jack_port_t *output_port = NULL;
static jack_client_t *client = NULL;
static jack_ringbuffer_t *tmpbuf = NULL;
static unsigned int BufferSize;
static int process(jack_nframes_t nframes, void *arg)
{
jack_default_audio_sample_t *out = (jack_default_audio_sample_t *) jack_port_get_buffer(output_port, nframes);
size_t canread;
canread = jack_ringbuffer_read_space(tmpbuf) / sizeof(jack_default_audio_sample_t);
if(canread > nframes)
canread = nframes;
jack_ringbuffer_read(tmpbuf, out,canread * sizeof(jack_default_audio_sample_t));
nframes -= canread;
if(nframes) /* Buffer underflow. Hmm. */
{
}
}
uint32 GetMaxSound(void)
{
return(BufferSize);
}
uint32 GetWriteSound(void)
{
return(jack_readbuffer_write_space / sizeof(jack_default_audio_sample_t));
}
static void DeadSound(void *arg)
{
puts("AGH! Sound server hates us! Let's go on a rampage.");
}
int InitSound(FCEUGI *gi)
{
const char **ports;
client = jack_client_new("FCE Ultra");
jack_set_process_callback(client, process, 0);
jack_on_shutdown(client, DeadSound, 0);
printf("%ld\n",jack_get_sample_rate(client));
output_port = jack_port_register(client, "output", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
BufferSize = soundbufsize * soundrate / 1000;
tmpbuf = jack_ringbuffer_create(BufferSize * sizeof(jack_default_audio_sample_t));
jack_activate(client);
ports = jack_get_ports(client, NULL, NULL, JackPortIsPhysical | JackPortIsInput);
jack_connect(client, jack_port_name(output_port), ports[0]);
free(ports);
}
void WriteSound(int32 *buf, int Count)
{
jack_default_audio_sample_t jbuf[Count];
int x;
for(x=0;x<Count;x++,buf++)
jbuf[x] = *buf;
jack_ringbuffer_write(tmpbuf, jbuf, sizeof(jack_default_audio_sample_t) * Count);
}
void SilenceSound(int n)
{
}
int KillSound(void)
{
if(tmpbuf)
{
jack_ringbuffer_free(tmpbuf);
tmpbuf = NULL;
}
if(client)
{
jack_client_close(client);
client = NULL;
}
return(1);
}
#else /* So we'll use SDL's evil sound support. Ok. */
static volatile int *Buffer = 0;
static unsigned int BufferSize;
static unsigned int BufferRead;
static unsigned int BufferWrite;
static volatile unsigned int BufferIn;
static void fillaudio(void *udata, uint8 *stream, int len)
{
int16 *tmps = (int16*)stream;
len >>= 1;
while(len)
{
int16 sample = 0;
if(BufferIn)
{
sample = Buffer[BufferRead];
BufferRead = (BufferRead + 1) % BufferSize;
BufferIn--;
}
else sample = 0;
*tmps = sample;
tmps++;
len--;
}
}
int InitSound(FCEUGI *gi)
{
SDL_AudioSpec spec;
if(!_sound) return(0);
memset(&spec,0,sizeof(spec));
if(SDL_InitSubSystem(SDL_INIT_AUDIO)<0)
{
puts(SDL_GetError());
KillSound();
return(0);
}
spec.freq = soundrate;
spec.format = AUDIO_S16SYS;
spec.channels = 1;
spec.samples = 256;
spec.callback = fillaudio;
spec.userdata = 0;
BufferSize = soundbufsize * soundrate / 1000;
BufferSize -= spec.samples * 2; /* SDL uses at least double-buffering, so
multiply by 2. */
if(BufferSize < spec.samples) BufferSize = spec.samples;
Buffer = malloc(sizeof(int) * BufferSize);
BufferRead = BufferWrite = BufferIn = 0;
//printf("SDL Size: %d, Internal size: %d\n",spec.samples,BufferSize);
if(SDL_OpenAudio(&spec,0)<0)
{
puts(SDL_GetError());
KillSound();
return(0);
}
SDL_PauseAudio(0);
FCEUI_Sound(soundrate);
return(1);
}
uint32 GetMaxSound(void)
{
return(BufferSize);
}
uint32 GetWriteSound(void)
{
return(BufferSize - BufferIn);
}
void WriteSound(int32 *buf, int Count)
{
while(Count)
{
while(BufferIn == BufferSize) SDL_Delay(1);
Buffer[BufferWrite] = *buf;
Count--;
BufferWrite = (BufferWrite + 1) % BufferSize;
BufferIn++;
buf++;
}
}
void SilenceSound(int n)
{
SDL_PauseAudio(n);
}
int KillSound(void)
{
FCEUI_Sound(0);
SDL_CloseAudio();
SDL_QuitSubSystem(SDL_INIT_AUDIO);
if(Buffer)
{
free(Buffer);
Buffer = 0;
}
return(0);
}
#endif
static int mute=0;
static int soundvolume=100;
void FCEUD_SoundVolumeAdjust(int n)
{
switch(n)
{
case -1: soundvolume-=10; if(soundvolume<0) soundvolume=0; break;
case 0: soundvolume=100; break;
case 1: soundvolume+=10; if(soundvolume>150) soundvolume=150; break;
}
mute=0;
FCEUI_SetSoundVolume(soundvolume);
FCEU_DispMessage("Sound volume %d.", soundvolume);
}
void FCEUD_SoundToggle(void)
{
if(mute)
{
mute=0;
FCEUI_SetSoundVolume(soundvolume);
FCEU_DispMessage("Sound mute off.");
}
else
{
mute=1;
FCEUI_SetSoundVolume(0);
FCEU_DispMessage("Sound mute on.");
}
}

View File

@ -0,0 +1,86 @@
#include "sdl.h"
#include "throttle.h"
static uint64 tfreq;
static uint64 desiredfps;
static int32 fps_scale_table[]=
{ 3, 3, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 2048 };
int32 fps_scale = 256;
#define fps_table_size (sizeof(fps_scale_table)/sizeof(fps_scale_table[0]))
void RefreshThrottleFPS(void)
{
desiredfps=FCEUI_GetDesiredFPS()>>8;
desiredfps=(desiredfps*fps_scale)>>8;
tfreq=10000000;
tfreq<<=16; /* Adjustment for fps returned from FCEUI_GetDesiredFPS(). */
}
void SpeedThrottle(void)
{
static uint64 ttime,ltime=0;
waiter:
ttime=SDL_GetTicks();
ttime*=10000;
if( (ttime-ltime) < (tfreq/desiredfps) )
{
int64 delay;
delay=(tfreq/desiredfps)-(ttime-ltime);
if(delay>0)
SDL_Delay(delay/10000);
//printf("%d\n",(tfreq/desiredfps)-(ttime-ltime));
//SDL_Delay((tfreq/desiredfps)-(ttime-ltime));
goto waiter;
}
if( (ttime-ltime) >= (tfreq*4/desiredfps))
ltime=ttime;
else
ltime+=tfreq/desiredfps;
}
void IncreaseEmulationSpeed(void)
{
int i;
for(i=1; fps_scale_table[i]<fps_scale; i++)
;
fps_scale = fps_scale_table[i+1];
RefreshThrottleFPS();
FCEU_DispMessage("emulation speed %d%%",(fps_scale*100)>>8);
}
void DecreaseEmulationSpeed(void)
{
int i;
for(i=1; fps_scale_table[i]<fps_scale; i++)
;
fps_scale = fps_scale_table[i-1];
RefreshThrottleFPS();
FCEU_DispMessage("emulation speed %d%%",(fps_scale*100)>>8);
}
void FCEUD_SetEmulationSpeed(int cmd)
{
switch(cmd)
{
case EMUSPEED_SLOWEST: fps_scale=fps_scale_table[0]; break;
case EMUSPEED_SLOWER: DecreaseEmulationSpeed(); break;
case EMUSPEED_NORMAL: fps_scale=256; break;
case EMUSPEED_FASTER: IncreaseEmulationSpeed(); break;
case EMUSPEED_FASTEST: fps_scale=fps_scale_table[fps_table_size-1]; break;
default:
return;
}
RefreshThrottleFPS();
FCEU_DispMessage("emulation speed %d%%",(fps_scale*100)>>8);
}

432
src/attic/pc/sdl-video.c Normal file
View File

@ -0,0 +1,432 @@
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2002 Xodnizel
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "sdl.h"
#include "sdl-opengl.h"
#include "../common/vidblit.h"
#include "sdl-icon.h"
#include "dface.h"
SDL_Surface *screen;
SDL_Surface *BlitBuf; // Used as a buffer when using hardware-accelerated blits.
SDL_Surface *IconSurface=NULL;
static int curbpp;
static int srendline,erendline;
static int tlines;
static int inited=0;
#ifdef OPENGL
extern int sdlhaveogl;
static int usingogl;
static double exs,eys;
#else
static int exs,eys;
#endif
static int eefx;
#define NWIDTH (256-((eoptions&EO_CLIPSIDES)?16:0))
#define NOFFSET (eoptions&EO_CLIPSIDES?8:0)
static int paletterefresh;
/* Return 1 if video was killed, 0 otherwise(video wasn't initialized). */
int KillVideo(void)
{
if(IconSurface)
{
SDL_FreeSurface(IconSurface);
IconSurface=0;
}
if(inited&1)
{
#ifdef OPENGL
if(usingogl)
KillOpenGL();
else
#endif
if(curbpp>8)
KillBlitToHigh();
SDL_QuitSubSystem(SDL_INIT_VIDEO);
inited&=~1;
return(1);
}
inited=0;
return(0);
}
static int sponge;
int InitVideo(FCEUGI *gi)
{
const SDL_VideoInfo *vinf;
int flags=0;
FCEUI_printf("Initializing video...");
FCEUI_GetCurrentVidSystem(&srendline,&erendline);
if(_fullscreen) sponge=Settings.specialfs;
else sponge=Settings.special;
#ifdef OPENGL
usingogl=0;
if(_opengl && sdlhaveogl && !sponge)
{
flags=SDL_OPENGL;
usingogl=1;
}
#endif
#ifdef EXTGUI
GUI_SetVideo(_fullscreen, 0, 0);
#endif
if(!(SDL_WasInit(SDL_INIT_VIDEO)&SDL_INIT_VIDEO))
if(SDL_InitSubSystem(SDL_INIT_VIDEO)==-1)
{
FCEUD_PrintError(SDL_GetError());
return(0);
}
inited|=1;
SDL_ShowCursor(0);
tlines=erendline-srendline+1;
vinf=SDL_GetVideoInfo();
if(vinf->hw_available)
flags|=SDL_HWSURFACE;
if(_fullscreen)
flags|=SDL_FULLSCREEN;
flags|=SDL_HWPALETTE;
//flags|=SDL_DOUBLEBUF;
#ifdef OPENGL
if(usingogl)
{
FCEU_printf("\n Initializing with OpenGL(Use \"-opengl 0\" to disable).\n");
if(_doublebuf)
SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
}
else
#endif
if(_doublebuf && (flags&SDL_HWSURFACE))
flags|=SDL_DOUBLEBUF;
if(_fullscreen)
{
int desbpp=_bpp;
exs=_xscalefs;
eys=_yscalefs;
eefx=_efxfs;
#ifdef OPENGL
if(!usingogl) {exs=(int)exs;eys=(int)eys;}
else desbpp=0;
if(sponge)
{
exs=eys=2;
if(sponge == 3 || sponge == 4) exs = eys = 3;
eefx=0;
if(sponge == 1 || sponge == 3) desbpp = 32;
}
if( (usingogl && !_stretchx) || !usingogl)
#endif
if(_xres<NWIDTH*exs || exs <= 0.01)
{
FCEUD_PrintError("xscale out of bounds.");
KillVideo();
return(0);
}
#ifdef OPENGL
if( (usingogl && !_stretchy) || !usingogl)
#endif
if(_yres<tlines*eys || eys <= 0.01)
{
FCEUD_PrintError("yscale out of bounds.");
KillVideo();
return(0);
}
#ifdef EXTGUI
GUI_SetVideo(_fullscreen, _xres, _yres);
#endif
#ifdef OPENGL
if(!(screen = SDL_SetVideoMode(_xres, _yres, desbpp, flags)))
#else
if(!(screen = SDL_SetVideoMode(_xres, _yres, desbpp, flags)))
#endif
{
FCEUD_PrintError(SDL_GetError());
return(0);
}
}
else
{
int desbpp=0;
exs=_xscale;
eys=_yscale;
eefx=_efx;
if(sponge)
{
exs=eys=2;
if(sponge >= 3) exs=eys=3;
eefx=0;
// SDL's 32bpp->16bpp code is slighty faster than mine, at least :/
if(sponge == 1 || sponge == 3) desbpp=32;
}
#ifdef OPENGL
if(!usingogl) {exs=(int)exs;eys=(int)eys;}
if(exs <= 0.01)
{
FCEUD_PrintError("xscale out of bounds.");
KillVideo();
return(0);
}
if(eys <= 0.01)
{
FCEUD_PrintError("yscale out of bounds.");
KillVideo();
return(0);
}
#endif
#ifdef EXTGUI
GUI_SetVideo(_fullscreen, (NWIDTH*exs), tlines*eys);
#endif
screen = SDL_SetVideoMode((NWIDTH*exs), tlines*eys, desbpp, flags);
}
curbpp=screen->format->BitsPerPixel;
if(!screen)
{
FCEUD_PrintError(SDL_GetError());
KillVideo();
return(0);
}
//BlitBuf=SDL_CreateRGBSurface(SDL_HWSURFACE,256,240,screen->format->BitsPerPixel,screen->format->Rmask,screen->format->Gmask,screen->format->Bmask,0);
inited=1;
FCEU_printf(" Video Mode: %d x %d x %d bpp %s\n",screen->w,screen->h,screen->format->BitsPerPixel,_fullscreen?"full screen":"");
if(curbpp!=16 && curbpp!=24 && curbpp!=8 && curbpp!=32)
{
FCEU_printf(" Sorry, %dbpp modes are not supported by FCE Ultra. Supported bit depths are 8bpp, 16bpp, and 32bpp.\n",curbpp);
KillVideo();
return(0);
}
if(gi->name)
SDL_WM_SetCaption(gi->name,gi->name);
else
SDL_WM_SetCaption("FCE Ultra","FCE Ultra");
#ifdef LSB_FIRST
IconSurface=SDL_CreateRGBSurfaceFrom((void *)fceu_playicon.pixel_data,32,32,24,32*3,0xFF,0xFF00,0xFF0000,0x00);
#else
IconSurface=SDL_CreateRGBSurfaceFrom((void *)fceu_playicon.pixel_data,32,32,24,32*3,0xFF0000,0xFF00,0xFF,0x00);
#endif
SDL_WM_SetIcon(IconSurface,0);
paletterefresh=1;
if(curbpp>8)
#ifdef OPENGL
if(!usingogl)
#endif
InitBlitToHigh(curbpp>>3,screen->format->Rmask,screen->format->Gmask,screen->format->Bmask,eefx,sponge);
#ifdef OPENGL
if(usingogl)
if(!InitOpenGL((eoptions&EO_CLIPSIDES)?8:0,256-((eoptions&EO_CLIPSIDES)?8:0),srendline,erendline+1,exs,eys,eefx,_openglip,_stretchx,_stretchy,screen))
{
FCEUD_PrintError("Error initializing OpenGL.");
KillVideo();
return(0);
}
#endif
return 1;
}
void ToggleFS(void)
{
extern FCEUGI *CurGame;
KillVideo();
_fullscreen=!_fullscreen;
if(!InitVideo(CurGame))
{
_fullscreen=!_fullscreen;
InitVideo(CurGame);
}
}
static SDL_Color psdl[256];
void FCEUD_SetPalette(uint8 index, uint8 r, uint8 g, uint8 b)
{
psdl[index].r=r;
psdl[index].g=g;
psdl[index].b=b;
paletterefresh=1;
}
void FCEUD_GetPalette(uint8 index, uint8 *r, uint8 *g, uint8 *b)
{
*r=psdl[index].r;
*g=psdl[index].g;
*b=psdl[index].b;
}
static void RedoPalette(void)
{
#ifdef OPENGL
if(usingogl)
SetOpenGLPalette((uint8*)psdl);
else
#endif
{
if(curbpp>8)
SetPaletteBlitToHigh((uint8*)psdl);
else
{
SDL_SetPalette(screen,SDL_PHYSPAL,psdl,0,256);
}
}
}
void LockConsole(){}
void UnlockConsole(){}
void BlitScreen(uint8 *XBuf)
{
SDL_Surface *TmpScreen;
uint8 *dest;
int xo=0,yo=0;
if(!screen) return;
if(paletterefresh)
{
RedoPalette();
paletterefresh=0;
}
#ifdef OPENGL
if(usingogl)
{
BlitOpenGL(XBuf);
return;
}
#endif
XBuf+=srendline*256;
if(BlitBuf) TmpScreen=BlitBuf;
else TmpScreen=screen;
if(SDL_MUSTLOCK(TmpScreen))
if(SDL_LockSurface(TmpScreen))
{
return;
}
dest=(uint8*)TmpScreen->pixels;
if(_fullscreen)
{
xo=(((TmpScreen->w-NWIDTH*exs))/2);
dest+=xo*(curbpp>>3);
if(TmpScreen->h>(tlines*eys))
{
yo=((TmpScreen->h-tlines*eys)/2);
dest+=yo*TmpScreen->pitch;
}
}
if(curbpp>8)
{
if(BlitBuf)
Blit8ToHigh(XBuf+NOFFSET,dest, NWIDTH, tlines, TmpScreen->pitch,1,1);
else
Blit8ToHigh(XBuf+NOFFSET,dest, NWIDTH, tlines, TmpScreen->pitch,exs,eys);
}
else
{
if(BlitBuf)
Blit8To8(XBuf+NOFFSET,dest, NWIDTH, tlines, TmpScreen->pitch,1,1,0,sponge);
else
Blit8To8(XBuf+NOFFSET,dest, NWIDTH, tlines, TmpScreen->pitch,exs,eys,eefx,sponge);
}
if(SDL_MUSTLOCK(TmpScreen))
SDL_UnlockSurface(TmpScreen);
if(BlitBuf)
{
SDL_Rect srect;
SDL_Rect drect;
srect.x=0;
srect.y=0;
srect.w=NWIDTH;
srect.h=tlines;
drect.x=0;
drect.y=0;
drect.w=exs*NWIDTH;
drect.h=eys*tlines;
SDL_BlitSurface(BlitBuf, &srect,screen,&drect);
}
SDL_UpdateRect(screen, xo, yo, NWIDTH*exs, tlines*eys);
if(screen->flags&SDL_DOUBLEBUF)
SDL_Flip(screen);
}
uint32 PtoV(uint16 x, uint16 y)
{
y=(double)y/eys;
x=(double)x/exs;
if(eoptions&EO_CLIPSIDES)
x+=8;
y+=srendline;
return(x|(y<<16));
}

1
src/attic/pc/sdl-video.h Normal file
View File

@ -0,0 +1 @@
uint32 PtoV(uint16 x, uint16 y);

429
src/attic/pc/sdl.c Normal file
View File

@ -0,0 +1,429 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sdl.h"
#include "sdl-video.h"
#ifdef NETWORK
#include "unix-netplay.h"
#endif
DSETTINGS Settings;
CFGSTRUCT DriverConfig[]={
#ifdef OPENGL
AC(_stretchx),
AC(_stretchy),
AC(_opengl),
AC(_openglip),
#endif
AC(Settings.special),
AC(Settings.specialfs),
AC(_doublebuf),
AC(_xscale),
AC(_yscale),
AC(_xscalefs),
AC(_yscalefs),
AC(_bpp),
AC(_efx),
AC(_efxfs),
AC(_fullscreen),
AC(_xres),
AC(_yres),
#ifdef NETWORK
ACS(netplaynick),
AC(netlocalplayers),
AC(tport),
ACS(netpassword),
ACS(netgamekey),
#endif
ENDCFGSTRUCT
};
//-fshack x Set the environment variable SDL_VIDEODRIVER to \"x\" when
// entering full screen mode and x is not \"0\".
char *DriverUsage=
"-xres x Set horizontal resolution to x for full screen mode.\n\
-yres x Set vertical resolution to x for full screen mode.\n\
-xscale(fs) x Multiply width by x(Real numbers >0 with OpenGL, otherwise integers >0).\n\
-yscale(fs) x Multiply height by x(Real numbers >0 with OpenGL, otherwise integers >0).\n\
-bpp(fs) x Bits per pixel for SDL surface(and video mode in fs). 8, 16, 32.\n\
-opengl x Enable OpenGL support if x is 1.\n\
-openglip x Enable OpenGL linear interpolation if x is 1.\n\
-doublebuf x \n\
-special(fs) x Specify special scaling filter.\n\
-stretch(x/y) x Stretch to fill surface on x or y axis(fullscreen, only with OpenGL).\n\
-efx(fs) x Enable special effects. Logically OR the following together:\n\
1 = scanlines(for yscale>=2).\n\
2 = TV blur(for bpp of 16 or 32).\n\
-fs x Select full screen mode if x is non zero.\n\
-connect s Connect to server 's' for TCP/IP network play.\n\
-netnick s Set the nickname to use in network play.\n\
-netgamekey s Use key 's' to create a unique session for the game loaded.\n\
-netpassword s Password to use for connecting to the server.\n\
-netlocalplayers x Set the number of local players.\n\
-netport x Use TCP/IP port x for network play.";
ARGPSTRUCT DriverArgs[]={
#ifdef OPENGL
{"-opengl",0,&_opengl,0},
{"-openglip",0,&_openglip,0},
{"-stretchx",0,&_stretchx,0},
{"-stretchy",0,&_stretchy,0},
#endif
{"-special",0,&Settings.special,0},
{"-specialfs",0,&Settings.specialfs,0},
{"-doublebuf",0,&_doublebuf,0},
{"-bpp",0,&_bpp,0},
{"-xscale",0,&_xscale,2},
{"-yscale",0,&_yscale,2},
{"-efx",0,&_efx,0},
{"-xscalefs",0,&_xscalefs,2},
{"-yscalefs",0,&_yscalefs,2},
{"-efxfs",0,&_efxfs,0},
{"-xres",0,&_xres,0},
{"-yres",0,&_yres,0},
{"-fs",0,&_fullscreen,0},
//{"-fshack",0,&_fshack,0x4001},
#ifdef NETWORK
{"-connect",0,&netplayhost,0x4001},
{"-netport",0,&tport,0},
{"-netlocalplayers",0,&netlocalplayers,0},
{"-netnick",0,&netplaynick,0x4001},
{"-netpassword",0,&netpassword,0x4001},
#endif
{0,0,0,0}
};
static void SetDefaults(void)
{
Settings.special=Settings.specialfs=0;
_bpp=8;
_xres=640;
_yres=480;
_fullscreen=0;
_xscale=2.50;
_yscale=2;
_xscalefs=_yscalefs=2;
_efx=_efxfs=0;
//_fshack=_fshacksave=0;
#ifdef OPENGL
_opengl=1;
_stretchx=1;
_stretchy=0;
_openglip=1;
#endif
}
void DoDriverArgs(void)
{
#ifdef BROKEN
if(_fshack)
{
if(_fshack[0]=='0')
if(_fshack[1]==0)
{
free(_fshack);
_fshack=0;
}
}
#endif
}
int InitMouse(void)
{
return(0);
}
void KillMouse(void){}
void GetMouseData(uint32 *d)
{
if(FCEUI_IsMovieActive()<0)
return;
int x,y;
uint32 t;
t=SDL_GetMouseState(&x,&y);
#ifdef EXTGUI
GUI_GetMouseState(&t,&x,&y);
#endif
d[2]=0;
if(t&SDL_BUTTON(1))
d[2]|=1;
if(t&SDL_BUTTON(3))
d[2]|=2;
t=PtoV(x,y);
d[0]=t&0xFFFF;
d[1]=(t>>16)&0xFFFF;
}
int InitKeyboard(void)
{
return(1);
}
int UpdateKeyboard(void)
{
return(1);
}
void KillKeyboard(void)
{
}
void UpdatePhysicalInput(void)
{
SDL_Event event;
while(SDL_PollEvent(&event))
{
switch(event.type)
{
//case SDL_SYSWMEVENT: puts("Nifty keen");break;
//case SDL_VIDEORESIZE: puts("Okie dokie");break;
case SDL_QUIT: CloseGame();puts("Quit");break;
}
//printf("Event: %d\n",event.type);
//fflush(stdout);
}
//SDL_PumpEvents();
}
static uint8 *KeyState=NULL;
char *GetKeyboard(void)
{
KeyState=SDL_GetKeyState(0);
#ifdef EXTGUI
{ char *tmp=GUI_GetKeyboard(); if(tmp) KeyState=tmp; }
#endif
return((char *)KeyState);
}
#ifdef WIN32
#include <windows.h>
/* Stupid SDL */
#ifdef main
#undef main
#endif
#endif
#ifndef EXTGUI
uint8 *GetBaseDirectory(void)
{
uint8 *ol;
uint8 *ret;
ol=getenv("HOME");
if(ol)
{
ret=malloc(strlen(ol)+1+strlen("./fceultra"));
strcpy(ret,ol);
strcat(ret,"/.fceultra");
}
else
{
#ifdef WIN32
char *sa;
ret=malloc(MAX_PATH+1);
GetModuleFileName(NULL,ret,MAX_PATH+1);
sa=strrchr(ret,'\\');
if(sa)
*sa = 0;
#else
ret=malloc(1);
ret[0]=0;
#endif
printf("%s\n",ret);
}
return(ret);
}
#endif
#ifdef OPENGL
int sdlhaveogl;
#endif
int DTestButton(ButtConfig *bc)
{
int x;
for(x=0;x<bc->NumC;x++)
{
if(bc->ButtType[x]==BUTTC_KEYBOARD)
{
if(KeyState[bc->ButtonNum[x]])
return(1);
}
else if(bc->ButtType[x]==BUTTC_JOYSTICK)
{
if(DTestButtonJoy(bc))
return(1);
}
}
return(0);
}
static int bcpv,bcpj;
int ButtonConfigBegin(void)
{
SDL_Surface *screen;
SDL_QuitSubSystem(SDL_INIT_VIDEO);
bcpv=KillVideo();
bcpj=KillJoysticks();
if(!(SDL_WasInit(SDL_INIT_VIDEO)&SDL_INIT_VIDEO))
if(SDL_InitSubSystem(SDL_INIT_VIDEO)==-1)
{
FCEUD_Message(SDL_GetError());
return(0);
}
screen = SDL_SetVideoMode(300, 1, 8, 0);
SDL_WM_SetCaption("Button Config",0);
InitJoysticks();
return(1);
}
void ButtonConfigEnd(void)
{
extern FCEUGI *CurGame;
KillJoysticks();
SDL_QuitSubSystem(SDL_INIT_VIDEO);
if(bcpv) InitVideo(CurGame);
if(bcpj) InitJoysticks();
}
int DWaitButton(const uint8 *text, ButtConfig *bc, int wb)
{
SDL_Event event;
static int32 LastAx[64][64];
int x,y;
SDL_WM_SetCaption(text,0);
#ifndef EXTGUI
puts(text);
#endif
for(x=0;x<64;x++)
for(y=0;y<64;y++)
LastAx[x][y]=0x100000;
while(SDL_WaitEvent(&event))
{
switch(event.type)
{
case SDL_KEYDOWN:bc->ButtType[wb]=BUTTC_KEYBOARD;
bc->DeviceNum[wb]=0;
bc->ButtonNum[wb]=event.key.keysym.sym;
return(1);
case SDL_JOYBUTTONDOWN:bc->ButtType[wb]=BUTTC_JOYSTICK;
bc->DeviceNum[wb]=event.jbutton.which;
bc->ButtonNum[wb]=event.jbutton.button;
return(1);
case SDL_JOYHATMOTION:if(event.jhat.value != SDL_HAT_CENTERED)
{
bc->ButtType[wb]=BUTTC_JOYSTICK;
bc->DeviceNum[wb]=event.jhat.which;
bc->ButtonNum[wb]=0x2000|((event.jhat.hat&0x1F)<<8)|event.jhat.value;
return(1);
}
break;
case SDL_JOYAXISMOTION:
if(LastAx[event.jaxis.which][event.jaxis.axis]==0x100000)
{
if(abs(event.jaxis.value)<1000)
LastAx[event.jaxis.which][event.jaxis.axis]=event.jaxis.value;
}
else
{
if(abs(LastAx[event.jaxis.which][event.jaxis.axis]-event.jaxis.value)>=8192)
{
bc->ButtType[wb]=BUTTC_JOYSTICK;
bc->DeviceNum[wb]=event.jaxis.which;
bc->ButtonNum[wb]=0x8000|(event.jaxis.axis)|((event.jaxis.value<0)?0x4000:0);
return(1);
}
}
break;
}
}
return(0);
}
#ifdef EXTGUI
int FCEUSDLmain(int argc, char *argv[])
#else
int main(int argc, char *argv[])
#endif
{
FCEUD_Message("\nStarting FCE Ultra "FCEU_VERSION"...\n");
#ifdef WIN32
/* Taken from win32 sdl_main.c */
SDL_SetModuleHandle(GetModuleHandle(NULL));
#endif
if(SDL_Init(SDL_INIT_VIDEO)) /* SDL_INIT_VIDEO Needed for (joystick config) event processing? */
{
printf("Could not initialize SDL: %s.\n", SDL_GetError());
return(-1);
}
#ifdef OPENGL
#ifdef APPLEOPENGL
sdlhaveogl = 1; /* Stupid something... Hack. */
#else
if(!SDL_GL_LoadLibrary(0)) sdlhaveogl=1;
else sdlhaveogl=0;
#endif
#endif
SetDefaults();
{
int ret=CLImain(argc,argv);
SDL_Quit();
return(ret?0:-1);
}
}
uint64 FCEUD_GetTime(void)
{
return(SDL_GetTicks());
}
uint64 FCEUD_GetTimeFreq(void)
{
return(1000);
}
// dummy functions
#define DUMMY(f) void f(void) {FCEU_DispMessage("Not implemented.");}
DUMMY(FCEUD_HideMenuToggle)
DUMMY(FCEUD_TurboOn)
DUMMY(FCEUD_TurboOff)
DUMMY(FCEUD_SaveStateAs)
DUMMY(FCEUD_LoadStateFrom)
DUMMY(FCEUD_MovieRecordTo)
DUMMY(FCEUD_MovieReplayFrom)
DUMMY(FCEUD_ToggleStatusIcon)
DUMMY(FCEUD_AviRecordTo)
DUMMY(FCEUD_AviStop)
void FCEUI_AviVideoUpdate(const unsigned char* buffer) {FCEU_DispMessage("Not implemented.");}
int FCEUD_ShowStatusIcon(void) {FCEU_DispMessage("Not implemented."); return 0; }
int FCEUI_AviIsRecording(void) {return 0;}

50
src/attic/pc/sdl.h Normal file
View File

@ -0,0 +1,50 @@
#include <SDL.h>
#include "main.h"
#include "dface.h"
#include "input.h"
int DTestButtonJoy(ButtConfig *bc);
typedef struct {
int xres;
int yres;
double xscale,yscale;
double xscalefs,yscalefs;
int efx,efxfs;
int bpp;
int fullscreen;
int doublebuf;
char *fshack;
char *fshacksave;
#ifdef OPENGL
int opengl;
int openglip;
int stretchx,stretchy;
#endif
int special,specialfs;
} DSETTINGS;
extern DSETTINGS Settings;
#define _doublebuf Settings.doublebuf
#define _bpp Settings.bpp
#define _xres Settings.xres
#define _yres Settings.yres
#define _fullscreen Settings.fullscreen
#define _xscale Settings.xscale
#define _yscale Settings.yscale
#define _xscalefs Settings.xscalefs
#define _yscalefs Settings.yscalefs
#define _efx Settings.efx
#define _efxfs Settings.efxfs
#define _ebufsize Settings.ebufsize
#define _fshack Settings.fshack
#define _fshacksave Settings.fshacksave
#ifdef OPENGL
#define _opengl Settings.opengl
#define _openglip Settings.openglip
#define _stretchx Settings.stretchx
#define _stretchy Settings.stretchy
#endif

41
src/attic/pc/throttle.c Normal file
View File

@ -0,0 +1,41 @@
#include <sys/time.h>
#include "main.h"
#include "throttle.h"
static uint64 tfreq;
static uint64 desiredfps;
void RefreshThrottleFPS(void)
{
desiredfps=FCEUI_GetDesiredFPS()>>8;
tfreq=1000000;
tfreq<<=16; /* Adjustment for fps returned from FCEUI_GetDesiredFPS(). */
}
static uint64 GetCurTime(void)
{
uint64 ret;
struct timeval tv;
gettimeofday(&tv,0);
ret=(uint64)tv.tv_sec*1000000;
ret+=tv.tv_usec;
return(ret);
}
void SpeedThrottle(void)
{
static uint64 ttime,ltime=0;
waiter:
ttime=GetCurTime();
if( (ttime-ltime) < (tfreq/desiredfps) )
goto waiter;
if( (ttime-ltime) >= (tfreq*4/desiredfps))
ltime=ttime;
else
ltime+=tfreq/desiredfps;
}

2
src/attic/pc/throttle.h Normal file
View File

@ -0,0 +1,2 @@
void RefreshThrottleFPS(void);
void SpeedThrottle(void);

292
src/attic/pc/unix-netplay.c Normal file
View File

@ -0,0 +1,292 @@
#ifdef NETWORK
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2002 Xodnizel
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <netdb.h>
#include <errno.h>
#include <fcntl.h>
#include "main.h"
#include "dface.h"
#include "unix-netplay.h"
#include "../../md5.h"
#ifndef socklen_t
#define socklen_t int
#endif
#ifndef SOL_TCP
#define SOL_TCP IPPROTO_TCP
#endif
char *netplayhost=0;
char *netplaynick=0;
char *netgamekey = 0;
char *netpassword = 0;
int netlocalplayers = 1;
int Port=0xFCE;
int FCEUDnetplay=0;
int tport=0xFCE;
static int Socket=-1;
static void en32(uint8 *buf, uint32 morp)
{
buf[0]=morp;
buf[1]=morp>>8;
buf[2]=morp>>16;
buf[3]=morp>>24;
}
/*
static uint32 de32(uint8 *morp)
{
return(morp[0]|(morp[1]<<8)|(morp[2]<<16)|(morp[3]<<24));
}
*/
int FCEUD_NetworkConnect(void)
{
struct sockaddr_in sockin; /* I want to play with fighting robots. */
struct hostent *phostentb;
unsigned long hadr;
int TSocket;
int netdivisor;
if(!netplayhost) return(0);
if( (TSocket=socket(AF_INET,SOCK_STREAM,0))==-1)
{
puts("Error creating stream socket.");
FCEUD_NetworkClose();
return(0);
}
{
int tcpopt = 1;
#ifdef BEOS
if(setsockopt(TSocket, SOL_SOCKET, TCP_NODELAY, &tcpopt, sizeof(int)))
#else
if(setsockopt(TSocket, SOL_TCP, TCP_NODELAY, &tcpopt, sizeof(int)))
#endif
puts("Nodelay fail");
}
memset(&sockin,0,sizeof(sockin));
sockin.sin_family=AF_INET;
hadr=inet_addr(netplayhost);
if(hadr!=INADDR_NONE)
sockin.sin_addr.s_addr=hadr;
else
{
puts("*** Looking up host name...");
if(!(phostentb=gethostbyname((const char *)netplayhost)))
{
puts("Error getting host network information.");
close(TSocket);
FCEUD_NetworkClose();
return(0);
}
memcpy(&sockin.sin_addr,phostentb->h_addr,phostentb->h_length);
}
sockin.sin_port=htons(tport);
puts("*** Connecting to remote host...");
if(connect(TSocket,(struct sockaddr *)&sockin,sizeof(sockin))==-1)
{
puts("Error connecting to remote host.");
close(TSocket);
FCEUD_NetworkClose();
return(0);
}
Socket=TSocket;
puts("*** Sending initialization data to server...");
{
uint8 *sendbuf;
uint8 buf[5];
uint32 sblen;
sblen = 4 + 16 + 16 + 64 + 1 + (netplaynick?strlen(netplaynick):0);
sendbuf = malloc(sblen);
memset(sendbuf, 0, sblen);
en32(sendbuf, sblen - 4);
if(netgamekey)
{
struct md5_context md5;
uint8 md5out[16];
md5_starts(&md5);
md5_update(&md5, CurGame->MD5, 16);
md5_update(&md5, netgamekey, strlen(netgamekey));
md5_finish(&md5, md5out);
memcpy(sendbuf + 4, md5out, 16);
}
else
memcpy(sendbuf + 4, CurGame->MD5, 16);
if(netpassword)
{
struct md5_context md5;
uint8 md5out[16];
md5_starts(&md5);
md5_update(&md5, netpassword, strlen(netpassword));
md5_finish(&md5, md5out);
memcpy(sendbuf + 4 + 16, md5out, 16);
}
memset(sendbuf + 4 + 16 + 16, 0, 64);
sendbuf[4 + 16 + 16 + 64] = netlocalplayers;
if(netplaynick)
memcpy(sendbuf + 4 + 16 + 16 + 64 + 1,netplaynick,strlen(netplaynick));
send(Socket, sendbuf, sblen, 0);
free(sendbuf);
recv(Socket, buf, 1, MSG_WAITALL);
netdivisor = buf[0];
}
puts("*** Connection established.");
FCEUDnetplay = 1;
FCEUI_NetplayStart(netlocalplayers, netdivisor);
return(1);
}
int FCEUD_SendData(void *data, uint32 len)
{
int check;
if(!ioctl(fileno(stdin),FIONREAD,&check))
if(check)
{
char buf[1024];
char *f;
fgets(buf,1024,stdin);
if((f=strrchr(buf,'\n')))
*f=0;
FCEUI_NetplayText(buf);
}
send(Socket, data, len ,0);
return(1);
}
int FCEUD_RecvData(void *data, uint32 len)
{
NoWaiting&=~2;
for(;;)
{
fd_set funfun;
struct timeval popeye;
popeye.tv_sec=0;
popeye.tv_usec=100000;
FD_ZERO(&funfun);
FD_SET(Socket,&funfun);
switch(select(Socket + 1,&funfun,0,0,&popeye))
{
case 0: continue;
case -1:return(0);
}
if(FD_ISSET(Socket,&funfun))
{
if(recv(Socket,data,len,MSG_WAITALL) == len)
{
//unsigned long beefie;
FD_ZERO(&funfun);
FD_SET(Socket, &funfun);
popeye.tv_sec = popeye.tv_usec = 0;
if(select(Socket + 1, &funfun, 0, 0, &popeye) == 1)
//if(!ioctl(Socket,FIONREAD,&beefie))
// if(beefie)
{
NoWaiting|=2;
//puts("Yaya");
}
return(1);
}
else
return(0);
}
}
return 0;
}
void FCEUD_NetworkClose(void)
{
if(Socket>0)
{
#ifdef BEOS
closesocket(Socket);
#else
close(Socket);
#endif
}
Socket=-1;
if(FCEUDnetplay)
FCEUI_NetplayStop();
FCEUDnetplay = 0;
}
void FCEUD_NetplayText(uint8 *text)
{
char *tot = malloc(strlen(text) + 1);
char *tmp;
strcpy(tot, text);
tmp = tot;
while(*tmp)
{
if(*tmp < 0x20) *tmp = ' ';
tmp++;
}
puts(tot);
free(tot);
}
#endif

View File

@ -0,0 +1,9 @@
#ifdef NETWORK
extern char *netplaynick;
extern char *netplayhost;
extern char *netpassword;
extern char *netgamekey;
extern int tport;
extern int netlocalplayers;
#endif

60
src/attic/pc/usage.h Normal file
View File

@ -0,0 +1,60 @@
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2002 Xodnizel
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
void ShowUsage(char *prog)
{
printf("\nUsage is as follows:\n%s <options> filename\n\n",prog);
puts("Options:");
puts(DriverUsage);
puts("-cpalette x Load a custom global palette from file x.\n\
-ntsccol x Emulate an NTSC's TV's colors.\n\
0 = Disabled.\n\
1 = Enabled.\n\
-pal x Emulate a PAL NES if x is 1.\n\
-sound x Sound.\n\
0 = Disabled.\n\
Otherwise, x = playback rate.\n\
-soundvol x Sound volume. x is an integral percentage value.\n\
-soundq x Sets sound quality.\n\
0 = Low quality.\n\
1 = High quality.\n\
-inputx str Select device mapped to virtual input port x(1-2).\n\
str may be: none, gamepad, zapper, powerpada, powerpadb,\n\
arkanoid\n\
-fcexp str Select Famicom expansion port device.\n\
str may be: none, shadow, arkanoid, 4player, fkb\n\
-inputcfg s Configure virtual input device \"s\".\n\
-nofs x Disables Four-Score emulation if x is 1.\n\
-gg x Enable Game Genie emulation if x is 1.\n\
-no8lim x Disables the 8 sprites per scanline limitation.\n\
0 = Limitation enabled.\n\
1 = Limitation disabled.\n\
-snapname x Selects what type of file name snapshots will have.\n\
0 = Numeric(0.png)\n\
1 = File base and numeric(mario-0.png)\n\
-nothrottle x Disable artificial speed throttling if x is non-zero.\n\
-clipsides x Clip leftmost and rightmost 8 columns of pixels of video output.\n\
0 = No clipping.\n\
1 = Clipping.\n\
-slstart x Set the first drawn emulated scanline. Valid values for x are\n\
0 through 239.\n\
-slend x Set the last drawn emulated scanline. Valid values for x are\n\
0 through 239.");
}

168
src/attic/pc/vgatweak.c Normal file
View File

@ -0,0 +1,168 @@
/* This file is "#include"d from dos-video.c and svga-video.c */
typedef struct {
uint8 p;
uint8 i;
uint8 v;
} vgareg;
vgareg v256x224_103[25] =
{
{ 0xc2, 0x0, 0xe7},
{ 0xd4, 0x0, 0x45},
{ 0xd4, 0x1, 0x3f},
{ 0xd4, 0x2, 0x40},
{ 0xd4, 0x3, 0x86},
{ 0xd4, 0x4, 0x3f},
{ 0xd4, 0x5, 0x10},
{ 0xd4, 0x6, 0xcd},
{ 0xd4, 0x7, 0x1f},
{ 0xd4, 0x8, 0x0},
{ 0xd4, 0x9, 0x41},
{ 0xd4, 0x10, 0xc0},
{ 0xd4, 0x11, 0xac},
{ 0xd4, 0x12, 0xbf},
{ 0xd4, 0x13, 0x20},
{ 0xd4, 0x14, 0x40}, //
{ 0xd4, 0x15, 0xe7},
{ 0xd4, 0x16, 0x06}, //
{ 0xd4, 0x17, 0xa3},
{ 0xc4, 0x1, 0x1},
{ 0xc4, 0x4, 0xe}, //
{ 0xce, 0x5, 0x40},
{ 0xce, 0x6, 0x5},
{ 0xc0, 0x10, 0x41},
{ 0xc0, 0x13, 0x0},
};
vgareg v256x240[25] =
{
{ 0xc2, 0x0, 0xe3},
{ 0xd4, 0x0, 0x4f},
{ 0xd4, 0x1, 0x3f},
{ 0xd4, 0x2, 0x40},
{ 0xd4, 0x3, 0x92},
{ 0xd4, 0x4, 0x44},
{ 0xd4, 0x5, 0x10},
{ 0xd4, 0x6, 0x0a},
{ 0xd4, 0x7, 0x3e},
{ 0xd4, 0x8, 0x00},
{ 0xd4, 0x9, 0x41},
{ 0xd4, 0x10, 0xea},
{ 0xd4, 0x11, 0xac},
{ 0xd4, 0x12, 0xdf},
{ 0xd4, 0x13, 0x20},
{ 0xd4, 0x14, 0x40},
{ 0xd4, 0x15, 0xe7},
{ 0xd4, 0x16, 0x06},
{ 0xd4, 0x17, 0xa3},
{ 0xc4, 0x1, 0x1},
{ 0xc4, 0x4, 0xe},
{ 0xce, 0x5, 0x40},
{ 0xce, 0x6, 0x5},
{ 0xc0, 0x10, 0x41},
{ 0xc0, 0x13, 0x0}
};
vgareg v256x224S[25] =
{
{ 0xc2, 0x0, 0xe3},
{ 0xd4, 0x0, 0x5f},
{ 0xd4, 0x1, 0x3f},
{ 0xd4, 0x2, 0x40},
{ 0xd4, 0x3, 0x82},
{ 0xd4, 0x4, 0x4e},
{ 0xd4, 0x5, 0x96},
{ 0xd4, 0x6, 0x5},
{ 0xd4, 0x7, 0x1},
{ 0xd4, 0x8, 0x0},
{ 0xd4, 0x9, 0x40},
{ 0xd4, 0x10, 0xea},
{ 0xd4, 0x11, 0xac},
{ 0xd4, 0x12, 0xdf},
{ 0xd4, 0x13, 0x20},
{ 0xd4, 0x14, 0x40},
{ 0xd4, 0x15, 0xe7},
{ 0xd4, 0x16, 0x0},
{ 0xd4, 0x17, 0xe3},
{ 0xc4, 0x1, 0x1},
{ 0xc4, 0x4, 0xe},
{ 0xce, 0x5, 0x40},
{ 0xce, 0x6, 0x5},
{ 0xc0, 0x10, 0x41},
{ 0xc0, 0x13, 0x0}
};
vgareg v256x256[25] =
{
{ 0xc2, 0x0, 0xe7},
{ 0xd4, 0x0, 0x5f},
{ 0xd4, 0x1, 0x3f},
{ 0xd4, 0x2, 0x40},
{ 0xd4, 0x3, 0x82},
{ 0xd4, 0x4, 0x4a},
{ 0xd4, 0x5, 0x9a},
{ 0xd4, 0x6, 0x23},
{ 0xd4, 0x7, 0xb2},
{ 0xd4, 0x8, 0x0},
{ 0xd4, 0x9, 0x61},
{ 0xd4, 0x10, 0xa},
{ 0xd4, 0x11, 0xac},
{ 0xd4, 0x12, 0xff},
{ 0xd4, 0x13, 0x20},
{ 0xd4, 0x14, 0x40},
{ 0xd4, 0x15, 0x7},
{ 0xd4, 0x16, 0x1a},
{ 0xd4, 0x17, 0xa3},
{ 0xc4, 0x1, 0x1},
{ 0xc4, 0x4, 0xe},
{ 0xce, 0x5, 0x40},
{ 0xce, 0x6, 0x5},
{ 0xc0, 0x10, 0x41},
{ 0xc0, 0x13, 0x0}
};
vgareg v256x256S[25] =
{
{ 0xc2, 0x00, 0xe7},{ 0xd4, 0x00, 0x5F},{ 0xd4, 0x01, 0x3f},
{ 0xd4, 0x02, 0x40},{ 0xd4, 0x03, 0x82},{ 0xd4, 0x04, 0x4a},
{ 0xd4, 0x05, 0x9a},{ 0xd4, 0x06, 0x25},{ 0xd4, 0x07, 0x15},
{ 0xd4, 0x08, 0x00},{ 0xd4, 0x09, 0x60},{ 0xd4, 0x10, 0x0a},
{ 0xd4, 0x11, 0xac},{ 0xd4, 0x12, 0xff},{ 0xd4, 0x13, 0x20},
{ 0xd4, 0x14, 0x40},{ 0xd4, 0x15, 0x07},{ 0xd4, 0x16, 0x1a},
{ 0xd4, 0x17, 0xa3},{ 0xc4, 0x01, 0x01},{ 0xc4, 0x04, 0x0e},
{ 0xce, 0x05, 0x40},{ 0xce, 0x06, 0x05},{ 0xc0, 0x10, 0x41},
{ 0xc0, 0x13, 0x00}
};
static void VGAPortSet(vgareg R)
{
int p,i,v;
p=0x300|R.p;
i=R.i;
v=R.v;
switch(p)
{
case 0x3C0: inportb(0x3DA);
outportb(0x3C0,i);
outportb(0x3C0,v);
break;
case 0x3C2:
case 0x3C3:
default: outportb(p, v);
break;
case 0x3C4: if(i==1)
{
outportw(0x3c4,0x100);
outportw(0x3c4,(v<<8)|1);
outportw(0x3c4,0x300);
break;
}
case 0x3CE:
case 0x3D4: outportw(p,i|(v<<8));
break;
}
}

View File

@ -0,0 +1,14 @@
#include "sexyal.h"
#include "convert.h"
static inline uint16_t FLIP16(uint16_t b)
{
return((b<<8)|((b>>8)&0xFF));
}
static inline uint32_t FLIP32(uint32_t b)
{
return( (b<<24) | ((b>>8)&0xFF00) | ((b<<8)&0xFF0000) | ((b>>24)&0xFF) );
}
#include "convert.inc"

View File

@ -0,0 +1,2 @@
void SexiALI_Convert(SexyAL_format *srcformat, SexyAL_format *destformat, void *vdest, void *vsrc, uint32_t frames);

20387
src/attic/sexyal/convert.inc Normal file

File diff suppressed because it is too large Load Diff

BIN
src/attic/sexyal/convertgen Normal file

Binary file not shown.

View File

@ -0,0 +1,155 @@
#include <stdio.h>
char *check[]={"SEXYAL_FMT_PCMS8","SEXYAL_FMT_PCMU8","SEXYAL_FMT_PCMS16",
"SEXYAL_FMT_PCMU16","SEXYAL_FMT_PCMS32S16","SEXYAL_FMT_PCMU32U16","SEXYAL_FMT_PCMS32S24","SEXYAL_FMT_PCMU32U24"};
char *str[]={"int8_t","uint8_t","int16_t","uint16_t","int32_t","uint32_t","int32_t","uint32_t"};
int bitss[]={8,8,16,16,16,16,24,24};
int bitsreal[]={8,8,16,16,32,32,32,32};
void Fetch(int x,char *wt)
{
//printf(" int32_t tmp%s=*src;\n",wt);
printf("tmp%s=*src;\n",wt);
printf(" src++;\n");
}
void BitConv(int src, int dest, char *wt)
{
if((src^dest)&1) /* signed/unsigned change. */
if(src&1) /* Source unsigned, dest signed. */
{
if(src==1) printf(" tmp%s-=128;\n",wt);
else if(src==3) printf(" tmp%s-=32768;\n",wt);
else if(src==5) printf(" tmp%s-=32768;\n",wt);
else if(src==7) printf(" tmp%s-=(1<<23);\n",wt);
}
else /* Source signed, dest unsigned */
{
if(src==0) printf(" tmp%s+=128;\n",wt);
else if(src==2) printf(" tmp%s+=32768;\n",wt);
else if(src==4) printf(" tmp%s+=32768;\n",wt);
else if(src==6) printf(" tmp%s+=(1<<23);\n",wt);
}
if((src>>1) != (dest>>1))
{
int shifty=bitss[src]-bitss[dest];
if(shifty>0)
printf(" tmp%s >>= %d;\n",wt,shifty);
else
printf(" tmp%s <<= %d;\n",wt,-shifty);
}
}
void Save(int x, char *wt)
{
printf(" *dest=tmp%s;\n",wt);
printf(" dest++;\n");
}
main()
{
int srcbits,destbits,srcchannels,destchannels;
int srcbo,destbo;
puts("void SexiALI_Convert(SexyAL_format *srcformat, SexyAL_format *destformat, void *vdest, void *vsrc, uint32_t frames)");
puts("{");
for(srcbits=0;srcbits<8;srcbits++)
{
printf("if(srcformat->sampformat == %s)\n{\n",check[srcbits]);
printf("%s* src=vsrc;\n",str[srcbits]);
for(destbits=0;destbits<8;destbits++)
{
printf("if(destformat->sampformat == %s)\n{\n",check[destbits]);
printf("%s* dest=vdest;\n",str[destbits]);
for(srcchannels=0;srcchannels<2;srcchannels++)
{
printf("if(srcformat->channels == %c)\n{\n",'1'+srcchannels);
for(destchannels=0;destchannels<2;destchannels++)
{
printf("if(destformat->channels == %c)\n{\n",'1'+destchannels);
for(srcbo=0;srcbo<2;srcbo++)
{
printf("if(srcformat->byteorder == %d)\n{\n",srcbo);
for(destbo=0;destbo<2;destbo++)
{
printf("if(destformat->byteorder == %d)\n{\n",destbo);
//printf("if(srcformat->sampformat==%s && destformat->sameck[srcbits],check[destbits]);
printf("while(frames--)\n{\n");
puts("int32_t tmp;");
if(srcchannels)
puts("int32_t tmp2;");
Fetch(srcbits,"");
if(srcbo)
{
if(bitsreal[srcbits]==16)
puts("FLIP16(tmp);");
else
puts("FLIP32(tmp);");
}
if(srcchannels)
{
Fetch(srcbits,"2");
if(srcbo)
{
if(bitsreal[srcbits]==16)
puts("FLIP16(tmp2);");
else
puts("FLIP32(tmp2);");
}
}
BitConv(srcbits,destbits,"");
if(srcchannels) BitConv(srcbits,destbits,"2");
if(destbo)
{
if(bitsreal[srcbits]==16)
puts("FLIP16(tmp);");
else
puts("FLIP32(tmp);");
if(srcchannels && destchannels && destbo)
{
if(bitsreal[srcbits]==16)
puts("FLIP16(tmp2);");
else
puts("FLIP32(tmp2);");
}
}
if(srcchannels && !destchannels)
printf("tmp = (tmp+tmp2)>>1;\n");
Save(destbits,"");
if(!srcchannels && destchannels)
Save(destbits,"");
if(srcchannels && destchannels)
Save(destbits,"2");
puts("}");
puts("}");
} // destbo
puts("}");
} // srcbo
puts("}");
}
puts("}");
}
puts("}");
}
puts("}");
}
puts("}");
}

View File

@ -0,0 +1,276 @@
#include <windows.h>
#include <windowsx.h>
#include <stdio.h>
#undef WINNT
#define NONAMELESSUNION
#define DIRECTSOUND_VERSION 0x0300
#include <dsound.h>
#include "../sexyal.h"
typedef struct
{
LPDIRECTSOUND ppDS; /* DirectSound interface object. */
LPDIRECTSOUNDBUFFER ppbuf; /* Primary buffer. */
LPDIRECTSOUNDBUFFER ppbufsec; /* Secondary buffer. */
LPDIRECTSOUNDBUFFER ppbufw; /* Buffer to do writes to. */
WAVEFORMATEX wf; /* Format of the primary and secondary buffers. */
long DSBufferSize; /* The size of the buffer that we can write to, in bytes. */
long BufHowMuch; /* How many bytes we should try to buffer. */
DWORD ToWritePos; /* Position which the next write to the buffer
should write to.
*/
} DSFobby;
static void CheckStatus(DSFobby *tmp)
{
DWORD status=0;
IDirectSoundBuffer_GetStatus(tmp->ppbufw, &status);
if(status&DSBSTATUS_BUFFERLOST)
IDirectSoundBuffer_Restore(tmp->ppbufw);
if(!(status&DSBSTATUS_PLAYING))
{
tmp->ToWritePos=0;
IDirectSoundBuffer_SetCurrentPosition(tmp->ppbufsec,0);
IDirectSoundBuffer_SetFormat(tmp->ppbufw,&tmp->wf);
IDirectSoundBuffer_Play(tmp->ppbufw,0,0,DSBPLAY_LOOPING);
}
}
SexyAL_device *SexyALI_DSound_Open(uint64_t id, SexyAL_format *format, SexyAL_buffering *buffering)
{
SexyAL_device *dev;
DSFobby *fobby;
DSBUFFERDESC DSBufferDesc;
DSCAPS dscaps;
DSBCAPS dsbcaps;
dev=malloc(sizeof(SexyAL_device));
fobby=malloc(sizeof(DSFobby));
memset(fobby,0,sizeof(DSFobby));
memset(&fobby->wf,0,sizeof(WAVEFORMATEX));
fobby->wf.wFormatTag = WAVE_FORMAT_PCM;
fobby->wf.nChannels = format->channels;
fobby->wf.nSamplesPerSec = format->rate;
if(DirectSoundCreate(0,&fobby->ppDS,0) != DS_OK)
{
free(dev);
free(fobby);
return(0);
}
{
//HWND hWnd = GetForegroundWindow(); // Ugly.
//if(!hWnd)
//{ hWnd=GetDesktopWindow(); exit(1); }
HWND hWnd;
hWnd=GetDesktopWindow();
IDirectSound_SetCooperativeLevel(fobby->ppDS,hWnd,DSSCL_PRIORITY);
}
memset(&dscaps,0x00,sizeof(dscaps));
dscaps.dwSize=sizeof(dscaps);
IDirectSound_GetCaps(fobby->ppDS,&dscaps);
IDirectSound_Compact(fobby->ppDS);
/* Create primary buffer */
memset(&DSBufferDesc,0x00,sizeof(DSBUFFERDESC));
DSBufferDesc.dwSize=sizeof(DSBufferDesc);
DSBufferDesc.dwFlags=DSBCAPS_PRIMARYBUFFER;
if(IDirectSound_CreateSoundBuffer(fobby->ppDS,&DSBufferDesc,&fobby->ppbuf,0) != DS_OK)
{
IDirectSound_Release(fobby->ppDS);
free(dev);
free(fobby);
return(0);
}
/* Set primary buffer format. */
if(format->sampformat == SEXYAL_FMT_PCMU8)
fobby->wf.wBitsPerSample=8;
else // if(format->sampformat == SEXYAL_FMT_PCMS16)
{
fobby->wf.wBitsPerSample=16;
format->sampformat=SEXYAL_FMT_PCMS16;
}
fobby->wf.nBlockAlign=fobby->wf.wBitsPerSample>>3;
fobby->wf.nAvgBytesPerSec=fobby->wf.nSamplesPerSec*fobby->wf.nBlockAlign;
if(IDirectSoundBuffer_SetFormat(fobby->ppbuf,&fobby->wf) != DS_OK)
{
IDirectSound_Release(fobby->ppbuf);
IDirectSound_Release(fobby->ppDS);
free(dev);
free(fobby);
return(0);
}
/* Create secondary sound buffer */
IDirectSoundBuffer_GetFormat(fobby->ppbuf,&fobby->wf,sizeof(WAVEFORMATEX),0);
memset(&DSBufferDesc,0x00,sizeof(DSBUFFERDESC));
DSBufferDesc.dwSize=sizeof(DSBufferDesc);
DSBufferDesc.dwFlags=DSBCAPS_GETCURRENTPOSITION2;
DSBufferDesc.dwFlags|=DSBCAPS_GLOBALFOCUS;
DSBufferDesc.dwBufferBytes=65536;
DSBufferDesc.lpwfxFormat=&fobby->wf;
if(IDirectSound_CreateSoundBuffer(fobby->ppDS, &DSBufferDesc, &fobby->ppbufsec, 0) != DS_OK)
{
IDirectSound_Release(fobby->ppbuf);
IDirectSound_Release(fobby->ppDS);
free(dev);
free(fobby);
return(0);
}
fobby->DSBufferSize=65536;
IDirectSoundBuffer_SetCurrentPosition(fobby->ppbufsec,0);
fobby->ppbufw=fobby->ppbufsec;
memcpy(&dev->format,format,sizeof(SexyAL_format));
if(!buffering->ms)
buffering->ms=53;
buffering->totalsize=(int64_t)format->rate*buffering->ms/1000;
fobby->BufHowMuch=buffering->totalsize* format->channels * (format->sampformat>>4);
//printf("%d\n",fobby->BufHowMuch);
//fflush(stdout);
dev->private=fobby;
timeBeginPeriod(1);
return(dev);
}
uint32_t SexyALI_DSound_RawCanWrite(SexyAL_device *device)
{
DSFobby *tmp=device->private;
DWORD CurWritePos,CurPlayPos=0;
CheckStatus(tmp);
CurWritePos=0;
if(IDirectSoundBuffer_GetCurrentPosition(tmp->ppbufw,&CurPlayPos,&CurWritePos)==DS_OK)
{
//FCEU_DispMessage("%d",CurWritePos-CurPlayPos);
}
CurWritePos=(CurPlayPos+tmp->BufHowMuch)%tmp->DSBufferSize;
/* If the current write pos is >= half the buffer size less than the to write pos,
assume DirectSound has wrapped around.
*/
if(((int32_t)tmp->ToWritePos-(int32_t)CurWritePos) >= (tmp->DSBufferSize/2))
{
CurWritePos+=tmp->DSBufferSize;
//printf("Fixit: %d,%d,%d\n",tmp->ToWritePos,CurWritePos,CurWritePos-tmp->DSBufferSize);
}
if(tmp->ToWritePos<CurWritePos)
{
int32_t howmuch=(int32_t)CurWritePos-(int32_t)tmp->ToWritePos;
if(howmuch > tmp->BufHowMuch) /* Oopsie. Severe buffer overflow... */
{
tmp->ToWritePos=CurWritePos%tmp->DSBufferSize;
//IDirectSoundBuffer_Stop(tmp->ppbufsec);
//IDirectSoundBuffer_SetCurrentPosition(tmp->ppbufsec,tmp->ToWritePos);
//puts("Oops");
//fflush(stdout);
//return(0);
}
return(CurWritePos-tmp->ToWritePos);
}
else
return(0);
}
int SexyALI_DSound_RawWrite(SexyAL_device *device, void *data, uint32_t len)
{
DSFobby *tmp=device->private;
// uint32_t cw;
//printf("Pre: %d\n",SexyALI_DSound_RawCanWrite(device));
//fflush(stdout);
CheckStatus(tmp);
/* In this block, we write as much data as we can, then we write
the rest of it in >=1ms chunks.
*/
while(len)
{
VOID *LockPtr[2]={0,0};
DWORD LockLen[2]={0,0};
int32_t curlen;
while(!(curlen=SexyALI_DSound_RawCanWrite(device)))
{
Sleep(1);
}
if(curlen>len) curlen=len;
if(DS_OK == IDirectSoundBuffer_Lock(tmp->ppbufw,tmp->ToWritePos,curlen,&LockPtr[0],&LockLen[0],&LockPtr[1],&LockLen[1],0))
{
}
if(LockPtr[1] != 0 && LockPtr[1] != LockPtr[0])
{
memcpy(LockPtr[0],data,LockLen[0]);
memcpy(LockPtr[1],data+LockLen[0],len-LockLen[0]);
}
else if(LockPtr[0])
{
memcpy(LockPtr[0],data,curlen);
}
IDirectSoundBuffer_Unlock(tmp->ppbufw,LockPtr[0],LockLen[0],LockPtr[1],LockLen[1]);
tmp->ToWritePos=(tmp->ToWritePos+curlen)%tmp->DSBufferSize;
len-=curlen;
(uint8_t *) data+=curlen;
if(len)
Sleep(1);
} // end while(len) loop
return(1);
}
int SexyALI_DSound_Close(SexyAL_device *device)
{
if(device)
{
if(device->private)
{
DSFobby *tmp=device->private;
if(tmp->ppbufsec)
{
IDirectSoundBuffer_Stop(tmp->ppbufsec);
IDirectSoundBuffer_Release(tmp->ppbufsec);
}
if(tmp->ppbuf)
{
IDirectSoundBuffer_Stop(tmp->ppbuf);
IDirectSoundBuffer_Release(tmp->ppbuf);
}
if(tmp->ppDS)
{
IDirectSound_Release(tmp->ppDS);
}
free(device->private);
}
free(device);
timeEndPeriod(1);
return(1);
}
return(0);
}

View File

@ -0,0 +1,241 @@
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sched.h>
#include <sys/soundcard.h>
#include "../sexyal.h"
#include "../md5.h"
#include "../smallc.h"
#include "oss.h"
#define IDBASE 0x1000
void SexyALI_OSS_Enumerate(int (*func)(uint8_t *name, uint64_t id, void *udata), void *udata)
{
struct stat buf;
char fn[64];
unsigned int n;
n=0;
do
{
sal_strcpy(fn,"/dev/dsp");
sal_strcat(fn,sal_uinttos(n));
if(stat(fn,&buf)!=0) break;
} while(func(fn,n+IDBASE,udata));
}
static int FODevice(uint64_t id)
{
char fn[64];
if(id==SEXYAL_ID_DEFAULT)
{
sal_strcpy(fn,"/dev/dsp");
return(open(fn,O_WRONLY));
}
else if(id==SEXYAL_ID_UNUSED)
{
int x=-1;
int dspfd;
do
{
sal_strcpy(fn,"/dev/dsp");
if(x!=-1)
sal_strcat(fn,sal_uinttos(x));
dspfd=open(fn,O_WRONLY|O_NONBLOCK);
if(dspfd!=-1) break;
x++;
} while(errno!=ENOENT);
if(dspfd==-1) return(0);
fcntl(dspfd,F_SETFL,fcntl(dspfd,F_GETFL)&~O_NONBLOCK);
return(dspfd);
}
else
{
sal_strcpy(fn,"/dev/dsp");
sal_strcat(fn,sal_uinttos(id-IDBASE));
return(open(fn,O_WRONLY));
}
}
unsigned int Log2(unsigned int value)
{
int x=0;
value>>=1;
while(value)
{
value>>=1;
x++;
}
return(x?x:1);
}
SexyAL_device *SexyALI_OSS_Open(uint64_t id, SexyAL_format *format, SexyAL_buffering *buffering)
{
SexyAL_device *device;
int fd;
unsigned int temp;
if(!(fd=FODevice(id))) return(0);
/* Set sample format. */
/* TODO: Handle devices with byte order different from native byte order. */
/* TODO: Fix fragment size calculation to work well with lower/higher playback rates,
as reported by OSS.
*/
if(format->sampformat == SEXYAL_FMT_PCMU8)
temp=AFMT_U8;
else if(format->sampformat == SEXYAL_FMT_PCMS8)
temp=AFMT_S8;
else if(format->sampformat == SEXYAL_FMT_PCMU16)
temp=AFMT_U16_LE;
else
temp=AFMT_S16_NE;
format->byteorder=0;
ioctl(fd,SNDCTL_DSP_SETFMT,&temp);
switch(temp)
{
case AFMT_U8: format->sampformat = SEXYAL_FMT_PCMU8;break;
case AFMT_S8: format->sampformat = SEXYAL_FMT_PCMS8;break;
case AFMT_U16_LE:
#ifndef LSB_FIRST
format->byteorder=1;
#endif
format->sampformat = SEXYAL_FMT_PCMU16;break;
case AFMT_U16_BE:
#ifdef LSB_FIRST
format->byteorder=1;
#endif
format->sampformat = SEXYAL_FMT_PCMU16;break;
case AFMT_S16_LE:
#ifndef LSB_FIRST
format->byteorder=1;
#endif
format->sampformat = SEXYAL_FMT_PCMS16;break;
case AFMT_S16_BE:
#ifdef LSB_FIRST
format->byteorder=1;
#endif
format->sampformat = SEXYAL_FMT_PCMS16;break;
default: close(fd); return(0);
}
/* Set number of channels. */
temp=format->channels;
if(ioctl(fd,SNDCTL_DSP_CHANNELS,&temp)==-1)
{
close(fd);
return(0);
}
if(temp<1 || temp>2)
{
close(fd);
return(0);
}
format->channels=temp;
/* Set frame rate. */
temp=format->rate;
if(ioctl(fd,SNDCTL_DSP_SPEED,&temp)==-1)
{
close(fd);
return(0);
}
format->rate=temp;
device=malloc(sizeof(SexyAL_device));
sal_memcpy(&device->format,format,sizeof(SexyAL_format));
sal_memcpy(&device->buffering,buffering,sizeof(SexyAL_buffering));
if(buffering->fragcount == 0 || buffering->fragsize == 0)
{
buffering->fragcount=16;
buffering->fragsize=64;
}
else
{
if(buffering->fragsize<32) buffering->fragsize=32;
if(buffering->fragcount<2) buffering->fragcount=2;
}
if(buffering->ms)
{
int64_t tc;
//printf("%d\n",buffering->ms);
/* 2*, >>1, |1 for crude rounding(it will always round 0.5 up, so it is a bit biased). */
tc=2*buffering->ms * format->rate / 1000 / buffering->fragsize;
//printf("%f\n",(double)buffering->ms * format->rate / 1000 / buffering->fragsize);
buffering->fragcount=(tc>>1)+(tc&1); //1<<Log2(tc);
//printf("%d\n",buffering->fragcount);
}
temp=Log2(buffering->fragsize*(format->sampformat>>4)*format->channels);
temp|=buffering->fragcount<<16;
ioctl(fd,SNDCTL_DSP_SETFRAGMENT,&temp);
{
audio_buf_info info;
ioctl(fd,SNDCTL_DSP_GETOSPACE,&info);
buffering->fragsize=info.fragsize/(format->sampformat>>4)/format->channels;
buffering->fragcount=info.fragments;
buffering->totalsize=buffering->fragsize*buffering->fragcount;
//printf("Actual: %d, %d\n",buffering->fragsize,buffering->fragcount);
}
device->private=malloc(sizeof(int));
*(int*)device->private=fd;
return(device);
}
int SexyALI_OSS_Close(SexyAL_device *device)
{
if(device)
{
if(device->private)
{
close(*(int*)device->private);
free(device->private);
}
free(device);
return(1);
}
return(0);
}
uint32_t SexyALI_OSS_RawWrite(SexyAL_device *device, void *data, uint32_t len)
{
ssize_t bytes;
bytes = write(*(int *)device->private,data,len);
if(bytes <= 0) return(0); /* FIXME: What to do on -1? */
return(bytes);
}
uint32_t SexyALI_OSS_RawCanWrite(SexyAL_device *device)
{
struct audio_buf_info ai;
if(!ioctl(*(int *)device->private,SNDCTL_DSP_GETOSPACE,&ai))
return(ai.bytes);
else
return(0);
}

View File

@ -0,0 +1,6 @@
void SexyALI_OSS_Enumerate(int (*func)(uint8_t *name, uint64_t id, void *udata), void *udata);
SexyAL_device *SexyALI_OSS_Open(uint64_t id, SexyAL_format *format, SexyAL_buffering *buffering);
int SexyALI_OSS_Close(SexyAL_device *device);
uint32_t SexyALI_OSS_RawWrite(SexyAL_device *device, void *data, uint32_t len);
uint32_t SexyALI_OSS_RawCanWrite(SexyAL_device *device);

View File

@ -0,0 +1,5 @@
#include <AudioHardware.h>
#include <CoreAudioTypes.h>
#include <HostTime.h>

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