gnuboy: initial 1.0.3 import (last official release)

This commit is contained in:
rofl0r 2012-06-23 14:16:37 +02:00
commit 29076d4cef
131 changed files with 27998 additions and 0 deletions

339
COPYING Normal file
View File

@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
675 Mass Ave, Cambridge, MA 02139, 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
Appendix: 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) 19yy <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., 675 Mass Ave, Cambridge, MA 02139, 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) 19yy 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.

81
INSTALL Normal file
View File

@ -0,0 +1,81 @@
GNUBOY INSTALLATION
*NIX SYSTEMS
One or more of the following is required to compile on *nix: X, SDL,
svgalib, or Linux fbcon. Since basically everyone has X, this should
not be a problem. Please note that the SDL and fbcon ports are the
most functional, however. In the future, Sun console may also be
supported.
The best and easiest way to build gnuboy for *nix is with the
configure script:
./configure
make
make install
By default, heavy optimization and asm cpu and graphics cores will be
used if available on your platform. For information on compiletime
options related to performance and debugging, type:
./configure --help
Alternatively, if you don't like the GNU configure script, you may
copy the Makefile.nix to Makefile and edit it by hand to work with
your system. Make sure you uncomment -DIS_LITTLE_ENDIAN if your cpu is
little endian. Please note that not everything is supported when
compiling this way, and that it should only be done as a last resort.
The generic Makefile.nix may be removed in the future since it's extra
work to maintain.
Running make should produce the binaries xgnuboy, fbgnuboy, sgnuboy
and/or sdlgnuboy, depending on the availability of the various
interface libraries on your host. The install target will install
these to $(prefix)/bin, where prefix is specified to configure in the
usual way. The default prefix is of course /usr/local/.
Binary packages may be available for some platforms, but they are
usually not quite up to date, and are not built or supported by the
gnuboy team.
Binary package maintainers should be aware that, by default, gnuboy
will be built with optimizations specific to the exact host cpu it's
being compiled on, and may not work on older models. If you want your
binaries to work with older systems too, run configure with the
--disable-arch option to disable architecture specific compiler flags.
WINDOWS
Mingw32 and the SDL development files are required to compile gnuboy
for Windows. They may be obtained from www.mingw.org and
www.libsdl.org, respectively.
Just copy Makefile.mingw32 to Makefile and run make. When done, put
the resulting gnuboy.exe wherever you wish to install it.
Precompiled binaries are also available for Windows; check the site
from which you obtained gnuboy to see if it provides copies.
DOS
You'll need djgpp to use the included Makefile. Theoretically it
shouldn't be hard to port the dos-specific modules to work with other
compilers, but I see no reason why it should be necessary.
Since all DOS systems are basically alike, just copy Makefile.dos to
Makefile and type "make" to compile gnuboy. No configuration should be
necessary. If you do have build problems, let us know.
After compiling, place gnuboy.exe wherever you want.
Precompiled binaries are also available for DOS; check the site from
which you obtained gnuboy to see if it provides copies.

29
Makefile.dos Normal file
View File

@ -0,0 +1,29 @@
AS = $(CC)
LD = $(CC)
CFLAGS = -O3 -fstrength-reduce -fomit-frame-pointer -I./asm/i386
ASFLAGS = -x assembler-with-cpp
LDFLAGS = -s
THIN_NAMES = tl_main tl_log tl_timer tl_key tl_mouse tl_joy tl_dpp tl_event \
tl_bmp tl_vesa tl_vga tl_video tl_sb tl_sound tl_int
THIN_OBJS = $(THIN_NAMES:%=sys/thinlib/lib/%.o)
SYS_DEFS = -DIS_LITTLE_ENDIAN -DALLOW_UNALIGNED_IO -DALT_PATH_SEP -DUSE_ASM
SYS_INCS = -I./sys/dos -I./sys/thinlib/lib
SYS_OBJS = sys/dos/dos.o sys/thinlib/thinlib.o sys/thinlib/keymap.o $(THIN_OBJS) \
asm/i386/cpu.o asm/i386/lcd.o asm/i386/refresh.o
all: gnuboy.exe
include Rules
gnuboy.exe: $(OBJS) $(SYS_OBJS)
$(LD) $(CFLAGS) $(LDFLAGS) $(OBJS) $(SYS_OBJS) -o $@
clean:
rm -f gnuboy.exe gmon.out *.o sys/*.o sys/dos/*.o sys/pc/*.o asm/i386/*.o \
sys/thinlib/*.o sys/thinlib/*.exe sys/thinlib/*.o

70
Makefile.in Normal file
View File

@ -0,0 +1,70 @@
prefix = @prefix@
exec_prefix = @exec_prefix@
bindir = @bindir@
CC = @CC@
LD = $(CC)
AS = $(CC)
INSTALL = @INSTALL@
CFLAGS = @CFLAGS@
LDFLAGS = $(CFLAGS) @LDFLAGS@
ASFLAGS = $(CFLAGS)
TARGETS = @TARGETS@
ASM_OBJS = @ASM_OBJS@
SYS_DEFS = @DEFS@ @ENDIAN@ @ASM@ @SYS_DEFS@
SYS_OBJS = sys/nix/nix.o $(ASM_OBJS)
SYS_INCS = -I/usr/local/include @XINCS@ -I./sys/nix
FB_OBJS = @FB_OBJS@ @JOY@ @SOUND@
FB_LIBS =
SVGA_OBJS = sys/svga/svgalib.o sys/pc/keymap.o @JOY@ @SOUND@
SVGA_LIBS = -L/usr/local/lib -lvga
SDL_OBJS = sys/sdl/sdl.o sys/sdl/keymap.o
SDL_LIBS = @SDL_LIBS@
SDL_CFLAGS = @SDL_CFLAGS@
X11_OBJS = sys/x11/xlib.o sys/x11/keymap.o @JOY@ @SOUND@
X11_LIBS = @XLIBS@ -lX11 -lXext
all: $(TARGETS)
include Rules
fbgnuboy: $(OBJS) $(SYS_OBJS) $(FB_OBJS)
$(LD) $(LDFLAGS) $(OBJS) $(SYS_OBJS) $(FB_OBJS) -o $@ $(FB_LIBS)
sgnuboy: $(OBJS) $(SYS_OBJS) $(SVGA_OBJS)
$(LD) $(LDFLAGS) $(OBJS) $(SYS_OBJS) $(SVGA_OBJS) -o $@ $(SVGA_LIBS)
sdlgnuboy: $(OBJS) $(SYS_OBJS) $(SDL_OBJS)
$(LD) $(LDFLAGS) $(OBJS) $(SYS_OBJS) $(SDL_OBJS) -o $@ $(SDL_LIBS)
sys/sdl/sdl.o: sys/sdl/sdl.c
$(MYCC) $(SDL_CFLAGS) -c $< -o $@
sys/sdl/keymap.o: sys/sdl/keymap.c
$(MYCC) $(SDL_CFLAGS) -c $< -o $@
xgnuboy: $(OBJS) $(SYS_OBJS) $(X11_OBJS)
$(LD) $(LDFLAGS) $(OBJS) $(SYS_OBJS) $(X11_OBJS) -o $@ $(X11_LIBS)
install: all
$(INSTALL) -d $(bindir)
$(INSTALL) -m 755 $(TARGETS) $(bindir)
clean:
rm -f *gnuboy gmon.out *.o sys/*.o sys/*/*.o asm/*/*.o
distclean: clean
rm -f config.* sys/nix/config.h Makefile

64
Makefile.nix Normal file
View File

@ -0,0 +1,64 @@
#
# Makefile.nix
#
# This is a *bare minimum* makefile for building gnuboy on *nix systems.
# If you have trouble with the configure script you can try using this,
# but *please* try the configure script first. This file is mostly
# unmaintained and may break.
#
# If you *do* insist on using this makefile, you at least need to check
# SYS_DEFS below and uncomment -DIS_LITTLE_ENDIAN if your system is
# little endian. Also, you may want to enable the OSS sound module if
# your system supports it.
#
prefix = /usr/local
bindir = /bin
CC = gcc
AS = $(CC)
LD = $(CC)
INSTALL = /bin/install -c
CFLAGS = -O3
LDFLAGS =
ASFLAGS =
SYS_DEFS = #-DIS_LITTLE_ENDIAN
ASM_OBJS =
#SND_OBJS = sys/oss/oss.o
SND_OBJS = sys/dummy/nosound.o
JOY_OBJS = sys/dummy/nojoy.o
TARGETS = xgnuboy
SYS_OBJS = sys/nix/nix.o $(ASM_OBJS) $(SND_OBJS) $(JOY_OBJS)
SYS_INCS = -I/usr/local/include -I/usr/X11R6/include -I./sys/nix
X11_OBJS = sys/x11/xlib.o sys/x11/keymap.o
X11_LIBS = -L/usr/X11R6/lib -lX11 -lXext
all: $(TARGETS)
include Rules
xgnuboy: $(OBJS) $(SYS_OBJS) $(X11_OBJS)
$(LD) $(LDFLAGS) $(OBJS) $(SYS_OBJS) $(X11_OBJS) -o $@ $(X11_LIBS)
install: all
$(INSTALL) -m 755 $(TARGETS) $(prefix)$(bindir)
clean:
rm -f *gnuboy gmon.out *.o sys/*.o sys/*/*.o asm/*/*.o

31
Makefile.win Normal file
View File

@ -0,0 +1,31 @@
CC = gcc
AS = $(CC)
LD = $(CC)
CFLAGS = -O3 -I./asm/i386 -Dmain=SDL_main
LDFLAGS = -s -lmingw32 -lSDLmain -lSDL
ASFLAGS = -x assembler-with-cpp
SYS_DEFS = -DIS_LITTLE_ENDIAN -DALT_PATH_SEP -DUSE_ASM
ASM_OBJS = asm/i386/cpu.o asm/i386/lcd.o asm/i386/refresh.o
#SND_OBJS = sys/dummy/nosound.o
SYS_OBJS = $(ASM_OBJS) $(SND_OBJS) sys/windows/windows.o sys/windows/resource.o
SYS_INCS = -I./sys/windows
SDL_OBJS = sys/sdl/sdl.o sys/sdl/keymap.o
SDL_LIBS = -lSDL
all: gnuboy
include Rules
%.o: %.rc
windres -o $@ $<
gnuboy: $(OBJS) $(SYS_OBJS) $(SDL_OBJS)
$(LD) $(LDFLAGS) $(OBJS) $(SYS_OBJS) $(SDL_OBJS) -o $@ $(SDL_LIBS)
clean:
rm -f gnuboy.exe *.o sys/*.o sys/*/*.o asm/*/*.o

199
README Normal file
View File

@ -0,0 +1,199 @@
GNUBOY README
INTRO
Welcome to gnuboy, one of the few pieces of Free Software to emulate
the Game Boy handheld game console. Written in ANSI C with a few
optional assembler optimizations for particular cpus, gnuboy supports
a wide range of host systems, and has been tested successfully on:
GNU/Linux
FreeBSD
OpenBSD
BeOS
Linux/390 (IBM S/390 Mainframe)
SunOS/Sun Ultra60
IRIX/SGI O2
IRIX/SGI Indy
AIX/Unknown
DR-DOS
MS-DOS
Windows DOS box
Windows 9x/NT/2k
Additionally, gnuboy should run on any other *nix variants that have
ANSI C compilers and that are remotely POSIX compliant. As gnuboy is
Free Software, you're welcome to fix any problems you encounter
building it for a particular system, or to port it to entirely new
systems.
EMULATION
gnuboy emulates nearly all aspects of the (Color) Gameboy, including
all of the following and much more:
Full GBZ80 instruction set.
Scanline-based LCD engine.
Ten sprites per scanline limit.
Support for all CGB graphics extensions.
Sprite DMA, HDMA, and GDMA.
All four sound channels including digital samples.
MBC1, MBC2, MBC3 (including clock), and MBC5 mappers.
Wave pattern memory corruption when sound channel 3 is played.
Pad, timer, divide counter, and other basic hardware registers.
CGB double-speed CPU mode.
Aspects not emulated at this time include:
* Serial IO (link cable).
Undocumented 'extra' ram in OAM space on Gameboy Color.
All Super Gameboy extensions.
* GBC, HuC1, and HuC3 IR ports.
* Obscure mappers such as TAMA5.
Sorting sprites by X coordinate in DMG mode.
HALT instruction skipping in DMG mode.
CPU stalls during HDMA and GDMA.
Only the two marked by * are known to affect the playability of
actual games or demos; the rest are just listed for completeness'
sake.
FEATURES
In addition to basic emulation, gnuboy provides the following
features:
Highly flexible keybinding and configuration subsystem.
State saving and loading at any point.
Very precise timing/synchronization, preserved across save/load.
Joystick support on Linux, DOS, and all SDL-based ports.
Fully customizable palettes for DMG games.
Screen scaling by a factor of 2, 3, or 4 in all ports.
Hardware-based screen scaling on platforms where it's available.
Debug traces to stdout.
Dynamic palette allocation when run in 256-color modes...
OR simulated 3/3/2 bits per channel in 256-color modes.
For information on configuring and using these features, see the
additional documentation in the "docs" directory.
COMPATIBILITY
Out of over 300 results reported by testers, all games are known to
work perfectly on gnuboy with the following exceptions:
Fighting Phoenix (Japanese) may or may not work since it uses the
HuC1 memory controller, which is not implemented properly. There has
been no report either way so far.
Pocket Bomberman (Japanese version, which uses HuC1) runs, but can
be made to crash if the player jumps into the ceiling in the first
level. It's not clear whether this bug is MBC-related, something
else, or an actual bug in the original game.
Monster Go! Go! Go! (Japanese) is unplayable. The cause of the
problem is not fully known, but it's either a very bad dump or it's
using some sort of specialized MBC that's not documented.
Final Fantasy Adventure has visual problems with the fade between
screens. Does not affect gameplay.
Bubble Bobble 2 has some minor tile glitches right before gameplay
actually begins. Cause unknown. Does not affect gameplay.
Alone in the Dark is reported to have minor visual glitches. I
haven't seen it myself so I can't judge their severity.
Both new Zelda games are reported to have a visual glitch at the
beginning of the game, and on certain other screens. I haven't seen
the problem myself, but supposedly it impacts gameplay to some
extent.
Please report any other incompatibilities discovered directly to
gnuboy@unix-fu.org, so that they can be documented and hopefully
fixed.
FUTURE / WISHLIST
Here's a brief list of what may appear in gnuboy in the future:
Screenshots.
Integrated debugger.
Super Gameboy support.
Serial link over the internet.
Serial link to a real Gameboy with a custom cable.
Configurable color filters to provide more authentic LCD look.
Custom colorization of DMG games on a per-tile basis.
Support for more colorspaces in the hardware scaler.
Recording audio.
GBS player built from the same source tree.
Full recording and playback of emulation.
So-called "high level emulation" of certain typical dumb loops.
Features that are not likely to appear soon or at all include:
Rumble support - this would be nice, but SDL doesn't seem to support
force-feedback yet. We'll see about it in the long-term though.
Eagle/2xSaI/etc. - probably not feasible since these libraries don't
appear to be compatible with the terms of the GPL. We might work on
our own interpolation engine eventually, but that's low priority.
GUI/GUI-like features - such things are best handled by external
front-ends. We might eventually add a mechanism for external
programs to communicate with gnuboy and reconfigure it while it's
running, however.
Plugins - NO! The way I see it, plugins are just an attempt to work
around the GPL. In any case, even if you are adding plugin support
yourself, you are bound by the terms of the GPL when linking ANY
code to gnuboy, including dynamic-linked modules. However we'd
rather not deal with this mess to begin with.
Compressed ROMs/Saves - this one is very iffy. On most systems, this
is redundant; *nix users can just pipe the rom through a
decompression program, and Windows users can just double-click or
drag files from their favorite GUI unzipper program. Linking to zlib
isn't really acceptable since it's massively bloated and we don't
want to include it with gnuboy or add external dependencies. We may,
however, write our own tiny decompressor to use at some point.
Ideas and suggestions for other features are welcome, but won't
necessarily be used. You're of course also free to add features
yourself, and if they fit well into the main tree they may eventually
get included in the official release. See the file HACKING for more
details on modifying and/or contributing.
THANKS
Thanks goes out to everyone who's expressed interest in gnuboy by
writing -- users, porters, authors of other emulators, and so forth.
Apologies if we don't get a personal response out to everyone, but
either way, consider your feedback appreciated.
EPILOGUE
OK, that looks like about it. More to come, stick around...
-Laguna <laguna@aerifal.cx>

31
Rules Normal file
View File

@ -0,0 +1,31 @@
include Version
OBJS = lcd.o refresh.o lcdc.o palette.o cpu.o mem.o rtc.o hw.o sound.o \
events.o keytable.o \
loader.o save.o debug.o emu.o main.o \
rccmds.o rckeys.o rcvars.o rcfile.o exports.o \
split.o path.o inflate.o
INCS = -I.
MYCC = $(CC) $(CFLAGS) $(INCS) $(SYS_INCS) $(SYS_DEFS)
MYAS = $(AS) $(ASFLAGS) $(INCS) $(SYS_INCS) $(SYS_DEFS)
main.o: Version
.c.o:
$(MYCC) -c $< -o $@
.s.o:
$(MYAS) -c $< -o $@

5
Version Normal file
View File

@ -0,0 +1,5 @@
#define VERSION "1.0.3" /*
VERSION = 1.0.3
# */

35
asm/i386/asm.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef __ASM_H__
#define __ASM_H__
#define ASM_CPU_EMULATE
#define ASM_CPU_STEP
#define ASM_REFRESH_1
#define ASM_REFRESH_2
#define ASM_REFRESH_3
#define ASM_REFRESH_4
#define ASM_REFRESH_1_2X
#define ASM_REFRESH_2_2X
#define ASM_REFRESH_4_2X
#define ASM_REFRESH_1_3X
#define ASM_REFRESH_2_3X
#define ASM_REFRESH_4_3X
#define ASM_REFRESH_4_4X
#define ASM_UPDATEPATPIX
#define ASM_BG_SCAN_COLOR
#endif /* __ASM_H__ */

41
asm/i386/asmnames.h Normal file
View File

@ -0,0 +1,41 @@
#define cpu _cpu
#define hw _hw
#define ram _ram
#define mbc _mbc
#define lcd _lcd
#define scan _scan
#define patpix _patpix
#define anydirty _anydirty
#define patdirty _patdirty
#define cpu_emulate _cpu_emulate
#define cpu_step _cpu_step
#define lcdc_trans _lcdc_trans
#define debug_trace _debug_trace
#define updatepatpix _updatepatpix
#define debug_disassemble _debug_disassemble
#define bg_scan_color _bg_scan_color
#define refresh_1 _refresh_1
#define refresh_2 _refresh_2
#define refresh_3 _refresh_3
#define refresh_4 _refresh_4
#define refresh_1_2x _refresh_1_2x
#define refresh_2_2x _refresh_2_2x
#define refresh_3_2x _refresh_3_2x
#define refresh_4_2x _refresh_4_2x
#define refresh_1_3x _refresh_1_3x
#define refresh_2_3x _refresh_2_3x
#define refresh_3_3x _refresh_3_3x
#define refresh_4_3x _refresh_4_3x
#define refresh_1_4x _refresh_1_4x
#define refresh_2_4x _refresh_2_4x
#define refresh_3_4x _refresh_3_4x
#define refresh_4_4x _refresh_4_4x
#define mem_read _mem_read
#define mem_write _mem_write
#define cpu_idle _cpu_idle
#define die _die
#define printf _printf

2430
asm/i386/cpu.s Normal file

File diff suppressed because it is too large Load Diff

290
asm/i386/lcd.s Normal file
View File

@ -0,0 +1,290 @@
#include "asmnames.h"
.set vram, lcd
.set buf, scan+512
.set pal1, scan+768
.set pal2, scan+896
.set pal4, scan+1024
.set bg, scan
.set buf, scan+512
.set u, scan+1792+24
.set v, scan+1792+28
.set wx, scan+1792+32
.data
.balign 4
.text
.balign 32
debug: .string "%08x\n"
.macro _print arg=0
pushf
pusha
movl \arg, %eax
pushl %eax
pushl $debug
call printf
addl $8, %esp
popa
popf
.endm
.macro _patexpand k=0
movw (%esi,%ecx,2), %ax
andl $(0x0101<<\k), %eax
addb $0xff, %al
sbbb %bl, %bl
addb $0xff, %ah
sbbb %bh, %bh
andl $0x0201, %ebx
orb %bh, %bl
movb %bl, patpix+7-\k(%ebp,%ecx,8)
.endm
.macro _fastswap k=0
movl patpix+(16*\k)(%ebp), %eax
movl patpix+4+(16*\k)(%ebp), %ebx
movl patpix+8+(16*\k)(%ebp), %ecx
movl patpix+12+(16*\k)(%ebp), %edx
bswap %eax
bswap %ebx
bswap %ecx
bswap %edx
movl %eax, patpix+1024*64+4+(16*\k)(%ebp)
movl %ebx, patpix+1024*64+(16*\k)(%ebp)
movl %ecx, patpix+1024*64+12+(16*\k)(%ebp)
movl %edx, patpix+1024*64+8+(16*\k)(%ebp)
.endm
.globl updatepatpix
updatepatpix:
movb anydirty, %al
testb %al, %al
jnz .Lupdatepatpix
ret
.Lupdatepatpix:
pushl %ebp
pushl %ebx
pushl %esi
pushl %edi
movl $895, %edi
.Lmainloop:
cmpl $511, %edi
jnz .Lnoskip
movl $383, %edi
.Lnoskip:
movb patdirty(%edi), %al
testb %al, %al
jnz .Lpatdirty
decl %edi
jnl .Lmainloop
jmp .Lend
.Lpatdirty:
movb $0, %al
movb %al, patdirty(%edi)
movl %edi, %eax
movl $vram, %esi
shll $4, %eax
addl %eax, %esi
movl $7, %ecx
movl %edi, %ebp
shll $6, %ebp
.Lexpandline:
_patexpand 0
_patexpand 1
_patexpand 2
_patexpand 3
_patexpand 4
_patexpand 5
_patexpand 6
_patexpand 7
decl %ecx
jnl .Lexpandline
_fastswap 0
_fastswap 1
_fastswap 2
_fastswap 3
movl patpix(%ebp), %eax
movl patpix+4(%ebp), %ebx
movl patpix+8(%ebp), %ecx
movl patpix+12(%ebp), %edx
movl %eax, patpix+2048*64+56(%ebp)
movl %ebx, patpix+2048*64+60(%ebp)
movl %ecx, patpix+2048*64+48(%ebp)
movl %edx, patpix+2048*64+52(%ebp)
movl patpix+16(%ebp), %eax
movl patpix+20(%ebp), %ebx
movl patpix+24(%ebp), %ecx
movl patpix+28(%ebp), %edx
movl %eax, patpix+2048*64+40(%ebp)
movl %ebx, patpix+2048*64+44(%ebp)
movl %ecx, patpix+2048*64+32(%ebp)
movl %edx, patpix+2048*64+36(%ebp)
movl patpix+32(%ebp), %eax
movl patpix+36(%ebp), %ebx
movl patpix+40(%ebp), %ecx
movl patpix+44(%ebp), %edx
movl %eax, patpix+2048*64+24(%ebp)
movl %ebx, patpix+2048*64+28(%ebp)
movl %ecx, patpix+2048*64+16(%ebp)
movl %edx, patpix+2048*64+20(%ebp)
movl patpix+48(%ebp), %eax
movl patpix+52(%ebp), %ebx
movl patpix+56(%ebp), %ecx
movl patpix+60(%ebp), %edx
movl %eax, patpix+2048*64+8(%ebp)
movl %ebx, patpix+2048*64+12(%ebp)
movl %ecx, patpix+2048*64(%ebp)
movl %edx, patpix+2048*64+4(%ebp)
movl patpix+1024*64(%ebp), %eax
movl patpix+1024*64+4(%ebp), %ebx
movl patpix+1024*64+8(%ebp), %ecx
movl patpix+1024*64+12(%ebp), %edx
movl %eax, patpix+3072*64+56(%ebp)
movl %ebx, patpix+3072*64+60(%ebp)
movl %ecx, patpix+3072*64+48(%ebp)
movl %edx, patpix+3072*64+52(%ebp)
movl patpix+1024*64+16(%ebp), %eax
movl patpix+1024*64+20(%ebp), %ebx
movl patpix+1024*64+24(%ebp), %ecx
movl patpix+1024*64+28(%ebp), %edx
movl %eax, patpix+3072*64+40(%ebp)
movl %ebx, patpix+3072*64+44(%ebp)
movl %ecx, patpix+3072*64+32(%ebp)
movl %edx, patpix+3072*64+36(%ebp)
movl patpix+1024*64+32(%ebp), %eax
movl patpix+1024*64+36(%ebp), %ebx
movl patpix+1024*64+40(%ebp), %ecx
movl patpix+1024*64+44(%ebp), %edx
movl %eax, patpix+3072*64+24(%ebp)
movl %ebx, patpix+3072*64+28(%ebp)
movl %ecx, patpix+3072*64+16(%ebp)
movl %edx, patpix+3072*64+20(%ebp)
movl patpix+1024*64+48(%ebp), %eax
movl patpix+1024*64+52(%ebp), %ebx
movl patpix+1024*64+56(%ebp), %ecx
movl patpix+1024*64+60(%ebp), %edx
movl %eax, patpix+3072*64+8(%ebp)
movl %ebx, patpix+3072*64+12(%ebp)
movl %ecx, patpix+3072*64(%ebp)
movl %edx, patpix+3072*64+4(%ebp)
decl %edi
jnl .Lmainloop
.Lend:
movb $0, %al
movb %al, anydirty
popl %edi
popl %esi
popl %ebx
popl %ebp
ret
.globl bg_scan_color
bg_scan_color:
movb wx, %ch
cmpb $0, %ch
jb .Lbsc_done_nopop
pushl %ebx
pushl %ebp
pushl %esi
pushl %edi
movl v, %eax
movl $bg, %esi
movl $buf, %edi
leal patpix(,%eax,8), %ebp
movl (%esi), %eax
movl u, %ebx
shll $6, %eax
movb $-8, %cl
addl %ebx, %eax
addb %bl, %cl
movb 4(%esi), %bl
addl $8, %esi
addb %cl, %ch
.Lbsc_preloop:
movb (%ebp,%eax), %dl
incl %eax
orb %bl, %dl
movb %dl, (%edi)
incl %edi
incb %cl
jnz .Lbsc_preloop
cmpb $0, %ch
jb .Lbsc_done
subb $8, %ch
.Lbsc_loop:
movl (%esi), %eax
movl 4(%esi), %edx
shll $6, %eax
movb %dl, %dh
addl $8, %esi
movl %edx, %ebx
rorl $16, %edx
orl %edx, %ebx
movl (%ebp,%eax), %edx
orl %ebx, %edx
movl %edx, (%edi)
movl 4(%ebp,%eax), %edx
orl %ebx, %edx
movl %edx, 4(%edi)
addl $8, %edi
subb $8, %ch
jae .Lbsc_loop
addb $8, %ch
jz .Lbsc_done
movl (%esi), %eax
shll $6, %eax
movb 4(%esi), %bl
.Lbsc_postloop:
movb (%ebp,%eax), %dl
incl %eax
orb %bl, %dl
movb %dl, (%edi)
incl %edi
decb %ch
jnz .Lbsc_postloop
.Lbsc_done:
popl %edi
popl %esi
popl %ebp
popl %ebx
.Lbsc_done_nopop:
ret

285
asm/i386/refresh.s Normal file
View File

@ -0,0 +1,285 @@
#include "asmnames.h"
.text
.macro _enter
pushl %ebx
pushl %ebp
pushl %esi
pushl %edi
movl 20(%esp), %edi
movl 24(%esp), %esi
movl 28(%esp), %ebp
movl 32(%esp), %ecx
xorl %eax, %eax
xorl %ebx, %ebx
.endm
.macro _leave
popl %edi
popl %esi
popl %ebp
popl %ebx
ret
.endm
.globl refresh_1
refresh_1:
_enter
subl $4, %esi
subl $4, %edi
shrl $2, %ecx
.Lrefresh_1:
movb 2(%esi,%ecx,4), %al
movb 3(%esi,%ecx,4), %bl
movb (%ebp, %eax), %dl
movb (%esi,%ecx,4), %al
movb (%ebp, %ebx), %dh
movb 1(%esi,%ecx,4), %bl
rorl $16, %edx
movb (%ebp, %eax), %dl
movb (%ebp, %ebx), %dh
movl %edx, (%edi,%ecx,4)
decl %ecx
jnz .Lrefresh_1
_leave
.globl refresh_2
refresh_2:
_enter
subl $2, %esi
subl $4, %edi
shrl $1, %ecx
.Lrefresh_2:
movb 1(%esi,%ecx,2), %al
movb (%esi,%ecx,2), %bl
movw (%ebp,%eax,2), %dx
rorl $16, %edx
movw (%ebp,%ebx,2), %dx
movl %edx, (%edi,%ecx,4)
decl %ecx
jnz .Lrefresh_2
_leave
.globl refresh_3
refresh_3:
_enter
subl $2, %esi
leal (%ecx,%ecx,2), %edx
shrl $1, %ecx
addl %edx, %edi
.Lrefresh_3:
movb (%esi,%ecx,2), %al
subl $6, %edi
movb 1(%esi,%ecx,2), %bl
movl (%ebp,%eax,4), %edx
movb %dl, (%edi)
movb 2(%ebp,%eax,4), %dl
movb %dh, 1(%edi)
movb %dl, 2(%edi)
movl (%ebp,%ebx,4), %edx
movb %dl, 3(%edi)
movb 2(%ebp,%ebx,4), %dl
movb %dh, 4(%edi)
movb %dl, 5(%edi)
decl %ecx
jnz .Lrefresh_3
_leave
.globl refresh_4
refresh_4:
_enter
subl $2, %esi
subl $8, %edi
shrl $1, %ecx
.Lrefresh_4:
movb (%esi,%ecx,2), %al
movb 1(%esi,%ecx,2), %bl
movl (%ebp,%eax,4), %edx
movl %edx, (%edi,%ecx,8)
movl (%ebp,%ebx,4), %edx
movl %edx, 4(%edi,%ecx,8)
decl %ecx
jnz .Lrefresh_4
_leave
.globl refresh_1_2x
refresh_1_2x:
_enter
subl $2, %esi
subl $4, %edi
shrl $1, %ecx
.Lrefresh_1_2x:
movb 1(%esi,%ecx,2), %al
movb (%esi,%ecx,2), %bl
movb (%ebp,%eax), %al
movb %al, %dl
movb %al, %dh
movb (%ebp,%ebx), %bl
rorl $16, %edx
movb %bl, %dl
movb %bl, %dh
movl %edx, (%edi,%ecx,4)
decl %ecx
jnz .Lrefresh_1_2x
_leave
.globl refresh_2_2x
refresh_2_2x:
_enter
subl $2, %esi
subl $8, %edi
shrl $1, %ecx
.Lrefresh_2_2x:
movb (%esi,%ecx,2), %al
movb 1(%esi,%ecx,2), %bl
movw (%ebp,%eax,2), %dx
rorl $16, %edx
movw (%ebp,%eax,2), %dx
movl %edx, (%edi,%ecx,8)
movw (%ebp,%ebx,2), %dx
rorl $16, %edx
movw (%ebp,%ebx,2), %dx
movl %edx, 4(%edi,%ecx,8)
decl %ecx
jnz .Lrefresh_2_2x
_leave
.globl refresh_4_2x
refresh_4_2x:
_enter
subl $2, %esi
subl $16, %edi
.Lrefresh_4_2x:
movb (%esi,%ecx), %al
movb 1(%esi,%ecx), %bl
movl (%ebp,%eax,4), %edx
movl %edx, (%edi,%ecx,8)
movl %edx, 4(%edi,%ecx,8)
movl (%ebp,%ebx,4), %edx
movl %edx, 8(%edi,%ecx,8)
movl %edx, 12(%edi,%ecx,8)
subl $2, %ecx
jnz .Lrefresh_4_2x
_leave
.globl refrsh_1_3x
refresh_1_3x:
_enter
leal (%ecx,%ecx,2), %edx
shrl $1, %ecx
addl %edx, %edi
subl $2, %esi
.Lrefresh_1_3x:
movb (%esi,%ecx,2), %al
subl $6, %edi
movb 1(%esi,%ecx,2), %bl
movb (%ebp,%eax,2), %dl
movb %dl, (%edi)
movb %dl, 1(%edi)
movb %dl, 2(%edi)
movb (%ebp,%ebx,2), %dl
movb %dl, 3(%edi)
movb %dl, 4(%edi)
movb %dl, 5(%edi)
decl %ecx
jnz .Lrefresh_1_3x
_leave
.globl refresh_2_3x
refresh_2_3x:
_enter
shll $1, %ecx
addl %ecx, %edi
addl %ecx, %edi
addl %ecx, %edi
shrl $2, %ecx
subl $2, %esi
.Lrefresh_2_3x:
movb (%esi,%ecx,2), %al
subl $12, %edi
movb 1(%esi,%ecx,2), %bl
movw (%ebp,%eax,2), %dx
movw %dx, (%edi)
movw %dx, 2(%edi)
movw %dx, 4(%edi)
movw (%ebp,%ebx,2), %dx
movw %dx, 6(%edi)
movw %dx, 8(%edi)
movw %dx, 10(%edi)
decl %ecx
jnz .Lrefresh_2_3x
_leave
.globl refresh_4_3x
refresh_4_3x:
_enter
shll $2, %ecx
addl %ecx, %edi
addl %ecx, %edi
addl %ecx, %edi
shrl $3, %ecx
subl $2, %esi
.Lrefresh_4_3x:
movb (%esi,%ecx,2), %al
subl $24, %edi
movb 1(%esi,%ecx,2), %bl
movl (%ebp,%eax,4), %edx
movl %edx, (%edi)
movl %edx, 4(%edi)
movl %edx, 8(%edi)
movl (%ebp,%ebx,4), %edx
movl %edx, 12(%edi)
movl %edx, 16(%edi)
movl %edx, 20(%edi)
decl %ecx
jnz .Lrefresh_4_3x
_leave
.globl refresh_4_4x
refresh_4_4x:
_enter
shll $4, %ecx
addl %ecx, %edi
shrl $5, %ecx
subl $2, %esi
.Lrefresh_4_4x:
movb (%esi,%ecx,2), %al
subl $32, %edi
movb 1(%esi,%ecx,2), %bl
movl (%ebp,%eax,4), %edx
movl %edx, (%edi)
movl %edx, 4(%edi)
movl %edx, 8(%edi)
movl %edx, 12(%edi)
movl (%ebp,%ebx,4), %edx
movl %edx, 16(%edi)
movl %edx, 20(%edi)
movl %edx, 24(%edi)
movl %edx, 28(%edi)
decl %ecx
jnz .Lrefresh_4_4x
_leave

4095
configure vendored Executable file

File diff suppressed because it is too large Load Diff

259
configure.in Normal file
View File

@ -0,0 +1,259 @@
AC_INIT(cpu.c)
CFLAGS="$CFLAGS"
AC_PROG_CC
AC_PROG_CPP
AC_PROG_INSTALL
test "$cross_compiling" = "yes" || AC_C_BIGENDIAN
test "$ac_cv_c_bigendian" = "no" && ENDIAN="-DIS_LITTLE_ENDIAN"
AC_CHECK_FUNCS(usleep, ,[
AC_CHECK_FUNCS(select, ,[
AC_MSG_ERROR(your system must support either usleep or select)
])])
LIBS="$LIBS -L/usr/local/lib -L/usr/X11R6/lib"
AC_ARG_WITH(fb, [ --with-fb build framebuffer device interface], [], [with_fb=yes])
AC_ARG_WITH(svgalib, [ --with-svgalib build Linux svgalib interface], [], [with_svgalib=yes])
AC_ARG_WITH(sdl, [ --with-sdl build SDL interface], [], [with_sdl=yes])
SOUND=""
JOY=""
case `uname -s` in
Linux)
SYS_DEFS=-DIS_LINUX
AC_CHECK_HEADERS(sys/soundcard.h, [SOUND=sys/oss/oss.o])
AC_CHECK_HEADERS(linux/joystick.h, [JOY=sys/linux/joy.o])
test "$with_fb" = "no" || AC_CHECK_HEADERS(linux/fb.h, [with_fb=linux])
;;
FreeBSD)
SYS_DEFS=-DIS_FBSD
AC_CHECK_HEADERS(machine/soundcard.h, [SOUND=sys/oss/oss.o])
;;
OpenBSD)
SYS_DEFS=-DIS_OBSD
AC_CHECK_HEADERS(soundcard.h, [SOUND=sys/oss/oss.o])
;;
esac
test "$SOUND" || SOUND=sys/dummy/nosound.o
test "$JOY" || JOY=sys/dummy/nojoy.o
case "$with_fb" in
linux) FB_OBJS="sys/linux/fbdev.o sys/linux/kb.o sys/pc/keymap.o" ;;
*) FB_OBJS="" ; with_fb=no ;;
esac
if test "$with_svgalib" != "no" ; then
AC_CHECK_LIB(vga, vga_init, [
AC_CHECK_HEADERS(vga.h vgakeyboard.h, ,[
AC_MSG_WARN(svgalib found but headers are missing!!)
with_svgalib=no
])], [with_svgalib=no])
fi
if test "$with_sdl" != "no" ; then
AC_CHECK_PROG(SDL_CONFIG, sdl-config, yes)
if test "$SDL_CONFIG" ; then
SDL_LIBS="`sdl-config --libs`"
SDL_CFLAGS="`sdl-config --cflags`"
old_incs="$INCS"
INCS="$INCS $SDL_CFLAGS"
AC_CHECK_LIB(SDL, SDL_Init, [
AC_CHECK_HEADERS(SDL/SDL.h, ,[
AC_MSG_WARN(SDL found but headers are missing!!)
with_sdl=no
])], [with_sdl=no], $SDL_LIBS)
INCS="$old_incs"
else
with_sdl=no
fi
fi
AC_PATH_X
if test "$no_x" != "yes" ; then
with_x=yes
AC_CHECK_LIB(Xext, XShmCreateImage)
AC_CHECK_HEADERS(sys/ipc.h sys/shm.h X11/extensions/XShm.h)
test "$x_includes" && XINCS="-I$x_includes"
test "$x_libraries" && XLIBS="-L$x_libraries"
else
with_x=no
fi
test "$with_x" = "no" || TARGETS="$TARGETS xgnuboy"
test "$with_fb" = "no" || TARGETS="$TARGETS fbgnuboy"
test "$with_svgalib" = "no" || TARGETS="$TARGETS sgnuboy"
test "$with_sdl" = "no" || TARGETS="$TARGETS sdlgnuboy"
AC_ARG_ENABLE(warnings, [ --enable-warnings enable selected compiler warnings], [], [enable_warnings=yes])
AC_ARG_ENABLE(debug, [ --enable-debug include debugging symbols], [])
AC_ARG_ENABLE(profile, [ --enable-profile enable performance profiling], [])
AC_ARG_ENABLE(arch, [ --enable-arch compile for specific host cpu architecture], [], [enable_arch=yes])
AC_ARG_ENABLE(optimize, [ --enable-optimize=LEVEL select optimization level (full,low,none)], [], [enable_optimize=yes])
AC_ARG_ENABLE(asm, [ --enable-asm use hand-optimized asm cores], [], [enable_asm=yes])
if test "$enable_warnings" = yes ; then
case "$CC" in *gcc*)
AC_MSG_RESULT(enabling selected compiler warnings)
CFLAGS="$CFLAGS -ansi -pedantic -Wall -Wno-implicit -Wno-long-long" ;;
*)
AC_MSG_RESULT(disabling warnings for non-gcc compiler) ;;
esac
fi
if test "$enable_debug" = yes ; then
AC_MSG_RESULT(including debugging symbols)
CFLAGS="$CFLAGS -g"
fi
if test "$enable_profile" = yes ; then
AC_MSG_RESULT(enabling performance profiling)
CFLAGS="$CFLAGS -pg"
fi
if test "$enable_arch" = yes ; then
if test `uname -s` = Linux -a -f /proc/cpuinfo ; then
case `grep "model name" /proc/cpuinfo` in
*AMD-K6*) enable_arch=k6 ;;
*Pentium*Pro*|*Pentium\ I*|*Klamath*) enable_arch=i686 ;;
*Pentium*|*586*) enable_arch=i586 ;;
*486*) enable_arch=i486 ;;
*386*) enable_arch=i386 ;;
*) enable_arch=no ;;
esac
else
enable_arch=no
#case `uname -m` in
#i686) enable_arch=i686 ;;
#i586) enable_arch=i586 ;;
#i486) enable_arch=i486 ;;
#i386) enable_arch=i386 ;;
#*) enable_arch=no ;;
#esac
fi
fi
case `$CC --version` in
2.9*|3.*)
case "$enable_arch" in
k6|i686|i586|i486|i386) CFLAGS="$CFLAGS -march=$enable_arch" ;;
no) ;;
*) AC_MSG_WARN(unknown architecture $enable_arch) ;;
esac ;;
*)
case "$enable_arch" in
k6|i686|i586) AC_MSG_WARN(your compiler is too old to support $enable_arch optimizations) ;;
i486) CFLAGS="$CFLAGS -m486" ;;
i386) CFLAGS="$CFLAGS -m386" ;;
no) ;;
*) AC_MSG_WARN(unknown architecture $enable_arch) ;;
esac ;;
esac
case "$enable_optimize" in
yes|full)
AC_MSG_RESULT(producing heavily optimized code)
CFLAGS="$CFLAGS -O3"
case `uname -m` in
i?86) CFLAGS="$CFLAGS -DALLOW_UNALIGNED_IO" ;;
esac
#case `$CC --version` in
#2.9*|3.*)
CFLAGS="$CFLAGS -fstrength-reduce -fthread-jumps \
-fcse-follow-jumps -fcse-skip-blocks -frerun-cse-after-loop \
-fexpensive-optimizations -fforce-mem -fforce-addr"
#;;
#*)
#AC_MSG_WARN(your compiler is too old for fancy optimizations)
#;;
#esac
if test "$enable_debug" != yes -a "$enable_profile" != yes ; then
CFLAGS="$CFLAGS -fomit-frame-pointer"
LDFLAGS="$LDFLAGS -s"
fi ;;
low)
AC_MSG_RESULT(using minimal optimizations)
CFLAGS="$CFLAGS -O3" ;;
esac
if test "$enable_asm" = yes ; then
case `uname -m` in
i?86)
AC_MSG_RESULT(using optimized i386 cores)
ASM="-DUSE_ASM -I./asm/i386" ; ASM_OBJS="asm/i386/cpu.o asm/i386/lcd.o asm/i386/refresh.s" ;;
*)
AC_MSG_RESULT(no optimized asm core available for `uname -m`) ;;
esac
fi
AC_SUBST(SYS_DEFS)
AC_SUBST(ENDIAN)
AC_SUBST(SOUND)
AC_SUBST(JOY)
AC_SUBST(ASM)
AC_SUBST(ASM_OBJS)
AC_SUBST(FB_OBJS)
AC_SUBST(SDL_CFLAGS)
AC_SUBST(SDL_LIBS)
AC_SUBST(TARGETS)
AC_SUBST(XINCS)
AC_SUBST(XLIBS)
AC_CONFIG_HEADER(sys/nix/config.h)
AC_OUTPUT(Makefile)

878
cpu.c Normal file
View File

@ -0,0 +1,878 @@
#include "defs.h"
#include "regs.h"
#include "hw.h"
#include "cpu.h"
#include "mem.h"
#include "fastmem.h"
#include "cpuregs.h"
#include "cpucore.h"
#ifdef USE_ASM
#include "asm.h"
#endif
struct cpu cpu;
#define ZFLAG(n) ( (n) ? 0 : FZ )
#define PUSH(w) ( (SP -= 2), (writew(xSP, (w))) )
#define POP(w) ( ((w) = readw(xSP)), (SP += 2) )
#define FETCH_OLD ( mbc.rmap[PC>>12] \
? mbc.rmap[PC>>12][PC++] \
: mem_read(PC++) )
#define FETCH (readb(PC++))
#define INC(r) { ((r)++); \
F = (F & (FL|FC)) | incflag_table[(r)]; }
#define DEC(r) { ((r)--); \
F = (F & (FL|FC)) | decflag_table[(r)]; }
#define INCW(r) ( (r)++ )
#define DECW(r) ( (r)-- )
#define ADD(n) { \
W(acc) = (un16)A + (un16)(n); \
F = (ZFLAG(LB(acc))) \
| (FH & ((A ^ (n) ^ LB(acc)) << 1)) \
| (HB(acc) << 4); \
A = LB(acc); }
#define ADC(n) { \
W(acc) = (un16)A + (un16)(n) + (un16)((F&FC)>>4); \
F = (ZFLAG(LB(acc))) \
| (FH & ((A ^ (n) ^ LB(acc)) << 1)) \
| (HB(acc) << 4); \
A = LB(acc); }
#define ADDW(n) { \
DW(acc) = (un32)HL + (un32)(n); \
F = (F & (FZ)) \
| (FH & ((H ^ ((n)>>8) ^ HB(acc)) << 1)) \
| (acc.b[HI][LO] << 4); \
HL = W(acc); }
#define ADDSP(n) { \
DW(acc) = (un32)SP + (un32)(n8)(n); \
F = (FH & (((SP>>8) ^ ((n)>>8) ^ HB(acc)) << 1)) \
| (acc.b[HI][LO] << 4); \
SP = W(acc); }
#define LDHLSP(n) { \
DW(acc) = (un32)SP + (un32)(n8)(n); \
F = (FH & (((SP>>8) ^ ((n)>>8) ^ HB(acc)) << 1)) \
| (acc.b[HI][LO] << 4); \
HL = W(acc); }
#define CP(n) { \
W(acc) = (un16)A - (un16)(n); \
F = FN \
| (ZFLAG(LB(acc))) \
| (FH & ((A ^ (n) ^ LB(acc)) << 1)) \
| ((un8)(-(n8)HB(acc)) << 4); }
#define SUB(n) { CP((n)); A = LB(acc); }
#define SBC(n) { \
W(acc) = (un16)A - (un16)(n) - (un16)((F&FC)>>4); \
F = FN \
| (ZFLAG((n8)LB(acc))) \
| (FH & ((A ^ (n) ^ LB(acc)) << 1)) \
| ((un8)(-(n8)HB(acc)) << 4); \
A = LB(acc); }
#define AND(n) { A &= (n); \
F = ZFLAG(A) | FH; }
#define XOR(n) { A ^= (n); \
F = ZFLAG(A); }
#define OR(n) { A |= (n); \
F = ZFLAG(A); }
#define RLCA(r) { (r) = ((r)>>7) | ((r)<<1); \
F = (((r)&0x01)<<4); }
#define RRCA(r) { (r) = ((r)<<7) | ((r)>>1); \
F = (((r)&0x80)>>3); }
#define RLA(r) { \
LB(acc) = (((r)&0x80)>>3); \
(r) = ((r)<<1) | ((F&FC)>>4); \
F = LB(acc); }
#define RRA(r) { \
LB(acc) = (((r)&0x01)<<4); \
(r) = ((r)>>1) | ((F&FC)<<3); \
F = LB(acc); }
#define RLC(r) { RLCA(r); F |= ZFLAG(r); }
#define RRC(r) { RRCA(r); F |= ZFLAG(r); }
#define RL(r) { RLA(r); F |= ZFLAG(r); }
#define RR(r) { RRA(r); F |= ZFLAG(r); }
#define SLA(r) { \
LB(acc) = (((r)&0x80)>>3); \
(r) <<= 1; \
F = ZFLAG((r)) | LB(acc); }
#define SRA(r) { \
LB(acc) = (((r)&0x01)<<4); \
(r) = (un8)(((n8)(r))>>1); \
F = ZFLAG((r)) | LB(acc); }
#define SRL(r) { \
LB(acc) = (((r)&0x01)<<4); \
(r) >>= 1; \
F = ZFLAG((r)) | LB(acc); }
#define CPL(r) { \
(r) = ~(r); \
F |= (FH|FN); }
#define SCF { F = (F & (FZ)) | FC; }
#define CCF { F = (F & (FZ|FC)) ^ FC; }
#define DAA { \
A += (LB(acc) = daa_table[((((int)F)&0x70)<<4) | A]); \
F = (F & (FN)) | ZFLAG(A) | daa_carry_table[LB(acc)>>2]; }
#define SWAP(r) { \
(r) = swap_table[(r)]; \
F = ZFLAG((r)); }
#define BIT(n,r) { F = (F & FC) | ZFLAG(((r) & (1 << (n)))) | FH; }
#define RES(n,r) { (r) &= ~(1 << (n)); }
#define SET(n,r) { (r) |= (1 << (n)); }
#define CB_REG_CASES(r, n) \
case 0x00|(n): RLC(r); break; \
case 0x08|(n): RRC(r); break; \
case 0x10|(n): RL(r); break; \
case 0x18|(n): RR(r); break; \
case 0x20|(n): SLA(r); break; \
case 0x28|(n): SRA(r); break; \
case 0x30|(n): SWAP(r); break; \
case 0x38|(n): SRL(r); break; \
case 0x40|(n): BIT(0, r); break; \
case 0x48|(n): BIT(1, r); break; \
case 0x50|(n): BIT(2, r); break; \
case 0x58|(n): BIT(3, r); break; \
case 0x60|(n): BIT(4, r); break; \
case 0x68|(n): BIT(5, r); break; \
case 0x70|(n): BIT(6, r); break; \
case 0x78|(n): BIT(7, r); break; \
case 0x80|(n): RES(0, r); break; \
case 0x88|(n): RES(1, r); break; \
case 0x90|(n): RES(2, r); break; \
case 0x98|(n): RES(3, r); break; \
case 0xA0|(n): RES(4, r); break; \
case 0xA8|(n): RES(5, r); break; \
case 0xB0|(n): RES(6, r); break; \
case 0xB8|(n): RES(7, r); break; \
case 0xC0|(n): SET(0, r); break; \
case 0xC8|(n): SET(1, r); break; \
case 0xD0|(n): SET(2, r); break; \
case 0xD8|(n): SET(3, r); break; \
case 0xE0|(n): SET(4, r); break; \
case 0xE8|(n): SET(5, r); break; \
case 0xF0|(n): SET(6, r); break; \
case 0xF8|(n): SET(7, r); break;
#define ALU_CASES(base, imm, op, label) \
case (imm): b = FETCH; goto label; \
case (base): b = B; goto label; \
case (base)+1: b = C; goto label; \
case (base)+2: b = D; goto label; \
case (base)+3: b = E; goto label; \
case (base)+4: b = H; goto label; \
case (base)+5: b = L; goto label; \
case (base)+6: b = readb(HL); goto label; \
case (base)+7: b = A; \
label: op(b); break;
#define JR ( PC += 1+(n8)readb(PC) )
#define JP ( PC = readw(PC) )
#define CALL ( PUSH(PC+2), JP )
#define NOJR ( clen--, PC++ )
#define NOJP ( clen--, PC+=2 )
#define NOCALL ( clen-=3, PC+=2 )
#define NORET ( clen-=3 )
#define RST(n) { PUSH(PC); PC = (n); }
#define RET ( POP(PC) )
#define EI ( IMA = 1 )
#define DI ( cpu.halt = IMA = IME = 0 )
#define PRE_INT ( DI, PUSH(PC) )
#define THROW_INT(n) ( (IF &= ~(1<<(n))), (PC = 0x40+((n)<<3)) )
void cpu_reset()
{
cpu.speed = 0;
cpu.halt = 0;
cpu.div = 0;
cpu.tim = 0;
cpu.lcdc = 40;
IME = 0;
IMA = 0;
PC = 0x0100;
SP = 0xFFFE;
AF = 0x01B0;
BC = 0x0013;
DE = 0x00D8;
HL = 0x014D;
if (hw.cgb) A = 0x11;
if (hw.gba) B = 0x01;
}
void div_advance(int cnt)
{
cpu.div += (cnt<<1);
if (cpu.div >= 256)
{
R_DIV += (cpu.div >> 8);
cpu.div &= 0xff;
}
}
void timer_advance(int cnt)
{
int unit, tima;
if (!(R_TAC & 0x04)) return;
unit = ((-R_TAC) & 3) << 1;
cpu.tim += (cnt<<unit);
if (cpu.tim >= 512)
{
tima = R_TIMA + (cpu.tim >> 9);
cpu.tim &= 0x1ff;
if (tima >= 256)
{
hw_interrupt(IF_TIMER, IF_TIMER);
hw_interrupt(0, IF_TIMER);
}
while (tima >= 256)
tima = tima - 256 + R_TMA;
R_TIMA = tima;
}
}
void lcdc_advance(int cnt)
{
cpu.lcdc -= cnt;
if (cpu.lcdc <= 0) lcdc_trans();
}
void sound_advance(int cnt)
{
cpu.snd += cnt;
}
void cpu_timers(int cnt)
{
div_advance(cnt << cpu.speed);
timer_advance(cnt << cpu.speed);
lcdc_advance(cnt);
sound_advance(cnt);
}
int cpu_idle(int max)
{
int cnt, unit;
if (!(cpu.halt && IME)) return 0;
if (R_IF & R_IE)
{
cpu.halt = 0;
return 0;
}
/* Make sure we don't miss lcdc status events! */
if ((R_IE & (IF_VBLANK | IF_STAT)) && (max > cpu.lcdc))
max = cpu.lcdc;
/* If timer interrupt cannot happen, this is very simple! */
if (!((R_IE & IF_TIMER) && (R_TAC & 0x04)))
{
cpu_timers(max);
return max;
}
/* Figure out when the next timer interrupt will happen */
unit = ((-R_TAC) & 3) << 1;
cnt = (511 - cpu.tim + (1<<unit)) >> unit;
cnt += (255 - R_TIMA) << (9 - unit);
if (max < cnt)
cnt = max;
cpu_timers(cnt);
return cnt;
}
#ifndef ASM_CPU_EMULATE
extern int debug_trace;
int cpu_emulate(int cycles)
{
int i;
byte op, cbop;
int clen;
static union reg acc;
static byte b;
static word w;
i = cycles;
next:
if ((clen = cpu_idle(i)))
{
i -= clen;
if (i > 0) goto next;
return cycles-i;
}
if (IME && (IF & IE))
{
PRE_INT;
switch ((byte)(IF & IE))
{
case 0x01: case 0x03: case 0x05: case 0x07:
case 0x09: case 0x0B: case 0x0D: case 0x0F:
case 0x11: case 0x13: case 0x15: case 0x17:
case 0x19: case 0x1B: case 0x1D: case 0x1F:
THROW_INT(0); break;
case 0x02: case 0x06: case 0x0A: case 0x0E:
case 0x12: case 0x16: case 0x1A: case 0x1E:
THROW_INT(1); break;
case 0x04: case 0x0C: case 0x14: case 0x1C:
THROW_INT(2); break;
case 0x08: case 0x18:
THROW_INT(3); break;
case 0x10:
THROW_INT(4); break;
}
}
IME = IMA;
if (debug_trace) debug_disassemble(PC, 1);
op = FETCH;
clen = cycles_table[op];
switch(op)
{
case 0x00: /* NOP */
case 0x40: /* LD B,B */
case 0x49: /* LD C,C */
case 0x52: /* LD D,D */
case 0x5B: /* LD E,E */
case 0x64: /* LD H,H */
case 0x6D: /* LD L,L */
case 0x7F: /* LD A,A */
break;
case 0x41: /* LD B,C */
B = C; break;
case 0x42: /* LD B,D */
B = D; break;
case 0x43: /* LD B,E */
B = E; break;
case 0x44: /* LD B,H */
B = H; break;
case 0x45: /* LD B,L */
B = L; break;
case 0x46: /* LD B,(HL) */
B = readb(xHL); break;
case 0x47: /* LD B,A */
B = A; break;
case 0x48: /* LD C,B */
C = B; break;
case 0x4A: /* LD C,D */
C = D; break;
case 0x4B: /* LD C,E */
C = E; break;
case 0x4C: /* LD C,H */
C = H; break;
case 0x4D: /* LD C,L */
C = L; break;
case 0x4E: /* LD C,(HL) */
C = readb(xHL); break;
case 0x4F: /* LD C,A */
C = A; break;
case 0x50: /* LD D,B */
D = B; break;
case 0x51: /* LD D,C */
D = C; break;
case 0x53: /* LD D,E */
D = E; break;
case 0x54: /* LD D,H */
D = H; break;
case 0x55: /* LD D,L */
D = L; break;
case 0x56: /* LD D,(HL) */
D = readb(xHL); break;
case 0x57: /* LD D,A */
D = A; break;
case 0x58: /* LD E,B */
E = B; break;
case 0x59: /* LD E,C */
E = C; break;
case 0x5A: /* LD E,D */
E = D; break;
case 0x5C: /* LD E,H */
E = H; break;
case 0x5D: /* LD E,L */
E = L; break;
case 0x5E: /* LD E,(HL) */
E = readb(xHL); break;
case 0x5F: /* LD E,A */
E = A; break;
case 0x60: /* LD H,B */
H = B; break;
case 0x61: /* LD H,C */
H = C; break;
case 0x62: /* LD H,D */
H = D; break;
case 0x63: /* LD H,E */
H = E; break;
case 0x65: /* LD H,L */
H = L; break;
case 0x66: /* LD H,(HL) */
H = readb(xHL); break;
case 0x67: /* LD H,A */
H = A; break;
case 0x68: /* LD L,B */
L = B; break;
case 0x69: /* LD L,C */
L = C; break;
case 0x6A: /* LD L,D */
L = D; break;
case 0x6B: /* LD L,E */
L = E; break;
case 0x6C: /* LD L,H */
L = H; break;
case 0x6E: /* LD L,(HL) */
L = readb(xHL); break;
case 0x6F: /* LD L,A */
L = A; break;
case 0x70: /* LD (HL),B */
b = B; goto __LD_HL;
case 0x71: /* LD (HL),C */
b = C; goto __LD_HL;
case 0x72: /* LD (HL),D */
b = D; goto __LD_HL;
case 0x73: /* LD (HL),E */
b = E; goto __LD_HL;
case 0x74: /* LD (HL),H */
b = H; goto __LD_HL;
case 0x75: /* LD (HL),L */
b = L; goto __LD_HL;
case 0x77: /* LD (HL),A */
b = A;
__LD_HL:
writeb(xHL,b);
break;
case 0x78: /* LD A,B */
A = B; break;
case 0x79: /* LD A,C */
A = C; break;
case 0x7A: /* LD A,D */
A = D; break;
case 0x7B: /* LD A,E */
A = E; break;
case 0x7C: /* LD A,H */
A = H; break;
case 0x7D: /* LD A,L */
A = L; break;
case 0x7E: /* LD A,(HL) */
A = readb(xHL); break;
case 0x01: /* LD BC,imm */
BC = readw(xPC); PC += 2; break;
case 0x11: /* LD DE,imm */
DE = readw(xPC); PC += 2; break;
case 0x21: /* LD HL,imm */
HL = readw(xPC); PC += 2; break;
case 0x31: /* LD SP,imm */
SP = readw(xPC); PC += 2; break;
case 0x02: /* LD (BC),A */
writeb(xBC, A); break;
case 0x0A: /* LD A,(BC) */
A = readb(xBC); break;
case 0x12: /* LD (DE),A */
writeb(xDE, A); break;
case 0x1A: /* LD A,(DE) */
A = readb(xDE); break;
case 0x22: /* LDI (HL),A */
writeb(xHL, A); HL++; break;
case 0x2A: /* LDI A,(HL) */
A = readb(xHL); HL++; break;
case 0x32: /* LDD (HL),A */
writeb(xHL, A); HL--; break;
case 0x3A: /* LDD A,(HL) */
A = readb(xHL); HL--; break;
case 0x06: /* LD B,imm */
B = FETCH; break;
case 0x0E: /* LD C,imm */
C = FETCH; break;
case 0x16: /* LD D,imm */
D = FETCH; break;
case 0x1E: /* LD E,imm */
E = FETCH; break;
case 0x26: /* LD H,imm */
H = FETCH; break;
case 0x2E: /* LD L,imm */
L = FETCH; break;
case 0x36: /* LD (HL),imm */
b = FETCH; writeb(xHL, b); break;
case 0x3E: /* LD A,imm */
A = FETCH; break;
case 0x08: /* LD (imm),SP */
writew(readw(xPC), SP); PC += 2; break;
case 0xEA: /* LD (imm),A */
writeb(readw(xPC), A); PC += 2; break;
case 0xE0: /* LDH (imm),A */
writehi(FETCH, A); break;
case 0xE2: /* LDH (C),A */
writehi(C, A); break;
case 0xF0: /* LDH A,(imm) */
A = readhi(FETCH); break;
case 0xF2: /* LDH A,(C) (undocumented) */
A = readhi(C); break;
case 0xF8: /* LD HL,SP+imm */
b = FETCH; LDHLSP(b); break;
case 0xF9: /* LD SP,HL */
SP = HL; break;
case 0xFA: /* LD A,(imm) */
A = readb(readw(xPC)); PC += 2; break;
ALU_CASES(0x80, 0xC6, ADD, __ADD)
ALU_CASES(0x88, 0xCE, ADC, __ADC)
ALU_CASES(0x90, 0xD6, SUB, __SUB)
ALU_CASES(0x98, 0xDE, SBC, __SBC)
ALU_CASES(0xA0, 0xE6, AND, __AND)
ALU_CASES(0xA8, 0xEE, XOR, __XOR)
ALU_CASES(0xB0, 0xF6, OR, __OR)
ALU_CASES(0xB8, 0xFE, CP, __CP)
case 0x09: /* ADD HL,BC */
w = BC; goto __ADDW;
case 0x19: /* ADD HL,DE */
w = DE; goto __ADDW;
case 0x39: /* ADD HL,SP */
w = SP; goto __ADDW;
case 0x29: /* ADD HL,HL */
w = HL;
__ADDW:
ADDW(w);
break;
case 0x04: /* INC B */
INC(B); break;
case 0x0C: /* INC C */
INC(C); break;
case 0x14: /* INC D */
INC(D); break;
case 0x1C: /* INC E */
INC(E); break;
case 0x24: /* INC H */
INC(H); break;
case 0x2C: /* INC L */
INC(L); break;
case 0x34: /* INC (HL) */
b = readb(xHL);
INC(b);
writeb(xHL, b);
break;
case 0x3C: /* INC A */
INC(A); break;
case 0x03: /* INC BC */
INCW(BC); break;
case 0x13: /* INC DE */
INCW(DE); break;
case 0x23: /* INC HL */
INCW(HL); break;
case 0x33: /* INC SP */
INCW(SP); break;
case 0x05: /* DEC B */
DEC(B); break;
case 0x0D: /* DEC C */
DEC(C); break;
case 0x15: /* DEC D */
DEC(D); break;
case 0x1D: /* DEC E */
DEC(E); break;
case 0x25: /* DEC H */
DEC(H); break;
case 0x2D: /* DEC L */
DEC(L); break;
case 0x35: /* DEC (HL) */
b = readb(xHL);
DEC(b);
writeb(xHL, b);
break;
case 0x3D: /* DEC A */
DEC(A); break;
case 0x0B: /* DEC BC */
DECW(BC); break;
case 0x1B: /* DEC DE */
DECW(DE); break;
case 0x2B: /* DEC HL */
DECW(HL); break;
case 0x3B: /* DEC SP */
DECW(SP); break;
case 0x07: /* RLCA */
RLCA(A); break;
case 0x0F: /* RRCA */
RRCA(A); break;
case 0x17: /* RLA */
RLA(A); break;
case 0x1F: /* RRA */
RRA(A); break;
case 0x27: /* DAA */
DAA; break;
case 0x2F: /* CPL */
CPL(A); break;
case 0x18: /* JR */
__JR:
JR; break;
case 0x20: /* JR NZ */
if (!(F&FZ)) goto __JR; NOJR; break;
case 0x28: /* JR Z */
if (F&FZ) goto __JR; NOJR; break;
case 0x30: /* JR NC */
if (!(F&FC)) goto __JR; NOJR; break;
case 0x38: /* JR C */
if (F&FC) goto __JR; NOJR; break;
case 0xC3: /* JP */
__JP:
JP; break;
case 0xC2: /* JP NZ */
if (!(F&FZ)) goto __JP; NOJP; break;
case 0xCA: /* JP Z */
if (F&FZ) goto __JP; NOJP; break;
case 0xD2: /* JP NC */
if (!(F&FC)) goto __JP; NOJP; break;
case 0xDA: /* JP C */
if (F&FC) goto __JP; NOJP; break;
case 0xE9: /* JP HL */
PC = HL; break;
case 0xC9: /* RET */
__RET:
RET; break;
case 0xC0: /* RET NZ */
if (!(F&FZ)) goto __RET; NORET; break;
case 0xC8: /* RET Z */
if (F&FZ) goto __RET; NORET; break;
case 0xD0: /* RET NC */
if (!(F&FC)) goto __RET; NORET; break;
case 0xD8: /* RET C */
if (F&FC) goto __RET; NORET; break;
case 0xD9: /* RETI */
IME = IMA = 1; goto __RET;
case 0xCD: /* CALL */
__CALL:
CALL; break;
case 0xC4: /* CALL NZ */
if (!(F&FZ)) goto __CALL; NOCALL; break;
case 0xCC: /* CALL Z */
if (F&FZ) goto __CALL; NOCALL; break;
case 0xD4: /* CALL NC */
if (!(F&FC)) goto __CALL; NOCALL; break;
case 0xDC: /* CALL C */
if (F&FC) goto __CALL; NOCALL; break;
case 0xC7: /* RST 0 */
b = 0x00; goto __RST;
case 0xCF: /* RST 8 */
b = 0x08; goto __RST;
case 0xD7: /* RST 10 */
b = 0x10; goto __RST;
case 0xDF: /* RST 18 */
b = 0x18; goto __RST;
case 0xE7: /* RST 20 */
b = 0x20; goto __RST;
case 0xEF: /* RST 28 */
b = 0x28; goto __RST;
case 0xF7: /* RST 30 */
b = 0x30; goto __RST;
case 0xFF: /* RST 38 */
b = 0x38;
__RST:
RST(b); break;
case 0xC1: /* POP BC */
POP(BC); break;
case 0xC5: /* PUSH BC */
PUSH(BC); break;
case 0xD1: /* POP DE */
POP(DE); break;
case 0xD5: /* PUSH DE */
PUSH(DE); break;
case 0xE1: /* POP HL */
POP(HL); break;
case 0xE5: /* PUSH HL */
PUSH(HL); break;
case 0xF1: /* POP AF */
POP(AF); break;
case 0xF5: /* PUSH AF */
PUSH(AF); break;
case 0xE8: /* ADD SP,imm */
b = FETCH; ADDSP(b); break;
case 0xF3: /* DI */
DI; break;
case 0xFB: /* EI */
EI; break;
case 0x37: /* SCF */
SCF; break;
case 0x3F: /* CCF */
CCF; break;
case 0x10: /* STOP */
PC++;
if (R_KEY1 & 1)
{
cpu.speed = cpu.speed ^ 1;
R_KEY1 = (R_KEY1 & 0x7E) | (cpu.speed << 7);
break;
}
/* NOTE - we do not implement dmg STOP whatsoever */
break;
case 0x76: /* HALT */
cpu.halt = 1;
break;
case 0xCB: /* CB prefix */
cbop = FETCH;
clen = cb_cycles_table[cbop];
switch (cbop)
{
CB_REG_CASES(B, 0);
CB_REG_CASES(C, 1);
CB_REG_CASES(D, 2);
CB_REG_CASES(E, 3);
CB_REG_CASES(H, 4);
CB_REG_CASES(L, 5);
CB_REG_CASES(A, 7);
default:
b = readb(xHL);
switch(cbop)
{
CB_REG_CASES(b, 6);
}
if ((cbop & 0xC0) != 0x40) /* exclude BIT */
writeb(xHL, b);
break;
}
break;
default:
die(
"invalid opcode 0x%02X at address 0x%04X, rombank = %d\n",
op, (PC-1) & 0xffff, mbc.rombank);
break;
}
clen <<= 1;
div_advance(clen);
timer_advance(clen);
clen >>= cpu.speed;
lcdc_advance(clen);
sound_advance(clen);
i -= clen;
if (i > 0) goto next;
return cycles-i;
}
#endif /* ASM_CPU_EMULATE */
#ifndef ASM_CPU_STEP
int cpu_step(int max)
{
int cnt;
if ((cnt = cpu_idle(max))) return cnt;
return cpu_emulate(1);
}
#endif /* ASM_CPU_STEP */

36
cpu.h Normal file
View File

@ -0,0 +1,36 @@
#ifndef __CPU_H__
#define __CPU_H__
#include "defs.h"
union reg
{
byte b[2][2];
word w[2];
un32 d; /* padding for alignment, carry */
};
struct cpu
{
union reg pc, sp, bc, de, hl, af;
int ime, ima;
int speed;
int halt;
int div, tim;
int lcdc;
int snd;
};
extern struct cpu cpu;
#endif

290
cpucore.h Normal file
View File

@ -0,0 +1,290 @@
#include "defs.h"
const static byte cycles_table[256] =
{
1, 3, 2, 2, 1, 1, 2, 1, 5, 2, 2, 2, 1, 1, 2, 1,
1, 3, 2, 2, 1, 1, 2, 1, 3, 2, 2, 2, 1, 1, 2, 1,
3, 3, 2, 2, 1, 1, 2, 1, 3, 2, 2, 2, 1, 1, 2, 1,
3, 3, 2, 2, 1, 3, 3, 3, 3, 2, 2, 2, 1, 1, 2, 1,
1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1,
1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1,
1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1,
2, 2, 2, 2, 2, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1,
1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1,
1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1,
1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1,
1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1,
5, 3, 4, 4, 6, 4, 2, 4, 5, 4, 4, 1, 6, 6, 2, 4,
5, 3, 4, 0, 6, 4, 2, 4, 5, 4, 4, 0, 6, 0, 2, 4,
3, 3, 2, 0, 0, 4, 2, 4, 4, 1, 4, 0, 0, 0, 2, 4,
3, 3, 2, 1, 0, 4, 2, 4, 3, 2, 4, 1, 0, 0, 2, 4,
};
const static byte cb_cycles_table[256] =
{
2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2,
2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2,
2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2,
2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2,
2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2,
2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2,
2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2,
2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2,
2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2,
2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2,
2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2,
2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2,
2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2,
2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2,
2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2,
2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2,
};
const static byte zflag_table[256] =
{
FZ, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
const static byte incflag_table[256] =
{
FZ|FH, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
FH, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
FH, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
FH, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
FH, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
FH, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
FH, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
FH, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
FH, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
FH, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
FH, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
FH, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
FH, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
FH, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
FH, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
FH, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
const static byte decflag_table[256] =
{
FZ|FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN|FH,
FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN|FH,
FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN|FH,
FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN|FH,
FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN|FH,
FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN|FH,
FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN|FH,
FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN|FH,
FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN|FH,
FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN|FH,
FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN|FH,
FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN|FH,
FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN|FH,
FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN|FH,
FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN|FH,
FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN, FN|FH
};
const static byte swap_table[256] =
{
0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0,
0x01, 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71, 0x81, 0x91, 0xA1, 0xB1, 0xC1, 0xD1, 0xE1, 0xF1,
0x02, 0x12, 0x22, 0x32, 0x42, 0x52, 0x62, 0x72, 0x82, 0x92, 0xA2, 0xB2, 0xC2, 0xD2, 0xE2, 0xF2,
0x03, 0x13, 0x23, 0x33, 0x43, 0x53, 0x63, 0x73, 0x83, 0x93, 0xA3, 0xB3, 0xC3, 0xD3, 0xE3, 0xF3,
0x04, 0x14, 0x24, 0x34, 0x44, 0x54, 0x64, 0x74, 0x84, 0x94, 0xA4, 0xB4, 0xC4, 0xD4, 0xE4, 0xF4,
0x05, 0x15, 0x25, 0x35, 0x45, 0x55, 0x65, 0x75, 0x85, 0x95, 0xA5, 0xB5, 0xC5, 0xD5, 0xE5, 0xF5,
0x06, 0x16, 0x26, 0x36, 0x46, 0x56, 0x66, 0x76, 0x86, 0x96, 0xA6, 0xB6, 0xC6, 0xD6, 0xE6, 0xF6,
0x07, 0x17, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77, 0x87, 0x97, 0xA7, 0xB7, 0xC7, 0xD7, 0xE7, 0xF7,
0x08, 0x18, 0x28, 0x38, 0x48, 0x58, 0x68, 0x78, 0x88, 0x98, 0xA8, 0xB8, 0xC8, 0xD8, 0xE8, 0xF8,
0x09, 0x19, 0x29, 0x39, 0x49, 0x59, 0x69, 0x79, 0x89, 0x99, 0xA9, 0xB9, 0xC9, 0xD9, 0xE9, 0xF9,
0x0A, 0x1A, 0x2A, 0x3A, 0x4A, 0x5A, 0x6A, 0x7A, 0x8A, 0x9A, 0xAA, 0xBA, 0xCA, 0xDA, 0xEA, 0xFA,
0x0B, 0x1B, 0x2B, 0x3B, 0x4B, 0x5B, 0x6B, 0x7B, 0x8B, 0x9B, 0xAB, 0xBB, 0xCB, 0xDB, 0xEB, 0xFB,
0x0C, 0x1C, 0x2C, 0x3C, 0x4C, 0x5C, 0x6C, 0x7C, 0x8C, 0x9C, 0xAC, 0xBC, 0xCC, 0xDC, 0xEC, 0xFC,
0x0D, 0x1D, 0x2D, 0x3D, 0x4D, 0x5D, 0x6D, 0x7D, 0x8D, 0x9D, 0xAD, 0xBD, 0xCD, 0xDD, 0xED, 0xFD,
0x0E, 0x1E, 0x2E, 0x3E, 0x4E, 0x5E, 0x6E, 0x7E, 0x8E, 0x9E, 0xAE, 0xBE, 0xCE, 0xDE, 0xEE, 0xFE,
0x0F, 0x1F, 0x2F, 0x3F, 0x4F, 0x5F, 0x6F, 0x7F, 0x8F, 0x9F, 0xAF, 0xBF, 0xCF, 0xDF, 0xEF, 0xFF,
};
const static byte daa_table[4096] =
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0,
0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0,
0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0,
0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0,
0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0,
0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0,
0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0,
0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0,
0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0,
0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0,
0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0,
0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0,
0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0,
0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0,
0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0,
0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0,
0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA,
0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA,
0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA,
0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA,
0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA,
0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA,
0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA,
0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA,
0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA,
0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA,
0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA,
0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA,
0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA,
0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA,
0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA,
0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA,
0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A,
0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A,
0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A,
0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A,
0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A,
0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A,
0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A,
0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A,
0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A,
0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A,
0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A,
0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A,
0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A,
0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A,
0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A,
0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A,
};
const static byte daa_carry_table[64] =
{
00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
00, 00, 00, 00, 00, 00, 00, 00, FC, FC, 00, 00, 00, 00, 00, 00,
FC, FC, FC, FC, FC, FC, FC, FC, FC, FC, FC, FC, FC, FC, FC, FC,
FC, FC, FC, FC, FC, FC, FC, FC, FC, FC, FC, FC, FC, FC, 00, FC,
};

56
cpuregs.h Normal file
View File

@ -0,0 +1,56 @@
#ifndef __CPUREGS_H__
#define __CPUREGS_H__
#include "defs.h"
#include "cpu.h"
#define LB(r) ((r).b[LO][LO])
#define HB(r) ((r).b[LO][HI])
#define W(r) ((r).w[LO])
#define DW(r) ((r).d)
#define A HB(cpu.af)
#define F LB(cpu.af)
#define B HB(cpu.bc)
#define C LB(cpu.bc)
#define D HB(cpu.de)
#define E LB(cpu.de)
#define H HB(cpu.hl)
#define L LB(cpu.hl)
#define AF W(cpu.af)
#define BC W(cpu.bc)
#define DE W(cpu.de)
#define HL W(cpu.hl)
#define PC W(cpu.pc)
#define SP W(cpu.sp)
#define xAF DW(cpu.af)
#define xBC DW(cpu.bc)
#define xDE DW(cpu.de)
#define xHL DW(cpu.hl)
#define xPC DW(cpu.pc)
#define xSP DW(cpu.sp)
#define IMA cpu.ima
#define IME cpu.ime
#define IF R_IF
#define IE R_IE
#define FZ 0x80
#define FN 0x40
#define FH 0x20
#define FC 0x10
#define FL 0x0F /* low unused portion of flags */
#endif /* __CPUREGS_H__ */

689
debug.c Normal file
View File

@ -0,0 +1,689 @@
#include <stdlib.h>
#include <stdio.h>
#include "defs.h"
#include "cpu.h"
#include "mem.h"
#include "fastmem.h"
#include "regs.h"
#include "rc.h"
#include "cpuregs.h"
static char *mnemonic_table[256] =
{
"NOP",
"LD BC,%w",
"LD (BC),A",
"INC BC",
"INC B",
"DEC B",
"LD B,%b",
"RLCA",
"LD (%w),SP",
"ADD HL,BC",
"LD A,(BC)",
"DEC BC",
"INC C",
"DEC C",
"LD C,%b",
"RRCA",
"STOP",
"LD DE,%w",
"LD (DE),A",
"INC DE",
"INC D",
"DEC D",
"LD D,%b",
"RLA",
"JR %o",
"ADD HL,DE",
"LD A,(DE)",
"DEC DE",
"INC E",
"DEC E",
"LD E,%b",
"RRA",
"JR NZ,%o",
"LD HL,%w",
"LD (HLI),A",
"INC HL",
"INC H",
"DEC H",
"LD H,%b",
"DAA",
"JR Z,%o",
"ADD HL,HL",
"LD A,(HLI)",
"DEC HL",
"INC L",
"DEC L",
"LD L,%b",
"CPL",
"JR NC,%o",
"LD SP,%w",
"LD (HLD),A",
"INC SP",
"INC (HL)",
"DEC (HL)",
"LD (HL),%b",
"SCF",
"JR C,%o",
"ADD HL,SP",
"LD A,(HLD)",
"DEC SP",
"INC A",
"DEC A",
"LD A,%b",
"CCF",
"LD B,B",
"LD B,C",
"LD B,D",
"LD B,E",
"LD B,H",
"LD B,L",
"LD B,(HL)",
"LD B,A",
"LD C,B",
"LD C,C",
"LD C,D",
"LD C,E",
"LD C,H",
"LD C,L",
"LD C,(HL)",
"LD C,A",
"LD D,B",
"LD D,C",
"LD D,D",
"LD D,E",
"LD D,H",
"LD D,L",
"LD D,(HL)",
"LD D,A",
"LD E,B",
"LD E,C",
"LD E,D",
"LD E,E",
"LD E,H",
"LD E,L",
"LD E,(HL)",
"LD E,A",
"LD H,B",
"LD H,C",
"LD H,D",
"LD H,E",
"LD H,H",
"LD H,L",
"LD H,(HL)",
"LD H,A",
"LD L,B",
"LD L,C",
"LD L,D",
"LD L,E",
"LD L,H",
"LD L,L",
"LD L,(HL)",
"LD L,A",
"LD (HL),B",
"LD (HL),C",
"LD (HL),D",
"LD (HL),E",
"LD (HL),H",
"LD (HL),L",
"HALT",
"LD (HL),A",
"LD A,B",
"LD A,C",
"LD A,D",
"LD A,E",
"LD A,H",
"LD A,L",
"LD A,(HL)",
"LD A,A",
"ADD A,B",
"ADD A,C",
"ADD A,D",
"ADD A,E",
"ADD A,H",
"ADD A,L",
"ADD A,(HL)",
"ADD A,A",
"ADC A,B",
"ADC A,C",
"ADC A,D",
"ADC A,E",
"ADC A,H",
"ADC A,L",
"ADC A,(HL)",
"ADC A",
"SUB B",
"SUB C",
"SUB D",
"SUB E",
"SUB H",
"SUB L",
"SUB (HL)",
"SUB A",
"SBC A,B",
"SBC A,C",
"SBC A,D",
"SBC A,E",
"SBC A,H",
"SBC A,L",
"SBC A,(HL)",
"SBC A,A",
"AND B",
"AND C",
"AND D",
"AND E",
"AND H",
"AND L",
"AND (HL)",
"AND A",
"XOR B",
"XOR C",
"XOR D",
"XOR E",
"XOR H",
"XOR L",
"XOR (HL)",
"XOR A",
"OR B",
"OR C",
"OR D",
"OR E",
"OR H",
"OR L",
"OR (HL)",
"OR A",
"CP B",
"CP C",
"CP D",
"CP E",
"CP H",
"CP L",
"CP (HL)",
"CP A",
"RET NZ",
"POP BC",
"JP NZ,%w",
"JP %w",
"CALL NZ,%w",
"PUSH BC",
"ADD A,%b",
"RST 0h",
"RET Z",
"RET",
"JP Z,%w",
NULL,
"CALL Z,%w",
"CALL %w",
"ADC A,%b",
"RST 8h",
"RET NC",
"POP DE",
"JP NC,%w",
NULL,
"CALL NC,%w",
"PUSH DE",
"SUB %b",
"RST 10h",
"RET C",
"RETI",
"JP C,%w",
NULL,
"CALL C,%w",
NULL,
"SBC A,%b",
"RST 18h",
"LD (FF00+%b),A",
"POP HL",
"LD (FF00+C),A",
NULL,
NULL,
"PUSH HL",
"AND %b",
"RST 20h",
"ADD SP,%o",
"JP HL",
"LD (%w),A",
NULL,
NULL,
NULL,
"XOR %b",
"RST 28h",
"LD A,(FF00+%b)",
"POP AF",
"LD A,(FF00+C)",
"DI",
NULL,
"PUSH AF",
"OR %b",
"RST 30h",
"LD HL,SP%o",
"LD SP,HL",
"LD A,(%w)",
"EI",
NULL,
NULL,
"CP %b",
"RST 38h"
};
static char *cb_mnemonic_table[256] =
{
"RLC B",
"RLC C",
"RLC D",
"RLC E",
"RLC H",
"RLC L",
"RLC (HL)",
"RLC A",
"RRC B",
"RRC C",
"RRC D",
"RRC E",
"RRC H",
"RRC L",
"RRC (HL)",
"RRC A",
"RL B",
"RL C",
"RL D",
"RL E",
"RL H",
"RL L",
"RL (HL)",
"RL A",
"RR B",
"RR C",
"RR D",
"RR E",
"RR H",
"RR L",
"RR (HL)",
"RR A",
"SLA B",
"SLA C",
"SLA D",
"SLA E",
"SLA H",
"SLA L",
"SLA (HL)",
"SLA A",
"SRA B",
"SRA C",
"SRA D",
"SRA E",
"SRA H",
"SRA L",
"SRA (HL)",
"SRA A",
"SWAP B",
"SWAP C",
"SWAP D",
"SWAP E",
"SWAP H",
"SWAP L",
"SWAP (HL)",
"SWAP A",
"SRL B",
"SRL C",
"SRL D",
"SRL E",
"SRL H",
"SRL L",
"SRL (HL)",
"SRL A",
"BIT 0,B",
"BIT 0,C",
"BIT 0,D",
"BIT 0,E",
"BIT 0,H",
"BIT 0,L",
"BIT 0,(HL)",
"BIT 0,A",
"BIT 1,B",
"BIT 1,C",
"BIT 1,D",
"BIT 1,E",
"BIT 1,H",
"BIT 1,L",
"BIT 1,(HL)",
"BIT 1,A",
"BIT 2,B",
"BIT 2,C",
"BIT 2,D",
"BIT 2,E",
"BIT 2,H",
"BIT 2,L",
"BIT 2,(HL)",
"BIT 2,A",
"BIT 3,B",
"BIT 3,C",
"BIT 3,D",
"BIT 3,E",
"BIT 3,H",
"BIT 3,L",
"BIT 3,(HL)",
"BIT 3,A",
"BIT 4,B",
"BIT 4,C",
"BIT 4,D",
"BIT 4,E",
"BIT 4,H",
"BIT 4,L",
"BIT 4,(HL)",
"BIT 4,A",
"BIT 5,B",
"BIT 5,C",
"BIT 5,D",
"BIT 5,E",
"BIT 5,H",
"BIT 5,L",
"BIT 5,(HL)",
"BIT 5,A",
"BIT 6,B",
"BIT 6,C",
"BIT 6,D",
"BIT 6,E",
"BIT 6,H",
"BIT 6,L",
"BIT 6,(HL)",
"BIT 6,A",
"BIT 7,B",
"BIT 7,C",
"BIT 7,D",
"BIT 7,E",
"BIT 7,H",
"BIT 7,L",
"BIT 7,(HL)",
"BIT 7,A",
"RES 0,B",
"RES 0,C",
"RES 0,D",
"RES 0,E",
"RES 0,H",
"RES 0,L",
"RES 0,(HL)",
"RES 0,A",
"RES 1,B",
"RES 1,C",
"RES 1,D",
"RES 1,E",
"RES 1,H",
"RES 1,L",
"RES 1,(HL)",
"RES 1,A",
"RES 2,B",
"RES 2,C",
"RES 2,D",
"RES 2,E",
"RES 2,H",
"RES 2,L",
"RES 2,(HL)",
"RES 2,A",
"RES 3,B",
"RES 3,C",
"RES 3,D",
"RES 3,E",
"RES 3,H",
"RES 3,L",
"RES 3,(HL)",
"RES 3,A",
"RES 4,B",
"RES 4,C",
"RES 4,D",
"RES 4,E",
"RES 4,H",
"RES 4,L",
"RES 4,(HL)",
"RES 4,A",
"RES 5,B",
"RES 5,C",
"RES 5,D",
"RES 5,E",
"RES 5,H",
"RES 5,L",
"RES 5,(HL)",
"RES 5,A",
"RES 6,B",
"RES 6,C",
"RES 6,D",
"RES 6,E",
"RES 6,H",
"RES 6,L",
"RES 6,(HL)",
"RES 6,A",
"RES 7,B",
"RES 7,C",
"RES 7,D",
"RES 7,E",
"RES 7,H",
"RES 7,L",
"RES 7,(HL)",
"RES 7,A",
"SET 0,B",
"SET 0,C",
"SET 0,D",
"SET 0,E",
"SET 0,H",
"SET 0,L",
"SET 0,(HL)",
"SET 0,A",
"SET 1,B",
"SET 1,C",
"SET 1,D",
"SET 1,E",
"SET 1,H",
"SET 1,L",
"SET 1,(HL)",
"SET 1,A",
"SET 2,B",
"SET 2,C",
"SET 2,D",
"SET 2,E",
"SET 2,H",
"SET 2,L",
"SET 2,(HL)",
"SET 2,A",
"SET 3,B",
"SET 3,C",
"SET 3,D",
"SET 3,E",
"SET 3,H",
"SET 3,L",
"SET 3,(HL)",
"SET 3,A",
"SET 4,B",
"SET 4,C",
"SET 4,D",
"SET 4,E",
"SET 4,H",
"SET 4,L",
"SET 4,(HL)",
"SET 4,A",
"SET 5,B",
"SET 5,C",
"SET 5,D",
"SET 5,E",
"SET 5,H",
"SET 5,L",
"SET 5,(HL)",
"SET 5,A",
"SET 6,B",
"SET 6,C",
"SET 6,D",
"SET 6,E",
"SET 6,H",
"SET 6,L",
"SET 6,(HL)",
"SET 6,A",
"SET 7,B",
"SET 7,C",
"SET 7,D",
"SET 7,E",
"SET 7,H",
"SET 7,L",
"SET 7,(HL)",
"SET 7,A"
};
static byte operand_count[256] =
{
1, 3, 1, 1, 1, 1, 2, 1, 3, 1, 1, 1, 1, 1, 2, 1,
1, 3, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 2, 1,
2, 3, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 2, 1,
2, 3, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 2, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 3, 3, 3, 1, 2, 1, 1, 1, 3, 2, 3, 3, 2, 1,
1, 1, 3, 1, 3, 1, 2, 1, 1, 1, 3, 1, 3, 1, 2, 1,
2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 1, 1, 2, 1,
2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 1, 1, 2, 1
};
/* replace with a real interactive debugger eventually... */
int debug_trace = 0;
rcvar_t debug_exports[] =
{
RCV_BOOL("trace", &debug_trace),
RCV_END
};
void debug_disassemble(addr a, int c)
{
static int i, j, k;
static byte code;
static byte ops[3];
static int opaddr;
static char mnemonic[256];
static char *pattern;
if (!debug_trace) return;
while (c > 0)
{
k = 0;
opaddr = a;
code = ops[k++] = readb(a); a++;
if (code != 0xCB)
{
pattern = mnemonic_table[code];
if (!pattern)
pattern = "***INVALID***";
}
else
{
code = ops[k++] = readb(a); a++;
pattern = cb_mnemonic_table[code];
}
i = j = 0;
while (pattern[i])
{
if (pattern[i] == '%')
{
switch (pattern[++i])
{
case 'B':
case 'b':
ops[k] = readb(a); a++;
j += sprintf(mnemonic + j,
"%02Xh", ops[k++]);
break;
case 'W':
case 'w':
ops[k] = readb(a); a++;
ops[k+1] = readb(a); a++;
j += sprintf(mnemonic + j, "%04Xh",
((ops[k+1] << 8) | ops[k]));
k += 2;
break;
case 'O':
case 'o':
ops[k] = readb(a); a++;
j += sprintf(mnemonic + j, "%+d",
(n8)(ops[k++]));
break;
}
i++;
}
else
{
mnemonic[j++] = pattern[i++];
}
}
mnemonic[j] = 0;
printf("%04X ", opaddr);
switch (operand_count[ops[0]]) {
case 1:
printf("%02X ", ops[0]);
break;
case 2:
printf("%02X %02X ", ops[0], ops[1]);
break;
case 3:
printf("%02X %02X %02X ", ops[0], ops[1], ops[2]);
break;
}
printf("%-16.16s", mnemonic);
printf(
" SP=%04X.%04X BC=%04X.%02X.%02X DE=%04X.%02X "
"HL=%04X.%02X A=%02X F=%02X %c%c%c%c%c",
SP, readw(SP),
BC, readb(BC), readb(0xFF00 | C),
DE, readb(DE),
HL, readb(HL), A,
F, (IME ? 'I' : '-'),
((F & 0x80) ? 'Z' : '-'),
((F & 0x40) ? 'N' : '-'),
((F & 0x20) ? 'H' : '-'),
((F & 0x10) ? 'C' : '-')
);
printf(
" IE=%02X IF=%02X LCDC=%02X STAT=%02X LY=%02X LYC=%02X",
R_IE, R_IF, R_LCDC, R_STAT, R_LY, R_LYC
);
printf("\n");
fflush(stdout);
c--;
}
}

36
defs.h Normal file
View File

@ -0,0 +1,36 @@
#ifndef __DEFS_H__
#define __DEFS_H__
#ifdef IS_LITTLE_ENDIAN
#define LO 0
#define HI 1
#else
#define LO 1
#define HI 0
#endif
typedef unsigned char byte;
typedef unsigned char un8;
typedef unsigned short un16;
typedef unsigned int un32;
typedef signed char n8;
typedef signed short n16;
typedef signed int n32;
typedef un16 word;
typedef word addr;
#endif

322
docs/CHANGES Normal file
View File

@ -0,0 +1,322 @@
GNUBOY CHANGES FILE -- detailed list of all changes made
Each release is labelled with the date it appeared; unreleased
versions are listed without a date stamp for purely historical
reasons. When checking what's changed since the last release, be
sure to look over intermediate unreleased versions as well.
For an easy-to-read user-oriented list of major changes in each
release, please refer to the file WHATSNEW.
1.0.3
fixed a typo in the SDL keymap file that kept . from working
added support for binding keys to the ' key
fixed bug related in nix.c's sys_checkdir that made it always demand writable
fixed loader.c since it depended on this bug
removed the literal newlines inside the copyright string in main.c
removed some parentheses in save.c's macros
(these were perfectly valid but caused problems with a broken compiler, lcc)
added return-type prototypes for strdup since it's not always in string.h
(many of the above were old bugfixes sent in by Damian M Gryski)
added slow, primitive support for gzipped rom files
(thanks to David Madore for the portable inflate code taken from his quine)
various minor source cleanups
more preliminary work on the fast register/himem io routines (not used yet)
fixed HuC3 emulation, according to TGB's sources
added hacks to work around HuC3's IR port not being implemented
(Robopon Sun and Star now seem to run fine)
fixed a few sound inconsistencies after loading a savestate
various fixes to keybinding system and X11 keymap
integrated Jonathan Gevaryahu's color filter
added "gbamode" rcvar to unlock gba-only features in some cgb games
(this has NOTHING to do with gba emulation!)
fixed DMG sprite sorting code! it's now enabled by default
reverted mistaken CGB wave pattern "fix" in 1.0.2
don't always reset sound positions on sound init (is this correct?)
1.0.2 (2001-09-18)
fixed bugs in rc_setvar calls in sys_initpath
fixed multiple definition of cpu struct in cpu.h
corrected behavior of ch1 sweep function when freq is written during sweep
emulated wave pattern corruption to fool "emulator-detectors"
updated savestate code to handle these changes, incremented minor version
(this should not affect compatibility with old savestates)
fixed major bugs in sound channel 4
perfected channel 4 output sequence to sound like a real Gameboy
(much thanks goes out to Lord Nightmare for all his hard work on this!)
default channel 3 square wave is no longer 4 octaves too high
make DI cancel pending HALT...is this correct? (fixes Konami Collection #1)
fixed reversed stereo channels
tweaked frequency cutoff points that prevent aliasing
corrected default wave pattern (noise) when running in dmg mode
fixed sound reset bug that messed up pitch after pausing in Bubble Ghost
added new sample palettes
fixed major interrupt/HALT bug that kept Amazing Penguin from running
fixed sound channel 3 length regiser (info in gbspec.txt is bogus)
tweaked volume of channel 4
removed FFL3 tile glitch from the known bugs in the README
(it was caused by a bad dump and/or hacked rom, not a bug in gnuboy)
added emulation of DMG STAT register write bug (causes interrupt)
(this fixes Legend of Zerd and perhaps one or two other games)
1.0.1 (2001-07-09)
fixed problem in "make install" if dest dir doesn't exist
cleaned up some compiler warnings
fixed a problem with --bindir= not working in the autoconf process
renamed several things from mingw32 to just plain windows
fixed lots of keys that were still missing on the SDL port
1.0.0 (2001-06-29)
renamed Makefile.mingw32 to Makefile.win to be 8.3 filename friendly
finally fixed up configure to work around broken gcc 2.96 on Redhat 7, et al
added lots of new documentation
added autoconf option to disable cpu-specific code generation
minor improvements to matroxfb hardware scaling code
cleaned up some old deprecated variables
0.9.15
various preparations for 1.0 release
cleaned up nix.c to remove old code and prevent errors on some systems
fixed Makefile.nix to be usable but minimal
0.9.14
changed default dmg palette to be less yellow
changed default keybindings not to use modifier keys
moved vid_begin to after doevents in emu.c
(this should fix alt+enter fullscreen toggle not working on windows)
changed --help, etc to use stdout rather than stderr
auto-loading config files on a per-rom basis
0.9.13 (2001-04-09)
added matroxfb YUV scaling support
moved lcd_refreshline from the 3->0 stat change to the 2->3 one
(this fixes a slight visual glitch in Alleyway)
experimental: no LYC=LY interrupt during VBLANK...?
fixed emulation bug (RL/RR) in asm cpu core that broke Montezuma's Return
fixed some minor bugs in the matrox scaler register settings
fixed SWAP (HL) instruction in the asm core (fixes Pokemon Yellow)
added more assembly language scalers for performance
0.9.12 (2001-04-02)
started adding HuC3 MBC support
removed some code that was accidentally left in that broke dos/win builds
fixed a bug in SDL joystick support -- thanks Ralf Hoffmann
unused bits in VBK register should be 1, not 0 -- this broke Binary Chaos
fixed bug that kept dmg palette from restoring properly after loading savestate
integrated hardware YUV scaling w/SDL thanks to Magnus Damm
SDL code now turns off fb.enabled when window is iconified
HDMA timing correction back in 0.9.6 broke Wacky Races; it's disabled for now
0.9.11 (2001-04-01)
fixed bug that kept video mode setting from working with svgalib
implemented program counter skip after STOP instruction (konami collections)
fixed SDL hardware surface support -- thanks Dave Kiddell
also fixed another bug in gnuboy graphics code related to that problem
removed sdl_hwsurface rcvar (no longer needed)
changed SDL code to use SDL_Flip rather than SDL_UpdateRect - much faster
most ports now can auto-choose screen size for given scale if no vmode is given
optional shm sync skipping for x11 -- boost performance but looses frames
lots of new scaling stuff
allow HDMA while LCDC is off -- fixes Worms Armageddon
correct initial values for HDMA regs -- fixes first hang in Turok 3
major timer fixes!! fixes second hang in Turok 3
0.9.10 (2001-03-26)
hopefully fixed issue with X header locations affecting some users
rewrote refresh_* functions to be faster and more flexible
added scale-by-two versions of the above, including super-fast asm versions
implemented primitive but fully functional scale-by-two mode
added vmode rcvar to set the video mode
disabled dmg sprite sorting by default because it doesn't seem to work right
removed deprecated rcvars from various display modules
heavily updated README
changed VBLANK timings slightly - seems to fix Daedalian Opus
enlarged OSS dma output buffer slightly; this may reduce occurance of underruns
cleaned up all warnings
fixed bug that prevented reading from OAM
fixed all compiler warnings except implicit functions
found and fixed a few minor bugs in the process
added spacebar to SDL keymap
up to 16 joystick buttons are now supported w/linux and SDL
added sdl_hwsurface rcvar to turn hardware surface back on
added static palette mode
quick and dirty hack to make super rc pro-am work
fixed bug that made OAM unreadable
0.9.9 (2001-03-23)
removed some unused code from mingw32.c, fixed some bugs there too
fixed a bad sound bug in sdl.c
eliminated sound pops at startup with SDL
eliminaed compiletime error about SDL_DISABLE on SDL ver < 1.1.8
integrated new fully-thinlib-based DOS code from Matthew Conte
added surface locking to SDL code; maybe this will fix windows+fullscreen?
fixed serious bug in savestate loading (call mem_updatemap on load)
new asm -- significant speed boosts for color games on older machines
removed SDL_HWSURFACE from SDL code - this should fix fullscreen on windows
disabled surface locking calls for now
properly initialize default path on DOS
added SDL_ANYFORMAT to SDL flags so we can natively support all color depths
0.9.8 (2001-03-07)
enabled support for dmg sprite sorting; not sure it works yet
added "sprsort" rcvar to toggle this since it's usually not needed
fixed a potential crash that could happen if sound init failed on dos
added native SDL sound support
fixed lots of bugs in the SDL port
removed stupid sys_realtime() function in favor of the simple ANSI C time()
roms can now be loaded from stdin by specifying - as the rom name
removed lots of useless bloat from system interface modules
take advantage of ANSI atexit() to simplify termination logic
hide mouse cursor with SDL
SDL fullscreen mode
optional alt+enter fullscreen toggle for SDL
SDL rcvars sdl_fullscreen and sdl_altenter to control these features
changed bswapl to bswap in asm to make it work on mingw32
added ram size code 05 for 4 banks, this seems to make Pokemon Crystal work
backed out hack for Altered Space and W&W because it broke other games
new code to make them work, hopefully this time it's right
now we give an error on unknown rom/ram size to prevent crashing
integrated Windows port by mewse
0.9.7 (2001-02-27)
added support for mono sound
initial work on implementing sound blaster output on dos
fixed envelope bug that made notes trail off (or amplify) too fast
integrated dos sound support contributed by Matthew Conte using his thinlib
added Matthew Conte to CREDITS
tried to fix strange occasional keyboard misbehavior on dos
build stripped binaries by default if debugging and profiling are off
0.9.6
updated the INSTALL file
fixed something stupid that broke building SDL joystick support on non-Linux
added Mattias Wadman to the CREDITS
fixed VBLANK timing slightly; now altered space and wizards & warriors work
reverted change; it breaks other games
new trick that might fix things...
fixed bug in command line parsing that kept --savename=- from working
fixed warning in oss.c
fixed an old bug in HDMA/HBLANK that only recently became apparent
vesa support on dos is now working!
0.9.5 (2001-02-22)
added Ralf to the CREDITS, apologies for the prior omission
show name from rom header in window title on X11 and SDL
fixed bug that made highcolor screen flicker
(this used to glitch sfalpha, but for some reason its ok now)
updated README
fixed cap on sound 3 frequency to eliminate bogus beeps
began work on optimizing memory io in the C code
updated HACKING slightly
got new fast memory io functions integrated!
moved all of high memory (registers, wave pattern, stack) to one array
(eventually this will make memory io faster)
changed savestate format, but old saves should still load fine
(hopefully new format makes more sense)
began implementing fast access to high memory
discovered that low bits of the flags register don't exist
optimized instruction emulation accordingly
a few optimizations to the outer asm cpu loop
fixed off-by-one error in C graphics code that made far right column blank
added slow, experimental 24bpp (packed, unaligned) video support
improved the configure script, now --without-* works
use sdl-config instead of explicit -lpthread to support more systems
removed stupid section directives from the asm
got the asm cores working on dos!
oss sound support *might* work on freebsd and openbsd now
SDL joystick code has been integrated, but I haven't tested it
fixed bug in new savestate code
added David Lau to the CREDITS (SDL joystick support)
GNU make should no longer be required to compile
0.9.4 (2001-02-18)
various changes to lots of the system interface organization
separation of linux fb and keyboard handling code into two modules
integrated linux joystick support contributed by Ralf Hoffmann
dummy joystick code for systems without real support yet
fixed HDMA; now DKC runs perfectly
0.9.3
explicit link of SDL target with -lpthread, tell me if this causes problems
better cpu detection in configure script
more big fixes in sweep, and now it's actually tested, so it SHOULD work (!)
implemented default wave ram pattern
added linux fbcon display support - very functional
fix to allow new custom palette to take effect after loading dmg savestates
0.9.2 (2001-02-12)
mbc3 rtc support, including save and resync across sessions
updated README
implemented sound channel 4
fixed yet another bug in sweep (!!)
fixed nasty aliasing when sound frequency was higher than sample rate permits
finally, all sound registers can be adjusted while sound is being produced
made it so the proper shutdown functions actually get called on exit
added SDL port by Damian M Gryski, should be auto-detected by configure
added Damian to the CREDITS file
cleaned up sound code to minimize the amount of state data kept
added sound and rtc status to savestates; this won't break old saves
changed lots of lookup tables to const
0.9.1 (2001-02-11)
fixed yet another critical bug in sweep
fixed STAT interrupt bug
added support for changing more sound params while sound is active
fixed yet another major bug in envelope for channel 2
fixed bug in HDMA, but DKC still fails
updated README, HACKING
made samplerate and sound (on/off) configurable rcvars
changed command line parsing to make setting bools more intuitive
added --showvars to list all available rcvars
0.9.0
fixed bugs in sweep and envelope functions
added sound channel 3
0.8.5
various minor optimizations in the C code
same in the i386 asm cpu core
initial work on sound framework
oss sound output for use on *nix
dummy sound output for use on systems without sound
sound channels 1 and 2
0.8.4 (2001-02-06)
updated README to cover new features
fixed off-by-one-line visual error introduced in 0.8.2
gbspec.txt is wrong, ram size code 0 != no ram, big suprise... (!)
workaround for undocumented 512 byte ram size, won't necessarily work
changes in saved state format
slight improvements to asm cpu core
cleaned up HDMA code
removed outdated comments
more changes to lcdc/cpu timing interaction, fixing harmless bugs
this may slightly impact performance, i'll compensate later
hopefully fixed bug in svgalib interface that corrupted console on exit
updated HACKING to reflect new code, detail more internals
workaround for a bug that would never happen but could lock the emulator
fixed another visual glitch introduced in 0.8.2
optimized i386 cpu.s to keep PC in a register at all times
0.8.3
changed install dir from $prefix/games to $prefix/bin
fixed major bug in ramsize lookup table (!)
updated HACKING to note that it's outdated
implemented saved states!
0.8.2 (2001-02-03)
rewrote lcdc state behavior completely, fixed lots of compat issues
implemented serial io failure for roms that need it, fixed more compat
now, mk1, sml2, and alleyway are all fixed!
additions to input.h and keytable.c to allow future joystick support
0.8.1
fixed stupid timer interrupt bug in asm cpu core
renamed screen to fb so as not to conflict with allegro symbol names
0.8.0 (2001-02-01)
initial release

645
docs/CONFIG Normal file
View File

@ -0,0 +1,645 @@
GUIDE TO CONFIGURING GNUBOY
[ P A R T I ]
OVERVIEW
There are two major ways of configuring the way gnuboy behaves at
runtime: setting the values of variables, and binding commands to keys
and joystick buttons. Each can be done either on the command line, or
from a config (rc) file.
If you don't want to read all this detailed info, look at the sample
rc files provided, then browse back through this file to clarify
anything that seems confusing. You might also skip down to Part II if
you're already familiar with the syntax of gnuboy rc files and such;
the second part explains the configurable variables which you can play
with.
WHAT HAPPENS AT STARTUP
When gnuboy is started, it first processes gnuboy.rc, the primary
configuration file. On *nix systems, gnuboy will initially look for
its rc files in ~/.gnuboy, or if that fails, the present working
directory. On DOS and Windows, the current directory will be searched
first, followed by the directory containing the gnuboy executable.
After finishing with gnuboy.rc, gnuboy next looks for an rc file with
the same base name as the rom to be loaded. For example, if the name
of the rom is mygame.gb, gnuboy will process mygame.rc, if it exists.
This allows you to configure different preferences on a per-rom
basis. The locations searched for the rom-specific rc file are the
same as those searched for gnuboy.rc, unless gnuboy.rc has changed the
search path (see below for more info).
Finally, options on the command line are processed. The command line
will override any settings in the auto-loaded rc files. This is a good
place for options that you just want to use occasionally, but not on a
regular basis.
After all of the above is finished, gnuboy loads the rom and starts
emulation.
RC FILES
The rc files gnuboy uses are plain text files, with one command on
each line. Lines that start with # are treated as comments, that is to
say they are ignored, and blank lines are ignored as well.
There are three major types of commands.
RC FILES -- SETTING VARIABLES
First of all, there is the "set" command, which is used for setting
the values of variables. For example,
set scale 2
will set the screen scaling factor to 2. If you need to include a
space in the value of a variable, you can do something like this:
set savename "I like spaces in my filenames"
and then your save files will be named something like:
I like spaces in my filenames.sav
I like spaces in my filenames.000
I like spaces in my filenames.001
I like spaces in my filenames.002
etc.
Finally, some variables allow multiple numbers to be given. For
example, to set the video mode to 640x480, 16bpp, you might do
something like this:
set vmode 640 480 16
Observe that each number is separate, and there are no quotation marks
used.
RC FILES -- KEYBINDINGS
Next, we have commands that deal with key and joystick bindings. These
are fairly simple.
The "unbindall" command removes all existing keybindings -- be
careful! -- and its main use is for people who want to redefine their
keyboard controls entirely and throw away the defaults. Be warned that
if you unbind the quit key and don't bind a new key for quitting, you
may be unable to exit gnuboy cleanly!
The "unbind" command is similar, but it only unbinds one key at a
time. For example, to unbind the "space" key, use this command:
unbind space
See below for a list of key names to use with unbind.
Now we get to the main useful keybinding command: "bind". For example,
if you want the "tab" key to perform the Gameboy "select" button
function, use the following bind command:
bind tab +select
The significance of the + sign will be explained below. As with the
"set" command, quotation marks can be used with bind if the command
needs to contain spaces.
KEY NAMES FOR BINDINGS
When using the bind and unbind commands, you need to tell gnuboy which
key you wish to affect. Most of the keys that correspond to a
character you can type can just be referenced by that character. For
example, the alphabetical keys are bound by the lowercase letter they
represent, and the numeral keys (on the main keyboard, not the numeric
keypad) can be bound by their numeral. Other keys require a name. Some
are really obvious:
shift, ctrl, alt, up, down, right, left
enter, tab, space, home, end, esc, pause
f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12
Others are a bit less obvious but still should make sense. Some of
these can also be referenced by other names; read the source file
keytable.c for a full list:
bs Backspace
ins Insert
del Delete
prior Page Up
next Page Down
caps Caps Lock
numlock Num Lock
scroll Scroll Lock
minus - or _
equals = or +
tilde ` or ~
slash / or ?
bslash \ or |
semi ; or :
quote ' or "
The numeric keypad is referenced as follows
num0-num9 Numeral keys 0-9 (on keypad)
numplus Numeric keypad +
numminus Numeric keypad -
nummul Numeric keypad *
numdiv Numeric keypad /
numdot Numeric keypad .
numenter Numeric keypad Enter key
Joystick buttons and directions also have names for binding, and they
are bound just like ordinary keys. Their names are as follows:
joyup Joystick up
joydown Joystick down
joyleft Joystick left
joyright Joystick right
joy0-joy15 Joystick buttons
The way joystick buttons are numbered varies from one joystick to
another. Experiment to find the names that are right for the buttons
you want to use.
RC FILES -- THE SOURCE COMMAND
The "source" command simply causes gnuboy to process another rc file
before resuming processing of the current one. It is useful for
splitting up your config into multiple parts, perhaps one file
auto-generated by a front-end and another hand-customized. Use of this
command is very simple:
source myfile.rc
will perform all the commands in myfile.rc. Note that the source
command also provides a method for binding multiple commands to a
single key. For example, simply
bind f1 "source f1stuff.rc"
and then f1stuff.rc will be run whenever you press F1.
RC FILES -- ACTION COMMANDS
Finally, we have rc commands that perform actions. These commands are
probably only useful when bound to a key, and might cause unexpected
behavior or even crashes if used by themselves in an rc file loaded at
startup.
First of all, the "quit" command should be obvious. It simply exits
the emulator. If the rom that's loaded uses battery backed save ram or
realtime clock, these files will automatically be saved at exit.
The "reset" command should also be fairly obvious. It acts as a reset
button, restarting execution of the loaded rom at the beginning, as if
you turned the Gameboy power off and back on.
Slightly more interesting are the "savestate" and "loadstate"
commands. These are used for saving and resuming "saved state" files,
which allow you to save the exact status of the emulation environment
and restore it later, effectively letting you "save game" at any point
in any game. If a number is specified after either of those commands,
the indicated save slot number is used. Otherwise, the slot set in the
"saveslot" variable will be used. See the information on variables
below for more info.
Most importantly, we have the action commands that control the
emulated Gameboy input pad. They are described below:
COMMANDS THAT BEGIN WITH A PLUS SIGN
Normally, gnuboy only performs the command bound to a key when the key
is pressed; nothing happens when it is released. But for some things,
particularly the Gameboy pad buttons, it's important for something to
happen when the bound key is released. This is the purpose of commands
that begin with a + sign. When a key is released, gnuboy checks to see
if the bound command begins with +, and if so, it changes the + to -
and performs the resulting command. This causes the Gameboy pad
buttons to go back to their normal state when the keys bound to them
are released.
The Gameboy pad commands, which should be self-explanatory, are as
follows:
+a, +b, +start, +select, +up, +down, +left, +right
If you're at all familiar with Quake's config system, this should all
be clear.
THE GNUBOY COMMAND LINE
Additional rc files to process, variable settings, and keybindings can
be specified on the command line when gnuboy is run.
Processing an extra config file is simple:
gnuboy --source myfile.rc game.gb
Specifying an extra rc file on the command line like this is
especially useful for frontends, which may want to put all the options
they set in one rc file so they don't have to pass a super-long
command line to gnuboy.
Binding keys is also pretty simple. Just use something like:
gnuboy --bind tab +select game.gb
Setting variables is where things get a bit more complicated. For
on/off (boolean) settings, you can just do something like
gnuboy --no-sound game.gb
to turn a variable (sound) off, i.e. set it to 0. Likewise, boolean
variables can be turned on via something like
gnuboy --rgb332 game.gb
which turns the "rgb332" variable on (see below for information on
what it does).
For other variables where you actually want to set a number or a
string, use this form:
gnuboy --savename=mygame2 game.gb
Finally, for variables with multiple numbers to be set, you can
separate them by commas as follows:
gnuboy --vmode=512,384,32
to avoid having to quote the spaces.
[ P A R T I I ]
GUIDE TO CONFIGURABLE VARIABLES
What follows is a detailed explanation of most of the configuration
variables available for your tweaking. They are organized by what part
of gnuboy's behavior they affect -- graphics, sound, emulation, and so
on.
Some variables may or may not be available depending on how gnuboy was
built. For example, if you built gnuboy on a system without sound
support, some variables related to sound may not exist for you, and
attempts to set them will be silently ignored. In most cases, it's
noted in the documentation when variables might not be available.
Also, there are a few highly system-specific variables, such as names
of devices to use for video and sound on *nix systems. These are
listed separately at the end, and it should go without saying that
they will not be available on all builds of gnuboy.
VIDEO AND GRAPHICS SETTINGS
Since this is everyone's favorite thing to customize, video seems a
good place to start.
SCREEN SCALING
There are a number of variables that control how gnuboy scales the
display. The most basic is the "scale" option, which is just the
factor to scale by. For example
set scale 2
will double the size of the display. Set the scale factor to 1 for no
scaling.
There are two ways gnuboy can go about doing scaling. The preferable
way is to use your computer's graphics hardware to do all the work.
This cuts down on the amount of CPU time consumed and provides
filtering to smooth out the blocky pixels, but it's not available on
all systems. The other way is for gnuboy to scale the screen itself.
Normally gnuboy will choose hardware scaling automatically if it's
available, but if you want to force it on or off, you can set the
option "yuv" (for hardware YUV-colorspace scaling) to 1 or 0. Yes,
this option is poorly named, and is likely to change in future
versions of gnuboy.
On one display platform, Linux fbcon, it's possible to disable the
interpolation filter in the hardware scaling. To do this, set the
variable "yuvinterp" to 0. Some users who like a crisper display may
prefer this setting, especially on video cards that make the picture
look "muddy" when they scale it. Unfortunately SDL does not seem to
provide such an option, so interpolation is always enabled on the SDL
based ports.
When hardware scaling is disabled or not available, gnuboy will do its
own scaling. However, the scale factor is limited to 1, 2, 3, or 4.
Also, when performing its own scaling, gnuboy defaults to leaving some
scanlines blank. This saves a lot of CPU time and allows gnuboy to run
full speed on slower systems. You can configure what portion gets
filled in with the "density" variable. For example.
set scale 4
set density 4
will give you 4x scaling with no blank scanlines. Keep in mind that a
fairly fast computer (at least 400 MHz or so on x86, or something
comparable on other types of CPUs) is required to run fullspeed with
this setting. In general, "density" is the number of lines that get
filled in, so set it the same as "scale" if you want everything filled
in, or lower if you need more speed.
VIDEO MODE
The variable for setting the desired video mode is called "vmode", and
it's made up of three parts: width, height, and bits-per-pixel. For
example, to set 640x480x16bpp mode, use
set vmode 640 480 16
By default gnuboy will enable hardware scaling and try to scale to the
entire screen size if a video mode at least 320x288 is specified. If
you don't want this behavior, set the "yuv" option (see above) to 0.
Also, if you're setting the "scale" variable to do scaling, you
probably don't need to use the "vmode" option, since gnuboy will
try to automatically pick a mode that fits the scale. It's there in
case you need it, though.
Note that the DOS port is not yet capable of auto-choosing a video
mode, so if you want anything but the default 320x200x8bpp you'll have
to set "vmode" yourself. Also, not all ports are capable of all modes.
Experiment to find what works for you. Video mode selection is a
little bit messy and confusing at this time, and we hope to improve it
a good deal in the future.
FULLSCREEN VIDEO
Some versions of gnuboy provide both fullscreen and windowed
operation. The variable "fullscreen" can be set to 1 or 0 to enable or
disable fullscreen mode. Also, the variable "altenter" can be set to
enable or disable switching between fullscreen and windowed mode at
runtime with the Alt+Enter key combination. Unfortunately, this does
not yet work on Windows; we hope to fix this limitation in the
future.
DMG PALETTE SELECTION
gnuboy allows you to set the palette used for grayscale when running
DMG (original mono Gameboy) roms. There are four variables for this
purpose, allowing the background, window, and both sprite palettes to
be colored differently. Each one is made up of four numbers, the color
to use for each shade of gray, from lightest to darkest. Colors are
represented as 24bit numbers, with red in the low (rightmost) places
and blue in the upper (leftmost) places. Although you could specify
colors in decimal (base 10) if you really wanted, they'd be very
difficult to read, so it's preferable to use hex (base 16).
For example, to set the background to shades of white, the window to
shades of red, and the sprite palettes to shades of green and blue,
you could use:
set dmg_bgp 0xffffff 0xaaaaaa 0x555555 0x000000
set dmg_wndp 0x0000ff 0x0000aa 0x000055 0x000000
set dmg_obp0 0x00ff00 0x00aa00 0x005500 0x000000
set dmg_obp1 0xff0000 0xaa0000 0x550000 0x000000
This will of course look rather ugly, but it does the job illustrating
how you set various colors.
For more extensive examples, see the sample file palette.rc included
with gnuboy, which provides a number of sample palettes to try.
RGB MODE WITH ONLY 256 COLORS
Normally when run in 256-color (8bpp) modes, gnuboy will dynamically
allocate colors in the palette as they're needed. However, on the
Gameboy Color, it's possible to have well over 1000 colors on the
screen at a time, and in games that make use of these "hicolor"
tricks, gnuboy will run out of colors and things will look bad.
If you prefer, you can set the "rgb332" variable:
set rgb332 1
This tells gnuboy that instead of using 256-color mode as a
palette-based mode, you want it to setup a static palette and pretend
8bpp is just a really low quality "truecolor" mode, with only 3 bits
of precision in red and green, and only 2 bits of precision in blue.
In general this will make most games look worse, since colors have to
be approximated fairly poorly and since smooth color gradients are not
possible, but it will make "hicolor" Gameboy Color games look a good
deal better. Also, rgb332 mode should run slightly faster since it
avoids the overhead in dynamic palette allocation.
If you have to run at 8bpp mode, try it with and without this option
and see which way you like better. Of course, the better solution, if
at all possible, is to use 16bpp or higher mode, but that may run too
slowly on older computers.
COLOR FILTERING
Optionally, gnuboy can filter screen colors to make them look more
washed out or faded like on a real GBC. To enable this feature,
set colorfilter 1
By default, gnuboy will not apply the filter when running DMG (mono)
games, since many of the sample palettes are already designed to
immitate a Gameboy LCD. If you would like to have the filter also take
effect when running in DMG mode,
set filterdmg 1
You can also customize the filter parameters to get different color
effects from the default ones. See the sample file filters.rc for
examples.
SPRITE SORTING
Normally sprites are sorted and prioritized according to their x
coordinate when in DMG mode. However, this takes a little bit of extra
cpu time, and it's not needed by most DMG games, so it can be disabled
as follows:
set sprsort 0
Note that although sprite sorting was disabled in previous releases
because it was not working properly, it now works great, so unless you
really need to maximize performance, you should probably leave it
enabled.
SOUND OPTIONS
Fortunately sound is a lot simpler than video. At this time, there are
no fancy interpolation or filtering options, only your basic audio
parameters.
To enable or disable sound, set the "sound" variable to 1 or 0. By
default, it's enabled.
To enable or disable stereo sound, set the "stereo" variable to 1 or
0. It defaults to 1 on most ports, but since stereo sometimes fails
to work properly on DOS, it's disabled by default on the DOS port.
Disabling stereo in no way improves performance, so it should only be
done if stereo mode causes a problem on your computer.
To set the audio sampling rate, use the "samplerate" variable. The
default is 44100 Hz. Setting this lower can improve performance. For
example, if you have a really slow computer, you might use:
set samplerate 8000
Keep in mind that this will sound really really bad.
FILESYSTEM OPTIONS
There are a good deal of options that affect where and how files are
saved and loaded by gnuboy. First, there's "rcpath", which specifies
where gnuboy searches for rc files. The default depends on your
operating system; see the beginning of this file for details.
The search path for rc files can contain multiple directories.
Normally, the list is separated by colons (:), but on DOS and Windows
the colon is used for drive letters, so semicolon (;) must be used
instead. Here are some examples, first for *nix:
set rcpath "/home/laguna/.gnuboy:/usr/local/etc/gnuboy"
set rcpath "."
and for DOS/Windows:
set rcpath "c:/gnuboy;."
set rcpath "c:/Program Files/Gnuboy"
If you really insist on using backslashes on DOS or Windows, you'll
have to double them up, since the backslash normally means "treat the
next character literally." For example,
set rcpath "c:\\gnuboy"
This is untested, and your milage may vary. I recommend just using
forward slashes and keeping things simple.
SAVE RELATED OPTIONS
These are all fairly simple, so I'll just list them quickly, then give
a couple examples.
savedir - directory to store saved games (SRAM and savestates) in
savename - base filename to use for saves
saveslot - which savestate slot to use
forcebatt - always save SRAM even on carts that don't have battery
nobatt - never save SRAM
syncrtc - resync the realtime clock for elapsed time when loading
The "savename" variable is particularly useful if you wish to have
more than one save associated with a particular rom. Just do something
like:
gnuboy --savename=mygame2 mygame.gb
and the save files will be called mygame2.sav, mygame2.000, etc rather
than just mygame.sav, etc.
The "saveslot" variable is normally just changed by keybindings, so
you can pick a savestate slot while you're playing a game. However, if
you for example prefer that the default slot at startup be 1 rather
than 0, you can use:
set saveslot 1
The "forcebatt" and "nobatt" options are fairly self-explanatory and
not very useful, except perhaps for debugging or use with corrupted
roms.
The "syncrtc" option needs a bit of explanation. Some roms, notably
Pokemon ones and Harvest Moon, use a realtime clock to keep track of
the time of day even when they're not running. Since gnuboy is just an
emulator, it can't work like a real cartridge and make things like
this keep happening while the emulator is not running. However, it can
resync the Gameboy realtime clock based on your computer's clock when
it starts. This is what the "syncrtc" option does. By default it's
enabled. If you disable it, then no time will seem to have elapsed
between when you exit the emulator once and when you start it again
the next time.
JOYSTICK OPTIONS
So far there is just one joystick option, "joy", used to enable or
disable joystick support.
DEBUGGING OPTIONS
These probably won't be useful to most people, but if you're trying to
debug a homebrew game you're writing or fix a bug in gnuboy they might
be of help:
The "trace" variable, if enabled, dumps a full execution trace to
stdout. Be prepared for at least 20 megs of logs to look through at
minimum, and more like 150 megs if you want enough to find anything
useful. Redirecting stdout to a file is a must!
The "sprdebug" variable is used to see how many sprites are visible
per line. Try it and see!
PLATFORM-SPECIFIC OPTIONS
On certain *nix systems, you may need to specify device nodes to use
if the defaults don't work:
oss_device - Open Sound System "DSP" device
fb_device - Video framebuffer device
joy_device - Joystick device
The Linux fbcon version of gnuboy does not support the "vmode" option
yet, but it can set the mode for you by running the "fbset" program,
if you have it. Just set the "fb_mode" variable to the exact name of
the mode you want in /etc/fb.modes. For example,
set fb_mode 640x480-90
You can also override the default color depth with the "fb_depth"
variable.
The DOS port of gnuboy has support for real console system gamepads
via the "Directpad Pro" (DPP) connector. To enable this feature, set
"dpp" to 1, set "dpp_port" to the IO port number the pad is connected
to (e.g. 0x378 -- be sure to prefix it with 0x for hex!!), and set
"dpp_pad" to the number of the pad you want to use. This code has not
been heavily tested, so it may or may not work for you. Be sure to get
the port number right or bad things may happen!
CONCLUSION
Well, that's about it for configuration. Hopefully this document
clears up a lot of the confusion about what you can and can't
configure in gnuboy yet, and how you go about doing it. Again, check
the sample.rc, palette.rc, and classic.rc files for lots of examples
of how rc files work.
As always, more info will come as time passes on. Keep on the lookout
for new releases and more features. Thank you for flying gnuboy and
have a nice day.
- Laguna

65
docs/CREDITS Normal file
View File

@ -0,0 +1,65 @@
Gilgamesh --
concept
research
testing
debug and tools coding
website and build maintainence
publicity
dos and windows builds
Laguna --
design
main program
asm optimizations
documentation
publicity
Damian M Gryski --
SDL port
various bugfix patches
Ralf Hoffmann --
Linux joystick code
SDL joystick bugfix
David Lau --
SDL joystick code
Mattias Wadman --
help with OpenBSD portability issues
LCDC behavior information
Matthew Conte --
DOS sound code
thinlib
Markus F.X.J. Oberhumer --
SDL fullscreen code
Dave Kiddell --
Windows port (SDL+mingw32)
SDL bugfixes
Magnus Damm --
YUV colorspace code
SDL YUV hardware scaling support
Gerd Knorr --
fbtv, from which mga accel code was taken
Jonathan Gevaryahu AKA Lord Nightmare --
tons of help improving sound emulation!
color filter code and default filter values
misc debugging support
Neil Stevens --
collecting samples for sound channel 4 (noise)
David Madore --
public domain "inflate" decompressor
Hii (author of TGB) --
lots of info on various memory mappers

90
docs/FAQ Normal file
View File

@ -0,0 +1,90 @@
GNUBOY FREQUENTLY ASKED QUESTIONS
Q: How do I configure gnuboy?
A: You can specify various options to gnuboy by means of "rcvars",
either on the command line or in your gnuboy.rc file. To set rcvars
from the command line, just use --varname=value, or --no-varname to
turn off yes/no options. If you wish to use a gnuboy.rc file, create
it with any text editor and save it in ~/.gnuboy (for *nix systems) or
the same directory as gnuboy.exe (for DOS/Windows systems). In this
file you can set rcvars with lines of the form "set varname value".
See the sample.rc file included for examples.
Q: Are you planning to add serial cable (gamelink) emulation?
A: Yes, read the wishlist in the README. At this time we don't have
all the technical information to emulate it 100% correctly, so if you
think you can help us find the info, get in touch.
Q: gnuboy is too slow. How can I make it run faster?
A: You can try turning the sampling rate for sound down (for example,
--samplerate=22050) or disabling sound entirely (--no-sound). Also,
running at 8bpp with --rgb565 enabled will result in the highest video
performance at the expense of some color quality. Of course, gnuboy is
very fast, and shouldn't need any performance tweaks as long as your
system is at least as fast as a Pentium/120.
Q: Why did the keybindings change in 1.0?
A: Even though lots of emulators do it, we figured it wasn't a very
good idea to use modifier keys for the controls, since some systems
may be configured to trap these for other uses. This especially became
a problem after adding support for Alt+Enter to toggle fullscreen. If
you want to go back to the old bindings and don't want to configure
them yourself, you can find all the old settings (including the old
default palette) in classic.rc. Just copy and paste to your gnuboy.rc.
Q: Will gnuboy ever support recording and playback?
A: We get this question fairly often, and I'm never sure how to answer
it. We are planning to support recording audio output before too
terribly long, but whether full demo recording and playback will ever
be supported is uncertain. We'll keep it in mind for the future,
though.
Q: Why doesn't gnuboy do anything when I run it?
A: You need to specify the name of the ROM to load on the command
line. One way to do this on Windows is to drag the ROM file onto
gnuboy.exe (or a shortcut to it). Associating *.gb and *.gbc with
gnuboy also works. Of course, you can also just use the run command on
the start menu, or open a dos prompt. Unix users of course are
expected to know how to run programs.
Q: gnuboy doesn't run on WinNT/2k!!
A: Set the following environment variables before running:
SDL_VIDEODRIVER=windib
SDL_AUDIODRIVER=waveout
Doing so should fix problems with other SDL programs too. If it still
doesn't work, let us know. Your milage may vary; some people have
reported that this doesn't help.
Q: Why is sound pitch off by about 1% in gnuboy?
A: You have a very good ear. It's a rounding issue that won't be
outright fixed for a while. If you want a workaround, set the sample
rate to a power of two (for example 32768 works well) on the command
line or in your gnuboy.rc.
Q: Why does sound have ugly static noise on Windows?
A: This is a bug in SDL's DirectSound support. Try setting the
environment variable:
SDL_AUDIODRIVER=waveout
before running gnuboy. Hopefully this won't cause any problems.

472
docs/HACKING Normal file
View File

@ -0,0 +1,472 @@
HACKING ON THE GNUBOY SOURCE TREE
BASIC INFO
In preparation for the first release, I'm putting together a simple
document to aid anyone interested in playing around with or improving
the gnuboy source. First of all, before working on anything, you
should know my policies as maintainer. I'm happy to accept contributed
code, but there are a few guidelines:
* Obviously, all code must be able to be distributed under the GNU
GPL. This means that your terms of use for the code must be equivalent
to or weaker than those of the GPL. Public domain and MIT-style
licenses are perfectly fine for new code that doesn't incorporate
existing parts of gnuboy, e.g. libraries, but anything derived from or
built upon the GPL'd code can only be distributed under GPL. When in
doubt, read COPYING.
* Please stick to a coding and naming convention similar to the
existing code. I can reformat contributions if I need to when
integrating them, but it makes it much easier if that's already done
by the coder. In particular, indentions are a single tab (char 9), and
all symbols are all lowercase, except for macros which are all
uppercase.
* All code must be completely deterministic and consistent across all
platforms. this results in the two following rules...
* No floating point code whatsoever. Use fixed point or better yet
exact analytical integer methods as opposed to any approximation.
* No threads. Emulation with threads is a poor approximation if done
sloppily, and it's slow anyway even if done right since things must be
kept synchronous. Also, threads are not portable. Just say no to
threads.
* All non-portable code belongs in the sys/ or asm/ trees. #ifdef
should be avoided except for general conditionally-compiled code, as
opposed to little special cases for one particular cpu or operating
system. (i.e. #ifdef USE_ASM is ok, #ifdef __i386__ is NOT!)
* That goes for *nix code too. gnuboy is written in ANSI C, and I'm
not going to go adding K&R function declarations or #ifdef's to make
sure the standard library is functional. If your system is THAT
broken, fix the system, don't "fix" the emulator.
* Please no feature-creep. If something can be done through an
external utility or front-end, or through clever use of the rc
subsystem, don't add extra code to the main program.
* On that note, the modules in the sys/ tree serve the singular
purpose of implementing calls necessary to get input and display
graphics (and eventually sound). Unlike in poorly-designed emulators,
they are not there to give every different target platform its own gui
and different set of key bindings.
* Furthermore, the main loop is not in the platform-specific code, and
it will never be. Windows people, put your code that would normally go
in a message loop in ev_refresh and/or sys_sleep!
* Commented code is welcome but not required.
* I prefer asm in AT&T syntax (the style used by *nix assemblers and
likewise DJGPP) as opposed to Intel/NASM/etc style. If you really must
use a different style, I can convert it, but I don't want to add extra
dependencies on nonstandard assemblers to the build process. Also,
portable C versions of all code should be available.
* Have fun with it. If my demands stifle your creativity, feel free to
fork your own projects. I can always adapt and merge code later if
your rogue ideas are good enough. :)
OK, enough of that. Now for the fun part...
THE SOURCE TREE STRUCTURE
[documentation]
README - general information related to using gnuboy
INSTALL - compiling and installation instructions
HACKING - this file, obviously
COPYING - the gnu gpl, grants freedom under condition of preseving it
[build files]
Version - doubles as a C and makefile include, identifies version number
Rules - generic build rules to be included by makefiles
Makefile.* - system-specific makefiles
configure* - script for generating *nix makefiles
[non-portable code]
sys/*/* - hardware and software platform-specific code
asm/*/* - optimized asm versions of some code, not used yet
asm/*/asm.h - header specifying which functions are replaced by asm
asm/i386/asmnames.h - #defines to fix _ prefix brain damage on DOS/Windows
[main emulator stuff]
main.c - entry point, event handler...basically a mess
loader.c - handles file io for rom and ram
emu.c - another mess, basically the frame loop that calls state.c
debug.c - currently just cpu trace, eventually interactive debugging
hw.c - interrupt generation, gamepad state, dma, etc.
mem.c - memory mapper, read and write operations
fastmem.h - short static functions that will inline for fast memory io
regs.h - macros for accessing hardware registers
save.c - savestate handling
[cpu subsystem]
cpu.c - main cpu emulation
cpuregs.h - macros for cpu registers and flags
cpucore.h - data tables for cpu emulation
asm/i386/cpu.s - entire cpu core, rewritten in asm
[graphics subsystem]
fb.h - abstract framebuffer definition, extern from platform-specifics
lcd.c - main control of refresh procedure
lcd.h - vram, palette, and internal structures for refresh
asm/i386/lcd.s - asm versions of a few critical functions
lcdc.c - lcdc phase transitioning
[input subsystem]
input.h - internal keycode definitions, etc.
keytables.c - translations between key names and internal keycodes
events.c - event queue
[resource/config subsystem]
rc.h - structure defs
rccmds.c - command parser/processor
rcvars.c - variable exports and command to set rcvars
rckeys.c - keybindingds
[misc code]
path.c - path searching
split.c - general purpose code to split strings into argv-style arrays
OVERVIEW OF PROGRAM FLOW
The initial entry point main() main.c, which will process the command
line, call the system/video initialization routines, load the
rom/sram, and pass control to the main loop in emu.c. Note that the
system-specific main() hook has been removed since it is not needed.
There have been significant changes to gnuboy's main loop since the
original 0.8.0 release. The former state.c is no more, and the new
code that takes its place, in lcdc.c, is now called from the cpu loop,
which although slightly unfortunate for performance reasons, is
necessary to handle some strange special cases.
Still, unlike some emulators, gnuboy's main loop is not the cpu
emulation loop. Instead, a main loop in emu.c which handles video
refresh, polling events, sleeping between frames, etc. calls
cpu_emulate passing it an idea number of cycles to run. The actual
number of cycles for which the cpu runs will vary slightly depending
on the length of the final instruction processed, but it should never
be more than 8 or 9 beyond the ideal cycle count passed, and the
actual number will be returned to the calling function in case it
needs this information. The cpu code now takes care of all timer and
lcdc events in its main loop, so the caller no longer needs to be
aware of such things.
Note that all cycle counts are measured in CGB double speed MACHINE
cycles (2**21 Hz), NOT hardware clock cycles (2**23 Hz). This is
necessary because the cpu speed can be switched between single and
double speed during a single call to cpu_emulate. When running in
single speed or DMG mode, all instruction lengths are doubled.
As for the LCDC state, things are much simpler now. No more huge
glorious state table, no more P/Q/R, just a couple simple functions.
Aside from the number of cycles left before the next state change, all
the state information fits nicely in the locations the Game Boy itself
provides for it -- the LCDC, STAT, and LY registers.
If the special cases for the last line of VBLANK look strange to you,
good. There's some weird stuff going on here. According to documents
I've found, LY changes from 153 to 0 early in the last line, then
remains at 0 until the end of the first visible scanline. I don't
recall finding any roms that rely on this behavior, but I implemented
it anyway.
That covers the basics. As for flow of execution, here's a simplified
call tree that covers most of the significant function calls taking
place in normal operation:
main sys/
\_ real_main main.c
|_ sys_init sys/
|_ vid_init sys/
|_ loader_init loader.c
|_ emu_reset emu.c
\_ emu_run emu.c
|_ cpu_emulate cpu.c
| |_ div_advance cpu.c *
| |_ timer_advance cpu.c *
| |_ lcdc_advance cpu.c *
| | \_ lcdc_trans lcdc.c
| | |_ lcd_refreshline lcd.c
| | |_ stat_change lcdc.c
| | | \_ lcd_begin lcd.c
| | \_ stat_trigger lcdc.c
| \_ sound_advance cpu.c *
|_ vid_end sys/
|_ sys_elapsed sys/
|_ sys_sleep sys/
|_ vid_begin sys/
\_ doevents main.c
(* included in cpu.c so they can inline; also in cpu.s)
MEMORY READ/WRITE MAP
Whenever possible, gnuboy avoids emulating memory reads and writes
with a function call. To this end, two pointer tables are kept -- one
for reading, the other for writing. They are indexed by bits 12-15 of
the address in Game Boy memory space, and yield a base pointer from
which the whole address can be used as an offset to access Game Boy
memory with no function calls whatsoever. For regions that cannot be
accessed without function calls, the pointer in the table is NULL.
For example, reading from address addr can be accomplished by testing
to make sure mbc.rmap[addr>>12] is not NULL, then simply reading
mbc.rmap[addr>>12][addr].
And for the disbelievers in this optimization, here are some numbers
to compare. First, FFL2 with memory tables disabled:
% cumulative self self total
time seconds seconds calls us/call us/call name
28.69 0.57 0.57 refresh_2
13.17 0.84 0.26 4307863 0.06 0.06 mem_read
11.63 1.07 0.23 cpu_emulate
Now, with memory tables enabled:
38.86 0.66 0.66 refresh_2
8.42 0.80 0.14 156380 0.91 0.91 spr_enum
6.76 0.91 0.11 483134 0.24 1.31 lcdc_trans
6.16 1.02 0.10 cpu_emulate
.
.
.
0.59 1.61 0.01 216497 0.05 0.05 mem_read
As you can see, not only does mem_read take up (proportionally) 1/20
as much time, since it is rarely called, but the main cpu loop in
cpu_emulate also runs considerably faster with all the function call
overhead and cache misses avoided.
These tests were performed on K6-2/450 with the assembly cores
enabled; your milage may vary. Regardless, however, I think it's clear
that using the address mapping tables is quite a worthwhile
optimization.
LCD RENDERING CORE DESIGN
The LCD core presently used in gnuboy is very much a high-level one,
performing the task of rasterizing scanlines as many independent steps
rather than one big loop, as is often seen in other emulators and the
original gnuboy LCD core. In some ways, this is a bit of a tradeoff --
there's a good deal of overhead in rebuilding the tile pattern cache
for roms that change their tile patterns frequently, such as full
motion video demos. Even still, I consider the method we're presently
using far superior to generating the output display directly from the
gameboy tiledata -- in the vast majority of roms, tiles are changed so
infrequently that the overhead is irrelevant. Even if the tiles are
changed rapidly, the only chance for overhead beyond what would be
present in a monolithic rendering loop lies in (host cpu) cache misses
and the possibility that we might (tile pattern) cache a tile that has
changed but that will never actually be used, or that will only be
used in one orientation (horizontally and vertically flipped versions
of all tiles are cached as well). Such tile caching issues could be
addressed in the long term if they cause a problem, but I don't see it
hurting performance too significantly at the present. As for host cpu
cache miss issues, I find that putting multiple data decoding and
rendering steps together in a single loop harms performance much more
significantly than building a 256k (pattern) cache table, on account
of interfering with branch prediction, register allocation, and so on.
Well, with those justifications given, let's proceed to the steps
involved in rendering a scanline:
updatepatpix() - updates tile pattern cache.
tilebuf() - reads gb tile memory according to its complicated tile
addressing system which can be changed via the LCDC register, and
outputs nice linear arrays of the actual tile indices used in the
background and window on the present line.
Before continuing, let me explain the output format used by the
following functions. There is a byte array scan.buf, accessible by
macro as BUF, which is the output buffer for the line. The structure
of this array is simple: it is composed of 6 bpp gameboy color
numbers, where the bits 0-1 are the color number from the tile, bits
2-4 are the (cgb or dmg) palette index, and bit 5 is 0 for background
or window, 1 for sprite.
What is the justification for using a strange format like this, rather
than raw host color numbers for output? Well, believe it or not, it
improves performance. It's already necessary to have the gameboy color
numbers available for use in sprite priority. And, when running in
mono gb mode, building this output data is VERY fast -- it's just a
matter of doing 64 bit copies from the tile pattern cache to the
output buffer.
Furthermore, using a unified output format like this eliminates the
need to have separate rendering functions for each host color depth or
mode. We just call a one-line function to apply a palette to the
output buffer as we copy it to the video display, and we're done. And,
if you're not convinced about performance, just do some profiling.
You'll see that the vast majority of the graphics time is spent in the
one-line copy function (render_[124] depending on bytes per pixel),
even when using the fast asm versions of those routines. That is to
say, any overhead in the following functions is for all intents and
purposes irrelevant to performance. With that said, here they are:
bg_scan() - expands the background layer to the output buffer.
wnd_scan() - expands the window layer.
spr_scan() - expands the sprites. Note that this requires spr_enum()
to have been called already to build a list of which sprites are
visible on the current scanline and sort them by priority.
It should be noted that the background and window functions also have
color counterparts, which are considerably slower due to merging of
palette data. At this point, they're staying down around 8% time
according to the profiler, so I don't see a major need to rewrite them
anytime soon. It should be considered, however, that a different
intermediate format could be used for gbc, or that asm versions of
these two routines could be written, in the long term.
Finally, some notes on palettes. You may be wondering why the 6 bpp
intermediate output can't be used directly on 256-color display
targets. After all, that would give a huge performance boost. The
problem, however, is that the gameboy palette can change midscreen,
whereas none of the presently targetted host systems can handle such a
thing, much less do it portably. For color roms, using our own
internal color mappings in addition to the host system palette is
essential. For details on how this is accomplished, read palette.c.
Now, in the long term, it MAY be possible to use the 6 bpp color
"almost" directly for mono roms. Note that I say almost. The idea is
this. Using the color number as an index into a table is slow. It
takes an extra read and causes various pipeline stalls depending on
the host cpu architecture. But, since there are relatively few
possible mono palettes, it may actually be possible to set up the host
palette in a clever way so as to cover all the possibilities, then use
some fancy arithmetic or bit-twiddling to convert without a lookup
table -- and this could presumably be done 4 pixels at a time with
32bit operations. This area remains to be explored, but if it works,
it might end up being the last hurdle to getting realtime emulation
working on very low-end systems like i486.
SOUND
Rather than processing sound after every few instructions (and thus
killing the cache coherency), we update sound in big chunks. Yet this
in no way affects precise sound timing, because sound_mix is always
called before reading or writing a sound register, and at the end of
each frame.
The main sound module interfaces with the system-specific code through
one structure, pcm, and a few functions: pcm_init, pcm_close, and
pcm_submit. While the first two should be obvious, pcm_submit needs
some explaining. Whenever realtime sound output is operational,
pcm_submit is responsible for timing, and should not return until it
has successfully processed all the data in its input buffer (pcm.buf).
On *nix sound devices, this typically means just waiting for the write
syscall to return, but on systems such as DOS where low level IO must
be handled in the program, pcm_submit needs to delay until the current
position in the DMA buffer has advanced sufficiently to make space for
the new samples, then copy them.
For special sound output implementations like write-to-file or the
dummy sound device, pcm_submit should write the data immediately and
return 0, indicating to the caller that other methods must be used for
timing. On real sound devices that are presently functional,
pcm_submit should return 1, regardless of whether it buffered or
actually wrote the sound data.
And yes, for unices without OSS, we hope to add piped audio output
soon. Perhaps Sun audio device and a few others as well.
OPTIMIZED ASSEMBLY CODE
A lot can be said on this matter. Nothing has been said yet.
INTERACTIVE DEBUGGER
Apologies, there is no interactive debugger in gnuboy at present. I'm
still working out the design for it. In the long run, it should be
integrated with the rc subsystem, kinda like a cross between gdb and
Quake's ever-famous console. Whether it will require a terminal device
or support the graphical display remains to be determined.
In the mean time, you can use the debug trace code already
implemented. Just "set trace 1" from your gnuboy.rc or the command
line. Read debug.c for info on how to interpret the output, which is
condensed as much as possible and not quite self-explanatory.
PORTING
On all systems on which it is available, the gnu compiler should
probably be used. Writing code specific to non-free compilers makes it
impossible for free software users to actively contribute. On the
other hand, compiler-specific code should always be kept to a minimum,
to make porting to or from non-gnu compilers easier.
Porting to new cpu architectures should not be necessary. Just make
sure you unset IS_LITTLE_ENDIAN in the makefiles to enable the big
endian default if the target system is big endian. If you do have
problems building on certain cpus, however, let us know. Eventually,
we will also want asm cpu and graphics code for popular host cpus, but
this can wait, since the c code should be sufficiently fast on most
platforms.
The bulk of porting efforts will probably be spent on adding support
for new operating systems, and on systems with multiple video (or
sound, once that's implemented) architectures, new interfaces for
those. In general, the operating system interface code goes in a
directory under sys/ named for the os (e.g. sys/nix/ for *nix
systems), and display interfaces likewise go in their respective
directories under sys/ (e.g. sys/x11/ for the x window system
interface).
For guidelines in writing new system and display interface modules, i
recommend reading the files in the sys/dos/, sys/svga/, and sys/nix/
directories. These are some of the simpler versions (aside from the
tricky dos keyboard handling), as opposed to all the mess needed for
x11 support.
Also, please be aware that the existing system and display interface
modules are somewhat primitive; they are designed to be as quick and
sloppy as possible while still functioning properly. Eventually they
will be greatly improved.
Finally, remember your obligations under the GNU GPL. If you produce
any binaries that are compiled strictly from the source you received,
and you intend to release those, you *must* also release the exact
sources you used to produce those binaries. This is not pseudo-free
software like Snes9x where binaries usually appear before the latest
source, and where the source only compiles on one or two platforms;
this is true free software, and the source to all binaries always
needs to be available at the same time or sooner than the
corresponding binaries, if binaries are to be released at all. This of
course applies to all releases, not just new ports, but from
experience i find that ports people usually need the most reminding.
EPILOGUE
That's it for now. More info will eventually follow. Happy hacking!

6
docs/LIBERTY Normal file
View File

@ -0,0 +1,6 @@
LIBERTY
For the true meaning of liberty, please visit the the Foundation's
philosopy page at http://www.gnu.org/philosophy/philosophy.html.

355
docs/README.old Normal file
View File

@ -0,0 +1,355 @@
GNUBOY README
INTRO
Welcome to gnuboy, one of the few pieces of Free Software to emulate
the Game Boy handheld game console. Written in ANSI C with a few
optional assembler optimizations for particular cpus, gnuboy supports
a wide range of host systems, and has been tested successfully on:
GNU/Linux
FreeBSD
OpenBSD
BeOS
Linux/390 (IBM S/390 Mainframe)
SunOS/Sun Ultra60
IRIX/SGI O2
IRIX/SGI Indy
AIX/Unknown
DR-DOS
MS-DOS
Windows DOS box
Windows 9x/NT/2k
Additionally, gnuboy should run on any other *nix variants that have
ANSI C compilers and that are remotely POSIX compliant. As gnuboy is
Free Software, you're welcome to fix any problems you encounter
building it for a particular system, or to port it to entirely new
systems.
For build instructions, see the file INSTALL. For information on the
structure of the source tree, program flow, design decisions and
guidelines, porting, and so on, read HACKING.
GENERAL USAGE
Just pass the name of the rom to load on the command line. Default
keybindings are as follows:
esc - exit
arrow keys - d-pad
alt - a
ctrl - b
space - select
enter - start
0-9 - select savestate slot
ins - save current state
del - return to saved state
joypad - d-pad
joy0 - b
joy1 - a
joy2 - select
joy3 - start
(Note: joystick is not available on all platforms at this time.)
If you want to change these or other options, you should create a
gnuboy.rc file. See the system-specific info below for where to put
it.
The rc subsystem is very similar to Quake's console in many
respects. You have commands and variables. First, the commands:
quit - exit gnuboy (saving sram)
reset - reset to powerup state
source - process another rc file
set - set a variable's value
bind - bind keys
unbind - remove a keybinding
unbindall - remove all keybindings
savestate - save current state
loadstate - return to saved state
Additionally, each gameboy pad button has two commands, one to press
it, and another to release it -- for example, +start and -start. When
a key is bound to one of these commands that starts with a +, it will
perform the corresponding - command when it's released, as expected.
Here's a list of the + commands, though they should be obvious:
+start +select +a +b +up +down +left +right
Now for the variables. To set any of the rc variables, just put the
command of the form "set variable value" in your gnuboy.rc or other rc
file. Some of the more interesting variables are:
rcpath - search path for loading extra rc files
savedir - the directory where save files will be stored
savename - base of filename to use for sram and savestates
forcedmg - set to 1/true/yes to force color roms to play mono
framelen - delay in microseconds between frames
framecount - run only the given number of frames, then exit
dmg_bgp - specify 4 custom colors to be used for mono background
dmg_wndp - same thing, but for the window layer
dmg_obp0 - and for sprite palette 0
dmg_obp1 - and sprite palette 1
scale - factor for screen scaling; currently only 1 and 2 work
density - density level for screen scaling; see description below
sprsort - x-sort sprites for correctness on dmg roms
syncrtc - fake elapsed time on rtc since last session at startup
trace - output a complete cpu trace to stdout
sprdebug - display bars indicating sprite count per line
There are a few others which may or not be useful. Also, certain
system and display targets have their own variables, which will be
described in the relevant sections below. For more info on how the
variables work, read the source.
For sample rc files, look in the etc/ directory.
Finally, to display help, version, or copying information, use the
--help, --version, or --copying options respectively on the gnuboy
command line.
USAGE - *NIX SYSTEMS
The file gnuboy.rc should be placed in ~/.gnuboy/. If it is not found
in this location, the current working directory will be
searched. The following defaults will be used:
rcpath - ~/.gnuboy:.
savedir - ~/.gnuboy/saves
If you don't like these, override them with gnuboy.rc.
There are presently four *nix targets supported: X11, SDL, and Linux
fbcon and svgalib. In the future other fb devices (such as the Sun
console) should be supported as well.
If you have problems with gnuboy running too slowly on svgalib, turn
off the vsync option, i.e. set vsync 0. Putting --no-vsync on the
command line works as well. At this point svgalib is the only one that
supports vsync, so it's a non-issue on the others.
USAGE - DOS and Windows
Place your gnuboy.rc in the same directory as gnuboy.exe. You need to
specify a save directory in it; otherwise the working directory will
be used, which is probably not what you want. For example, if you've
installed gnuboy.exe in c:/gnuboy, and you want your saves to be
stored in c:/gnuboy/saves, place the following in a plain text file
called gnuboy.rc in c:/gnuboy.
set savedir c:/gnuboy/saves
By default stereo sound is disabled on DOS since it doesn't work right
on some of the systems we've tested; to enable it, add the following
line to your gnuboy.rc:
set stereo 1
VIDEO MODES
Now all the display targets except Linux fbcon support the uniform
"vmode" rcvar to set the video mode. From the rc file, you can specify
a video mode like this (for 640x480, 16bpp):
set vmode 640 480 16
Or you can specify the mode on the command line, as
--vmode=640,480,16
If the requested video mode is not available, gnuboy may either give
an error message or use a similar available mode.
SCREEN SCALING
Scaling by integral factors 1-4 is now supported. Just set the rcvar
"scale" to the desired scaling factor. Most of the display targets
will automatically choose a video mode appropriate to the chosen
scale, but DOS and Linux fbcon users should be aware they they need to
set the mode manually, as described above. Of course, if you prefer,
you can always set it manually.
By default, for performance reasons, vertical scaling will not be
fully dense but will leave some blank scanlines. This behavior can be
adjusted by means of the "density" rcvar. Density 0, the default,
skips every other line. Nonzero values N fill in the first N copies of
the scanline, and leave the remaining scale-N scanlines blank. So, if
you want a fully filled in display (and the worst performance), you
should set density to the same value as scale.
Please be aware that this code is still slightly experimental, and the
ways of configuring scaling may change considerably in the next few
releases.
HARDWARE ACCELERATED YUV-SPACE SCREEN SCALING
If you're using the SDL display target and your video card/driver
supports it, hardware screen scaling is available. This feature
provides scaling to any size with almost no cpu usage! It's enabled by
default if the screen resolution is set to 320x288 or higher; manually
set the "yuv" rcvar to 0 or 1 to force it off or on, respectively.
Scaling will be performed to fill the entire requested video mode.
For example, to scale to 640x480, either add the following line to
your gnuboy.rc:
set vmode 640 480
or put --vmode=640,480 on the command line. A better alternative is to
just request a particular scale, for example with:
set scale 4
or --scale=4 on the command line; this way the gameboy's near 1:1
aspect ratio won't become distorted.
SOUND SUPPORT
At this point all features are implemented and everything should be
right, so any incorrect sound output should be treated as a bug, which
we'll try to fix as soon as possible.
JOYSTICK AND GAMEPAD SUPPORT
At this time, the Linux and SDL joystick devices are the only ones
supported. We hope to have DOS joystick support soon.
Binding joystick controls works the same way as for the keyboard. Just
use the key names joyup, joydown, joyright, joyleft, joy0, joy1, joy2,
etc. Default bindings should probably be ok for most users, except
that A/B might be backwards on some gamepads.
PERFORMANCE
Here are some performance estimates i've gathered (given in percent
cpu utilization, running at full 60 fps)...
Optimized C Assembly
AMD K6-2/450 12% 8%
Pentium/75 (too slow) 70%
SGI O2 25% (no asm)
SGI Indy 70% (no asm)
Sun Ultra60 3-20% (no asm)
IBM S/390 about 0.3% (no asm)
Note that these figures were computed before sound was implemented.
Until the sound code is further optimized, gnuboy may run somewhat
slower than these numbers suggest.
SUPPORTED MEMORY BANK CONTROLLERS (MBCS)
At this time, gnuboy supports MBC1, MBC2, MBC3 (including realtime
clock), and MBC5 (including the version with rumble support, although
that functionality is omitted).
GRAPHICS SUPPORT
Color Gameboy roms are supported completely, including so-called
"highcolor" tricks. Yes, even in 256-color mode, although in games
that use too many colors on one screen, the later ones will only be
approximated. Use a 16 bpp (or higher) display mode if this is a
problem.
Alternatively, for games that look bad in 256 color mode, you can
run in simulated 3/3/2 bits per channel truecolor. Just set the
variable rgb332 to something nonzero (or just putting --rgb332 on the
command line will work). Color precision is lost somewhat, especially
in smooth gradients, but for the most part it looks very good.
Much care has been put into ensuring that the lcd timings and
interrupts behave as closely to the real hardware as possible. A few
features remain unimplemented, such as reduced length HBLANK depending
on the number of sprites visible on the scanline, but the vast
majority of display tricks used in current roms work fine.
We do, however, lack information on proper GDMA timings, which could
theoretically cause problems for some roms. If you can provide us with
accurate information, please do!
COMPATIBILITY
Eventually I'll put detailed information here. For now, just see known
bugs for the few cases where roms fails.
KNOWN BUGS
The portal between the temple and the Talon in FFL3 is glitched and
the game freezes for a second or two building the incorrect animation
when entering those screens.
GDMA finishes instantly, whereas it should take time. Making it take
time breaks Wacky Races, last I checked, so apparently the documents I
have on GDMA timing are incorrect. Same goes for HDMA. Good
information would be much appreciated.
The main loop in emu_run is very sloppy and not quite right, but it
works.
Sound mixing is taking way too much cpu time. I have some ideas for
fixing this, and I plan to write optimized assembly code for sound
eventually. If it's a problem try turning down the sample rate.
YUV-space hardware scaling only supports the common "YUY2" mode so
far. More modes will be added in the future.
REPORTING OR FIXING BUGS
Found a bug not mentioned above, or better yet, fixed one? Send bug
reports or patches to gnuboy@starfox.org. Please be aware that
distribution of any code based on gnuboy must follow the provisions of
the GPL, so if you don't agree to this, don't send code to us or
anyone else. Let us know if you wish to be included in the credits.
For guidelines regarding code contributions, see the file HACKING.
Please be aware that evaluating contributed code and figuring out if
or how to work it in can take time. If we haven't done anything with
your code yet, please be patient.
THANKS
Thanks goes out to everyone who's expressed interest in gnuboy by
writing -- users, porters, authors of other emulators, and so forth.
Apologies if we don't get a personal response out to everyone, but
either way, consider your feedback well appreciated.
EPILOGUE
OK, that looks like about it. More to come, stick around...
-Laguna

386
docs/WHATSNEW Normal file
View File

@ -0,0 +1,386 @@
* WHAT'S NEW *
Here you will find a summary of the changes made in each released
version of gnuboy, organized from an end-user perspective.
Webmasters, please use this file as a basis for announcing new
versions; the CHANGES file is too technical and unorganized.
RELEASE 1.0.3
All ANSI C incompatibilities should be fixed. Please report any that
remain.
Various bugs encountered when building gnuboy on strange compilers
have been fixed.
Internal support for decompressing gzipped roms now exists in a
minimal form. The inflate code used is taken from a quine
(self-reproducing program) written by David Madore and placed in the
public domain. This code is very portable but is rather slow and may
crash when given invalid data; however, there should be no impact on
security. Currently only gzip files (not pkzip files) are supported.
HuC3 emultaion has been fixed to some extent. Robopon Sun and Star are
both playable now, but many features of the HuC3 are still not
implemented.
Color filtering to make gnuboy look much more like a real CGB is now
included, thanks to the work of Jonathan Gevaryahu.
A new rcvar "gbamode" has been added to unlock the GBA-only features
present in some newer CGB games. (This has nothing to do with GBA
emulation, which gnuboy does not do and will not do in the future.)
Sprite sorting in DMG mode has been fixed. This should improve things
in various DMG games where sprites previously overlapped in the wrong
order.
RELEASE 1.0.2
A minor problem in the frequency sweep function on sound channel 1 was
fixed, correcting the sound of the ice beam and metroid encounter
sound in Metroid 2.
Sound channel 3 waveform data is now trashed when the sound is
played. This makes it more difficult for games and demos to detect
that they are running on an emulator.
The channel 3 waveform is now properly initialized in both DMG and CGB
modes. Before it was incorrectly initialized to have frequency 16
times too high, and the initial "random noise" pattern exhibited by
DMG wasn't emulated. R-Type now sounds much better.
The sound length register for channel 3 now works properly, fixing the
title screen music in MegaMan 2. No thanks to gbspec.txt for having
blatently wrong info about this matter.
Major problems with sound quality on channel 4 (noise) have been
fixed, and the pseudorandom sequence has been replaced to very closely
resemble that of a real Gameboy, thanks to the hard work of Lord
Nightmare. All these changes make a significant improvement in many
games, notably Metroid 2, Final Fantasy Legend II and III, Camp
Deadly, and Wario Land.
Stereo channels are no longer backwards.
The DMG STAT register write bug, which causes an interrupt if the STAT
register is written while in HBLANK or VBLANK, is now emulated. This
fixes Legend of Zerd and probably any other DMG game that will not run
on a real Gameboy Color.
A hack/potential fix for a problem that kept Konami Collection Vol 1
from working has been put in place.
A major interrupt bug that prevented Amazing Penguin from running has
been fixed.
Several bugs that could have resulted in crashes under strange
circumstances have been fixed.
Other minor sound issues have been tweaked or fixed.
RELEASE 1.0.1
BUGS FIXED
Keys that didn't work in the SDL-based ports have now been fixed.
The --bindir= option to the configure script now works properly.
Running "make install" no longer fails when the destination directory
doesn't already exist.
Various minor cleanups.
RELEASE 1.0.0
NEW FEATURES
Auto-loading rc files on a per-rom basis.
Less intense yellow in the default mono palette.
Default keybindings no longer use modifier keys.
Lots of new documentation.
Hardware scaling on matroxfb now looks better.
BUGS FIXED
Disabled some useless optimizations to work around a bug in gcc 2.96, which
despite being a broken compiler has become rather popular since Redhat
decided to package it without sufficient testing. This will fix the problems
lots of people have reported when compiling.
Added --disable-arch option to configure to prevent the binaries built from
being dependent on the exact host cpu they're built on. This will allow
distro maintainers to build packages that work even on older cpus.
RELEASE 0.9.13
NEW FEATURES
Hardware scaling support on fbcon with matroxfb.
PERFORMANCE
Now all software scaling code has assembly implementations.
COMPATIBILITY
Invalid opcode crash in Montezuma's Return has been fixed.
Visual glitches in Pokemon Yellow are now fixed.
BUGS FIXED
Line refresh was taking place too late, causing visual glitch in Alleyway.
Flags behavior on the RL, RR, RLC, and RRC opcodes was completely bogus in the
asm cpu core. Miracle it didn't break more things. Should be correct now.
The SWAP (HL) instruction in the asm cpu core was nonfunctional.
RELEASE 0.9.12
NEW FEATURES
Hardware YUV scaling with full interpolation on systems that support it,
using SDL -- thanks goes to Magnus Damm.
COMPATIBILITY
Binary Chaos no longer has graphical glitches, and is fully playable.
Wacky Races now displays correctly again (broken since 0.9.7).
BUGS FIXED
Some test code was left in cpu.s, breaking DOS/Windows builds and
slowing things down across the board.
SDL joystick code was generating bogus release events.
Unused bits of VBK register were 0's; they should be 1's.
DMG palette was not being restored correctly after loading savestates.
RELEASE 0.9.11
NEW FEATURES
Most ports can auto-choose screen size appropriate to scale given.
Scaling to 2x, 3x, and 4x is now possible at all color depths.
PERFORMANCE
SDL port should run considerably faster than before at hires modes.
New X11 rcvar "x_shmsync" can be turned off for performance boost,
but it can result in heavy shearing and skipped frames.
The new scaling code is slightly faster than before.
COMPATIBILITY
Konami Collection (GBC) series now works in color mode.
Worms Armageddon is now playable, might still have some glitches though.
Turok 3 no longer hangs at startup and seems fully playable.
Sound samples that played too slow in many games now sound correct.
BUGS FIXED
Video mode setting was not working with svgalib.
Undocumented behavior - STOP instruction causes PC to skip forward.
SDL hardware surface mode is now always on and should always work fine.
More undocumented behavior - HDMA can occur while LCDC is off...?
HDMA5 register was not being initialized correctly on reset.
Timer was running at half the speed it should when in high-speed mode.
RELEASE 0.9.10
NEW FEATURES
Primitive but fast and fully functional scale-by-two support.
New rcvar "vmode" sets video mode in all the targets that support it.
Now up to 16 joystick buttons can be used, as opposed to 8 before.
Static palette mode (rcvar "rgb332"), which can make highcolor games
look better when run at 8bpp by faking a 3/3/2 color channel mode.
COMPATIBILITY
Added a quick hack to fake serial I/O so that Super RC Pro-Am works.
Spiderman's web is now visible in Spiderman GBC.
PERFORMANCE
New refresh code blits lines slightly faster.
Added sdl_hwsurface rcvar to turn hardware surface on for speed boost,
disabled by default because it crashes on Windows.
BUGS FIXED
Hopefully fixed issue with X header locations affecting some users.
DMG sprite sorting isn't working right. It's now disabled by default.
VBLANK timings have been changed slightly to fix Daedalian Opus.
Fixed various minor bugs found by enabling compiler warnings.
Spacebar was not working with SDL. Fixed.
Fixed bug that made OAM unreadable.
RELEASE 0.9.9
NEW FEATURES
DOS port is now entirely thinlib-based. Should be more robust, featureful.
For example, custom video modes such as 256x224 are available.
PERFORMANCE
New asm routines for displaying the background layer on color games.
This results in a significant performance boost low-end machines (~P75).
SDL code no longer fakes 16bpp but uses the native color depth.
BUGS FIXED
SDL gnuboy no longer produces random pops in sound at startup.
Mono/stereo should now be handled correctly in the SDL code.
Eliminated compiletime error about SDL_DISABLE on SDL ver < 1.1.8.
Serious bug -- loading savestates was not updating the memory bank map --
resulted in bogus behavior after loading in certain situations.
Scrapped SDL_HWSURFACE to make fullscreen work on Windows.
RELEASE 0.9.8
NEW FEATURES
Native SDL sound support. This means sound will work on BeOS and Windows.
Sprites are priority sorted on DMG games by default now.
ROMs can be loaded from stdin using "-" as the filename.
SDL port now runs fullscreen by default. Set sdl_fullscreen to 0 to disable.
SDL port now supports alt+enter fullscreen toggle.
Integrated Windows port by mewse (using SDL and mingw32).
COMPATIBILITY
Added support for undocumented ram size 05 (presumably 256 kbit).
This fixes the crash with Pokemon Crystal.
Removed hacks for Altered Space and W&W because they break other games.
Added new VBLANK code that should fix them again without breaking other stuff.
BUGS FIXED
SDL keyboard handler was not accepting the numeral keys.
SDL video was messed up bad when it couldn't get a true 160x144 display.
Loading a ROM with bogus ROM/RAM size headers used to crash the program.
RELEASE 0.9.7
NEW FEATURES
DOS sound support!
VESA video modes on DOS!
Mono sound for cards that don't support stereo.
COMPATIBILITY
Altered Space and Wizards & Warriors now run.
Change was made to VBLANK behavior; hopefully it doesn't break other games.
BUGS FIXED
Envelope length was being computed wrong.
HDMA was incorrectly taking place all at once.
Command line parsing incorrectly changed - to _ in places it shouldn't.
SDL joystick support was not building correctly on non-Linux systems.
RELEASE 0.9.5
NEW FEATURES
Showing the name from the ROM header in X11 and SDL titlebars.
The various targets can be disabled at compiletime with --without-* options.
OSS sound support might work on FreeBSD and OpenBSD but is untested.
SDL joystick support.
GNU make no longer required to compile.
OPTIMIZATIONS
Flags handling in the C and asm cpu cores should be faster.
Word-at-a-time memory I/O is now done more efficiently.
BUGS FIXED
Highcolor screens no longer flicker.
Misplaced high-pitched beeps in sound have been removed, again.
C graphics code was misaligning the screen by one pixel. Fixed.
RELEASE 0.9.4
NEW FEATURES
Linux fbcon display support.
Linux joystick device support.
COMPATIBILITY
HDMA now works in DKC and Lemmings Color.
Default wave pattern is now supported.
RELEASE 0.9.2
NEW FEATURES
SDL port!
Sound channel 4.
MBC3 RTC support.
COMPATIBILITY
Sound is now properly handled in savestates.
Pokemon Silver and Gold should now be playable.
BUGS FIXED
Another bug in sweep.
Nasty aliasing from extremely high sound frequencies.
RELEASE 0.9.1
NEW FEATURES
Experimental sound support!
Better command line parsing.
Added --showvars command line option.
Various optimizations.
BUGS FIXED
HDMA bug.
LCDC STAT interrupt bug.
NOTE: Earlier releases have not yet been documented in this file.
Hopefully they'll be added eventually, at least for the sake of
completing the records.

110
emu.c Normal file
View File

@ -0,0 +1,110 @@
#include "defs.h"
#include "regs.h"
#include "hw.h"
#include "cpu.h"
#include "mem.h"
#include "lcd.h"
#include "rc.h"
static int framelen = 16743;
static int framecount;
rcvar_t emu_exports[] =
{
RCV_INT("framelen", &framelen),
RCV_INT("framecount", &framecount),
RCV_END
};
void emu_init()
{
}
/*
* emu_reset is called to initialize the state of the emulated
* system. It should set cpu registers, hardware registers, etc. to
* their appropriate values at powerup time.
*/
void emu_reset()
{
hw_reset();
lcd_reset();
cpu_reset();
mbc_reset();
sound_reset();
}
void emu_step()
{
cpu_emulate(cpu.lcdc);
}
/* This mess needs to be moved to another module; it's just here to
* make things work in the mean time. */
void *sys_timer();
void emu_run()
{
void *timer = sys_timer();
int delay;
vid_begin();
lcd_begin();
for (;;)
{
cpu_emulate(2280);
while (R_LY > 0 && R_LY < 144)
emu_step();
vid_end();
rtc_tick();
sound_mix();
if (!pcm_submit())
{
delay = framelen - sys_elapsed(timer);
sys_sleep(delay);
sys_elapsed(timer);
}
doevents();
vid_begin();
if (framecount) { if (!--framecount) die("finished\n"); }
if (!(R_LCDC & 0x80))
cpu_emulate(32832);
while (R_LY > 0) /* wait for next frame */
emu_step();
}
}

41
etc/classic.rc Normal file
View File

@ -0,0 +1,41 @@
#
# classic.rc - change keybindings, palette, etc
# to match the old pre-1.0 settings.
#
unbindall
bind esc quit
bind up +up
bind down +down
bind left +left
bind right +right
bind alt +a
bind ctrl +b
bind enter +start
bind space +select
bind joyup +up
bind joydown +down
bind joyleft +left
bind joyright +right
bind joy0 +b
bind joy1 +a
bind joy2 +select
bind joy3 +start
bind 1 "set saveslot 1"
bind 2 "set saveslot 2"
bind 3 "set saveslot 3"
bind 4 "set saveslot 4"
bind 5 "set saveslot 5"
bind 6 "set saveslot 6"
bind 7 "set saveslot 7"
bind 8 "set saveslot 8"
bind 9 "set saveslot 9"
bind 0 "set saveslot 0"
bind ins savestate
bind del loadstate
set dmg_bgp 0x78f0f0 0x58b8b8 0x487878 0x184848
set dmg_wndp 0x78f0f0 0x58b8b8 0x487878 0x184848
set dmg_obp0 0x78f0f0 0x58b8b8 0x487878 0x184848
set dmg_obp1 0x78f0f0 0x58b8b8 0x487878 0x184848

43
etc/filters.rc Normal file
View File

@ -0,0 +1,43 @@
#
# Explanation of the filters:
#
# If, for example, red is set to "a b c d",
# where a, b, c, and d are four numbers,
# then the output red value for a given input
# color will be computed as follows:
#
# output_red = ( a * input_red +
# b * input_green +
# c * input_blue ) / 256 + d
#
# So, a, b, and c are scale factors
# (out of 256) for how much weight the input
# red, green, and blue components have in the
# output color, and d is a constant base
# value for the output.
#
# Below are some sample filters, which should
# make everything more clear.
#
# Default filter as of 1.0.3
set red 195 25 0 35
set green 25 170 25 35
set blue 25 60 125 40
# Do-nothing filer
set red 256 0 0 0
set green 0 256 0 0
set blue 0 0 256 0
# Lighten the display uniformly
set red 128 0 0 128
set green 0 128 0 128
set blue 0 0 128 128
# Grayscale
set red 85 85 85 0
set green 85 85 85 0
set blue 85 85 85 0

30
etc/laguna.pgp Normal file
View File

@ -0,0 +1,30 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1.0.1 (GNU/Linux)
Comment: For info see http://www.gnupg.org
mQGiBDp47IARBACeZ/JuuNwArXaMl+9DGG0BSMPv1qJUYNdDqFREg60GDZRrChAV
WhSgpZ/NmSNAS8p4AYTtKIIrb0rMbNnEiaPqjftBFFe2JJ52fXsEosw3xk/v556E
spAdimPPnng7z6hb4QZitrtco5SxfKUAkZzBVJbZRYCuLu4XHZcbooLshwCg5Jb9
Wr6ha8LPNh/nL39J0dDki2UD/RQKzlJWctiSc7jGgCy49GABOb5BiI07cUdOkfwL
eDUS+x/O6Dx8pxmwZJMw9LVOlIHhCwwSlT03Rh7bG6l7sWTuij+HFfSYe4UAgj/E
Vi6n5j8efNcc61x8NMvrZxuyBwomxKX9y9m1OOxKSpcxFTW2to7YGn9JUXsvSoOV
O2dtBACVazyx6Up+8FVKBzM+W4XW1n5em9dqzDbuxAUwvQ7PpC3lP4OJfiyk1ZcX
US0bs4Hu8CyitLTm+UIY7+Yfjov5Iye5gjZ4/KnTQndT9uPPmpBNE1xNfrRsaM2j
ZYt4rRAUvExKmVUE/x2Z5FxGHmCgw6u/2DEzS/yR8B5sk7SjFrQbTGFndW5hIDxn
bnVib3lAc3RhcmZveC5vcmc+iFYEExECABYFAjp47IEECwoEAwMVAwIDFgIBAheA
AAoJEBc0OAlJqLdjGNMAn0JBGXpsfdwPpDJlnU8C0hVxLGZyAKCderbZzS1YXkdB
WXwkqXuvZ6mjZLkCDQQ6eOyhEAgAwtcGa52r71LZJryOkpE/spACxlCfQyAIrQFC
bT9h5aJ5uzmUe8F8446Ex+yGqk+uvxLKFuIpO8iqkbKoVEPC0lneH/1msV8begii
sz9VjFD+HEtbmSWU9SG5/Dzf6GV6anSWanzP+kZ9rDX6GgUyxy1LSJSUjyTOaxy2
+jSrbaf/HDmG7NVuRy40Yql2fgpvC8zrb5C3kjUY5c4CZTUnTSdE7xlDD2d4+xL/
3KA2JFTU98kKVp23nn7NVfaQbUCg7mqa3iEjTS5WbRH+yaLga4s0WfNXjadbCjfl
jR5ZtnfPfm4dNNtSpbNf8AYL0H6R/dW9d3NtL8785XoqAwREgwADBQgAiWBsrxYC
gImk6tycqUyeHKf2y+jwoXYGkNc1gM2uMcAeculE/q8eA9chF+aL3fkGr4/JsVUL
FGD6cc5Uz+QSFZQNU522SVAa8kqM+GiuFm7vszn3j3Y4KaLAYTb6tr/Zi4BymGqg
oPEcAhvCGX/CcktFaU21pFDihrmete3j6SrGYSISWtVP9v1SDg+ooXw6FnrT46kd
8skCCTEEEA9tfwSOxyNKuAIA0BWQuskmRwaovJSswusZ7VfDojTQm7dthLobj6A1
l9I9mssTElKKslx0dGDyJ1mCrLtBzNHV1br0rck9rGVcJD6K5s5D8wgyhoYPe3NZ
8K4jAiogvnaiRIhGBBgRAgAGBQI6eOyhAAoJEBc0OAlJqLdjMWYAoLYCWUmd21fl
YHa2CuCBshplXI9sAKCXSy1eGBA1a9LQUhH6JoYJ1e6WpQ==
=cjF6
-----END PGP PUBLIC KEY BLOCK-----

97
etc/palettes.rc Normal file
View File

@ -0,0 +1,97 @@
#
# palettes.rc - lots of sample dmg palettes
# copy the one you want to use into your gnuboy.rc
#
# New default palette for version 1.0.
set dmg_bgp 0x98d0e0 0x68a0b0 0x60707C 0x2C3C3C
set dmg_wndp 0x98d0e0 0x68a0b0 0x60707C 0x2C3C3C
set dmg_obp0 0x98d0e0 0x68a0b0 0x60707C 0x2C3C3C
set dmg_obp1 0x98d0e0 0x68a0b0 0x60707C 0x2C3C3C
# Old default palette from 0.8.0 thru 0.9.13.
# This was designed for use on a laptop display,
# so it's probably way too yellowish.
set dmg_bgp 0x78f0f0 0x58b8b8 0x487878 0x184848
set dmg_wndp 0x78f0f0 0x58b8b8 0x487878 0x184848
set dmg_obp0 0x78f0f0 0x58b8b8 0x487878 0x184848
set dmg_obp1 0x78f0f0 0x58b8b8 0x487878 0x184848
# Old dim grayscale.
set dmg_bgp 0xc0c0c0 0x808080 0x404040 0x000000
set dmg_wndp 0xc0c0c0 0x808080 0x404040 0x000000
set dmg_obp0 0xc0c0c0 0x808080 0x404040 0x000000
set dmg_obp1 0xc0c0c0 0x808080 0x404040 0x000000
# Light grayscale.
set dmg_bgp 0xffffff 0xc0c0c0 0x808080 0x404040
set dmg_wndp 0xffffff 0xc0c0c0 0x808080 0x404040
set dmg_obp0 0xffffff 0xc0c0c0 0x808080 0x404040
set dmg_obp1 0xffffff 0xc0c0c0 0x808080 0x404040
# Full contrast grayscale.
set dmg_bgp 0xffffff 0xaaaaaa 0x555555 0x000000
set dmg_wndp 0xffffff 0xaaaaaa 0x555555 0x000000
set dmg_obp0 0xffffff 0xaaaaaa 0x555555 0x000000
set dmg_obp1 0xffffff 0xaaaaaa 0x555555 0x000000
# Debug palette.
# Each of the four layers is colored differently,
# making it easier to debug visual glitches in roms
# that use them all together cleverly.
set dmg_bgp 0xf898c8 0xf83098 0xc80060 0x600030
set dmg_wndp 0xd0c0c0 0xa88080 0x785050 0x382828
set dmg_obp0 0x9898f8 0x3030f8 0x0000c8 0x000060
set dmg_obp1 0xc8f898 0x98f830 0x60c800 0x306000
# Sprites standout.
# Similar to above, but the colors are more subdued and
# the window and background are the same. This palette
# may actually be suitable for playing some games...
set dmg_bgp 0xd0c0c0 0xa88080 0x785050 0x382828
set dmg_wndp 0xd0c0c0 0xa88080 0x785050 0x382828
set dmg_obp0 0xc8e0f8 0x90a8e8 0x4878a8 0x183850
set dmg_obp1 0x98b8f8 0x3050f8 0x2040a8 0x002060
# LCD yellows and grays.
# An earlier version of the new default palette.
set dmg_bgp 0x88e0f0 0x68a8b8 0x586878 0x283838
set dmg_wndp 0x88e0f0 0x68a8b8 0x586878 0x283838
set dmg_obp0 0x88e0f0 0x68a8b8 0x586878 0x283838
set dmg_obp1 0x88e0f0 0x68a8b8 0x586878 0x283838
# LCD yellowscale.
# And another similar one.
set dmg_bgp 0x88e0f0 0x68a8b8 0x486878 0x203838
set dmg_wndp 0x88e0f0 0x68a8b8 0x486878 0x203838
set dmg_obp0 0x88e0f0 0x68a8b8 0x486878 0x203838
set dmg_obp1 0x88e0f0 0x68a8b8 0x486878 0x203838
# Slightly colorful.
# Not just a plain lightness gradient, but some change
# in hue as well. Looks ok with some games; designed in
# particular for the FFL series.
set dmg_bgp 0x98e0f8 0x78a0c0 0x747080 0x604038
set dmg_wndp 0x98e0f8 0x78a0c0 0x747080 0x604038
set dmg_obp0 0x98e0f8 0x78a0c0 0x747080 0x604038
set dmg_obp1 0x98e0f8 0x78a0c0 0x747080 0x604038
# Optionally use these with the above palette to make
# sprites stand out a bit.
set dmg_obp0 0x98e0f8 0x5090c0 0x507898 0x583838
set dmg_obp1 0x98e0f8 0x5090c0 0x686078 0x383838
# R-Type 1 palette from R-Type DX
set dmg_bgp 0xc0ffff 0x408080 0x204040 0x000000
set dmg_wndp 0xc0ffff 0x408080 0x204040 0x000000
set dmg_obp0 0xc0ffff 0x408080 0x204040 0x000000
set dmg_obp1 0xc0ffff 0x408080 0x204040 0x000000

49
etc/sample.rc Normal file
View File

@ -0,0 +1,49 @@
#
# Sample rc file for gnuboy
#
# You may want to rename this to gnuboy.rc to use it.
# Lines that begin with # are comments.
#
# Some keybindings
bind q quit
bind r reset
bind d +a
bind s +b
# Normal speed/fast forward
# Note that these only work with sound disabled
bind - "set framelen 16743"
bind + "set framelen 0"
# Set video mode to 400x300x16bpp
set vmode 400 300 16
# Enable full 2x screen scaling
# This will not work if your video mode is smaller than 320x288!
set scale 2
set density 2
# Enable stereo sound. Doesn't work on all systems
#set stereo true
# Path settings for DOS port - uncomment to use them!
#set rcpath c:/gnuboy
#set savedir c:/gnuboy/saves
#
# You get the idea by now...
# See the README for more information on rc commands and vars.
#

60
events.c Normal file
View File

@ -0,0 +1,60 @@
/*
* events.c
*
* Event queue.
*/
#include "input.h"
char keystates[MAX_KEYS];
int nkeysdown;
#define MAX_EVENTS 32
static event_t eventqueue[MAX_EVENTS];
static int eventhead, eventpos;
int ev_postevent(event_t *ev)
{
int nextevent;
nextevent = (eventhead+1)%MAX_EVENTS;
if (nextevent == eventpos)
return 0;
eventqueue[eventhead] = *ev;
eventhead = nextevent;
return 1;
}
int ev_getevent(event_t *ev)
{
if (eventpos == eventhead)
{
ev->type = EV_NONE;
return 0;
}
*ev = eventqueue[eventpos];
eventpos = (eventpos+1)%MAX_EVENTS;
if (ev->type == EV_PRESS)
{
keystates[ev->code] = 1;
nkeysdown++;
}
if (ev->type == EV_RELEASE)
{
keystates[ev->code] = 0;
nkeysdown--;
if (nkeysdown < 0) nkeysdown = 0;
}
return 1;
}

43
exports.c Normal file
View File

@ -0,0 +1,43 @@
#include <stdlib.h>
#include "rc.h"
extern rcvar_t rcfile_exports[], emu_exports[], loader_exports[],
lcd_exports[], rtc_exports[], debug_exports[], sound_exports[],
vid_exports[], joy_exports[], pcm_exports[];
rcvar_t *sources[] =
{
rcfile_exports,
emu_exports,
loader_exports,
lcd_exports,
rtc_exports,
debug_exports,
sound_exports,
vid_exports,
joy_exports,
pcm_exports,
NULL
};
void init_exports()
{
rcvar_t **s = sources;
while (*s)
rc_exportvars(*(s++));
}
void show_exports()
{
int i, j;
for (i = 0; sources[i]; i++)
for (j = 0; sources[i][j].name; j++)
printf("%s\n", sources[i][j].name);
}

54
fastmem.c Normal file
View File

@ -0,0 +1,54 @@
#include "fastmem.h"
#define D 0 /* direct */
#define C 1 /* direct cgb-only */
#define R 2 /* io register */
#define S 3 /* sound register */
#define W 4 /* wave pattern */
#define F 0xFF /* fail */
const byte himask[256];
const byte hi_rmap[256] =
{
0, 0, R, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, S,
S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, S,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, C, 0, C,
0, C, C, C, C, C, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, C, C, C, C, 0, 0, 0, 0,
C, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
const byte hi_wmap[256] =
{
R, R, R, R, R, R, R, R, R, R, R, R, R, R, R, R,
S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, S,
S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, S,
S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, S,
R, R, R, R, R, R, R, R, R, R, R, R, 0, R, 0, R,
0, C, C, C, C, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, R, R, R, R, 0, 0, 0, 0,
R, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, R
};
void sound_write();
static void no_write()
{
}

99
fastmem.h Normal file
View File

@ -0,0 +1,99 @@
#ifndef __FASTMEM_H__
#define __FASTMEM_H__
#include "defs.h"
#include "mem.h"
static byte readb(int a)
{
byte *p = mbc.rmap[a>>12];
if (p) return p[a];
else return mem_read(a);
}
static void writeb(int a, byte b)
{
byte *p = mbc.wmap[a>>12];
if (p) p[a] = b;
else mem_write(a, b);
}
static int readw(int a)
{
if ((a+1) & 0xfff)
{
byte *p = mbc.rmap[a>>12];
if (p)
{
#ifdef IS_LITTLE_ENDIAN
#ifndef ALLOW_UNALIGNED_IO
if (a&1) return p[a] | (p[a+1]<<8);
#endif
return *(word *)(p+a);
#else
return p[a] | (p[a+1]<<8);
#endif
}
}
return mem_read(a) | (mem_read(a+1)<<8);
}
static void writew(int a, int w)
{
if ((a+1) & 0xfff)
{
byte *p = mbc.wmap[a>>12];
if (p)
{
#ifdef IS_LITTLE_ENDIAN
#ifndef ALLOW_UNALIGNED_IO
if (a&1)
{
p[a] = w;
p[a+1] = w >> 8;
return;
}
#endif
*(word *)(p+a) = w;
return;
#else
p[a] = w;
p[a+1] = w >> 8;
return;
#endif
}
}
mem_write(a, w);
mem_write(a+1, w>>8);
}
static byte readhi(int a)
{
return readb(a | 0xff00);
}
static void writehi(int a, byte b)
{
writeb(a | 0xff00, b);
}
#if 0
static byte readhi(int a)
{
byte (*rd)() = hi_read[a];
return rd ? rd(a) : (ram.hi[a] | himask[a]);
}
static void writehi(int a, byte b)
{
byte (*wr)() = hi_write[a];
if (wr) wr(a, b);
else ram.hi[a] = b & ~himask[a];
}
#endif
#endif

35
fb.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef __FB_H__
#define __FB_H__
#include "defs.h"
struct fb
{
byte *ptr;
int w, h;
int pelsize;
int pitch;
int indexed;
struct
{
int l, r;
} cc[4];
int yuv;
int enabled;
int dirty;
};
extern struct fb fb;
#endif

182
hw.c Normal file
View File

@ -0,0 +1,182 @@
#include "defs.h"
#include "cpu.h"
#include "hw.h"
#include "regs.h"
#include "lcd.h"
#include "mem.h"
#include "fastmem.h"
struct hw hw;
/*
* hw_interrupt changes the virtual interrupt lines included in the
* specified mask to the values the corresponding bits in i take, and
* in doing so, raises the appropriate bit of R_IF for any interrupt
* lines that transition from low to high.
*/
void hw_interrupt(byte i, byte mask)
{
byte oldif = R_IF;
i &= 0x1F & mask;
R_IF |= i & (hw.ilines ^ i);
/* FIXME - is this correct? not sure the docs understand... */
if ((R_IF & (R_IF ^ oldif) & R_IE) && cpu.ime) cpu.halt = 0;
/* if ((i & (hw.ilines ^ i) & R_IE) && cpu.ime) cpu.halt = 0; */
/* if ((i & R_IE) && cpu.ime) cpu.halt = 0; */
hw.ilines &= ~mask;
hw.ilines |= i;
}
/*
* hw_dma performs plain old memory-to-oam dma, the original dmg
* dma. Although on the hardware it takes a good deal of time, the cpu
* continues running during this mode of dma, so no special tricks to
* stall the cpu are necessary.
*/
void hw_dma(byte b)
{
int i;
addr a;
a = ((addr)b) << 8;
for (i = 0; i < 160; i++, a++)
lcd.oam.mem[i] = readb(a);
}
void hw_hdma_cmd(byte c)
{
int cnt;
addr sa;
int da;
/* Begin or cancel HDMA */
if ((hw.hdma|c) & 0x80)
{
hw.hdma = c;
R_HDMA5 = c & 0x7f;
return;
}
/* Perform GDMA */
sa = ((addr)R_HDMA1 << 8) | (R_HDMA2&0xf0);
da = 0x8000 | ((int)(R_HDMA3&0x1f) << 8) | (R_HDMA4&0xf0);
cnt = ((int)c)+1;
/* FIXME - this should use cpu time! */
/*cpu_timers(102 * cnt);*/
cnt <<= 4;
while (cnt--)
writeb(da++, readb(sa++));
R_HDMA1 = sa >> 8;
R_HDMA2 = sa & 0xF0;
R_HDMA3 = 0x1F & (da >> 8);
R_HDMA4 = da & 0xF0;
R_HDMA5 = 0xFF;
}
void hw_hdma()
{
int cnt;
addr sa;
int da;
sa = ((addr)R_HDMA1 << 8) | (R_HDMA2&0xf0);
da = 0x8000 | ((int)(R_HDMA3&0x1f) << 8) | (R_HDMA4&0xf0);
cnt = 16;
while (cnt--)
writeb(da++, readb(sa++));
R_HDMA1 = sa >> 8;
R_HDMA2 = sa & 0xF0;
R_HDMA3 = 0x1F & (da >> 8);
R_HDMA4 = da & 0xF0;
R_HDMA5--;
hw.hdma--;
}
/*
* pad_refresh updates the P1 register from the pad states, generating
* the appropriate interrupts (by quickly raising and lowering the
* interrupt line) if a transition has been made.
*/
void pad_refresh()
{
byte oldp1;
oldp1 = R_P1;
R_P1 &= 0x30;
R_P1 |= 0xc0;
if (!(R_P1 & 0x10))
R_P1 |= (hw.pad & 0x0F);
if (!(R_P1 & 0x20))
R_P1 |= (hw.pad >> 4);
R_P1 ^= 0x0F;
if (oldp1 & ~R_P1 & 0x0F)
{
hw_interrupt(IF_PAD, IF_PAD);
hw_interrupt(0, IF_PAD);
}
}
/*
* These simple functions just update the state of a button on the
* pad.
*/
void pad_press(byte k)
{
if (hw.pad & k)
return;
hw.pad |= k;
pad_refresh();
}
void pad_release(byte k)
{
if (!(hw.pad & k))
return;
hw.pad &= ~k;
pad_refresh();
}
void pad_set(byte k, int st)
{
st ? pad_press(k) : pad_release(k);
}
void hw_reset()
{
hw.ilines = hw.pad = 0;
memset(ram.hi, 0, sizeof ram.hi);
R_P1 = 0xFF;
R_LCDC = 0x91;
R_BGP = 0xFC;
R_OBP0 = 0xFF;
R_OBP1 = 0xFF;
R_SVBK = 0x01;
R_HDMA5 = 0xFF;
R_VBK = 0xFE;
}

40
hw.h Normal file
View File

@ -0,0 +1,40 @@
#ifndef __HW_H__
#define __HW_H__
#include "defs.h"
#define PAD_RIGHT 0x01
#define PAD_LEFT 0x02
#define PAD_UP 0x04
#define PAD_DOWN 0x08
#define PAD_A 0x10
#define PAD_B 0x20
#define PAD_SELECT 0x40
#define PAD_START 0x80
#define IF_VBLANK 0x01
#define IF_STAT 0x02
#define IF_TIMER 0x04
#define IF_SERIAL 0x08
#define IF_PAD 0x10
struct hw
{
byte ilines;
byte pad;
int cgb, gba;
int hdma;
};
extern struct hw hw;
#endif

512
inflate.c Normal file
View File

@ -0,0 +1,512 @@
/* Slightly modified from its original form so as not to exit the
* program on errors. The resulting file remains in the public
* domain for all to use. */
/* --- GZIP file format uncompression routines --- */
/* The following routines (notably the unzip()) function below
* uncompress gzipped data. They are terribly slow at the task, but
* it is presumed that they work reasonably well. They don't do any
* error checking, but they're probably not too vulnerable to buggy
* data either. Another important limitation (but it would be pretty
* easy to get around) is that the data must reside in memory, it is
* not read as a stream. They have been very little tested. Anyway,
* whatever these functions are good for, I put them in the public
* domain. -- David Madore <david.madore@ens.fr> 1999/11/21 */
static unsigned int
peek_bits (const unsigned char *data, long p, int q)
/* Read q bits starting from bit p from the data pointed to by
* data. Data is in little-endian format. */
{
unsigned int answer;
int cnt; /* Number of bits already placed in answer */
char ob, lb; /* Offset and length of bit field within current byte */
answer = 0;
for ( cnt=0 ; cnt<q ; /* cnt updated in body */ )
{
ob = (p+cnt)%8;
lb = 8-ob;
if ( cnt+lb > q )
lb = q-cnt;
answer |= ((unsigned int)((data[(p+cnt)/8]>>ob)&((1U<<lb)-1)))<<cnt;
cnt += lb;
}
return answer;
}
static unsigned int
read_bits (const unsigned char *data, long *p, int q)
/* Read q bits as per peek_bits(), but also increase p by q. */
{
unsigned int answer;
answer = peek_bits (data, *p, q);
*p += q;
return answer;
}
static void
make_code_table (const char size_table[], int table_length,
unsigned int code_table[], int maxbits)
/* Make a code table from a length table. See rfc1951, section
* 3.2.2, for details on what this means. The size_table
* contains the length of the Huffman codes for each letter, and
* the code_table receives the computed codes themselves.
* table_length is the size of the tables (alphabet length) and
* maxbits is the maximal allowed code length. */
{
int i, j;
unsigned int code;
code = 0;
for ( i=1 ; i<=maxbits ; i++ )
{
for ( j=0 ; j<table_length ; j++ )
{
if ( size_table[j]==i )
code_table[j] = code++;
}
code <<= 1;
}
}
static int
decode_one (const unsigned char *data, long *p,
const char size_table[], int table_length,
const unsigned int code_table[], int maxbits)
/* Decode one alphabet letter from the data, starting at bit p
* (which will be increased by the appropriate amount) using
* size_table and code_table to decipher the Huffman encoding. */
{
unsigned int code;
int i, j;
code = 0;
/* Read as many bits as are likely to be necessary - backward, of
* course. */
for ( i=0 ; i<maxbits ; i++ )
code = (code<<1) + peek_bits (data, (*p)+i, 1);
/* Now examine each symbol of the table to find one that matches the
* first bits of the code read. */
for ( j=0 ; j<table_length ; j++ )
{
if ( size_table[j]
&& ( (code>>(maxbits-size_table[j])) == code_table[j] ) )
{
*p += size_table[j];
return j;
}
}
return -1;
}
/* I don't know what these should be. The rfc1951 doesn't seem to say
* (it only mentions them in the last paragraph of section 3.2.1). 15
* is almost certainly safe, and it is the largest I can put given the
* constraints on the size of integers in the C standard. */
#define CLEN_MAXBITS 15
#define HLIT_MAXBITS 15
#define HDIST_MAXBITS 15
/* The magical table sizes... */
#define CLEN_TSIZE 19
#define HLIT_TSIZE 288
#define HDIST_TSIZE 30
static int
get_tables (const unsigned char *data, long *p,
char hlit_size_table[HLIT_TSIZE],
unsigned int hlit_code_table[HLIT_TSIZE],
char hdist_size_table[HDIST_TSIZE],
unsigned int hdist_code_table[HDIST_TSIZE])
/* Fill the Huffman tables (first the code lengths table, and
* then, using it, the literal/length table and the distance
* table). See section 3.2.7 of rfc1951 for details. */
{
char hlit, hdist, hclen;
const int clen_weird_tangle[CLEN_TSIZE]
= { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
char clen_size_table[CLEN_TSIZE];
unsigned int clen_code_table[CLEN_TSIZE];
int j;
unsigned int b;
int remainder; /* See note at end of section 3.2.7 of rfc1951. */
char rem_val;
hlit = read_bits (data, p, 5);
hdist = read_bits (data, p, 5);
hclen = read_bits (data, p, 4);
for ( j=0 ; j<4+hclen ; j++ )
clen_size_table[clen_weird_tangle[j]]
= read_bits (data, p, 3);
for ( ; j<CLEN_TSIZE ; j++ )
clen_size_table[clen_weird_tangle[j]] = 0;
make_code_table (clen_size_table, CLEN_TSIZE,
clen_code_table, CLEN_MAXBITS);
remainder = 0;
rem_val = 0;
for ( j=0 ; j<257+hlit ; j++ )
{
b = decode_one (data, p, clen_size_table, CLEN_TSIZE,
clen_code_table, CLEN_MAXBITS);
if ( b<0 ) return -1;
if ( b<16 )
hlit_size_table[j] = b;
else if ( b == 16 )
{
int k, l;
k = read_bits (data, p, 2);
for ( l=0 ; l<k+3 && j+l<257+hlit ; l++ )
hlit_size_table[j+l] = hlit_size_table[j-1];
j += l-1;
remainder = k+3-l; /* THIS IS SO UGLY! */
rem_val = hlit_size_table[j-1];
}
else if ( b == 17 )
{
int k, l;
k = read_bits (data, p, 3);
for ( l=0 ; l<k+3 && j+l<257+hlit ; l++ )
hlit_size_table[j+l] = 0;
j += l-1;
remainder = k+3-l;
rem_val = 0;
}
else if ( b == 18 )
{
int k, l;
k = read_bits (data, p, 7);
for ( l=0 ; l<k+11 && j+l<257+hlit ; l++ )
hlit_size_table[j+l] = 0;
j += l-1;
remainder = k+11-l;
rem_val = 0;
}
}
for ( ; j<HLIT_TSIZE ; j++ )
hlit_size_table[j] = 0;
make_code_table (hlit_size_table, HLIT_TSIZE,
hlit_code_table, HLIT_MAXBITS);
for ( j=0 ; j<remainder ; j++ )
hdist_size_table[j] = rem_val;
for ( ; j<1+hdist ; j++ )
/* Can you spell: ``copy-paste''? */
{
b = decode_one (data, p, clen_size_table, CLEN_TSIZE,
clen_code_table, CLEN_MAXBITS);
if ( b<0 ) return -1;
if ( b<16 )
hdist_size_table[j] = b;
else if ( b == 16 )
{
int k, l;
k = read_bits (data, p, 2);
for ( l=0 ; l<k+3 && j+l<1+hdist ; l++ )
hdist_size_table[j+l] = hdist_size_table[j-1];
j += l-1;
}
else if ( b == 17 )
{
int k, l;
k = read_bits (data, p, 3);
for ( l=0 ; l<k+3 && j+l<1+hdist ; l++ )
hdist_size_table[j+l] = 0;
j += l-1;
}
else if ( b == 18 )
{
int k, l;
k = read_bits (data, p, 7);
for ( l=0 ; l<k+11 && j+l<1+hdist ; l++ )
hdist_size_table[j+l] = 0;
j += l-1;
}
}
for ( ; j<HDIST_TSIZE ; j++ )
hdist_size_table[j] = 0;
make_code_table (hdist_size_table, HDIST_TSIZE,
hdist_code_table, HDIST_MAXBITS);
return 0;
}
/* The (circular) output buffer. This lets us track
* backreferences. */
/* Minimal buffer size. Also the only useful value. */
#define BUFFER_SIZE 32768
/* Pointer to the character to be added to the buffer */
static unsigned int buffer_ptr = 0;
/* The buffer itself */
static unsigned char buffer[BUFFER_SIZE];
static void
pushout (unsigned char ch)
/* Store one byte in the output buffer so it may be retrieved if
* it is referenced again. */
{
buffer[buffer_ptr++] = ch;
buffer_ptr %= BUFFER_SIZE;
}
static unsigned char
pushin (unsigned int dist)
/* Retrieve one byte, dist bytes away, from the output buffer. */
{
return buffer[(buffer_ptr+(BUFFER_SIZE-dist))%BUFFER_SIZE];
}
static int
get_data (const unsigned char *data, long *p,
const char hlit_size_table[HLIT_TSIZE],
const unsigned int hlit_code_table[HLIT_TSIZE],
const char hdist_size_table[HDIST_TSIZE],
const unsigned int hdist_code_table[HDIST_TSIZE],
void (* callback) (unsigned char d))
/* Do the actual uncompressing. Call callback on each character
* uncompressed. */
{
unsigned int b;
while ( 1 ) {
b = decode_one (data, p, hlit_size_table, HLIT_TSIZE,
hlit_code_table, HLIT_MAXBITS);
if ( b<0 ) return -1;
if ( b < 256 )
/* Literal */
{
pushout ((unsigned char) b);
callback ((unsigned char) b);
}
else if ( b == 256 )
/* End of block */
return 0;
else if ( b >= 257 )
/* Back reference */
{
unsigned int bb;
unsigned int length, dist;
unsigned int l;
switch ( b )
{
case 257: length = 3; break;
case 258: length = 4; break;
case 259: length = 5; break;
case 260: length = 6; break;
case 261: length = 7; break;
case 262: length = 8; break;
case 263: length = 9; break;
case 264: length = 10; break;
case 265: length = 11 + read_bits (data, p, 1); break;
case 266: length = 13 + read_bits (data, p, 1); break;
case 267: length = 15 + read_bits (data, p, 1); break;
case 268: length = 17 + read_bits (data, p, 1); break;
case 269: length = 19 + read_bits (data, p, 2); break;
case 270: length = 23 + read_bits (data, p, 2); break;
case 271: length = 27 + read_bits (data, p, 2); break;
case 272: length = 31 + read_bits (data, p, 2); break;
case 273: length = 35 + read_bits (data, p, 3); break;
case 274: length = 43 + read_bits (data, p, 3); break;
case 275: length = 51 + read_bits (data, p, 3); break;
case 276: length = 59 + read_bits (data, p, 3); break;
case 277: length = 67 + read_bits (data, p, 4); break;
case 278: length = 83 + read_bits (data, p, 4); break;
case 279: length = 99 + read_bits (data, p, 4); break;
case 280: length = 115 + read_bits (data, p, 4); break;
case 281: length = 131 + read_bits (data, p, 5); break;
case 282: length = 163 + read_bits (data, p, 5); break;
case 283: length = 195 + read_bits (data, p, 5); break;
case 284: length = 227 + read_bits (data, p, 5); break;
case 285: length = 258; break;
default:
return -1;
}
bb = decode_one (data, p, hdist_size_table, HDIST_TSIZE,
hdist_code_table, HDIST_MAXBITS);
switch ( bb )
{
case 0: dist = 1; break;
case 1: dist = 2; break;
case 2: dist = 3; break;
case 3: dist = 4; break;
case 4: dist = 5 + read_bits (data, p, 1); break;
case 5: dist = 7 + read_bits (data, p, 1); break;
case 6: dist = 9 + read_bits (data, p, 2); break;
case 7: dist = 13 + read_bits (data, p, 2); break;
case 8: dist = 17 + read_bits (data, p, 3); break;
case 9: dist = 25 + read_bits (data, p, 3); break;
case 10: dist = 33 + read_bits (data, p, 4); break;
case 11: dist = 49 + read_bits (data, p, 4); break;
case 12: dist = 65 + read_bits (data, p, 5); break;
case 13: dist = 97 + read_bits (data, p, 5); break;
case 14: dist = 129 + read_bits (data, p, 6); break;
case 15: dist = 193 + read_bits (data, p, 6); break;
case 16: dist = 257 + read_bits (data, p, 7); break;
case 17: dist = 385 + read_bits (data, p, 7); break;
case 18: dist = 513 + read_bits (data, p, 8); break;
case 19: dist = 769 + read_bits (data, p, 8); break;
case 20: dist = 1025 + read_bits (data, p, 9); break;
case 21: dist = 1537 + read_bits (data, p, 9); break;
case 22: dist = 2049 + read_bits (data, p, 10); break;
case 23: dist = 3073 + read_bits (data, p, 10); break;
case 24: dist = 4097 + read_bits (data, p, 11); break;
case 25: dist = 6145 + read_bits (data, p, 11); break;
case 26: dist = 8193 + read_bits (data, p, 12); break;
case 27: dist = 12289 + read_bits (data, p, 12); break;
case 28: dist = 16385 + read_bits (data, p, 13); break;
case 29: dist = 24577 + read_bits (data, p, 13); break;
default:
return -1;
}
for ( l=0 ; l<length ; l++ )
{
unsigned char ch;
ch = pushin (dist);
pushout (ch);
callback (ch);
}
}
}
return 0;
}
static int
inflate (const unsigned char *data, long *p,
void (* callback) (unsigned char d))
/* Main uncompression function for the deflate method */
{
char blast, btype;
char hlit_size_table[HLIT_TSIZE];
unsigned int hlit_code_table[HLIT_TSIZE];
char hdist_size_table[HDIST_TSIZE];
unsigned int hdist_code_table[HDIST_TSIZE];
again:
blast = read_bits (data, p, 1);
btype = read_bits (data, p, 2);
if ( btype == 1 || btype == 2 )
{
if ( btype == 2 )
{
/* Dynamic Huffman tables */
if (get_tables (data, p,
hlit_size_table, hlit_code_table,
hdist_size_table, hdist_code_table) < 0) return -1;
}
else
/* Fixed Huffman codes */
{
int j;
for ( j=0 ; j<144 ; j++ )
hlit_size_table[j] = 8;
for ( ; j<256 ; j++ )
hlit_size_table[j] = 9;
for ( ; j<280 ; j++ )
hlit_size_table[j] = 7;
for ( ; j<HLIT_TSIZE ; j++ )
hlit_size_table[j] = 8;
make_code_table (hlit_size_table, HLIT_TSIZE,
hlit_code_table, HLIT_MAXBITS);
for ( j=0 ; j<HDIST_TSIZE ; j++ )
hdist_size_table[j] = 5;
make_code_table (hdist_size_table, HDIST_TSIZE,
hdist_code_table, HDIST_MAXBITS);
}
if (get_data (data, p,
hlit_size_table, hlit_code_table,
hdist_size_table, hdist_code_table,
callback) < 0) return -1;;
}
else if ( btype == 0 )
/* Non compressed block */
{
unsigned int len, nlen;
unsigned int l;
unsigned char b;
*p = (*p+7)/8; /* Jump to next byte boundary */
len = read_bits (data, p, 16);
nlen = read_bits (data, p, 16);
for ( l=0 ; l<len ; l++ )
{
b = read_bits (data, p, 8);
pushout (b);
callback (b);
}
}
else
{
return -1;
}
if ( ! blast )
goto again;
return 0;
}
int
unzip (const unsigned char *data, long *p,
void (* callback) (unsigned char d))
/* Uncompress gzipped data. data is a pointer to the data, p is
* a pointer to a long that is initialized to 0 (unless for some
* reason you want to start uncompressing further down the data),
* and callback is a function taking an unsigned char and
* returning void that will be called successively for every
* uncompressed byte. */
{
unsigned char cm, flg;
if ( read_bits (data, p, 8) != 0x1f
|| read_bits (data, p, 8) != 0x8b )
{
return -1;
}
cm = read_bits (data, p, 8);
if ( cm != 0x8 )
{
return -1;
}
flg = read_bits (data, p, 8);
if ( flg & 0xe0 )
/* fprintf (stderr, "Warning: unknown bits are set in flags.\n") */ ;
read_bits (data, p, 32); /* Ignore modification time */
read_bits (data, p, 8); /* Ignore extra flags */
read_bits (data, p, 8); /* Ignore OS type */
if ( flg & 0x4 )
{
/* Skip over extra data */
unsigned int xlen;
xlen = read_bits (data, p, 16);
*p += ((long)xlen)*8;
}
if ( flg & 0x8 )
{
/* Skip over file name */
while ( read_bits (data, p, 8) );
}
if ( flg & 0x10 )
{
/* Skip over comment */
while ( read_bits (data, p, 8) );
}
if ( flg & 0x2 )
/* Ignore CRC16 */
read_bits (data, p, 16);
return inflate (data, p, callback);
/* CRC32 and ISIZE are at the end. We don't even bother to look at
* them. */
}

140
input.h Normal file
View File

@ -0,0 +1,140 @@
/*
* input.h
*
* Definitions for input device stuff - buttons, keys, etc.
*/
#define M_IGNORE 0
#define M_RELATIVE 1
#define M_ABSOLUTE 2
#define K_SHIFT 0x101
#define K_CTRL 0x102
#define K_ALT 0x103
#define K_UP 0x10a
#define K_DOWN 0x10b
#define K_RIGHT 0x10c
#define K_LEFT 0x10d
#define K_ENTER '\r'
#define K_TAB '\t'
#define K_SPACE ' '
#define K_BS 010
#define K_DEL 127
#define K_INS 0x121
#define K_HOME 0x122
#define K_END 0x123
#define K_PRIOR 0x124
#define K_NEXT 0x125
#define K_ESC 033
#define K_SYSRQ 0x1fe
#define K_PAUSE 0x1ff
#define K_CAPS 0x1f1
#define K_NUMLOCK 0x1f2
#define K_SCROLL 0x1f3
#define K_MINUS '-'
#define K_EQUALS '='
#define K_TILDE '~'
#define K_SLASH '/'
#define K_BSLASH '\\'
#define K_SEMI ';'
#define K_QUOTE '\''
#define K_F1 0x131
#define K_F2 0x132
#define K_F3 0x133
#define K_F4 0x134
#define K_F5 0x135
#define K_F6 0x136
#define K_F7 0x137
#define K_F8 0x138
#define K_F9 0x139
#define K_F10 0x13a
#define K_F11 0x13b
#define K_F12 0x13c
#define K_NUM0 0x140
#define K_NUM1 0x141
#define K_NUM2 0x142
#define K_NUM3 0x143
#define K_NUM4 0x144
#define K_NUM5 0x145
#define K_NUM6 0x146
#define K_NUM7 0x147
#define K_NUM8 0x148
#define K_NUM9 0x149
#define K_NUMPLUS 0x14a
#define K_NUMMINUS 0x14b
#define K_NUMMUL 0x14c
#define K_NUMDIV 0x14d
#define K_NUMDOT 0x14e
#define K_NUMENTER 0x14f
#define K_MOUSE0 0x1a0
#define K_MOUSE1 0x1a1
#define K_MOUSE2 0x1a2
#define K_MOUSE3 0x1a3
#define K_MOUSE4 0x1a4
#define K_JOY0 0x1b0
#define K_JOY1 0x1b1
#define K_JOY2 0x1b2
#define K_JOY3 0x1b3
#define K_JOY4 0x1b4
#define K_JOY5 0x1b5
#define K_JOY6 0x1b6
#define K_JOY7 0x1b7
#define K_JOY8 0x1b8
#define K_JOY9 0x1b9
#define K_JOY10 0x1ba
#define K_JOY11 0x1bb
#define K_JOY12 0x1bc
#define K_JOY13 0x1bd
#define K_JOY14 0x1be
#define K_JOY15 0x1bf
#define K_JOYUP 0x1ca
#define K_JOYDOWN 0x1cb
#define K_JOYRIGHT 0x1cc
#define K_JOYLEFT 0x1cd
#define MAX_KEYS 0x200
typedef struct keytable_s
{
char *name;
int code;
} keytable_t;
extern keytable_t keytable[];
extern char keystates[MAX_KEYS];
extern int nkeysdown;
int k_keycode(char *name);
char *k_keyname(int code);
typedef struct event_s
{
int type;
int code;
int dx, dy;
int x, y;
} event_t;
#define EV_NONE 0
#define EV_PRESS 1
#define EV_RELEASE 2
#define EV_REPEAT 3
#define EV_MOUSE 4
int ev_postevent(event_t *ev);
int ev_getevent(event_t *ev);

238
install-sh Executable file
View File

@ -0,0 +1,238 @@
#! /bin/sh
#
# install - install a program, script, or datafile
# This comes from X11R5.
#
# 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.
#
# 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}"
tranformbasename=""
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
true
fi
if [ x"$dir_arg" != x ]; then
dst=$src
src=""
if [ -d $dst ]; then
instcmd=:
else
instcmd=mkdir
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 -o -d $src ]
then
true
else
echo "install: $src does not exist"
exit 1
fi
if [ x"$dst" = x ]
then
echo "install: no destination specified"
exit 1
else
true
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
true
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
true
fi
pathcomp="${pathcomp}/"
done
fi
if [ x"$dir_arg" != x ]
then
$doit $instcmd $dst &&
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; 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
true
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 true;fi &&
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
# Now rename the file to the real destination.
$doit $rmcmd -f $dstdir/$dstfile &&
$doit $mvcmd $dsttmp $dstdir/$dstfile
fi &&
exit 0

156
keytable.c Normal file
View File

@ -0,0 +1,156 @@
/*
* keytable.c
*
* Key names to keycodes mapping.
*/
#include <stdlib.h>
#include <string.h>
#include "input.h"
/* keytable - Mapping of key names to codes, and back. A single code
can have more than one name, in which case the first will be used
when saving config, but any may be used in setting config. */
keytable_t keytable[] =
{
{ "shift", K_SHIFT },
{ "ctrl", K_CTRL },
{ "alt", K_ALT },
{ "up", K_UP },
{ "down", K_DOWN },
{ "right", K_RIGHT },
{ "left", K_LEFT },
{ "enter", K_ENTER },
{ "tab", K_TAB },
{ "space", K_SPACE },
{ "bs", K_BS },
{ "backspace", K_BS }, /* dup */
{ "del", K_DEL },
{ "delete", K_DEL }, /* dup */
{ "ins", K_INS },
{ "insert", K_INS }, /* dup */
{ "home", K_HOME },
{ "end", K_END },
{ "prior", K_PRIOR },
{ "next", K_NEXT },
{ "pgup", K_PRIOR }, /* duplicate for pgup/pgdn fans */
{ "pgdn", K_NEXT }, /* ditto */
{ "esc", K_ESC },
{ "escape", K_ESC }, /* dup */
{ "pause", K_PAUSE },
{ "caps", K_CAPS },
{ "capslock", K_CAPS }, /* dup */
{ "numlock", K_NUMLOCK },
{ "scroll", K_SCROLL },
{ "minus", K_MINUS },
{ "_", K_MINUS }, /* dup */
{ "equals", K_EQUALS },
{ "plus", K_EQUALS }, /* dup */
{ "+", K_EQUALS }, /* dup */
{ "tilde", K_TILDE },
{ "backquote", K_TILDE }, /* dup */
{ "`", K_TILDE }, /* dup */
{ "slash", K_SLASH },
{ "question", K_SLASH }, /* dup */
{ "?", K_SLASH }, /* dup */
{ "bslash", K_BSLASH },
{ "backslash", K_BSLASH }, /* dup */
{ "pipe", K_BSLASH }, /* dup */
{ "|", K_BSLASH }, /* dup */
{ "semi", K_SEMI },
{ "semicolon", K_SEMI }, /* dup */
{ "quote", K_QUOTE },
{ "f1", K_F1 },
{ "f2", K_F2 },
{ "f3", K_F3 },
{ "f4", K_F4 },
{ "f5", K_F5 },
{ "f6", K_F6 },
{ "f7", K_F7 },
{ "f8", K_F8 },
{ "f9", K_F9 },
{ "f10", K_F10 },
{ "f11", K_F11 },
{ "f12", K_F12 },
{ "num0", K_NUM0 },
{ "num1", K_NUM1 },
{ "num2", K_NUM2 },
{ "num3", K_NUM3 },
{ "num4", K_NUM4 },
{ "num5", K_NUM5 },
{ "num6", K_NUM6 },
{ "num7", K_NUM7 },
{ "num8", K_NUM8 },
{ "num9", K_NUM9 },
{ "numplus", K_NUMPLUS },
{ "numminus", K_NUMMINUS },
{ "nummul", K_NUMMUL },
{ "numdiv", K_NUMDIV },
{ "numdot", K_NUMDOT },
{ "numenter", K_NUMENTER },
/* Note that these are not presently used... */
{ "mouse0", K_MOUSE0 },
{ "mouse1", K_MOUSE1 },
{ "mouse2", K_MOUSE2 },
{ "mouse3", K_MOUSE3 },
{ "mouse4", K_MOUSE4 },
{ "joyleft", K_JOYLEFT },
{ "joyright", K_JOYRIGHT },
{ "joyup", K_JOYUP },
{ "joydown", K_JOYDOWN },
{ "joy0", K_JOY0 },
{ "joy1", K_JOY1 },
{ "joy2", K_JOY2 },
{ "joy3", K_JOY3 },
{ "joy4", K_JOY4 },
{ "joy5", K_JOY5 },
{ "joy6", K_JOY6 },
{ "joy7", K_JOY7 },
{ "joy8", K_JOY8 },
{ "joy9", K_JOY9 },
{ "joy10", K_JOY10 },
{ "joy11", K_JOY11 },
{ "joy12", K_JOY12 },
{ "joy13", K_JOY13 },
{ "joy14", K_JOY14 },
{ "joy15", K_JOY15 },
{ NULL, 0 }
};
int k_keycode(char *name)
{
keytable_t *key;
for (key = keytable; key->name; key++)
if (!strcasecmp(key->name, name))
return key->code;
if (strlen(name) == 1)
return tolower(name[0]);
return 0;
}
char *k_keyname(int code)
{
keytable_t *key;
for (key = keytable; key->name; key++)
if (key->code == code)
return key->name;
return NULL;
}

868
lcd.c Normal file
View File

@ -0,0 +1,868 @@
#include "defs.h"
#include "regs.h"
#include "hw.h"
#include "mem.h"
#include "lcd.h"
#include "rc.h"
#include "fb.h"
#ifdef USE_ASM
#include "asm.h"
#endif
struct lcd lcd;
struct scan scan;
#define BG (scan.bg)
#define WND (scan.wnd)
#define BUF (scan.buf)
#define PRI (scan.pri)
#define PAL1 (scan.pal1)
#define PAL2 (scan.pal2)
#define PAL4 (scan.pal4)
#define VS (scan.vs) /* vissprites */
#define NS (scan.ns)
#define L (scan.l) /* line */
#define X (scan.x) /* screen position */
#define Y (scan.y)
#define S (scan.s) /* tilemap position */
#define T (scan.t)
#define U (scan.u) /* position within tile */
#define V (scan.v)
#define WX (scan.wx)
#define WY (scan.wy)
#define WT (scan.wt)
#define WV (scan.wv)
byte patpix[4096][8][8];
byte patdirty[1024];
byte anydirty;
static int scale = 1;
static int density = 0;
static int rgb332;
static int sprsort = 1;
static int sprdebug;
#define DEF_PAL { 0x98d0e0, 0x68a0b0, 0x60707C, 0x2C3C3C }
static int dmg_pal[4][4] = { DEF_PAL, DEF_PAL, DEF_PAL, DEF_PAL };
static int usefilter, filterdmg;
static int filter[3][4] = {
{ 195, 25, 0, 35 },
{ 25, 170, 25, 35 },
{ 25, 60, 125, 40 }
};
rcvar_t lcd_exports[] =
{
RCV_INT("scale", &scale),
RCV_INT("density", &density),
RCV_BOOL("rgb332", &rgb332),
RCV_VECTOR("dmg_bgp", dmg_pal[0], 4),
RCV_VECTOR("dmg_wndp", dmg_pal[1], 4),
RCV_VECTOR("dmg_obp0", dmg_pal[2], 4),
RCV_VECTOR("dmg_obp1", dmg_pal[3], 4),
RCV_BOOL("sprsort", &sprsort),
RCV_BOOL("sprdebug", &sprdebug),
RCV_BOOL("colorfilter", &usefilter),
RCV_BOOL("filterdmg", &filterdmg),
RCV_VECTOR("red", filter[0], 4),
RCV_VECTOR("green", filter[1], 4),
RCV_VECTOR("blue", filter[2], 4),
RCV_END
};
static byte *vdest;
#ifdef ALLOW_UNALIGNED_IO /* long long is ok since this is i386-only anyway? */
#define MEMCPY8(d, s) ((*(long long *)(d)) = (*(long long *)(s)))
#else
#define MEMCPY8(d, s) memcpy((d), (s), 8)
#endif
#ifndef ASM_UPDATEPATPIX
void updatepatpix()
{
int i, j, k;
int a, c;
byte *vram = lcd.vbank[0];
if (!anydirty) return;
for (i = 0; i < 1024; i++)
{
if (i == 384) i = 512;
if (i == 896) break;
if (!patdirty[i]) continue;
patdirty[i] = 0;
for (j = 0; j < 8; j++)
{
a = ((i<<4) | (j<<1));
for (k = 0; k < 8; k++)
{
c = vram[a] & (1<<k) ? 1 : 0;
c |= vram[a+1] & (1<<k) ? 2 : 0;
patpix[i+1024][j][k] = c;
}
for (k = 0; k < 8; k++)
patpix[i][j][k] =
patpix[i+1024][j][7-k];
}
for (j = 0; j < 8; j++)
{
for (k = 0; k < 8; k++)
{
patpix[i+2048][j][k] =
patpix[i][7-j][k];
patpix[i+3072][j][k] =
patpix[i+1024][7-j][k];
}
}
}
anydirty = 0;
}
#endif /* ASM_UPDATEPATPIX */
void tilebuf()
{
int i, cnt;
int base;
byte *tilemap, *attrmap;
int *tilebuf;
int *wrap;
static int wraptable[64] =
{
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,-32
};
base = ((R_LCDC&0x08)?0x1C00:0x1800) + (T<<5) + S;
tilemap = lcd.vbank[0] + base;
attrmap = lcd.vbank[1] + base;
tilebuf = BG;
wrap = wraptable + S;
cnt = ((WX + 7) >> 3) + 1;
if (hw.cgb)
{
if (R_LCDC & 0x10)
for (i = cnt; i > 0; i--)
{
*(tilebuf++) = *tilemap
| (((int)*attrmap & 0x08) << 6)
| (((int)*attrmap & 0x60) << 5);
*(tilebuf++) = (((int)*attrmap & 0x07) << 2);
attrmap += *wrap + 1;
tilemap += *(wrap++) + 1;
}
else
for (i = cnt; i > 0; i--)
{
*(tilebuf++) = (256 + ((n8)*tilemap))
| (((int)*attrmap & 0x08) << 6)
| (((int)*attrmap & 0x60) << 5);
*(tilebuf++) = (((int)*attrmap & 0x07) << 2);
attrmap += *wrap + 1;
tilemap += *(wrap++) + 1;
}
}
else
{
if (R_LCDC & 0x10)
for (i = cnt; i > 0; i--)
{
*(tilebuf++) = *(tilemap++);
tilemap += *(wrap++);
}
else
for (i = cnt; i > 0; i--)
{
*(tilebuf++) = (256 + ((n8)*(tilemap++)));
tilemap += *(wrap++);
}
}
if (WX >= 160) return;
base = ((R_LCDC&0x40)?0x1C00:0x1800) + (WT<<5);
tilemap = lcd.vbank[0] + base;
attrmap = lcd.vbank[1] + base;
tilebuf = WND;
cnt = ((160 - WX) >> 3) + 1;
if (hw.cgb)
{
if (R_LCDC & 0x10)
for (i = cnt; i > 0; i--)
{
*(tilebuf++) = *(tilemap++)
| (((int)*attrmap & 0x08) << 6)
| (((int)*attrmap & 0x60) << 5);
*(tilebuf++) = (((int)*(attrmap++)&7) << 2);
}
else
for (i = cnt; i > 0; i--)
{
*(tilebuf++) = (256 + ((n8)*(tilemap++)))
| (((int)*attrmap & 0x08) << 6)
| (((int)*attrmap & 0x60) << 5);
*(tilebuf++) = (((int)*(attrmap++)&7) << 2);
}
}
else
{
if (R_LCDC & 0x10)
for (i = cnt; i > 0; i--)
*(tilebuf++) = *(tilemap++);
else
for (i = cnt; i > 0; i--)
*(tilebuf++) = (256 + ((n8)*(tilemap++)));
}
}
void bg_scan()
{
int cnt;
byte *src, *dest;
int *tile;
if (WX <= 0) return;
cnt = WX;
tile = BG;
dest = BUF;
src = patpix[*(tile++)][V] + U;
memcpy(dest, src, 8-U);
dest += 8-U;
cnt -= 8-U;
if (cnt <= 0) return;
while (cnt >= 8)
{
src = patpix[*(tile++)][V];
MEMCPY8(dest, src);
dest += 8;
cnt -= 8;
}
src = patpix[*tile][V];
while (cnt--)
*(dest++) = *(src++);
}
void wnd_scan()
{
int cnt;
byte *src, *dest;
int *tile;
if (WX >= 160) return;
cnt = 160 - WX;
tile = WND;
dest = BUF + WX;
while (cnt >= 8)
{
src = patpix[*(tile++)][WV];
MEMCPY8(dest, src);
dest += 8;
cnt -= 8;
}
src = patpix[*tile][WV];
while (cnt--)
*(dest++) = *(src++);
}
static void blendcpy(byte *dest, byte *src, byte b, int cnt)
{
while (cnt--) *(dest++) = *(src++) | b;
}
static int priused(void *attr)
{
un32 *a = attr;
return (int)((a[0]|a[1]|a[2]|a[3]|a[4]|a[5]|a[6]|a[7])&0x80808080);
}
void bg_scan_pri()
{
int cnt, i;
byte *src, *dest;
if (WX <= 0) return;
i = S;
cnt = WX;
dest = PRI;
src = lcd.vbank[1] + ((R_LCDC&0x08)?0x1C00:0x1800) + (T<<5);
if (!priused(src))
{
memset(dest, 0, cnt);
return;
}
memset(dest, src[i++&31]&128, 8-U);
dest += 8-U;
cnt -= 8-U;
if (cnt <= 0) return;
while (cnt >= 8)
{
memset(dest, src[i++&31]&128, 8);
dest += 8;
cnt -= 8;
}
memset(dest, src[i&31]&128, cnt);
}
void wnd_scan_pri()
{
int cnt, i;
byte *src, *dest;
if (WX >= 160) return;
i = 0;
cnt = 160 - WX;
dest = PRI + WX;
src = lcd.vbank[1] + ((R_LCDC&0x40)?0x1C00:0x1800) + (WT<<5);
if (!priused(src))
{
memset(dest, 0, cnt);
return;
}
while (cnt >= 8)
{
memset(dest, src[i++]&128, 8);
dest += 8;
cnt -= 8;
}
memset(dest, src[i]&128, cnt);
}
#ifndef ASM_BG_SCAN_COLOR
void bg_scan_color()
{
int cnt;
byte *src, *dest;
int *tile;
if (WX <= 0) return;
cnt = WX;
tile = BG;
dest = BUF;
src = patpix[*(tile++)][V] + U;
blendcpy(dest, src, *(tile++), 8-U);
dest += 8-U;
cnt -= 8-U;
if (cnt <= 0) return;
while (cnt >= 8)
{
src = patpix[*(tile++)][V];
blendcpy(dest, src, *(tile++), 8);
dest += 8;
cnt -= 8;
}
src = patpix[*(tile++)][V];
blendcpy(dest, src, *(tile++), cnt);
}
#endif
void wnd_scan_color()
{
int cnt;
byte *src, *dest;
int *tile;
if (WX >= 160) return;
cnt = 160 - WX;
tile = WND;
dest = BUF + WX;
while (cnt >= 8)
{
src = patpix[*(tile++)][WV];
blendcpy(dest, src, *(tile++), 8);
dest += 8;
cnt -= 8;
}
src = patpix[*(tile++)][WV];
blendcpy(dest, src, *(tile++), cnt);
}
static void recolor(byte *buf, byte fill, int cnt)
{
while (cnt--) *(buf++) |= fill;
}
void spr_count()
{
int i;
struct obj *o;
NS = 0;
if (!(R_LCDC & 0x02)) return;
o = lcd.oam.obj;
for (i = 40; i; i--, o++)
{
if (L >= o->y || L + 16 < o->y)
continue;
if (L + 8 >= o->y && !(R_LCDC & 0x04))
continue;
if (++NS == 10) break;
}
}
void spr_enum()
{
int i, j;
struct obj *o;
struct vissprite ts[10];
int v, pat;
int l, x;
NS = 0;
if (!(R_LCDC & 0x02)) return;
o = lcd.oam.obj;
for (i = 40; i; i--, o++)
{
if (L >= o->y || L + 16 < o->y)
continue;
if (L + 8 >= o->y && !(R_LCDC & 0x04))
continue;
VS[NS].x = (int)o->x - 8;
v = L - (int)o->y + 16;
if (hw.cgb)
{
pat = o->pat | (((int)o->flags & 0x60) << 5)
| (((int)o->flags & 0x08) << 6);
VS[NS].pal = 32 + ((o->flags & 0x07) << 2);
}
else
{
pat = o->pat | (((int)o->flags & 0x60) << 5);
VS[NS].pal = 32 + ((o->flags & 0x10) >> 2);
}
VS[NS].pri = (o->flags & 0x80) >> 7;
if ((R_LCDC & 0x04))
{
pat &= ~1;
if (v >= 8)
{
v -= 8;
pat++;
}
if (o->flags & 0x40) pat ^= 1;
}
VS[NS].buf = patpix[pat][v];
if (++NS == 10) break;
}
if (!sprsort || hw.cgb) return;
/* not quite optimal but it finally works! */
for (i = 0; i < NS; i++)
{
l = 0;
x = VS[0].x;
for (j = 1; j < NS; j++)
{
if (VS[j].x < x)
{
l = j;
x = VS[j].x;
}
}
ts[i] = VS[l];
VS[l].x = 160;
}
memcpy(VS, ts, sizeof VS);
}
void spr_scan()
{
int i, x;
byte pal, b, ns = NS;
byte *src, *dest, *bg, *pri;
struct vissprite *vs;
static byte bgdup[256];
if (!ns) return;
memcpy(bgdup, BUF, 256);
vs = &VS[ns-1];
for (; ns; ns--, vs--)
{
x = vs->x;
if (x >= 160) continue;
if (x <= -8) continue;
if (x < 0)
{
src = vs->buf - x;
dest = BUF;
i = 8 + x;
}
else
{
src = vs->buf;
dest = BUF + x;
if (x > 152) i = 160 - x;
else i = 8;
}
pal = vs->pal;
if (vs->pri)
{
bg = bgdup + (dest - BUF);
while (i--)
{
b = src[i];
if (b && !(bg[i]&3)) dest[i] = pal|b;
}
}
else if (hw.cgb)
{
bg = bgdup + (dest - BUF);
pri = PRI + (dest - BUF);
while (i--)
{
b = src[i];
if (b && (!pri[i] || !(bg[i]&3)))
dest[i] = pal|b;
}
}
else while (i--) if (src[i]) dest[i] = pal|src[i];
/* else while (i--) if (src[i]) dest[i] = 31 + ns; */
}
if (sprdebug) for (i = 0; i < NS; i++) BUF[i<<1] = 36;
}
void lcd_begin()
{
if (fb.indexed)
{
if (rgb332) pal_set332();
else pal_expire();
}
while (scale * 160 > fb.w || scale * 144 > fb.h) scale--;
vdest = fb.ptr + ((fb.w*fb.pelsize)>>1)
- (80*fb.pelsize) * scale
+ ((fb.h>>1) - 72*scale) * fb.pitch;
WY = R_WY;
}
void lcd_refreshline()
{
int i;
byte scalebuf[160*4*4], *dest;
if (!fb.enabled) return;
if (!(R_LCDC & 0x80))
return; /* should not happen... */
updatepatpix();
L = R_LY;
X = R_SCX;
Y = (R_SCY + L) & 0xff;
S = X >> 3;
T = Y >> 3;
U = X & 7;
V = Y & 7;
WX = R_WX - 7;
if (WY>L || WY<0 || WY>143 || WX<-7 || WX>159 || !(R_LCDC&0x20))
WX = 160;
WT = (L - WY) >> 3;
WV = (L - WY) & 7;
spr_enum();
tilebuf();
if (hw.cgb)
{
bg_scan_color();
wnd_scan_color();
if (NS)
{
bg_scan_pri();
wnd_scan_pri();
}
}
else
{
bg_scan();
wnd_scan();
recolor(BUF+WX, 0x04, 160-WX);
}
spr_scan();
if (fb.dirty) memset(fb.ptr, 0, fb.pitch * fb.h);
fb.dirty = 0;
if (density > scale) density = scale;
if (scale == 1) density = 1;
dest = (density != 1) ? scalebuf : vdest;
switch (scale)
{
case 0:
case 1:
switch (fb.pelsize)
{
case 1:
refresh_1(dest, BUF, PAL1, 160);
break;
case 2:
refresh_2(dest, BUF, PAL2, 160);
break;
case 3:
refresh_3(dest, BUF, PAL4, 160);
break;
case 4:
refresh_4(dest, BUF, PAL4, 160);
break;
}
break;
case 2:
switch (fb.pelsize)
{
case 1:
refresh_2(dest, BUF, PAL2, 160);
break;
case 2:
refresh_4(dest, BUF, PAL4, 160);
break;
case 3:
refresh_3_2x(dest, BUF, PAL4, 160);
break;
case 4:
refresh_4_2x(dest, BUF, PAL4, 160);
break;
}
break;
case 3:
switch (fb.pelsize)
{
case 1:
refresh_3(dest, BUF, PAL4, 160);
break;
case 2:
refresh_2_3x(dest, BUF, PAL2, 160);
break;
case 3:
refresh_3_3x(dest, BUF, PAL4, 160);
break;
case 4:
refresh_4_3x(dest, BUF, PAL4, 160);
break;
}
break;
case 4:
switch (fb.pelsize)
{
case 1:
refresh_4(dest, BUF, PAL4, 160);
break;
case 2:
refresh_4_2x(dest, BUF, PAL4, 160);
break;
case 3:
refresh_3_4x(dest, BUF, PAL4, 160);
break;
case 4:
refresh_4_4x(dest, BUF, PAL4, 160);
break;
}
break;
default:
break;
}
if (density != 1)
{
for (i = 0; i < scale; i++)
{
if ((i < density) || ((density <= 0) && !(i&1)))
memcpy(vdest, scalebuf, 160 * fb.pelsize * scale);
vdest += fb.pitch;
}
}
else vdest += fb.pitch * scale;
}
static void updatepalette(int i)
{
int c, r, g, b, y, u, v, rr, gg;
c = (lcd.pal[i<<1] | ((int)lcd.pal[(i<<1)|1] << 8)) & 0x7FFF;
r = (c & 0x001F) << 3;
g = (c & 0x03E0) >> 2;
b = (c & 0x7C00) >> 7;
r |= (r >> 5);
g |= (g >> 5);
b |= (b >> 5);
if (usefilter && (filterdmg || hw.cgb))
{
rr = ((r * filter[0][0] + g * filter[0][1] + b * filter[0][2]) >> 8) + filter[0][3];
gg = ((r * filter[1][0] + g * filter[1][1] + b * filter[1][2]) >> 8) + filter[1][3];
b = ((r * filter[2][0] + g * filter[2][1] + b * filter[2][2]) >> 8) + filter[2][3];
r = rr;
g = gg;
}
if (fb.yuv)
{
y = (((r * 263) + (g * 516) + (b * 100)) >> 10) + 16;
u = (((r * 450) - (g * 377) - (b * 73)) >> 10) + 128;
v = (((r * -152) - (g * 298) + (b * 450)) >> 10) + 128;
if (y < 0) y = 0; if (y > 255) y = 255;
if (u < 0) u = 0; if (u > 255) u = 255;
if (v < 0) v = 0; if (v > 255) v = 255;
PAL4[i] = (y<<fb.cc[0].l) | (y<<fb.cc[3].l)
| (u<<fb.cc[1].l) | (v<<fb.cc[2].l);
return;
}
if (fb.indexed)
{
pal_release(PAL1[i]);
c = pal_getcolor(c, r, g, b);
PAL1[i] = c;
PAL2[i] = (c<<8) | c;
PAL4[i] = (c<<24) | (c<<16) | (c<<8) | c;
return;
}
r = (r >> fb.cc[0].r) << fb.cc[0].l;
g = (g >> fb.cc[1].r) << fb.cc[1].l;
b = (b >> fb.cc[2].r) << fb.cc[2].l;
c = r|g|b;
switch (fb.pelsize)
{
case 1:
PAL1[i] = c;
PAL2[i] = (c<<8) | c;
PAL4[i] = (c<<24) | (c<<16) | (c<<8) | c;
break;
case 2:
PAL2[i] = c;
PAL4[i] = (c<<16) | c;
break;
case 3:
case 4:
PAL4[i] = c;
break;
}
}
void pal_write(int i, byte b)
{
if (lcd.pal[i] == b) return;
lcd.pal[i] = b;
updatepalette(i>>1);
}
void pal_write_dmg(int i, int mapnum, byte d)
{
int j;
int *cmap = dmg_pal[mapnum];
int c, r, g, b;
if (hw.cgb) return;
/* if (mapnum >= 2) d = 0xe4; */
for (j = 0; j < 8; j += 2)
{
c = cmap[(d >> j) & 3];
r = (c & 0xf8) >> 3;
g = (c & 0xf800) >> 6;
b = (c & 0xf80000) >> 9;
c = r|g|b;
/* FIXME - handle directly without faking cgb */
pal_write(i+j, c & 0xff);
pal_write(i+j+1, c >> 8);
}
}
void vram_write(int a, byte b)
{
lcd.vbank[R_VBK&1][a] = b;
if (a >= 0x1800) return;
patdirty[((R_VBK&1)<<9)+(a>>4)] = 1;
anydirty = 1;
}
void vram_dirty()
{
anydirty = 1;
memset(patdirty, 1, sizeof patdirty);
}
void pal_dirty()
{
int i;
if (!hw.cgb)
{
pal_write_dmg(0, 0, R_BGP);
pal_write_dmg(8, 1, R_BGP);
pal_write_dmg(64, 2, R_OBP0);
pal_write_dmg(72, 3, R_OBP1);
}
for (i = 0; i < 64; i++)
updatepalette(i);
}
void lcd_reset()
{
memset(&lcd, 0, sizeof lcd);
lcd_begin();
vram_dirty();
pal_dirty();
}

57
lcd.h Normal file
View File

@ -0,0 +1,57 @@
#ifndef __LCD_H__
#define __LCD_H__
#include "defs.h"
struct vissprite
{
byte *buf;
int x;
byte pal, pri, pad[6];
};
struct scan
{
int bg[64];
int wnd[64];
byte buf[256];
byte pal1[128];
un16 pal2[64];
un32 pal4[64];
byte pri[256];
struct vissprite vs[16];
int ns, l, x, y, s, t, u, v, wx, wy, wt, wv;
};
struct obj
{
byte y;
byte x;
byte pat;
byte flags;
};
struct lcd
{
byte vbank[2][8192];
union
{
byte mem[256];
struct obj obj[40];
} oam;
byte pal[128];
};
extern struct lcd lcd;
extern struct scan scan;
#endif

179
lcdc.c Normal file
View File

@ -0,0 +1,179 @@
#include <stdlib.h>
#include "defs.h"
#include "hw.h"
#include "cpu.h"
#include "regs.h"
#define C (cpu.lcdc)
/*
* stat_trigger updates the STAT interrupt line to reflect whether any
* of the conditions set to be tested (by bits 3-6 of R_STAT) are met.
* This function should be called whenever any of the following occur:
* 1) LY or LYC changes.
* 2) A state transition affects the low 2 bits of R_STAT (see below).
* 3) The program writes to the upper bits of R_STAT.
* stat_trigger also updates bit 2 of R_STAT to reflect whether LY=LYC.
*/
void stat_trigger()
{
static const int condbits[4] = { 0x08, 0x30, 0x20, 0x00 };
int flag = 0;
if ((R_LY < 0x91) && (R_LY == R_LYC))
{
R_STAT |= 0x04;
if (R_STAT & 0x40) flag = IF_STAT;
}
else R_STAT &= ~0x04;
if (R_STAT & condbits[R_STAT&3]) flag = IF_STAT;
if (!(R_LCDC & 0x80)) flag = 0;
hw_interrupt(flag, IF_STAT);
}
void stat_write(byte b)
{
R_STAT = (R_STAT & 0x07) | (b & 0x78);
if (!hw.cgb && !(R_STAT & 2)) /* DMG STAT write bug => interrupt */
hw_interrupt(IF_STAT, IF_STAT);
stat_trigger();
}
/*
* stat_change is called when a transition results in a change to the
* LCD STAT condition (the low 2 bits of R_STAT). It raises or lowers
* the VBLANK interrupt line appropriately and calls stat_trigger to
* update the STAT interrupt line.
*/
static void stat_change(int stat)
{
stat &= 3;
R_STAT = (R_STAT & 0x7C) | stat;
if (stat != 1) hw_interrupt(0, IF_VBLANK);
/* hw_interrupt((stat == 1) ? IF_VBLANK : 0, IF_VBLANK); */
stat_trigger();
}
void lcdc_change(byte b)
{
byte old = R_LCDC;
R_LCDC = b;
if ((R_LCDC ^ old) & 0x80) /* lcd on/off change */
{
R_LY = 0;
stat_change(2);
C = 40;
lcd_begin();
}
}
void lcdc_trans()
{
if (!(R_LCDC & 0x80))
{
while (C <= 0)
{
switch ((byte)(R_STAT & 3))
{
case 0:
case 1:
stat_change(2);
C += 40;
break;
case 2:
stat_change(3);
C += 86;
break;
case 3:
stat_change(0);
if (hw.hdma & 0x80)
hw_hdma();
else
C += 102;
break;
}
return;
}
}
while (C <= 0)
{
switch ((byte)(R_STAT & 3))
{
case 1:
if (!(hw.ilines & IF_VBLANK))
{
C += 218;
hw_interrupt(IF_VBLANK, IF_VBLANK);
break;
}
if (R_LY == 0)
{
lcd_begin();
stat_change(2);
C += 40;
break;
}
else if (R_LY < 152)
C += 228;
else if (R_LY == 152)
C += 28;
else
{
R_LY = -1;
C += 200;
}
R_LY++;
stat_trigger();
break;
case 2:
lcd_refreshline();
stat_change(3);
C += 86;
break;
case 3:
stat_change(0);
if (hw.hdma & 0x80)
hw_hdma();
/* FIXME -- how much of the hblank does hdma use?? */
/* else */
C += 102;
break;
case 0:
if (++R_LY >= 144)
{
if (cpu.halt)
{
hw_interrupt(IF_VBLANK, IF_VBLANK);
C += 228;
}
else C += 10;
stat_change(1);
break;
}
stat_change(2);
C += 40;
break;
}
}
}

395
loader.c Normal file
View File

@ -0,0 +1,395 @@
#include "defs.h"
#include "regs.h"
#include "mem.h"
#include "hw.h"
#include "rtc.h"
#include "rc.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *strdup();
static int mbc_table[256] =
{
0, 1, 1, 1, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 3,
3, 3, 3, 3, 0, 0, 0, 0, 0, 5, 5, 5, MBC_RUMBLE, MBC_RUMBLE, MBC_RUMBLE, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, MBC_HUC3, MBC_HUC1
};
static int rtc_table[256] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0
};
static int batt_table[256] =
{
0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0,
1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0,
0
};
static int romsize_table[256] =
{
2, 4, 8, 16, 32, 64, 128, 256, 512,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 128, 128, 128
/* 0, 0, 72, 80, 96 -- actual values but bad to use these! */
};
static int ramsize_table[256] =
{
1, 1, 1, 4, 16,
4 /* FIXME - what value should this be?! */
};
static char *romfile;
static char *sramfile;
static char *rtcfile;
static char *saveprefix;
static char *savename;
static char *savedir;
static int saveslot;
static int forcebatt, nobatt;
static int forcedmg, gbamode;
static int memfill = -1, memrand = -1;
static void initmem(void *mem, int size)
{
char *p = mem;
if (memrand >= 0)
{
srand(memrand ? memrand : time(0));
while(size--) *(p++) = rand();
}
else if (memfill >= 0)
memset(p, memfill, size);
}
static byte *loadfile(FILE *f, int *len)
{
int c, l = 0, p = 0;
byte *d = 0, buf[512];
for(;;)
{
c = fread(buf, 1, sizeof buf, f);
if (c <= 0) break;
l += c;
d = realloc(d, l);
if (!d) return 0;
memcpy(d+p, buf, c);
p += c;
}
*len = l;
return d;
}
static byte *inf_buf;
static int inf_pos, inf_len;
static void inflate_callback(byte b)
{
if (inf_pos >= inf_len)
{
inf_len += 512;
inf_buf = realloc(inf_buf, inf_len);
if (!inf_buf) die("out of memory inflating file @ %d bytes\n", inf_pos);
}
inf_buf[inf_pos++] = b;
}
static byte *decompress(byte *data, int *len)
{
unsigned long pos = 0;
if (data[0] != 0x1f || data[1] != 0x8b)
return data;
inf_buf = 0;
inf_pos = inf_len = 0;
if (unzip(data, &pos, inflate_callback) < 0)
return data;
*len = inf_pos;
return inf_buf;
}
int rom_load()
{
FILE *f;
byte c, *data, *header;
int len = 0, rlen;
if (strcmp(romfile, "-")) f = fopen(romfile, "rb");
else f = stdin;
if (!f) die("cannot open rom file: %s\n", romfile);
data = loadfile(f, &len);
header = data = decompress(data, &len);
memcpy(rom.name, header+0x0134, 16);
if (rom.name[14] & 0x80) rom.name[14] = 0;
if (rom.name[15] & 0x80) rom.name[15] = 0;
rom.name[16] = 0;
c = header[0x0147];
mbc.type = mbc_table[c];
mbc.batt = (batt_table[c] && !nobatt) || forcebatt;
rtc.batt = rtc_table[c];
mbc.romsize = romsize_table[header[0x0148]];
mbc.ramsize = ramsize_table[header[0x0149]];
if (!mbc.romsize) die("unknown ROM size %02X\n", header[0x0148]);
if (!mbc.ramsize) die("unknown SRAM size %02X\n", header[0x0149]);
rlen = 16384 * mbc.romsize;
rom.bank = realloc(data, rlen);
if (rlen > len) memset(rom.bank[0]+len, 0xff, rlen - len);
ram.sbank = malloc(8192 * mbc.ramsize);
initmem(ram.sbank, 8192 * mbc.ramsize);
initmem(ram.ibank, 4096 * 8);
mbc.rombank = 1;
mbc.rambank = 0;
c = header[0x0143];
hw.cgb = ((c == 0x80) || (c == 0xc0)) && !forcedmg;
hw.gba = (hw.cgb && gbamode);
if (strcmp(romfile, "-")) fclose(f);
return 0;
}
int sram_load()
{
FILE *f;
if (!mbc.batt || !sramfile || !*sramfile) return -1;
/* Consider sram loaded at this point, even if file doesn't exist */
ram.loaded = 1;
f = fopen(sramfile, "rb");
if (!f) return -1;
fread(ram.sbank, 8192, mbc.ramsize, f);
fclose(f);
return 0;
}
int sram_save()
{
FILE *f;
/* If we crash before we ever loaded sram, DO NOT SAVE! */
if (!mbc.batt || !sramfile || !ram.loaded || !mbc.ramsize)
return -1;
f = fopen(sramfile, "wb");
if (!f) return -1;
fwrite(ram.sbank, 8192, mbc.ramsize, f);
fclose(f);
return 0;
}
void state_save(int n)
{
FILE *f;
char *name;
if (n < 0) n = saveslot;
if (n < 0) n = 0;
name = malloc(strlen(saveprefix) + 5);
sprintf(name, "%s.%03d", saveprefix, n);
if ((f = fopen(name, "wb")))
{
savestate(f);
fclose(f);
}
free(name);
}
void state_load(int n)
{
FILE *f;
char *name;
if (n < 0) n = saveslot;
if (n < 0) n = 0;
name = malloc(strlen(saveprefix) + 5);
sprintf(name, "%s.%03d", saveprefix, n);
if ((f = fopen(name, "rb")))
{
loadstate(f);
fclose(f);
vram_dirty();
pal_dirty();
sound_dirty();
mem_updatemap();
}
free(name);
}
void rtc_save()
{
FILE *f;
if (!rtc.batt) return;
if (!(f = fopen(rtcfile, "wb"))) return;
rtc_save_internal(f);
fclose(f);
}
void rtc_load()
{
FILE *f;
if (!rtc.batt) return;
if (!(f = fopen(rtcfile, "r"))) return;
rtc_load_internal(f);
fclose(f);
}
void loader_unload()
{
sram_save();
if (romfile) free(romfile);
if (sramfile) free(sramfile);
if (saveprefix) free(saveprefix);
if (rom.bank) free(rom.bank);
if (ram.sbank) free(ram.sbank);
romfile = sramfile = saveprefix = 0;
rom.bank = 0;
ram.sbank = 0;
mbc.type = mbc.romsize = mbc.ramsize = mbc.batt = 0;
}
static char *base(char *s)
{
char *p;
p = strrchr(s, '/');
if (p) return p+1;
return s;
}
static char *ldup(char *s)
{
int i;
char *n, *p;
p = n = malloc(strlen(s));
for (i = 0; s[i]; i++) if (isalnum(s[i])) *(p++) = tolower(s[i]);
*p = 0;
return n;
}
static void cleanup()
{
sram_save();
rtc_save();
/* IDEA - if error, write emergency savestate..? */
}
void loader_init(char *s)
{
char *name, *p;
sys_checkdir(savedir, 1); /* needs to be writable */
romfile = s;
rom_load();
vid_settitle(rom.name);
if (savename && *savename)
{
if (savename[0] == '-' && savename[1] == 0)
name = ldup(rom.name);
else name = strdup(savename);
}
else if (romfile && *base(romfile) && strcmp(romfile, "-"))
{
name = strdup(base(romfile));
p = strchr(name, '.');
if (p) *p = 0;
}
else name = ldup(rom.name);
saveprefix = malloc(strlen(savedir) + strlen(name) + 2);
sprintf(saveprefix, "%s/%s", savedir, name);
sramfile = malloc(strlen(saveprefix) + 5);
strcpy(sramfile, saveprefix);
strcat(sramfile, ".sav");
rtcfile = malloc(strlen(saveprefix) + 5);
strcpy(rtcfile, saveprefix);
strcat(rtcfile, ".rtc");
sram_load();
rtc_load();
atexit(cleanup);
}
rcvar_t loader_exports[] =
{
RCV_STRING("savedir", &savedir),
RCV_STRING("savename", &savename),
RCV_INT("saveslot", &saveslot),
RCV_BOOL("forcebatt", &forcebatt),
RCV_BOOL("nobatt", &nobatt),
RCV_BOOL("forcedmg", &forcedmg),
RCV_BOOL("gbamode", &gbamode),
RCV_INT("memfill", &memfill),
RCV_INT("memrand", &memrand),
RCV_END
};

28
loader.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef __LOADER_H__
#define __LOADER_H__
typedef struct loader_s
{
char *rom;
char *base;
char *sram;
char *state;
int ramloaded;
} loader_t;
extern loader_t loader;
int rom_load();
int sram_load();
int sram_save();
#endif

323
main.c Normal file
View File

@ -0,0 +1,323 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *strdup();
#include <stdarg.h>
#include <signal.h>
#include "input.h"
#include "rc.h"
#include "Version"
static char *defaultconfig[] =
{
"bind esc quit",
"bind up +up",
"bind down +down",
"bind left +left",
"bind right +right",
"bind d +a",
"bind s +b",
"bind enter +start",
"bind space +select",
"bind tab +select",
"bind joyup +up",
"bind joydown +down",
"bind joyleft +left",
"bind joyright +right",
"bind joy0 +b",
"bind joy1 +a",
"bind joy2 +select",
"bind joy3 +start",
"bind 1 \"set saveslot 1\"",
"bind 2 \"set saveslot 2\"",
"bind 3 \"set saveslot 3\"",
"bind 4 \"set saveslot 4\"",
"bind 5 \"set saveslot 5\"",
"bind 6 \"set saveslot 6\"",
"bind 7 \"set saveslot 7\"",
"bind 8 \"set saveslot 8\"",
"bind 9 \"set saveslot 9\"",
"bind 0 \"set saveslot 0\"",
"bind ins savestate",
"bind del loadstate",
"source gnuboy.rc",
NULL
};
static void banner()
{
printf("\ngnuboy " VERSION "\n");
}
static void copyright()
{
banner();
printf(
"Copyright (C) 2000-2001 Laguna and Gilgamesh\n"
"Portions contributed by other authors; see CREDITS for details.\n"
"\n"
"This program is free software; you can redistribute it and/or modify\n"
"it under the terms of the GNU General Public License as published by\n"
"the Free Software Foundation; either version 2 of the License, or\n"
"(at your option) any later version.\n"
"\n"
"This program is distributed in the hope that it will be useful,\n"
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
"GNU General Public License for more details.\n"
"\n"
"You should have received a copy of the GNU General Public License\n"
"along with this program; if not, write to the Free Software\n"
"Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n"
"\n");
}
static void usage(char *name)
{
copyright();
printf("Type %s --help for detailed help.\n\n", name);
exit(1);
}
static void copying()
{
copyright();
exit(0);
}
static void help(char *name)
{
banner();
printf("Usage: %s [options] romfile\n", name);
printf("\n"
" --source FILE read rc commands from FILE\n"
" --bind KEY COMMAND bind KEY to perform COMMAND\n"
" --VAR=VALUE set rc variable VAR to VALUE\n"
" --VAR set VAR to 1 (turn on boolean options)\n"
" --no-VAR set VAR to 0 (turn off boolean options)\n"
" --showvars list all available rc variables\n"
" --help display this help and exit\n"
" --version output version information and exit\n"
" --copying show copying permissions\n"
"");
exit(0);
}
static void version(char *name)
{
printf("%s-" VERSION "\n", name);
exit(0);
}
void doevents()
{
event_t ev;
int st;
ev_poll();
while (ev_getevent(&ev))
{
if (ev.type != EV_PRESS && ev.type != EV_RELEASE)
continue;
st = (ev.type != EV_RELEASE);
rc_dokey(ev.code, st);
}
}
static void shutdown()
{
vid_close();
pcm_close();
}
void die(char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
exit(1);
}
static int bad_signals[] =
{
/* These are all standard, so no need to #ifdef them... */
SIGINT, SIGSEGV, SIGTERM, SIGFPE, SIGABRT, SIGILL,
#ifdef SIGQUIT
SIGQUIT,
#endif
#ifdef SIGPIPE
SIGPIPE,
#endif
0
};
static void fatalsignal(int s)
{
die("Signal %d\n", s);
}
static void catch_signals()
{
int i;
for (i = 0; bad_signals[i]; i++)
signal(bad_signals[i], fatalsignal);
}
static char *base(char *s)
{
char *p;
p = strrchr(s, '/');
if (p) return p+1;
return s;
}
int main(int argc, char *argv[])
{
int i;
char *opt, *arg, *cmd, *s, *rom = 0;
/* Avoid initializing video if we don't have to */
for (i = 1; i < argc; i++)
{
if (!strcmp(argv[i], "--help"))
help(base(argv[0]));
else if (!strcmp(argv[i], "--version"))
version(base(argv[0]));
else if (!strcmp(argv[i], "--copying"))
copying();
else if (!strcmp(argv[i], "--bind")) i += 2;
else if (!strcmp(argv[i], "--source")) i++;
else if (!strcmp(argv[i], "--showvars"))
{
show_exports();
exit(0);
}
else if (argv[i][0] == '-' && argv[i][1] == '-');
else if (argv[i][0] == '-' && argv[i][1]);
else rom = argv[i];
}
if (!rom) usage(base(argv[0]));
/* If we have special perms, drop them ASAP! */
vid_preinit();
init_exports();
s = strdup(argv[0]);
sys_sanitize(s);
sys_initpath(s);
for (i = 0; defaultconfig[i]; i++)
rc_command(defaultconfig[i]);
cmd = malloc(strlen(rom) + 11);
sprintf(cmd, "source %s", rom);
s = strchr(cmd, '.');
if (s) *s = 0;
strcat(cmd, ".rc");
rc_command(cmd);
for (i = 1; i < argc; i++)
{
if (!strcmp(argv[i], "--bind"))
{
if (i + 2 >= argc) die("missing arguments to bind\n");
cmd = malloc(strlen(argv[i+1]) + strlen(argv[i+2]) + 9);
sprintf(cmd, "bind %s \"%s\"", argv[i+1], argv[i+2]);
rc_command(cmd);
free(cmd);
i += 2;
}
else if (!strcmp(argv[i], "--source"))
{
if (i + 1 >= argc) die("missing argument to source\n");
cmd = malloc(strlen(argv[i+1]) + 6);
sprintf(cmd, "source %s", argv[++i]);
rc_command(cmd);
free(cmd);
}
else if (!strncmp(argv[i], "--no-", 5))
{
opt = strdup(argv[i]+5);
while ((s = strchr(opt, '-'))) *s = '_';
cmd = malloc(strlen(opt) + 7);
sprintf(cmd, "set %s 0", opt);
rc_command(cmd);
free(cmd);
free(opt);
}
else if (argv[i][0] == '-' && argv[i][1] == '-')
{
opt = strdup(argv[i]+2);
if ((s = strchr(opt, '=')))
{
*s = 0;
arg = s+1;
}
else arg = "1";
while ((s = strchr(opt, '-'))) *s = '_';
while ((s = strchr(arg, ','))) *s = ' ';
cmd = malloc(strlen(opt) + strlen(arg) + 6);
sprintf(cmd, "set %s %s", opt, arg);
rc_command(cmd);
free(cmd);
free(opt);
}
/* short options not yet implemented */
else if (argv[i][0] == '-' && argv[i][1]);
}
/* FIXME - make interface modules responsible for atexit() */
atexit(shutdown);
catch_signals();
vid_init();
pcm_init();
rom = strdup(rom);
sys_sanitize(rom);
loader_init(rom);
emu_reset();
emu_run();
/* never reached */
return 0;
}

576
mem.c Normal file
View File

@ -0,0 +1,576 @@
#include <stdlib.h>
#include "defs.h"
#include "hw.h"
#include "regs.h"
#include "mem.h"
#include "rtc.h"
#include "lcd.h"
struct mbc mbc;
struct rom rom;
struct ram ram;
/*
* In order to make reads and writes efficient, we keep tables
* (indexed by the high nibble of the address) specifying which
* regions can be read/written without a function call. For such
* ranges, the pointer in the map table points to the base of the
* region in host system memory. For ranges that require special
* processing, the pointer is NULL.
*
* mem_updatemap is called whenever bank changes or other operations
* make the old maps potentially invalid.
*/
void mem_updatemap()
{
int n;
byte **map;
map = mbc.rmap;
map[0x0] = rom.bank[0];
map[0x1] = rom.bank[0];
map[0x2] = rom.bank[0];
map[0x3] = rom.bank[0];
if (mbc.rombank < mbc.romsize)
{
map[0x4] = rom.bank[mbc.rombank] - 0x4000;
map[0x5] = rom.bank[mbc.rombank] - 0x4000;
map[0x6] = rom.bank[mbc.rombank] - 0x4000;
map[0x7] = rom.bank[mbc.rombank] - 0x4000;
}
else map[0x4] = map[0x5] = map[0x6] = map[0x7] = NULL;
if (0 && (R_STAT & 0x03) == 0x03)
{
map[0x8] = NULL;
map[0x9] = NULL;
}
else
{
map[0x8] = lcd.vbank[R_VBK & 1] - 0x8000;
map[0x9] = lcd.vbank[R_VBK & 1] - 0x8000;
}
if (mbc.enableram && !(rtc.sel&8))
{
map[0xA] = ram.sbank[mbc.rambank] - 0xA000;
map[0xB] = ram.sbank[mbc.rambank] - 0xA000;
}
else map[0xA] = map[0xB] = NULL;
map[0xC] = ram.ibank[0] - 0xC000;
n = R_SVBK & 0x07;
map[0xD] = ram.ibank[n?n:1] - 0xD000;
map[0xE] = ram.ibank[0] - 0xE000;
map[0xF] = NULL;
map = mbc.wmap;
map[0x0] = map[0x1] = map[0x2] = map[0x3] = NULL;
map[0x4] = map[0x5] = map[0x6] = map[0x7] = NULL;
map[0x8] = map[0x9] = NULL;
if (mbc.enableram && !(rtc.sel&8))
{
map[0xA] = ram.sbank[mbc.rambank] - 0xA000;
map[0xB] = ram.sbank[mbc.rambank] - 0xA000;
}
else map[0xA] = map[0xB] = NULL;
map[0xC] = ram.ibank[0] - 0xC000;
n = R_SVBK & 0x07;
map[0xD] = ram.ibank[n?n:1] - 0xD000;
map[0xE] = ram.ibank[0] - 0xE000;
map[0xF] = NULL;
}
/*
* ioreg_write handles output to io registers in the FF00-FF7F,FFFF
* range. It takes the register number (low byte of the address) and a
* byte value to be written.
*/
void ioreg_write(byte r, byte b)
{
if (!hw.cgb)
{
switch (r)
{
case RI_VBK:
case RI_BCPS:
case RI_OCPS:
case RI_BCPD:
case RI_OCPD:
case RI_SVBK:
case RI_KEY1:
case RI_HDMA1:
case RI_HDMA2:
case RI_HDMA3:
case RI_HDMA4:
case RI_HDMA5:
return;
}
}
switch(r)
{
case RI_TIMA:
case RI_TMA:
case RI_TAC:
case RI_SCY:
case RI_SCX:
case RI_WY:
case RI_WX:
REG(r) = b;
break;
case RI_BGP:
if (R_BGP == b) break;
pal_write_dmg(0, 0, b);
pal_write_dmg(8, 1, b);
R_BGP = b;
break;
case RI_OBP0:
if (R_OBP0 == b) break;
pal_write_dmg(64, 2, b);
R_OBP0 = b;
break;
case RI_OBP1:
if (R_OBP1 == b) break;
pal_write_dmg(72, 3, b);
R_OBP1 = b;
break;
case RI_IF:
case RI_IE:
REG(r) = b & 0x1F;
break;
case RI_P1:
REG(r) = b;
pad_refresh();
break;
case RI_SC:
/* FIXME - this is a hack for stupid roms that probe serial */
if ((b & 0x81) == 0x81)
{
R_SB = 0xff;
hw_interrupt(IF_SERIAL, IF_SERIAL);
hw_interrupt(0, IF_SERIAL);
}
R_SC = b; /* & 0x7f; */
break;
case RI_DIV:
REG(r) = 0;
break;
case RI_LCDC:
lcdc_change(b);
break;
case RI_STAT:
stat_write(b);
break;
case RI_LYC:
REG(r) = b;
stat_trigger();
break;
case RI_VBK:
REG(r) = b | 0xFE;
mem_updatemap();
break;
case RI_BCPS:
R_BCPS = b & 0xBF;
R_BCPD = lcd.pal[b & 0x3F];
break;
case RI_OCPS:
R_OCPS = b & 0xBF;
R_OCPD = lcd.pal[64 + (b & 0x3F)];
break;
case RI_BCPD:
R_BCPD = b;
pal_write(R_BCPS & 0x3F, b);
if (R_BCPS & 0x80) R_BCPS = (R_BCPS+1) & 0xBF;
break;
case RI_OCPD:
R_OCPD = b;
pal_write(64 + (R_OCPS & 0x3F), b);
if (R_OCPS & 0x80) R_OCPS = (R_OCPS+1) & 0xBF;
break;
case RI_SVBK:
REG(r) = b & 0x07;
mem_updatemap();
break;
case RI_DMA:
hw_dma(b);
break;
case RI_KEY1:
REG(r) = (REG(r) & 0x80) | (b & 0x01);
break;
case RI_HDMA1:
REG(r) = b;
break;
case RI_HDMA2:
REG(r) = b & 0xF0;
break;
case RI_HDMA3:
REG(r) = b & 0x1F;
break;
case RI_HDMA4:
REG(r) = b & 0xF0;
break;
case RI_HDMA5:
hw_hdma_cmd(b);
break;
}
switch (r)
{
case RI_BGP:
case RI_OBP0:
case RI_OBP1:
/* printf("palette reg %02X write %02X at LY=%02X\n", r, b, R_LY); */
case RI_HDMA1:
case RI_HDMA2:
case RI_HDMA3:
case RI_HDMA4:
case RI_HDMA5:
/* printf("HDMA %d: %02X\n", r - RI_HDMA1 + 1, b); */
break;
}
/* printf("reg %02X => %02X (%02X)\n", r, REG(r), b); */
}
byte ioreg_read(byte r)
{
switch(r)
{
case RI_SC:
r = R_SC;
R_SC &= 0x7f;
return r;
case RI_P1:
case RI_SB:
case RI_DIV:
case RI_TIMA:
case RI_TMA:
case RI_TAC:
case RI_LCDC:
case RI_STAT:
case RI_SCY:
case RI_SCX:
case RI_LY:
case RI_LYC:
case RI_BGP:
case RI_OBP0:
case RI_OBP1:
case RI_WY:
case RI_WX:
case RI_IE:
case RI_IF:
return REG(r);
case RI_VBK:
case RI_BCPS:
case RI_OCPS:
case RI_BCPD:
case RI_OCPD:
case RI_SVBK:
case RI_KEY1:
case RI_HDMA1:
case RI_HDMA2:
case RI_HDMA3:
case RI_HDMA4:
case RI_HDMA5:
if (hw.cgb) return REG(r);
default:
return 0xff;
}
}
/*
* Memory bank controllers typically intercept write attempts to
* 0000-7FFF, using the address and byte written as instructions to
* change rom or sram banks, control special hardware, etc.
*
* mbc_write takes an address (which should be in the proper range)
* and a byte value written to the address.
*/
void mbc_write(int a, byte b)
{
byte ha = (a>>12);
/* printf("mbc %d: rom bank %02X -[%04X:%02X]-> ", mbc.type, mbc.rombank, a, b); */
switch (mbc.type)
{
case MBC_MBC1:
switch (ha & 0xE)
{
case 0x0:
mbc.enableram = ((b & 0x0F) == 0x0A);
break;
case 0x2:
if ((b & 0x1F) == 0) b = 0x01;
mbc.rombank = (mbc.rombank & 0x60) | (b & 0x1F);
break;
case 0x4:
if (mbc.model)
{
mbc.rambank = b & 0x03;
break;
}
mbc.rombank = (mbc.rombank & 0x1F) | ((int)(b&3)<<5);
break;
case 0x6:
mbc.model = b & 0x1;
break;
}
break;
case MBC_MBC2: /* is this at all right? */
if ((a & 0x0100) == 0x0000)
{
mbc.enableram = ((b & 0x0F) == 0x0A);
break;
}
if ((a & 0xE100) == 0x2100)
{
mbc.rombank = b & 0x0F;
break;
}
break;
case MBC_MBC3:
switch (ha & 0xE)
{
case 0x0:
mbc.enableram = ((b & 0x0F) == 0x0A);
break;
case 0x2:
if ((b & 0x7F) == 0) b = 0x01;
mbc.rombank = b & 0x7F;
break;
case 0x4:
rtc.sel = b & 0x0f;
mbc.rambank = b & 0x03;
break;
case 0x6:
rtc_latch(b);
break;
}
break;
case MBC_RUMBLE:
switch (ha & 0xF)
{
case 0x4:
case 0x5:
/* FIXME - save high bit as rumble state */
/* mask off high bit */
b &= 0x7;
break;
}
/* fall thru */
case MBC_MBC5:
switch (ha & 0xF)
{
case 0x0:
case 0x1:
mbc.enableram = ((b & 0x0F) == 0x0A);
break;
case 0x2:
if ((b & 0xFF) == 0) b = 0x01;
mbc.rombank = (mbc.rombank & 0x100) | (b & 0xFF);
break;
case 0x3:
mbc.rombank = (mbc.rombank & 0xFF) | ((int)(b&1)<<8);
break;
case 0x4:
case 0x5:
mbc.rambank = b & 0x0f;
break;
}
break;
case MBC_HUC1: /* FIXME - this is all guesswork -- is it right??? */
switch (ha & 0xE)
{
case 0x0:
mbc.enableram = ((b & 0x0F) == 0x0A);
break;
case 0x2:
if ((b & 0x1F) == 0) b = 0x01;
mbc.rombank = (mbc.rombank & 0x60) | (b & 0x1F);
break;
case 0x4:
if (mbc.model)
{
mbc.rambank = b & 0x03;
break;
}
mbc.rombank = (mbc.rombank & 0x1F) | ((int)(b&3)<<5);
break;
case 0x6:
mbc.model = b & 0x1;
break;
}
break;
case MBC_HUC3:
switch (ha & 0xE)
{
case 0x0:
mbc.enableram = ((b & 0x0F) == 0x0A);
break;
case 0x2:
b &= 0x7F;
mbc.rombank = b ? b : 1;
break;
case 0x4:
rtc.sel = b & 0x0f;
mbc.rambank = b & 0x03;
break;
case 0x6:
rtc_latch(b);
break;
}
break;
}
mbc.rombank &= (mbc.romsize - 1);
mbc.rambank &= (mbc.ramsize - 1);
/* printf("%02X\n", mbc.rombank); */
mem_updatemap();
}
/*
* mem_write is the basic write function. Although it should only be
* called when the write map contains a NULL for the requested address
* region, it accepts writes to any address.
*/
void mem_write(int a, byte b)
{
int n;
byte ha = (a>>12) & 0xE;
/* printf("write to 0x%04X: 0x%02X\n", a, b); */
switch (ha)
{
case 0x0:
case 0x2:
case 0x4:
case 0x6:
mbc_write(a, b);
break;
case 0x8:
/* if ((R_STAT & 0x03) == 0x03) break; */
vram_write(a & 0x1FFF, b);
break;
case 0xA:
if (!mbc.enableram) break;
if (rtc.sel&8)
{
rtc_write(b);
break;
}
ram.sbank[mbc.rambank][a & 0x1FFF] = b;
break;
case 0xC:
if ((a & 0xF000) == 0xC000)
{
ram.ibank[0][a & 0x0FFF] = b;
break;
}
n = R_SVBK & 0x07;
ram.ibank[n?n:1][a & 0x0FFF] = b;
break;
case 0xE:
if (a < 0xFE00)
{
mem_write(a & 0xDFFF, b);
break;
}
if ((a & 0xFF00) == 0xFE00)
{
/* if (R_STAT & 0x02) break; */
if (a < 0xFEA0) lcd.oam.mem[a & 0xFF] = b;
break;
}
/* return writehi(a & 0xFF, b); */
if (a >= 0xFF10 && a <= 0xFF3F)
{
sound_write(a & 0xFF, b);
break;
}
if ((a & 0xFF80) == 0xFF80 && a != 0xFFFF)
{
ram.hi[a & 0xFF] = b;
break;
}
ioreg_write(a & 0xFF, b);
}
}
/*
* mem_read is the basic read function...not useful for much anymore
* with the read map, but it's still necessary for the final messy
* region.
*/
byte mem_read(int a)
{
int n;
byte ha = (a>>12) & 0xE;
/* printf("read %04x\n", a); */
switch (ha)
{
case 0x0:
case 0x2:
return rom.bank[0][a];
case 0x4:
case 0x6:
return rom.bank[mbc.rombank][a & 0x3FFF];
case 0x8:
/* if ((R_STAT & 0x03) == 0x03) return 0xFF; */
return lcd.vbank[R_VBK&1][a & 0x1FFF];
case 0xA:
if (!mbc.enableram && mbc.type == MBC_HUC3)
return 0x01;
if (!mbc.enableram)
return 0xFF;
if (rtc.sel&8)
return rtc.regs[rtc.sel&7];
return ram.sbank[mbc.rambank][a & 0x1FFF];
case 0xC:
if ((a & 0xF000) == 0xC000)
return ram.ibank[0][a & 0x0FFF];
n = R_SVBK & 0x07;
return ram.ibank[n?n:1][a & 0x0FFF];
case 0xE:
if (a < 0xFE00) return mem_read(a & 0xDFFF);
if ((a & 0xFF00) == 0xFE00)
{
/* if (R_STAT & 0x02) return 0xFF; */
if (a < 0xFEA0) return lcd.oam.mem[a & 0xFF];
return 0xFF;
}
/* return readhi(a & 0xFF); */
if (a == 0xFFFF) return REG(0xFF);
if (a >= 0xFF10 && a <= 0xFF3F)
return sound_read(a & 0xFF);
if ((a & 0xFF80) == 0xFF80)
return ram.hi[a & 0xFF];
return ioreg_read(a & 0xFF);
}
return 0xff; /* not reached */
}
void mbc_reset()
{
mbc.rombank = 1;
mbc.rambank = 0;
mbc.enableram = 0;
mem_updatemap();
}

79
mem.h Normal file
View File

@ -0,0 +1,79 @@
#ifndef __MEM_H__
#define __MEM_H__
#include "defs.h"
#define MBC_NONE 0
#define MBC_MBC1 1
#define MBC_MBC2 2
#define MBC_MBC3 3
#define MBC_MBC5 5
#define MBC_RUMBLE 15
#define MBC_HUC1 0xC1
#define MBC_HUC3 0xC3
struct mbc
{
int type;
int model;
int rombank;
int rambank;
int romsize;
int ramsize;
int enableram;
int batt;
byte *rmap[0x10], *wmap[0x10];
};
struct rom
{
byte (*bank)[16384];
char name[20];
};
struct ram
{
byte hi[256];
byte ibank[8][4096];
byte (*sbank)[8192];
int loaded;
};
extern struct mbc mbc;
extern struct rom rom;
extern struct ram ram;
void mem_updatemap();
void ioreg_write(byte r, byte b);
void mbc_write(int a, byte b);
void mem_write(int a, byte b);
byte mem_read(int a);
#define READB(a) ( mbc.rmap[(a)>>12] \
? mbc.rmap[(a)>>12][(a)] \
: mem_read((a)) )
#define READW(a) ( READB((a)) | ((word)READB((a)+1)<<8) )
#define WRITEB(a, b) ( mbc.wmap[(a)>>12] \
? ( mbc.wmap[(a)>>12][(a)] = (b) ) \
: ( mem_write((a), (b)), (b) ) )
#define WRITEW(a, w) ( WRITEB((a), (w)&0xFF), WRITEB((a)+1, (w)>>8) )
#endif

57
newsound.c Normal file
View File

@ -0,0 +1,57 @@
/*
* new sound core for 1.1.x
*/
enum sevcode
{
SEV_S1E,
SEV_S2E,
SEV_S3E,
SEV_S4E,
SEV_S1L,
SEV_S2L,
SEV_S3L,
SEV_S4L,
SEV_SW,
SEV_WAV
};
struct sev
{
int prev, next;
int time;
};
static struct sev *sevs;
void sound_mix(int cycles)
{
}
void sound_update(int force)
{
int now = 0;
for (;;)
{
if (sevs->time > cpu.snd) break;
}
}

532
noise.h Normal file
View File

@ -0,0 +1,532 @@
#ifndef __NOISE_H__
#define __NOISE_H__
#include "defs.h"
static byte noise7[] =
{
0xfd,0xf3,0xd7,0x0d,0xd3,0x15,0x82,0xf1,
0xdb,0x25,0x21,0x39,0x68,0x8c,0xd5,0x00,
};
static byte noise15[] =
{
0xff,0xfd,0xff,0xf3,0xff,0xd7,0xff,0x0f,
0xfd,0xdf,0xf3,0x3f,0xd5,0x7f,0x00,0xfd,
0xfd,0xf3,0xf3,0xd7,0xd7,0x0f,0x0d,0xdd,
0xd3,0x33,0x15,0x55,0x80,0x02,0xff,0xf1,
0xff,0xdb,0xff,0x27,0xfd,0x2f,0xf1,0x1f,
0xd9,0xbf,0x2a,0x7d,0x02,0xf1,0xf1,0xdb,
0xdb,0x27,0x25,0x2d,0x21,0x11,0x39,0x99,
0x6a,0xa8,0x80,0x0c,0xff,0xd5,0xff,0x03,
0xfd,0xf7,0xf3,0xcf,0xd7,0x5f,0x0c,0x3d,
0xd7,0x73,0x0c,0xd5,0xd5,0x03,0x01,0xf5,
0xfb,0xc3,0xe7,0x77,0xac,0xce,0x15,0x5b,
0x80,0x26,0xff,0x29,0xfd,0x0b,0xf1,0xc7,
0xdb,0x6f,0x24,0x9d,0x24,0xb1,0x24,0x59,
0x26,0x29,0x2b,0x09,0x05,0xc9,0xe3,0x4b,
0xb4,0x46,0x46,0x6a,0x6a,0x82,0x80,0xf0,
0xfd,0xdd,0xf3,0x33,0xd5,0x57,0x00,0x0d,
0xff,0xd3,0xff,0x17,0xfd,0x8f,0xf2,0xdf,
0xd1,0x3f,0x19,0x7d,0xa8,0xf2,0x0d,0xd3,
0xd3,0x17,0x15,0x8d,0x82,0xd2,0xf1,0x11,
0xd9,0x9b,0x2a,0xa5,0x00,0x21,0xff,0x3b,
0xfd,0x67,0xf0,0xaf,0xdc,0x1f,0x37,0xbd,
0x4e,0x70,0x5a,0xde,0x21,0x3b,0x39,0x65,
0x68,0xa0,0x8c,0x3c,0xd7,0x75,0x0c,0xc1,
0xd5,0x7b,0x00,0xe5,0xfd,0xa3,0xf2,0x37,
0xd3,0x4f,0x14,0x5d,0x86,0x32,0xeb,0x51,
0x84,0x1a,0xe7,0xa1,0xae,0x3a,0x1b,0x63,
0xa4,0xb6,0x24,0x4b,0x26,0x45,0x2a,0x61,
0x02,0xb9,0xf0,0x6b,0xde,0x87,0x38,0xed,
0x6d,0x90,0x92,0x9c,0x90,0xb4,0x9c,0x44,
0xb6,0x64,0x4a,0xa6,0x40,0x2a,0x7f,0x02,
0xfd,0xf1,0xf3,0xdb,0xd7,0x27,0x0d,0x2d,
0xd1,0x13,0x19,0x95,0xaa,0x82,0x00,0xf3,
0xfd,0xd7,0xf3,0x0f,0xd5,0xdf,0x03,0x3d,
0xf5,0x73,0xc0,0xd7,0x7d,0x0c,0xf1,0xd5,
0xdb,0x03,0x25,0xf5,0x23,0xc1,0x37,0x79,
0x4c,0xe8,0x55,0x8e,0x02,0xdb,0xf1,0x27,
0xd9,0x2f,0x29,0x1d,0x09,0xb1,0xca,0x5b,
0x42,0x24,0x73,0x26,0xd5,0x29,0x01,0x09,
0xf9,0xcb,0xeb,0x47,0x84,0x6e,0xe6,0x99,
0xa8,0xaa,0x0c,0x03,0xd7,0xf7,0x0f,0xcd,
0xdf,0x53,0x3c,0x15,0x77,0x80,0xce,0xfd,
0x59,0xf0,0x2b,0xdf,0x07,0x3d,0xed,0x73,
0x90,0xd6,0x9d,0x08,0xb1,0xcc,0x5b,0x56,
0x24,0x0b,0x27,0xc5,0x2f,0x61,0x1c,0xb9,
0xb4,0x6a,0x46,0x82,0x68,0xf2,0x8d,0xd0,
0xd3,0x1d,0x15,0xb1,0x82,0x5a,0xf2,0x21,
0xd3,0x3b,0x15,0x65,0x80,0xa2,0xfc,0x31,
0xf7,0x5b,0xcc,0x27,0x57,0x2c,0x0d,0x17,
0xd1,0x8f,0x1a,0xdd,0xa1,0x32,0x39,0x53,
0x68,0x14,0x8f,0x84,0xde,0xe5,0x39,0xa1,
0x6a,0x38,0x83,0x6c,0xf4,0x95,0xc4,0x83,
0x64,0xf4,0xa5,0xc4,0x23,0x67,0x34,0xad,
0x44,0x10,0x67,0x9e,0xae,0xb8,0x18,0x6f,
0xae,0x9e,0x18,0xbb,0xac,0x66,0x16,0xab,
0x88,0x06,0xcf,0xe9,0x5f,0x88,0x3e,0xcf,
0x79,0x5c,0xe8,0x35,0x8f,0x42,0xdc,0x71,
0x36,0xd9,0x49,0x28,0x49,0x0e,0x49,0xda,
0x4b,0x22,0x45,0x32,0x61,0x52,0xb8,0x10,
0x6f,0x9e,0x9e,0xb8,0xb8,0x6c,0x6e,0x96,
0x98,0x88,0xac,0xcc,0x15,0x57,0x80,0x0e,
0xff,0xd9,0xff,0x2b,0xfd,0x07,0xf1,0xef,
0xdb,0x9f,0x26,0xbd,0x28,0x71,0x0e,0xd9,
0xd9,0x2b,0x29,0x05,0x09,0xe1,0xcb,0xbb,
0x46,0x64,0x6a,0xa6,0x80,0x28,0xff,0x0d,
0xfd,0xd3,0xf3,0x17,0xd5,0x8f,0x02,0xdd,
0xf1,0x33,0xd9,0x57,0x28,0x0d,0x0f,0xd1,
0xdf,0x1b,0x3d,0xa5,0x72,0x20,0xd3,0x3d,
0x15,0x71,0x80,0xda,0xfd,0x21,0xf1,0x3b,
0xd9,0x67,0x28,0xad,0x0c,0x11,0xd7,0x9b,
0x0e,0xa5,0xd8,0x23,0x2f,0x35,0x1d,0x41,
0xb0,0x7a,0x5e,0xe2,0x39,0xb3,0x6a,0x54,
0x82,0x04,0xf3,0xe5,0xd7,0xa3,0x0e,0x35,
0xdb,0x43,0x24,0x75,0x26,0xc1,0x29,0x79,
0x08,0xe9,0xcd,0x8b,0x52,0xc4,0x11,0x67,
0x98,0xae,0xac,0x18,0x17,0xaf,0x8e,0x1e,
0xdb,0xb9,0x26,0x69,0x2a,0x89,0x00,0xc9,
0xfd,0x4b,0xf0,0x47,0xde,0x6f,0x3a,0x9d,
0x60,0xb0,0xbc,0x5c,0x76,0x36,0xcb,0x49,
0x44,0x48,0x66,0x4e,0xaa,0x58,0x02,0x2f,
0xf3,0x1f,0xd5,0xbf,0x02,0x7d,0xf2,0xf3,
0xd1,0xd7,0x1b,0x0d,0xa5,0xd2,0x23,0x13,
0x35,0x95,0x42,0x80,0x70,0xfe,0xdd,0xf9,
0x33,0xe9,0x57,0x88,0x0e,0xcf,0xd9,0x5f,
0x28,0x3d,0x0f,0x71,0xdc,0xdb,0x35,0x25,
0x41,0x20,0x79,0x3e,0xe9,0x79,0x88,0xea,
0xcd,0x81,0x52,0xf8,0x11,0xef,0x9b,0x9e,
0xa6,0xb8,0x28,0x6f,0x0e,0x9d,0xd8,0xb3,
0x2c,0x55,0x16,0x01,0x8b,0xfa,0xc7,0xe1,
0x6f,0xb8,0x9e,0x6c,0xba,0x94,0x60,0x86,
0xbc,0xe8,0x75,0x8e,0xc2,0xd9,0x71,0x28,
0xd9,0x0d,0x29,0xd1,0x0b,0x19,0xc5,0xab,
0x62,0x04,0xb3,0xe4,0x57,0xa6,0x0e,0x2b,
0xdb,0x07,0x25,0xed,0x23,0x91,0x36,0x99,
0x48,0xa8,0x4c,0x0e,0x57,0xda,0x0f,0x23,
0xdd,0x37,0x31,0x4d,0x58,0x50,0x2e,0x1f,
0x1b,0xbd,0xa6,0x72,0x2a,0xd3,0x01,0x15,
0xf9,0x83,0xea,0xf7,0x81,0xce,0xfb,0x59,
0xe4,0x2b,0xa7,0x06,0x2d,0xeb,0x13,0x85,
0x96,0xe2,0x89,0xb0,0xca,0x5d,0x42,0x30,
0x73,0x5e,0xd4,0x39,0x07,0x69,0xec,0x8b,
0x94,0xc6,0x85,0x68,0xe0,0x8d,0xbc,0xd2,
0x75,0x12,0xc1,0x91,0x7a,0x98,0xe0,0xad,
0xbc,0x12,0x77,0x92,0xce,0x91,0x58,0x98,
0x2c,0xaf,0x14,0x1d,0x87,0xb2,0xee,0x51,
0x9a,0x1a,0xa3,0xa0,0x36,0x3f,0x4b,0x7c,
0x44,0xf6,0x65,0xca,0xa3,0x40,0x34,0x7f,
0x46,0xfc,0x69,0xf6,0x8b,0xc8,0xc7,0x4d,
0x6c,0x50,0x96,0x1c,0x8b,0xb4,0xc6,0x45,
0x6a,0x60,0x82,0xbc,0xf0,0x75,0xde,0xc3,
0x39,0x75,0x68,0xc0,0x8d,0x7c,0xd0,0xf5,
0x1d,0xc1,0xb3,0x7a,0x54,0xe2,0x05,0xb3,
0xe2,0x57,0xb2,0x0e,0x53,0xda,0x17,0x23,
0x8d,0x36,0xd1,0x49,0x18,0x49,0xae,0x4a,
0x1a,0x43,0xa2,0x76,0x32,0xcb,0x51,0x44,
0x18,0x67,0xae,0xae,0x18,0x1b,0xaf,0xa6,
0x1e,0x2b,0xbb,0x06,0x65,0xea,0xa3,0x80,
0x36,0xff,0x49,0xfc,0x4b,0xf6,0x47,0xca,
0x6f,0x42,0x9c,0x70,0xb6,0xdc,0x49,0x36,
0x49,0x4a,0x48,0x42,0x4e,0x72,0x5a,0xd2,
0x21,0x13,0x39,0x95,0x6a,0x80,0x80,0xfc,
0xfd,0xf5,0xf3,0xc3,0xd7,0x77,0x0c,0xcd,
0xd5,0x53,0x00,0x15,0xff,0x83,0xfe,0xf7,
0xf9,0xcf,0xeb,0x5f,0x84,0x3e,0xe7,0x79,
0xac,0xea,0x15,0x83,0x82,0xf6,0xf1,0xc9,
0xdb,0x4b,0x24,0x45,0x26,0x61,0x2a,0xb9,
0x00,0x69,0xfe,0x8b,0xf8,0xc7,0xed,0x6f,
0x90,0x9e,0x9c,0xb8,0xb4,0x6c,0x46,0x96,
0x68,0x8a,0x8c,0xc0,0xd5,0x7d,0x00,0xf1,
0xfd,0xdb,0xf3,0x27,0xd5,0x2f,0x01,0x1d,
0xf9,0xb3,0xea,0x57,0x82,0x0e,0xf3,0xd9,
0xd7,0x2b,0x0d,0x05,0xd1,0xe3,0x1b,0xb5,
0xa6,0x42,0x2a,0x73,0x02,0xd5,0xf1,0x03,
0xd9,0xf7,0x2b,0xcd,0x07,0x51,0xec,0x1b,
0x97,0xa6,0x8e,0x28,0xdb,0x0d,0x25,0xd1,
0x23,0x19,0x35,0xa9,0x42,0x08,0x73,0xce,
0xd7,0x59,0x0c,0x29,0xd7,0x0b,0x0d,0xc5,
0xd3,0x63,0x14,0xb5,0x84,0x42,0xe6,0x71,
0xaa,0xda,0x01,0x23,0xf9,0x37,0xe9,0x4f,
0x88,0x5e,0xce,0x39,0x5b,0x68,0x24,0x8f,
0x24,0xdd,0x25,0x31,0x21,0x59,0x38,0x29,
0x6f,0x08,0x9d,0xcc,0xb3,0x54,0x54,0x06,
0x07,0xeb,0xef,0x87,0x9e,0xee,0xb9,0x98,
0x6a,0xae,0x80,0x18,0xff,0xad,0xfe,0x13,
0xfb,0x97,0xe6,0x8f,0xa8,0xde,0x0d,0x3b,
0xd1,0x67,0x18,0xad,0xac,0x12,0x17,0x93,
0x8e,0x96,0xd8,0x89,0x2c,0xc9,0x15,0x49,
0x80,0x4a,0xfe,0x41,0xfa,0x7b,0xe2,0xe7,
0xb1,0xae,0x5a,0x1a,0x23,0xa3,0x36,0x35,
0x4b,0x40,0x44,0x7e,0x66,0xfa,0xa9,0xe0,
0x0b,0xbf,0xc6,0x7f,0x6a,0xfc,0x81,0xf4,
0xfb,0xc5,0xe7,0x63,0xac,0xb6,0x14,0x4b,
0x86,0x46,0xea,0x69,0x82,0x8a,0xf0,0xc1,
0xdd,0x7b,0x30,0xe5,0x5d,0xa0,0x32,0x3f,
0x53,0x7c,0x14,0xf7,0x85,0xce,0xe3,0x59,
0xb4,0x2a,0x47,0x02,0x6d,0xf2,0x93,0xd0,
0x97,0x1c,0x8d,0xb4,0xd2,0x45,0x12,0x61,
0x92,0xba,0x90,0x60,0x9e,0xbc,0xb8,0x74,
0x6e,0xc6,0x99,0x68,0xa8,0x8c,0x0c,0xd7,
0xd5,0x0f,0x01,0xdd,0xfb,0x33,0xe5,0x57,
0xa0,0x0e,0x3f,0xdb,0x7f,0x24,0xfd,0x25,
0xf1,0x23,0xd9,0x37,0x29,0x4d,0x08,0x51,
0xce,0x1b,0x5b,0xa4,0x26,0x27,0x2b,0x2d,
0x05,0x11,0xe1,0x9b,0xba,0xa6,0x60,0x2a,
0xbf,0x00,0x7d,0xfe,0xf3,0xf9,0xd7,0xeb,
0x0f,0x85,0xde,0xe3,0x39,0xb5,0x6a,0x40,
0x82,0x7c,0xf2,0xf5,0xd1,0xc3,0x1b,0x75,
0xa4,0xc2,0x25,0x73,0x20,0xd5,0x3d,0x01,
0x71,0xf8,0xdb,0xed,0x27,0x91,0x2e,0x99,
0x18,0xa9,0xac,0x0a,0x17,0xc3,0x8f,0x76,
0xdc,0xc9,0x35,0x49,0x40,0x48,0x7e,0x4e,
0xfa,0x59,0xe2,0x2b,0xb3,0x06,0x55,0xe2,
0x03,0x83,0xf6,0xf7,0xc9,0xcf,0x4b,0x5c,
0x04,0x3e,0x67,0x4e,0xac,0x60,0x17,0x7f,
0x80,0xfe,0xc1,0xf9,0x7b,0xe8,0xe7,0x87,
0xae,0xc2,0x19,0x93,0xfc,0x96,0x08,0x8f,
0xc0,0xe7,0xfc,0x2c,0xf0,0x1d,0xcc,0xc3,
0x9e,0x70,0x00,0xc0,0x63,0x7f,0x54,0x78,
0x40,0xfe,0x61,0x9b,0xf3,0x40,0x64,0x3f,
0x0f,0xf8,0x2c,0xf3,0x3f,0x99,0x83,0x2a,
0x79,0x07,0xcb,0xe1,0x9f,0xcc,0xce,0x60,
0x6c,0x00,0x84,0x7c,0x0f,0xf5,0xe8,0xcf,
0x15,0x66,0x80,0xb0,0xf8,0x5d,0xf4,0x33,
0x8a,0x57,0x44,0x0c,0x67,0xd6,0xaf,0x08,
0x1f,0xcf,0xb3,0x5e,0x54,0x3a,0x07,0x63,
0xec,0xb7,0x94,0x4e,0x86,0x58,0xea,0x2d,
0x83,0x12,0xf5,0x91,0xc2,0x9b,0x70,0xa4,
0xdc,0x25,0x37,0x21,0x4d,0x38,0x51,0x6e,
0x18,0x9b,0xac,0xa6,0x14,0x2b,0x87,0x06,
0xed,0xe9,0x93,0x8a,0x96,0xc0,0x89,0x7c,
0xc8,0xf5,0x4d,0xc0,0x53,0x7e,0x14,0xfb,
0x85,0xe6,0xe3,0xa9,0xb6,0x0a,0x4b,0xc2,
0x47,0x72,0x6c,0xd2,0x95,0x10,0x81,0x9c,
0xfa,0xb5,0xe0,0x43,0xbe,0x76,0x7a,0xca,
0xe1,0x41,0xb8,0x7a,0x6e,0xe2,0x99,0xb0,
0xaa,0x5c,0x02,0x37,0xf3,0x4f,0xd4,0x5f,
0x06,0x3d,0xeb,0x73,0x84,0xd6,0xe5,0x09,
0xa1,0xca,0x3b,0x43,0x64,0x74,0xa6,0xc4,
0x29,0x67,0x08,0xad,0xcc,0x13,0x57,0x94,
0x0e,0x87,0xd8,0xef,0x2d,0x9d,0x12,0xb1,
0x90,0x5a,0x9e,0x20,0xbb,0x3c,0x65,0x76,
0xa0,0xc8,0x3d,0x4f,0x70,0x5c,0xde,0x35,
0x3b,0x41,0x64,0x78,0xa6,0xec,0x29,0x97,
0x0a,0x8d,0xc0,0xd3,0x7d,0x14,0xf1,0x85,
0xda,0xe3,0x21,0xb5,0x3a,0x41,0x62,0x78,
0xb2,0xec,0x51,0x96,0x1a,0x8b,0xa0,0xc6,
0x3d,0x6b,0x70,0x84,0xdc,0xe5,0x35,0xa1,
0x42,0x38,0x73,0x6e,0xd4,0x99,0x04,0xa9,
0xe4,0x0b,0xa7,0xc6,0x2f,0x6b,0x1c,0x85,
0xb4,0xe2,0x45,0xb2,0x62,0x52,0xb2,0x10,
0x53,0x9e,0x16,0xbb,0x88,0x66,0xce,0xa9,
0x58,0x08,0x2f,0xcf,0x1f,0x5d,0xbc,0x32,
0x77,0x52,0xcc,0x11,0x57,0x98,0x0e,0xaf,
0xd8,0x1f,0x2f,0xbd,0x1e,0x71,0xba,0xda,
0x61,0x22,0xb9,0x30,0x69,0x5e,0x88,0x38,
0xcf,0x6d,0x5c,0x90,0x34,0x9f,0x44,0xbc,
0x64,0x76,0xa6,0xc8,0x29,0x4f,0x08,0x5d,
0xce,0x33,0x5b,0x54,0x24,0x07,0x27,0xed,
0x2f,0x91,0x1e,0x99,0xb8,0xaa,0x6c,0x02,
0x97,0xf0,0x8f,0xdc,0xdf,0x35,0x3d,0x41,
0x70,0x78,0xde,0xed,0x39,0x91,0x6a,0x98,
0x80,0xac,0xfc,0x15,0xf7,0x83,0xce,0xf7,
0x59,0xcc,0x2b,0x57,0x04,0x0d,0xe7,0xd3,
0xaf,0x16,0x1d,0x8b,0xb2,0xc6,0x51,0x6a,
0x18,0x83,0xac,0xf6,0x15,0xcb,0x83,0x46,
0xf4,0x69,0xc6,0x8b,0x68,0xc4,0x8d,0x64,
0xd0,0xa5,0x1c,0x21,0xb7,0x3a,0x4d,0x62,
0x50,0xb2,0x1c,0x53,0xb6,0x16,0x4b,0x8a,
0x46,0xc2,0x69,0x72,0x88,0xd0,0xcd,0x1d,
0x51,0xb0,0x1a,0x5f,0xa2,0x3e,0x33,0x7b,
0x54,0xe4,0x05,0xa7,0xe2,0x2f,0xb3,0x1e,
0x55,0xba,0x02,0x63,0xf2,0xb7,0xd0,0x4f,
0x1e,0x5d,0xba,0x32,0x63,0x52,0xb4,0x10,
0x47,0x9e,0x6e,0xba,0x98,0x60,0xae,0xbc,
0x18,0x77,0xae,0xce,0x19,0x5b,0xa8,0x26,
0x0f,0x2b,0xdd,0x07,0x31,0xed,0x5b,0x90,
0x26,0x9f,0x28,0xbd,0x0c,0x71,0xd6,0xdb,
0x09,0x25,0xc9,0x23,0x49,0x34,0x49,0x46,
0x48,0x6a,0x4e,0x82,0x58,0xf2,0x2d,0xd3,
0x13,0x15,0x95,0x82,0x82,0xf0,0xf1,0xdd,
0xdb,0x33,0x25,0x55,0x20,0x01,0x3f,0xf9,
0x7f,0xe8,0xff,0x8d,0xfe,0xd3,0xf9,0x17,
0xe9,0x8f,0x8a,0xde,0xc1,0x39,0x79,0x68,
0xe8,0x8d,0x8c,0xd2,0xd5,0x11,0x01,0x99,
0xfa,0xab,0xe0,0x07,0xbf,0xee,0x7f,0x9a,
0xfe,0xa1,0xf8,0x3b,0xef,0x67,0x9c,0xae,
0xb4,0x18,0x47,0xae,0x6e,0x1a,0x9b,0xa0,
0xa6,0x3c,0x2b,0x77,0x04,0xcd,0xe5,0x53,
0xa0,0x16,0x3f,0x8b,0x7e,0xc4,0xf9,0x65,
0xe8,0xa3,0x8c,0x36,0xd7,0x49,0x0c,0x49,
0xd6,0x4b,0x0a,0x45,0xc2,0x63,0x72,0xb4,
0xd0,0x45,0x1e,0x61,0xba,0xba,0x60,0x62,
0xbe,0xb0,0x78,0x5e,0xee,0x39,0x9b,0x6a,
0xa4,0x80,0x24,0xff,0x25,0xfd,0x23,0xf1,
0x37,0xd9,0x4f,0x28,0x5d,0x0e,0x31,0xdb,
0x5b,0x24,0x25,0x27,0x21,0x2d,0x39,0x11,
0x69,0x98,0x8a,0xac,0xc0,0x15,0x7f,0x80,
0xfe,0xfd,0xf9,0xf3,0xeb,0xd7,0x87,0x0e,
0xed,0xd9,0x93,0x2a,0x95,0x00,0x81,0xfc,
0xfb,0xf5,0xe7,0xc3,0xaf,0x76,0x1c,0xcb,
0xb5,0x46,0x40,0x6a,0x7e,0x82,0xf8,0xf1,
0xed,0xdb,0x93,0x26,0x95,0x28,0x81,0x0c,
0xf9,0xd5,0xeb,0x03,0x85,0xf6,0xe3,0xc9,
0xb7,0x4a,0x4c,0x42,0x56,0x72,0x0a,0xd3,
0xc1,0x17,0x79,0x8c,0xea,0xd5,0x81,0x02,
0xf9,0xf1,0xeb,0xdb,0x87,0x26,0xed,0x29,
0x91,0x0a,0x99,0xc0,0xab,0x7c,0x04,0xf7,
0xe5,0xcf,0xa3,0x5e,0x34,0x3b,0x47,0x64,
0x6c,0xa6,0x94,0x28,0x87,0x0c,0xed,0xd5,
0x93,0x02,0x95,0xf0,0x83,0xdc,0xf7,0x35,
0xcd,0x43,0x50,0x74,0x1e,0xc7,0xb9,0x6e,
0x68,0x9a,0x8c,0xa0,0xd4,0x3d,0x07,0x71,
0xec,0xdb,0x95,0x26,0x81,0x28,0xf9,0x0d,
0xe9,0xd3,0x8b,0x16,0xc5,0x89,0x62,0xc8,
0xb1,0x4c,0x58,0x56,0x2e,0x0b,0x1b,0xc5,
0xa7,0x62,0x2c,0xb3,0x14,0x55,0x86,0x02,
0xeb,0xf1,0x87,0xda,0xef,0x21,0x9d,0x3a,
0xb1,0x60,0x58,0xbe,0x2c,0x7b,0x16,0xe5,
0x89,0xa2,0xca,0x31,0x43,0x58,0x74,0x2e,
0xc7,0x19,0x6d,0xa8,0x92,0x0c,0x93,0xd4,
0x97,0x04,0x8d,0xe4,0xd3,0xa5,0x16,0x21,
0x8b,0x3a,0xc5,0x61,0x60,0xb8,0xbc,0x6c,
0x76,0x96,0xc8,0x89,0x4c,0xc8,0x55,0x4e,
0x00,0x5b,0xfe,0x27,0xfb,0x2f,0xe5,0x1f,
0xa1,0xbe,0x3a,0x7b,0x62,0xe4,0xb1,0xa4,
0x5a,0x26,0x23,0x2b,0x35,0x05,0x41,0xe0,
0x7b,0xbe,0xe6,0x79,0xaa,0xea,0x01,0x83,
0xfa,0xf7,0xe1,0xcf,0xbb,0x5e,0x64,0x3a,
0xa7,0x60,0x2c,0xbf,0x14,0x7d,0x86,0xf2,
0xe9,0xd1,0x8b,0x1a,0xc5,0xa1,0x62,0x38,
0xb3,0x6c,0x54,0x96,0x04,0x8b,0xe4,0xc7,
0xa5,0x6e,0x20,0x9b,0x3c,0xa5,0x74,0x20,
0xc7,0x3d,0x6d,0x70,0x90,0xdc,0x9d,0x34,
0xb1,0x44,0x58,0x66,0x2e,0xab,0x18,0x05,
0xaf,0xe2,0x1f,0xb3,0xbe,0x56,0x7a,0x0a,
0xe3,0xc1,0xb7,0x7a,0x4c,0xe2,0x55,0xb2,
0x02,0x53,0xf2,0x17,0xd3,0x8f,0x16,0xdd,
0x89,0x32,0xc9,0x51,0x48,0x18,0x4f,0xae,
0x5e,0x1a,0x3b,0xa3,0x66,0x34,0xab,0x44,
0x04,0x67,0xe6,0xaf,0xa8,0x1e,0x0f,0xbb,
0xde,0x67,0x3a,0xad,0x60,0x10,0xbf,0x9c,
0x7e,0xb6,0xf8,0x49,0xee,0x4b,0x9a,0x46,
0xa2,0x68,0x32,0x8f,0x50,0xdc,0x1d,0x37,
0xb1,0x4e,0x58,0x5a,0x2e,0x23,0x1b,0x35,
0xa5,0x42,0x20,0x73,0x3e,0xd5,0x79,0x00,
0xe9,0xfd,0x8b,0xf2,0xc7,0xd1,0x6f,0x18,
0x9d,0xac,0xb2,0x14,0x53,0x86,0x16,0xeb,
0x89,0x86,0xca,0xe9,0x41,0x88,0x7a,0xce,
0xe1,0x59,0xb8,0x2a,0x6f,0x02,0x9d,0xf0,
0xb3,0xdc,0x57,0x36,0x0d,0x4b,0xd0,0x47,
0x1e,0x6d,0xba,0x92,0x60,0x92,0xbc,0x90,
0x74,0x9e,0xc4,0xb9,0x64,0x68,0xa6,0x8c,
0x28,0xd7,0x0d,0x0d,0xd1,0xd3,0x1b,0x15,
0xa5,0x82,0x22,0xf3,0x31,0xd5,0x5b,0x00,
0x25,0xff,0x23,0xfd,0x37,0xf1,0x4f,0xd8,
0x5f,0x2e,0x3d,0x1b,0x71,0xa4,0xda,0x25,
0x23,0x21,0x35,0x39,0x41,0x68,0x78,0x8e,
0xec,0xd9,0x95,0x2a,0x81,0x00,0xf9,0xfd,
0xeb,0xf3,0x87,0xd6,0xef,0x09,0x9d,0xca,
0xb3,0x40,0x54,0x7e,0x06,0xfb,0xe9,0xe7,
0x8b,0xae,0xc6,0x19,0x6b,0xa8,0x86,0x0c,
0xeb,0xd5,0x87,0x02,0xed,0xf1,0x93,0xda,
0x97,0x20,0x8d,0x3c,0xd1,0x75,0x18,0xc1,
0xad,0x7a,0x10,0xe3,0x9d,0xb6,0xb2,0x48,
0x52,0x4e,0x12,0x5b,0x92,0x26,0x93,0x28,
0x95,0x0c,0x81,0xd4,0xfb,0x05,0xe5,0xe3,
0xa3,0xb6,0x36,0x4b,0x4a,0x44,0x42,0x66,
0x72,0xaa,0xd0,0x01,0x1f,0xf9,0xbf,0xea,
0x7f,0x82,0xfe,0xf1,0xf9,0xdb,0xeb,0x27,
0x85,0x2e,0xe1,0x19,0xb9,0xaa,0x6a,0x02,
0x83,0xf0,0xf7,0xdd,0xcf,0x33,0x5d,0x54,
0x30,0x07,0x5f,0xec,0x3f,0x97,0x7e,0x8c,
0xf8,0xd5,0xed,0x03,0x91,0xf6,0x9b,0xc8,
0xa7,0x4c,0x2c,0x57,0x16,0x0d,0x8b,0xd2,
0xc7,0x11,0x6d,0x98,0x92,0xac,0x90,0x14,
0x9f,0x84,0xbe,0xe4,0x79,0xa6,0xea,0x29,
0x83,0x0a,0xf5,0xc1,0xc3,0x7b,0x74,0xe4,
0xc5,0xa5,0x62,0x20,0xb3,0x3c,0x55,0x76,
0x00,0xcb,0xfd,0x47,0xf0,0x6f,0xde,0x9f,
0x38,0xbd,0x6c,0x70,0x96,0xdc,0x89,0x34,
0xc9,0x45,0x48,0x60,0x4e,0xbe,0x58,0x7a,
0x2e,0xe3,0x19,0xb5,0xaa,0x42,0x02,0x73,
0xf2,0xd7,0xd1,0x0f,0x19,0xdd,0xab,0x32,
0x05,0x53,0xe0,0x17,0xbf,0x8e,0x7e,0xda,
0xf9,0x21,0xe9,0x3b,0x89,0x66,0xc8,0xa9,
0x4c,0x08,0x57,0xce,0x0f,0x5b,0xdc,0x27,
0x37,0x2d,0x4d,0x10,0x51,0x9e,0x1a,0xbb,
0xa0,0x66,0x3e,0xab,0x78,0x04,0xef,0xe5,
0x9f,0xa2,0xbe,0x30,0x7b,0x5e,0xe4,0x39,
0xa7,0x6a,0x2c,0x83,0x14,0xf5,0x85,0xc2,
0xe3,0x71,0xb4,0xda,0x45,0x22,0x61,0x32,
0xb9,0x50,0x68,0x1e,0x8f,0xb8,0xde,0x6d,
0x3a,0x91,0x60,0x98,0xbc,0xac,0x74,0x16,
0xc7,0x89,0x6e,0xc8,0x99,0x4c,0xa8,0x54,
0x0e,0x07,0xdb,0xef,0x27,0x9d,0x2e,0xb1,
0x18,0x59,0xae,0x2a,0x1b,0x03,0xa5,0xf6,
0x23,0xcb,0x37,0x45,0x4c,0x60,0x56,0xbe,
0x08,0x7b,0xce,0xe7,0x59,0xac,0x2a,0x17,
0x03,0x8d,0xf6,0xd3,0xc9,0x17,0x49,0x8c,
0x4a,0xd6,0x41,0x0a,0x79,0xc2,0xeb,0x71,
0x84,0xda,0xe5,0x21,0xa1,0x3a,0x39,0x63,
0x68,0xb4,0x8c,0x44,0xd6,0x65,0x0a,0xa1,
0xc0,0x3b,0x7f,0x64,0xfc,0xa5,0xf4,0x23,
0xc7,0x37,0x6d,0x4c,0x90,0x54,0x9e,0x04,
0xbb,0xe4,0x67,0xa6,0xae,0x28,0x1b,0x0f,
0xa5,0xde,0x23,0x3b,0x35,0x65,0x40,0xa0,
0x7c,0x3e,0xf7,0x79,0xcc,0xeb,0x55,0x84,
0x02,0xe7,0xf1,0xaf,0xda,0x1f,0x23,0xbd,
0x36,0x71,0x4a,0xd8,0x41,0x2e,0x79,0x1a,
0xe9,0xa1,0x8a,0x3a,0xc3,0x61,0x74,0xb8,
0xc4,0x6d,0x66,0x90,0xa8,0x9c,0x0c,0xb7,
0xd4,0x4f,0x06,0x5d,0xea,0x33,0x83,0x56,
0xf4,0x09,0xc7,0xcb,0x6f,0x44,0x9c,0x64,
0xb6,0xa4,0x48,0x26,0x4f,0x2a,0x5d,0x02,
0x31,0xf3,0x5b,0xd4,0x27,0x07,0x2d,0xed,
0x13,0x91,0x96,0x9a,0x88,0xa0,0xcc,0x3d,
0x57,0x70,0x0c,0xdf,0xd5,0x3f,0x01,0x7d,
0xf8,0xf3,0xed,0xd7,0x93,0x0e,0x95,0xd8,
0x83,0x2c,0xf5,0x15,0xc1,0x83,0x7a,0xf4,
0xe1,0xc5,0xbb,0x62,0x64,0xb2,0xa4,0x50,
0x26,0x1f,0x2b,0xbd,0x06,0x71,0xea,0xdb,
0x81,0x26,0xf9,0x29,0xe9,0x0b,0x89,0xc6,
0xcb,0x69,0x44,0x88,0x64,0xce,0xa5,0x58,
0x20,0x2f,0x3f,0x1d,0x7d,0xb0,0xf2,0x5d,
0xd2,0x33,0x13,0x55,0x94,0x02,0x87,0xf0,
0xef,0xdd,0x9f,0x32,0xbd,0x50,0x70,0x1e,
0xdf,0xb9,0x3e,0x69,0x7a,0x88,0xe0,0xcd,
0xbd,0x52,0x70,0x12,0xdf,0x91,0x3e,0x99,
0x78,0xa8,0xec,0x0d,0x97,0xd2,0x8f,0x10,
0xdd,0x9d,0x32,0xb1,0x50,0x58,0x1e,0x2f,
0xbb,0x1e,0x65,0xba,0xa2,0x60,0x32,0xbf,
0x50,0x7c,0x1e,0xf7,0xb9,0xce,0x6b,0x5a,
0x84,0x20,0xe7,0x3d,0xad,0x72,0x10,0xd3,
0x9d,0x16,0xb1,0x88,0x5a,0xce,0x21,0x5b,
0x38,0x25,0x6f,0x20,0x9d,0x3c,0xb1,0x74,
0x58,0xc6,0x2d,0x6b,0x10,0x85,0x9c,0xe2,
0xb5,0xb0,0x42,0x5e,0x72,0x3a,0xd3,0x61,
0x14,0xb9,0x84,0x6a,0xe6,0x81,0xa8,0xfa,
0x0d,0xe3,0xd3,0xb7,0x16,0x4d,0x8a,0x52,
0xc2,0x11,0x73,0x98,0xd6,0xad,0x08,0x11,
0xcf,0x9b,0x5e,0xa4,0x38,0x27,0x6f,0x2c,
0x9d,0x14,0xb1,0x84,0x5a,0xe6,0x21,0xab,
0x3a,0x05,0x63,0xe0,0xb7,0xbc,0x4e,0x76,
0x5a,0xca,0x21,0x43,0x38,0x75,0x6e,0xc0,
0x99,0x7c,0xa8,0xf4,0x0d,0xc7,0xd3,0x6f,
0x14,0x9d,0x84,0xb2,0xe4,0x51,0xa6,0x1a,
0x2b,0xa3,0x06,0x35,0xeb,0x43,0x84,0x76,
0xe6,0xc9,0xa9,0x4a,0x08,0x43,0xce,0x77,
0x5a,0xcc,0x21,0x57,0x38,0x0d,0x6f,0xd0,
0x9f,0x1c,0xbd,0xb4,0x72,0x46,0xd2,0x69,
0x12,0x89,0x90,0xca,0x9d,0x40,0xb0,0x7c,
0x5e,0xf6,0x39,0xcb,0x6b,0x44,0x84,0x64,
0xe6,0xa5,0xa8,0x22,0x0f,0x33,0xdd,0x57,
0x30,0x0d,0x5f,0xd0,0x3f,0x1f,0x7d,0xbc,
0xf2,0x75,0xd2,0xc3,0x11,0x75,0x98,0xc2,
0xad,0x70,0x10,0xdf,0x9d,0x3e,0xb1,0x78,
0x58,0xee,0x2d,0x9b,0x12,0xa5,0x90,0x22,
0x9f,0x30,0xbd,0x5c,0x70,0x36,0xdf,0x49,
0x3c,0x49,0x76,0x48,0xca,0x4d,0x42,0x50,
0x72,0x1e,0xd3,0xb9,0x16,0x69,0x8a,0x8a,
0xc0,0xc1,0x7d,0x78,0xf0,0xed,0xdd,0x93,
0x32,0x95,0x50,0x80,0x1c,0xff,0xb5,0xfe,
0x43,0xfa,0x77,0xe2,0xcf,0xb1,0x5e,0x58,
0x3a,0x2f,0x63,0x1c,0xb5,0xb4,0x42,0x46,
0x72,0x6a,0xd2,0x81,0x10,0xf9,0x9d,0xea,
0xb3,0x80,0x56,0xfe,0x09,0xfb,0xcb,0xe7,
0x47,0xac,0x6e,0x16,0x9b,0x88,0xa6,0xcc,
0x29,0x57,0x08,0x0d,0xcf,0xd3,0x5f,0x14,
0x3d,0x87,0x72,0xec,0xd1,0x95,0x1a,0x81,
0xa0,0xfa,0x3d,0xe3,0x73,0xb4,0xd6,0x45,
0x0a,0x61,0xc2,0xbb,0x70,0x64,0xde,0xa5,
0x38,0x21,0x6f,0x38,0x9d,0x6c,0xb0,0x94,
0x5c,0x86,0x3e,0xeb,0x45,0x84,0x62,0xe6,
0xb1,0xa8,0x5a,0x0e,0x23,0xfb,0x33,0x25,
0x47,0x20,0x51,0x3e,0x19,0x7f,0xa8,0x66,
0x0c,0xfb,0xd0,0x07,0x13,0xe5,0x9f,0x83,
0xce,0x98,0x58,0xcd,0x2e,0x19,0x14,0x39,
0x86,0x3f,0xff,0x01,0x85,0xff,0xe1,0xe1,
0xb3,0xfc,0x46,0x63,0x0f,0xf8,0x00,0x53,
0xbe,0x1f,0xfb,0xc0,0xe6,0x7e,0xbc,0xf0,
0x01,0xe3,0xc3,0x9f,0xa6,0xcc,0x48,0x7e,
0x40,0x82,0x9d,0xf2,0xff,0xd6,0x07,0x13,
0xf5,0x87,0x80,0x0f,0x71,0x9c,0xfd,0x35,
0x61,0x43,0xf8,0x78,0x7e,0xcf,0x19,0x99,
0xa8,0x32,0x00,0x53,0xfc,0x17,0xfb,0x8f,
0xc6,0xdf,0xa9,0x3e,0x09,0x7b,0xc8,0xe7,
0x4d,0xac,0x52,0x16,0x13,0x8b,0x96,0xc6,
0x89,0x68,0xc8,0x8d,0x4c,0xd0,0x55,0x1e,
0x01,0xbb,0xfa,0x67,0xe2,0xaf,0xb0,0x1e,
0x5f,0xba,0x3e,0x63,0x7a,0xb4,0xe0,0x45,
0xbe,0x62,0x7a,0xb2,0xe0,0x51,0xbe,0x1a,
0x7b,0xa2,0xe6,0x31,0xab,0x5a,0x04,0x23,
0xe7,0x37,0xad,0x4e,0x10,0x5b,0x9e,0x26,
0xbb,0x28,0x65,0x0e,0xa1,0xd8,0x3b,0x2f,
0x65,0x1c,0xa1,0xb4,0x3a,0x47,0x62,0x6c,
0xb2,0x94,0x50,0x86,0x1c,0xeb,0xb5,0x86,
0x42,0xea,0x71,0x82,0xda,0xf1,0x21,0xd9,
0x3b,0x29,0x65,0x08,0xa1,0xcc,0x3b,0x57,
0x64,0x0c,0xa7,0xd4,0x2f,0x07,0x1d,0xed,
0xb3,0x92,0x56,0x92,0x08,0x93,0xcc,0x97,
0x54,0x8c,0x04,0xd7,0xe5,0x0f,0xa1,0xde,
0x3b,0x3b,0x65,0x64,0xa0,0xa4,0x3c,0x27,
0x77,0x2c,0xcd,0x15,0x51,0x80,0x1a,0xff,
0xa1,0xfe,0x3b,0xfb,0x67,0xe4,0xaf,0xa4,
0x1e,0x27,0xbb,0x2e,0x65,0x1a,0xa1,0xa0,
0x3a,0x3f,0x63,0x7c,0xb4,0xf4,0x45,0xc6,
0x63,0x6a,0xb4,0x80,0x44,0xfe,0x65,0xfa,
0xa3,0xe0,0x37,0xbf,0x4e,0x7c,0x5a,0xf6,
0x21,0xcb,0x3b,0x45,0x64,0x60,0xa6,0xbc,
0x28,0x77,0x0e,0xcd,0xd9,0x53,0x28,0x15,
0x0f,0x81,0xde,0xfb,0x39,0xe5,0x6b,0xa0,
0x86,0x3c,0xeb,0x75,0x84,0xc2,0xe5,0x71,
0xa0,0xda,0x3d,0x23,0x71,0x34,0xd9,0x45,
0x28,0x61,0x0e,0xb9,0xd8,0x6b,0x2e,0x85,
0x18,0xe1,0xad,0xba,0x12,0x63,0x92,0xb6,
0x90,0x48,0x9e,0x4c,0xba,0x54,0x62,0x06,
0xb3,0xe8,0x57,0x8e,0x0e,0xdb,0xd9,0x27,
0x29,0x2d,0x09,0x11,0xc9,0x9b,0x4a,0xa4,
0x40,0x26,0x7f,0x2a,0xfd,0x01,0xf1,0xfb,
0xdb,0xe7,0x27,0xad,0x2e,0x11,0x1b,0x99,
0xa6,0xaa,0x28,0x03,0x0f,0xf5,0xdf,0xc3,
0x3f,0x75,0x7c,0xc0,0xf5,0x7d,0xc0,0xf3,
0x7d,0xd4,0xf3,0x05,0xd5,0xe3,0x03,0xb5,
0xf6,0x43,0xca,0x77,0x42,0xcc,0x71,0x56,
0xd8,0x09,0x2f,0xc9,0x1f,0x49,0xbc,0x4a,
0x76,0x42,0xca,0x71,0x42,0xd8,0x71,0x2e,
0xd9,0x19,0x29,0xa9,0x0a,0x09,0xc3,0xcb,
0x77,0x44,0xcc,0x65,0x56,0xa0,0x08,0x3f,
0xcf,0x7f,0x5c,0xfc,0x35,0xf7,0x43,0xcc,
0x77,0x56,0xcc,0x09,0x57,0xc8,0x0f,0x4f,
0xdc,0x5f,0x36,0x3d,0x4b,0x70,0x44,0xde,
0x65,0x3a,0xa1,0x60,0x38,0xbf,0x6c,0x7c,
0x96,0xf4,0x89,0xc4,0xcb,0x65,0x44,0xa0,
0x64,0x3e,0xa7,0x78,0x2c,0xef,0x15,0x9d,
0x82,0xb2,0xf0,0x51,0xde,0x1b,0x3b,0xa5,
0x66,0x20,0xab,0x3c,0x05,0x77,0xe0,0xcf,
0xbd,0x5e,0x70,0x3a,0xdf,0x61,0x3c,0xb9,
0x74,0x68,0xc6,0x8d,0x68,0xd0,0x8d,0x1c,
0xd1,0xb5,0x1a,0x41,0xa2,0x7a,0x32,0xe3,
0x51,0xb4,0x1a,0x47,0xa2,0x6e,0x32,0x9b,
0x50,0xa4,0x1c,0x27,0xb7,0x2e,0x4d,0x1a,
0x51,0xa2,0x1a,0x33,0xa3,0x56,0x34,0x0b,
0x47,0xc4,0x6f,0x66,0x9c,0xa8,0xb4,0x0c,
0x47,0xd6,0x6f,0x0a,0x9d,0xc0,0xb3,0x7c,
0x54,0xf6,0x05,0xcb,0xe3,0x47,0xb4,0x6e,
0x46,0x9a,0x68,0xa2,0x8c,0x30,0xd7,0x5d,
0x0c,0x31,0xd7,0x5b,0x0c,0x25,0xd7,0x23,
0x0d,0x35,0xd1,0x43,0x18,0x75,0xae,0xc2,
0x19,0x73,0xa8,0xd6,0x0d,0x0b,0xd1,0xc7,
0x1b,0x6d,0xa4,0x92,0x24,0x93,0x24,0x95,
0x24,0x81,0x24,0xf9,0x25,0xe9,0x23,0x89,
0x36,0xc9,0x49,0x48,0x48,0x4e,0x4e,0x5a,
0x5a,0x22,0x23,0x33,0x35,0x55,0x40,0x00,
};
#endif

150
palette.c Normal file
View File

@ -0,0 +1,150 @@
#include "defs.h"
#include "fb.h"
static byte palmap[32768];
static byte pallock[256];
static int palrev[256];
/* Course color mapping, for when palette is exhausted. */
static byte crsmap[4][32768];
static int crsrev[4][256];
static const int crsmask[4] = { 0x7BDE, 0x739C, 0x6318, 0x4210 };
enum plstatus
{
pl_unused = 0,
pl_linger,
pl_active,
pl_locked
};
static byte bestmatch(int c)
{
byte n, best;
int r, g, b;
int r2, g2, b2, c2;
int err, besterr;
r = (c & 0x001F) << 3;
g = (c & 0x03E0) >> 2;
b = (c & 0x7C00) >> 7;
best = 0;
besterr = 1024;
for (n = 1; n; n++)
{
c2 = palrev[n];
r2 = (c2 & 0x001F) << 3;
g2 = (c2 & 0x03E0) >> 2;
b2 = (c2 & 0x7C00) >> 7;
err = abs(r-r2) + abs(b-b2) + abs(g-g2);
if (err < besterr)
{
besterr = err;
best = n;
}
}
return best;
}
static void makecourse(int c, byte n)
{
int i;
for (i = 0; i < 4; i++)
{
c &= crsmask[i];
crsmap[i][c] = n;
crsrev[i][n] = c;
}
}
static byte findcourse(int c)
{
int i;
byte n;
for (i = 0; i < 4; i++)
{
c &= crsmask[i];
n = crsmap[i][c];
if (crsrev[i][n] == c)
return n;
}
return 0;
}
void pal_lock(byte n)
{
if (!n) return;
if (pallock[n] >= pl_locked)
pallock[n]++;
else pallock[n] = pl_locked;
}
byte pal_getcolor(int c, int r, int g, int b)
{
byte n;
static byte l;
n = palmap[c];
if (n && pallock[n] && palrev[n] == c)
{
pal_lock(n);
return n;
}
for (n = l+1; n != l; n++)
{
if (!n || pallock[n] /* || n < 16 */) continue;
pal_lock(n);
palmap[c] = n;
palrev[n] = c;
makecourse(c, n);
vid_setpal(n, r, g, b);
return (l = n);
}
n = findcourse(c);
pal_lock(n);
return n;
}
void pal_release(byte n)
{
if (pallock[n] >= pl_locked)
pallock[n]--;
}
void pal_expire()
{
int i;
for (i = 0; i < 256; i++)
if (pallock[i] && pallock[i] < pl_locked)
pallock[i]--;
}
void pal_set332()
{
int i, r, g, b;
fb.indexed = 0;
fb.cc[0].r = 5;
fb.cc[1].r = 5;
fb.cc[2].r = 6;
fb.cc[0].l = 0;
fb.cc[1].l = 3;
fb.cc[2].l = 6;
i = 0;
for (b = 0; b < 4; b++) for (g = 0; g < 8; g++) for (r = 0; r < 8; r++)
vid_setpal(i++, (r<<5)|(r<<2)|(r>>1),
(g<<5)|(g<<2)|(g>>1), (b<<6)|(b<<4)|(b<<2)|b);
}

52
path.c Normal file
View File

@ -0,0 +1,52 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *strdup();
#ifdef ALT_PATH_SEP
#define SEP ';'
#else
#define SEP ':'
#endif
char *path_search(char *name, char *mode, char *path)
{
FILE *f;
static char *buf;
char *p, *n;
int l;
if (buf) free(buf); buf = 0;
if (!path || !*path || *name == '/')
return (buf = strdup(name));
buf = malloc(strlen(path) + strlen(name) + 2);
for (p = path; *p; p += l)
{
if (*p == SEP) p++;
n = strchr(p, SEP);
if (n) l = n - p;
else l = strlen(p);
strncpy(buf, p, l);
buf[l] = '/';
strcpy(buf+l+1, name);
if ((f = fopen(buf, mode)))
{
fclose(f);
return buf;
}
}
return name;
}

21
pcm.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef __PCM_H__
#define __PCM_H__
#include "defs.h"
struct pcm
{
int hz, len;
int stereo;
byte *buf;
int pos;
};
extern struct pcm pcm;
#endif

63
rc.h Normal file
View File

@ -0,0 +1,63 @@
#ifndef __RC_H__
#define __RC_H__
typedef enum rctype
{
rcv_end,
rcv_int,
rcv_string,
rcv_vector,
rcv_bool
} rcvtype_t;
typedef struct rcvar_s
{
char *name;
int type;
int len;
void *mem;
} rcvar_t;
#define RCV_END { 0, rcv_end, 0, 0 }
#define RCV_INT(n,v) { (n), rcv_int, 1, (v) }
#define RCV_STRING(n,v) { (n), rcv_string, 0, (v) }
#define RCV_VECTOR(n,v,l) { (n), rcv_vector, (l), (v) }
#define RCV_BOOL(n,v) { (n), rcv_bool, 1, (v) }
typedef struct rccmd_s
{
char *name;
int (*func)(int, char **);
} rccmd_t;
#define RCC(n,f) { (n), (f) }
#define RCC_END { 0, 0 }
void rc_export(rcvar_t *v);
void rc_exportvars(rcvar_t *vars);
int rc_findvar(char *name);
int rc_setvar_n(int i, int c, char **v);
int rc_setvar(char *name, int c, char **v);
int rc_getint_n(int i);
int *rc_getvec_n(int i);
char *rc_getstr_n(int i);
int rc_getint(char *name);
int *rc_getvec(char *name);
char *rc_getstr(char *name);
#endif

194
rccmds.c Normal file
View File

@ -0,0 +1,194 @@
#include <stdlib.h>
#include "defs.h"
#include "rc.h"
#include "hw.h"
/*
* define the command functions for the controller pad.
*/
#define CMD_PAD(b, B) \
static int (cmd_ ## b)(int c, char **v) \
{ pad_set((PAD_ ## B), v[0][0] == '+'); return 0; } \
static int (cmd_ ## b)(int c, char **v)
CMD_PAD(up, UP);
CMD_PAD(down, DOWN);
CMD_PAD(left, LEFT);
CMD_PAD(right, RIGHT);
CMD_PAD(a, A);
CMD_PAD(b, B);
CMD_PAD(start, START);
CMD_PAD(select, SELECT);
/*
* the set command is used to set rc-exported variables.
*/
static int cmd_set(int argc, char **argv)
{
if (argc < 3)
return -1;
return rc_setvar(argv[1], argc-2, argv+2);
}
/*
* the following commands allow keys to be bound to perform rc commands.
*/
static int cmd_bind(int argc, char **argv)
{
if (argc < 3)
return -1;
return rc_bindkey(argv[1], argv[2]);
}
static int cmd_unbind(int argc, char **argv)
{
if (argc < 2)
return -1;
return rc_unbindkey(argv[1]);
}
static int cmd_unbindall()
{
rc_unbindall();
return 0;
}
static int cmd_source(int argc, char **argv)
{
if (argc < 2)
return -1;
return rc_sourcefile(argv[1]);
}
static int cmd_quit()
{
exit(0);
/* NOT REACHED */
}
static int cmd_reset()
{
emu_reset();
return 0;
}
static int cmd_savestate(int argc, char **argv)
{
state_save(argc > 1 ? atoi(argv[1]) : -1);
return 0;
}
static int cmd_loadstate(int argc, char **argv)
{
state_load(argc > 1 ? atoi(argv[1]) : -1);
return 0;
}
/*
* table of command names and the corresponding functions to be called
*/
rccmd_t rccmds[] =
{
RCC("set", cmd_set),
RCC("bind", cmd_bind),
RCC("unbind", cmd_unbind),
RCC("unbindall", cmd_unbindall),
RCC("source", cmd_source),
RCC("reset", cmd_reset),
RCC("quit", cmd_quit),
RCC("savestate", cmd_savestate),
RCC("loadstate", cmd_loadstate),
RCC("+up", cmd_up),
RCC("-up", cmd_up),
RCC("+down", cmd_down),
RCC("-down", cmd_down),
RCC("+left", cmd_left),
RCC("-left", cmd_left),
RCC("+right", cmd_right),
RCC("-right", cmd_right),
RCC("+a", cmd_a),
RCC("-a", cmd_a),
RCC("+b", cmd_b),
RCC("-b", cmd_b),
RCC("+start", cmd_start),
RCC("-start", cmd_start),
RCC("+select", cmd_select),
RCC("-select", cmd_select),
RCC_END
};
int rc_command(char *line)
{
int i, argc, ret;
char *argv[128], *linecopy;
linecopy = malloc(strlen(line)+1);
strcpy(linecopy, line);
argc = splitline(argv, (sizeof argv)/(sizeof argv[0]), linecopy);
if (!argc)
{
free(linecopy);
return -1;
}
for (i = 0; rccmds[i].name; i++)
{
if (!strcmp(argv[0], rccmds[i].name))
{
ret = rccmds[i].func(argc, argv);
free(linecopy);
return ret;
}
}
/* printf("unknown command: %s\n", argv[0]); */
free(linecopy);
return -1;
}

46
rcfile.c Normal file
View File

@ -0,0 +1,46 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "defs.h"
#include "rc.h"
#include "hw.h"
char *rcpath;
char *path_search();
int rc_sourcefile(char *filename)
{
FILE *f;
char *name;
char line[256], *p;
name = path_search(filename, "r", rcpath);
f = fopen(name, "r");
if (!f) return -1;
for (;;)
{
if (feof(f)) break;
fgets(line, sizeof line, f);
if ((p = strpbrk(line, "#\r\n")))
*p = 0;
rc_command(line);
}
fclose(f);
return 0;
}
rcvar_t rcfile_exports[] =
{
RCV_STRING("rcpath", &rcpath),
RCV_END
};

86
rckeys.c Normal file
View File

@ -0,0 +1,86 @@
#include <stdlib.h>
#include <string.h>
char *strdup();
#include "defs.h"
#include "rc.h"
#include "input.h"
char *keybind[MAX_KEYS];
int rc_bindkey(char *keyname, char *cmd)
{
int key;
char *a;
key = k_keycode(keyname);
if (!key) return -1;
a = strdup(cmd);
if (!a) die("out of memory binding key\n");
if (keybind[key]) free(keybind[key]);
keybind[key] = a;
return 0;
}
int rc_unbindkey(char *keyname)
{
int key;
key = k_keycode(keyname);
if (!key) return -1;
if (keybind[key]) free(keybind[key]);
keybind[key] = NULL;
return 0;
}
void rc_unbindall()
{
int i;
for (i = 0; i < MAX_KEYS; i++)
{
if (keybind[i])
{
free(keybind[i]);
keybind[i] = NULL;
}
}
}
void rc_dokey(int key, int st)
{
if (!keybind[key]) return;
if (keybind[key][0] != '+' && !st) return;
if (st)
rc_command(keybind[key]);
else
{
keybind[key][0] = '-';
rc_command(keybind[key]);
keybind[key][0] = '+';
}
}

235
rcvars.c Normal file
View File

@ -0,0 +1,235 @@
#include <stdlib.h>
#include <string.h>
char *strdup();
#include "defs.h"
#include "rc.h"
static rcvar_t *rcvars;
static int nvars;
void rc_export(rcvar_t *v)
{
const rcvar_t end = RCV_END;
if (!v) return;
nvars++;
rcvars = realloc(rcvars, sizeof (rcvar_t) * (nvars+1));
if (!rcvars)
die("out of memory adding rcvar %s\n", v->name);
rcvars[nvars-1] = *v;
rcvars[nvars] = end;
}
void rc_exportvars(rcvar_t *vars)
{
while(vars->type)
rc_export(vars++);
}
int rc_findvar(char *name)
{
int i;
if (!rcvars) return -1;
for (i = 0; rcvars[i].name; i++)
if (!strcmp(rcvars[i].name, name))
break;
if (!rcvars[i].name)
return -1;
return i;
}
int my_atoi(const char *s)
{
int a = 0;
if (*s == '0')
{
s++;
if (*s == 'x' || *s == 'X')
{
s++;
while (*s)
{
if (isdigit(*s))
a = (a<<4) + *s - '0';
else if (strchr("ABCDEF", *s))
a = (a<<4) + *s - 'A' + 10;
else if (strchr("abcdef", *s))
a = (a<<4) + *s - 'a' + 10;
else return a;
s++;
}
return a;
}
while (*s)
{
if (strchr("01234567", *s))
a = (a<<3) + *s - '0';
else return a;
s++;
}
return a;
}
if (*s == '-')
{
s++;
for (;;)
{
if (isdigit(*s))
a = (a*10) + *s - '0';
else return -a;
s++;
}
}
while (*s)
{
if (isdigit(*s))
a = (a*10) + *s - '0';
else return a;
s++;
}
return a;
}
int rc_setvar_n(int i, int c, char **v)
{
int j;
int *n;
char **s;
switch (rcvars[i].type)
{
case rcv_int:
if (c < 1) return -1;
n = (int *)rcvars[i].mem;
*n = my_atoi(v[0]);
return 0;
case rcv_string:
if (c < 1) return -1;
s = (char **)rcvars[i].mem;
if (*s) free(*s);
*s = strdup(v[0]);
if (!*s)
die("out of memory setting rcvar %s\n", rcvars[i].name);
return 0;
case rcv_vector:
if (c > rcvars[i].len)
c = rcvars[i].len;
for (j = 0; j < c ; j++)
((int *)rcvars[i].mem)[j] = my_atoi(v[j]);
return 0;
case rcv_bool:
if (c < 1 || atoi(v[0]) || strchr("yYtT", v[0][0]))
*(int *)rcvars[i].mem = 1;
else if (strchr("0nNfF", v[0][0]))
*(int *)rcvars[i].mem = 0;
else
return -1;
return 0;
}
return -1;
}
int rc_setvar(char *name, int c, char **v)
{
int i;
i = rc_findvar(name);
if (i < 0) return i;
return rc_setvar_n(i, c, v);
}
void *rc_getmem_n(int i)
{
return rcvars[i].mem;
}
void *rc_getmem(char *name)
{
int i;
i = rc_findvar(name);
if (i < 0) return NULL;
return rcvars[i].mem;
}
int rc_getint_n(int i)
{
if (i < 0) return 0;
switch (rcvars[i].type)
{
case rcv_int:
case rcv_bool:
return *(int *)rcvars[i].mem;
}
return 0;
}
int *rc_getvec_n(int i)
{
if (i < 0) return NULL;
switch (rcvars[i].type)
{
case rcv_int:
case rcv_bool:
case rcv_vector:
return (int *)rcvars[i].mem;
}
return NULL;
}
char *rc_getstr_n(int i)
{
if (i < 0) return 0;
switch (rcvars[i].type)
{
case rcv_string:
return *(char **)rcvars[i].mem;
}
return 0;
}
int rc_getint(char *name)
{
return rc_getint_n(rc_findvar(name));
}
int *rc_getvec(char *name)
{
return rc_getvec_n(rc_findvar(name));
}
char *rc_getstr(char *name)
{
return rc_getstr_n(rc_findvar(name));
}

177
refresh.c Normal file
View File

@ -0,0 +1,177 @@
#include "defs.h"
#include "lcd.h"
#define BUF (scan.buf)
#ifdef USE_ASM
#include "asm.h"
#endif
#ifndef ASM_REFRESH_1
void refresh_1(byte *dest, byte *src, byte *pal, int cnt)
{
while(cnt--) *(dest++) = pal[*(src++)];
}
#endif
#ifndef ASM_REFRESH_2
void refresh_2(un16 *dest, byte *src, un16 *pal, int cnt)
{
while (cnt--) *(dest++) = pal[*(src++)];
}
#endif
#ifndef ASM_REFRESH_3
void refresh_3(byte *dest, byte *src, un32 *pal, int cnt)
{
un32 c;
while (cnt--)
{
c = pal[*(src++)];
*(dest++) = c;
*(dest++) = c>>8;
*(dest++) = c>>16;
}
}
#endif
#ifndef ASM_REFRESH_4
void refresh_4(un32 *dest, byte *src, un32 *pal, int cnt)
{
while (cnt--) *(dest++) = pal[*(src++)];
}
#endif
#ifndef ASM_REFRESH_1_2X
void refresh_1_2x(byte *dest, byte *src, byte *pal, int cnt)
{
byte c;
while (cnt--)
{
c = pal[*(src++)];
*(dest++) = c;
*(dest++) = c;
}
}
#endif
#ifndef ASM_REFRESH_2_2X
void refresh_2_2x(un16 *dest, byte *src, un16 *pal, int cnt)
{
un16 c;
while (cnt--)
{
c = pal[*(src++)];
*(dest++) = c;
*(dest++) = c;
}
}
#endif
#ifndef ASM_REFRESH_3_2X
void refresh_3_2x(byte *dest, byte *src, un32 *pal, int cnt)
{
un32 c;
while (cnt--)
{
c = pal[*(src++)];
dest[0] = dest[3] = c;
dest[1] = dest[4] = c>>8;
dest[2] = dest[5] = c>>16;
dest += 6;
}
}
#endif
#ifndef ASM_REFRESH_4_2X
void refresh_4_2x(un32 *dest, byte *src, un32 *pal, int cnt)
{
un32 c;
while (cnt--)
{
c = pal[*(src++)];
*(dest++) = c;
*(dest++) = c;
}
}
#endif
#ifndef ASM_REFRESH_2_3X
void refresh_2_3x(un16 *dest, byte *src, un16 *pal, int cnt)
{
un16 c;
while (cnt--)
{
c = pal[*(src++)];
*(dest++) = c;
*(dest++) = c;
*(dest++) = c;
}
}
#endif
#ifndef ASM_REFRESH_3_3X
void refresh_3_3x(byte *dest, byte *src, un32 *pal, int cnt)
{
un32 c;
while (cnt--)
{
c = pal[*(src++)];
dest[0] = dest[3] = dest[6] = c;
dest[1] = dest[4] = dest[7] = c>>8;
dest[2] = dest[5] = dest[8] = c>>16;
dest += 9;
}
}
#endif
#ifndef ASM_REFRESH_4_3X
void refresh_4_3x(un32 *dest, byte *src, un32 *pal, int cnt)
{
un32 c;
while (cnt--)
{
c = pal[*(src++)];
*(dest++) = c;
*(dest++) = c;
*(dest++) = c;
}
}
#endif
#ifndef ASM_REFRESH_3_4X
void refresh_3_4x(byte *dest, byte *src, un32 *pal, int cnt)
{
un32 c;
while (cnt--)
{
c = pal[*(src++)];
dest[0] = dest[3] = dest[6] = dest[9] = c;
dest[1] = dest[4] = dest[7] = dest[10] = c>>8;
dest[2] = dest[5] = dest[8] = dest[11] = c>>16;
dest += 12;
}
}
#endif
#ifndef ASM_REFRESH_4_4X
void refresh_4_4x(un32 *dest, byte *src, un32 *pal, int cnt)
{
un32 c;
while (cnt--)
{
c = pal[*(src++)];
*(dest++) = c;
*(dest++) = c;
*(dest++) = c;
*(dest++) = c;
}
}
#endif

181
regs.h Normal file
View File

@ -0,0 +1,181 @@
#ifndef __REGS_H__
#define __REGS_H__
#include "mem.h"
/* General internal/io stuff */
#define RI_P1 0x00
#define RI_SB 0x01
#define RI_SC 0x02
#define RI_DIV 0x04
#define RI_TIMA 0x05
#define RI_TMA 0x06
#define RI_TAC 0x07
#define RI_KEY1 0x4D
#define RI_RP 0x56
#define RI_SVBK 0x70
/* Interrupts flags */
#define RI_IF 0x0F
#define RI_IE 0xFF
/* LCDC */
#define RI_LCDC 0x40
#define RI_STAT 0x41
#define RI_SCY 0x42
#define RI_SCX 0x43
#define RI_LY 0x44
#define RI_LYC 0x45
#define RI_DMA 0x46
#define RI_BGP 0x47
#define RI_OBP0 0x48
#define RI_OBP1 0x49
#define RI_WY 0x4A
#define RI_WX 0x4B
#define RI_VBK 0x4F
#define RI_HDMA1 0x51
#define RI_HDMA2 0x52
#define RI_HDMA3 0x53
#define RI_HDMA4 0x54
#define RI_HDMA5 0x55
#define RI_BCPS 0x68
#define RI_BCPD 0x69
#define RI_OCPS 0x6A
#define RI_OCPD 0x6B
/* Sound */
#define RI_NR10 0x10
#define RI_NR11 0x11
#define RI_NR12 0x12
#define RI_NR13 0x13
#define RI_NR14 0x14
#define RI_NR21 0x16
#define RI_NR22 0x17
#define RI_NR23 0x18
#define RI_NR24 0x19
#define RI_NR30 0x1A
#define RI_NR31 0x1B
#define RI_NR32 0x1C
#define RI_NR33 0x1D
#define RI_NR34 0x1E
#define RI_NR41 0x20
#define RI_NR42 0x21
#define RI_NR43 0x22
#define RI_NR44 0x23
#define RI_NR50 0x24
#define RI_NR51 0x25
#define RI_NR52 0x26
#define REG(n) ram.hi[(n)]
/* General internal/io stuff */
#define R_P1 REG(RI_P1)
#define R_SB REG(RI_SB)
#define R_SC REG(RI_SC)
#define R_DIV REG(RI_DIV)
#define R_TIMA REG(RI_TIMA)
#define R_TMA REG(RI_TMA)
#define R_TAC REG(RI_TAC)
#define R_KEY1 REG(RI_KEY1)
#define R_RP REG(RI_RP)
#define R_SVBK REG(RI_SVBK)
/* Interrupts flags */
#define R_IF REG(RI_IF)
#define R_IE REG(RI_IE)
/* LCDC */
#define R_LCDC REG(RI_LCDC)
#define R_STAT REG(RI_STAT)
#define R_SCY REG(RI_SCY)
#define R_SCX REG(RI_SCX)
#define R_LY REG(RI_LY)
#define R_LYC REG(RI_LYC)
#define R_DMA REG(RI_DMA)
#define R_BGP REG(RI_BGP)
#define R_OBP0 REG(RI_OBP0)
#define R_OBP1 REG(RI_OBP1)
#define R_WY REG(RI_WY)
#define R_WX REG(RI_WX)
#define R_VBK REG(RI_VBK)
#define R_HDMA1 REG(RI_HDMA1)
#define R_HDMA2 REG(RI_HDMA2)
#define R_HDMA3 REG(RI_HDMA3)
#define R_HDMA4 REG(RI_HDMA4)
#define R_HDMA5 REG(RI_HDMA5)
#define R_BCPS REG(RI_BCPS)
#define R_BCPD REG(RI_BCPD)
#define R_OCPS REG(RI_OCPS)
#define R_OCPD REG(RI_OCPD)
/* Sound */
#define R_NR10 REG(RI_NR10)
#define R_NR11 REG(RI_NR11)
#define R_NR12 REG(RI_NR12)
#define R_NR13 REG(RI_NR13)
#define R_NR14 REG(RI_NR14)
#define R_NR21 REG(RI_NR21)
#define R_NR22 REG(RI_NR22)
#define R_NR23 REG(RI_NR23)
#define R_NR24 REG(RI_NR24)
#define R_NR30 REG(RI_NR30)
#define R_NR31 REG(RI_NR31)
#define R_NR32 REG(RI_NR32)
#define R_NR33 REG(RI_NR33)
#define R_NR34 REG(RI_NR34)
#define R_NR41 REG(RI_NR41)
#define R_NR42 REG(RI_NR42)
#define R_NR43 REG(RI_NR43)
#define R_NR44 REG(RI_NR44)
#define R_NR50 REG(RI_NR50)
#define R_NR51 REG(RI_NR51)
#define R_NR52 REG(RI_NR52)
#endif

130
rtc.c Normal file
View File

@ -0,0 +1,130 @@
#include <stdio.h>
#include "defs.h"
#include "mem.h"
#include "rtc.h"
#include "rc.h"
struct rtc rtc;
static int syncrtc = 1;
rcvar_t rtc_exports[] =
{
RCV_BOOL("syncrtc", &syncrtc),
RCV_END
};
void rtc_latch(byte b)
{
if ((rtc.latch ^ b) & b & 1)
{
rtc.regs[0] = rtc.s;
rtc.regs[1] = rtc.m;
rtc.regs[2] = rtc.h;
rtc.regs[3] = rtc.d;
rtc.regs[4] = (rtc.d>>9) | (rtc.stop<<6) | (rtc.carry<<7);
rtc.regs[5] = 0xff;
rtc.regs[6] = 0xff;
rtc.regs[7] = 0xff;
}
rtc.latch = b;
}
void rtc_write(byte b)
{
/* printf("write %02X: %02X (%d)\n", rtc.sel, b, b); */
if (!(rtc.sel & 8)) return;
switch (rtc.sel & 7)
{
case 0:
rtc.s = rtc.regs[0] = b;
while (rtc.s >= 60) rtc.s -= 60;
break;
case 1:
rtc.m = rtc.regs[1] = b;
while (rtc.m >= 60) rtc.m -= 60;
break;
case 2:
rtc.h = rtc.regs[2] = b;
while (rtc.h >= 24) rtc.h -= 24;
break;
case 3:
rtc.regs[3] = b;
rtc.d = (rtc.d & 0x100) | b;
break;
case 4:
rtc.regs[4] = b;
rtc.d = (rtc.d & 0xff) | ((b&1)<<9);
rtc.stop = (b>>6)&1;
rtc.carry = (b>>7)&1;
break;
}
}
void rtc_tick()
{
if (rtc.stop) return;
if (++rtc.t == 60)
{
if (++rtc.s == 60)
{
if (++rtc.m == 60)
{
if (++rtc.h == 24)
{
if (++rtc.d == 365)
{
rtc.d = 0;
rtc.carry = 1;
}
rtc.h = 0;
}
rtc.m = 0;
}
rtc.s = 0;
}
rtc.t = 0;
}
}
void rtc_save_internal(FILE *f)
{
fprintf(f, "%d %d %d %02d %02d %02d %02d\n%d\n",
rtc.carry, rtc.stop, rtc.d, rtc.h, rtc.m, rtc.s, rtc.t,
time(0));
}
void rtc_load_internal(FILE *f)
{
int rt = 0;
fscanf(
f, "%d %d %d %02d %02d %02d %02d\n%d\n",
&rtc.carry, &rtc.stop, &rtc.d,
&rtc.h, &rtc.m, &rtc.s, &rtc.t, &rt);
while (rtc.t >= 60) rtc.t -= 60;
while (rtc.s >= 60) rtc.s -= 60;
while (rtc.m >= 60) rtc.m -= 60;
while (rtc.h >= 24) rtc.h -= 24;
while (rtc.d >= 365) rtc.d -= 365;
rtc.stop &= 1;
rtc.carry &= 1;
if (rt) rt = (time(0) - rt) * 60;
if (syncrtc) while (rt-- > 0) rtc_tick();
}

25
rtc.h Normal file
View File

@ -0,0 +1,25 @@
#ifndef __RTC_H__
#define __RTC_H__
struct rtc
{
int batt;
int sel;
int latch;
int d, h, m, s, t;
int stop, carry;
byte regs[8];
};
extern struct rtc rtc;
#endif

286
save.c Normal file
View File

@ -0,0 +1,286 @@
#include <stdio.h>
#include "defs.h"
#include "cpu.h"
#include "cpuregs.h"
#include "hw.h"
#include "regs.h"
#include "lcd.h"
#include "rtc.h"
#include "mem.h"
#include "sound.h"
#ifdef IS_LITTLE_ENDIAN
#define LIL(x) (x)
#else
#define LIL(x) ((x<<24)|((x&0xff00)<<8)|((x>>8)&0xff00)|(x>>24))
#endif
#define I1(s, p) { 1, s, p }
#define I2(s, p) { 2, s, p }
#define I4(s, p) { 4, s, p }
#define R(r) I1(#r, &R_##r)
#define NOSAVE { -1, "\0\0\0\0", 0 }
#define END { 0, "\0\0\0\0", 0 }
struct svar
{
int len;
char key[4];
void *ptr;
};
static int ver;
static int sramblock, iramblock, vramblock;
static int hramofs, hiofs, palofs, oamofs, wavofs;
struct svar svars[] =
{
I4("GbSs", &ver),
I2("PC ", &PC),
I2("SP ", &SP),
I2("BC ", &BC),
I2("DE ", &DE),
I2("HL ", &HL),
I2("AF ", &AF),
I4("IME ", &cpu.ime),
I4("ima ", &cpu.ima),
I4("spd ", &cpu.speed),
I4("halt", &cpu.halt),
I4("div ", &cpu.div),
I4("tim ", &cpu.tim),
I4("lcdc", &cpu.lcdc),
I4("snd ", &cpu.snd),
I1("ints", &hw.ilines),
I1("pad ", &hw.pad),
I4("cgb ", &hw.cgb),
I4("gba ", &hw.gba),
I4("mbcm", &mbc.model),
I4("romb", &mbc.rombank),
I4("ramb", &mbc.rambank),
I4("enab", &mbc.enableram),
I4("batt", &mbc.batt),
I4("rtcR", &rtc.sel),
I4("rtcL", &rtc.latch),
I4("rtcC", &rtc.carry),
I4("rtcS", &rtc.stop),
I4("rtcd", &rtc.d),
I4("rtch", &rtc.h),
I4("rtcm", &rtc.m),
I4("rtcs", &rtc.s),
I4("rtct", &rtc.t),
I1("rtR8", &rtc.regs[0]),
I1("rtR9", &rtc.regs[1]),
I1("rtRA", &rtc.regs[2]),
I1("rtRB", &rtc.regs[3]),
I1("rtRC", &rtc.regs[4]),
I4("S1on", &snd.ch[0].on),
I4("S1p ", &snd.ch[0].pos),
I4("S1c ", &snd.ch[0].cnt),
I4("S1ec", &snd.ch[0].encnt),
I4("S1sc", &snd.ch[0].swcnt),
I4("S1sf", &snd.ch[0].swfreq),
I4("S2on", &snd.ch[1].on),
I4("S2p ", &snd.ch[1].pos),
I4("S2c ", &snd.ch[1].cnt),
I4("S2ec", &snd.ch[1].encnt),
I4("S3on", &snd.ch[2].on),
I4("S3p ", &snd.ch[2].pos),
I4("S3c ", &snd.ch[2].cnt),
I4("S4on", &snd.ch[3].on),
I4("S4p ", &snd.ch[3].pos),
I4("S4c ", &snd.ch[3].cnt),
I4("S4ec", &snd.ch[3].encnt),
I4("hdma", &hw.hdma),
I4("sram", &sramblock),
I4("iram", &iramblock),
I4("vram", &vramblock),
I4("hi ", &hiofs),
I4("pal ", &palofs),
I4("oam ", &oamofs),
I4("wav ", &wavofs),
/* NOSAVE is a special code to prevent the rest of the table
* from being saved, used to support old stuff for backwards
* compatibility... */
NOSAVE,
/* the following are obsolete as of 0x104 */
I4("hram", &hramofs),
R(P1), R(SB), R(SC),
R(DIV), R(TIMA), R(TMA), R(TAC),
R(IE), R(IF),
R(LCDC), R(STAT), R(LY), R(LYC),
R(SCX), R(SCY), R(WX), R(WY),
R(BGP), R(OBP0), R(OBP1),
R(DMA),
R(VBK), R(SVBK), R(KEY1),
R(BCPS), R(BCPD), R(OCPS), R(OCPD),
R(NR10), R(NR11), R(NR12), R(NR13), R(NR14),
R(NR21), R(NR22), R(NR23), R(NR24),
R(NR30), R(NR31), R(NR32), R(NR33), R(NR34),
R(NR41), R(NR42), R(NR43), R(NR44),
R(NR50), R(NR51), R(NR52),
I1("DMA1", &R_HDMA1),
I1("DMA2", &R_HDMA2),
I1("DMA3", &R_HDMA3),
I1("DMA4", &R_HDMA4),
I1("DMA5", &R_HDMA5),
END
};
void loadstate(FILE *f)
{
int i, j;
byte buf[4096];
un32 (*header)[2] = (un32 (*)[2])buf;
un32 d;
int irl = hw.cgb ? 8 : 2;
int vrl = hw.cgb ? 4 : 2;
int srl = mbc.ramsize << 1;
ver = hramofs = hiofs = palofs = oamofs = wavofs = 0;
fseek(f, 0, SEEK_SET);
fread(buf, 4096, 1, f);
for (j = 0; header[j][0]; j++)
{
for (i = 0; svars[i].ptr; i++)
{
if (header[j][0] != *(un32 *)svars[i].key)
continue;
d = LIL(header[j][1]);
switch (svars[i].len)
{
case 1:
*(byte *)svars[i].ptr = d;
break;
case 2:
*(un16 *)svars[i].ptr = d;
break;
case 4:
*(un32 *)svars[i].ptr = d;
break;
}
break;
}
}
/* obsolete as of version 0x104 */
if (hramofs) memcpy(ram.hi+128, buf+hramofs, 127);
if (hiofs) memcpy(ram.hi, buf+hiofs, sizeof ram.hi);
if (palofs) memcpy(lcd.pal, buf+palofs, sizeof lcd.pal);
if (oamofs) memcpy(lcd.oam.mem, buf+oamofs, sizeof lcd.oam);
if (wavofs) memcpy(snd.wave, buf+wavofs, sizeof snd.wave);
else memcpy(snd.wave, ram.hi+0x30, 16); /* patch data from older files */
fseek(f, iramblock<<12, SEEK_SET);
fread(ram.ibank, 4096, irl, f);
fseek(f, vramblock<<12, SEEK_SET);
fread(lcd.vbank, 4096, vrl, f);
fseek(f, sramblock<<12, SEEK_SET);
fread(ram.sbank, 4096, srl, f);
}
void savestate(FILE *f)
{
int i;
byte buf[4096];
un32 (*header)[2] = (un32 (*)[2])buf;
un32 d = 0;
int irl = hw.cgb ? 8 : 2;
int vrl = hw.cgb ? 4 : 2;
int srl = mbc.ramsize << 1;
ver = 0x105;
iramblock = 1;
vramblock = 1+irl;
sramblock = 1+irl+vrl;
wavofs = 4096 - 784;
hiofs = 4096 - 768;
palofs = 4096 - 512;
oamofs = 4096 - 256;
memset(buf, 0, sizeof buf);
for (i = 0; svars[i].len > 0; i++)
{
header[i][0] = *(un32 *)svars[i].key;
switch (svars[i].len)
{
case 1:
d = *(byte *)svars[i].ptr;
break;
case 2:
d = *(un16 *)svars[i].ptr;
break;
case 4:
d = *(un32 *)svars[i].ptr;
break;
}
header[i][1] = LIL(d);
}
header[i][0] = header[i][1] = 0;
memcpy(buf+hiofs, ram.hi, sizeof ram.hi);
memcpy(buf+palofs, lcd.pal, sizeof lcd.pal);
memcpy(buf+oamofs, lcd.oam.mem, sizeof lcd.oam);
memcpy(buf+wavofs, snd.wave, sizeof snd.wave);
fseek(f, 0, SEEK_SET);
fwrite(buf, 4096, 1, f);
fseek(f, iramblock<<12, SEEK_SET);
fwrite(ram.ibank, 4096, irl, f);
fseek(f, vramblock<<12, SEEK_SET);
fwrite(lcd.vbank, 4096, vrl, f);
fseek(f, sramblock<<12, SEEK_SET);
fwrite(ram.sbank, 4096, srl, f);
}

463
sound.c Normal file
View File

@ -0,0 +1,463 @@
#include "defs.h"
#include "pcm.h"
#include "sound.h"
#include "cpu.h"
#include "hw.h"
#include "regs.h"
#include "rc.h"
#include "noise.h"
const static byte dmgwave[16] =
{
0xac, 0xdd, 0xda, 0x48,
0x36, 0x02, 0xcf, 0x16,
0x2c, 0x04, 0xe5, 0x2c,
0xac, 0xdd, 0xda, 0x48
};
const static byte cgbwave[16] =
{
0x00, 0xff, 0x00, 0xff,
0x00, 0xff, 0x00, 0xff,
0x00, 0xff, 0x00, 0xff,
0x00, 0xff, 0x00, 0xff,
};
const static byte sqwave[4][8] =
{
{ 0, 0,-1, 0, 0, 0, 0, 0 },
{ 0,-1,-1, 0, 0, 0, 0, 0 },
{ -1,-1,-1,-1, 0, 0, 0, 0 },
{ -1, 0, 0,-1,-1,-1,-1,-1 }
};
const static int freqtab[8] =
{
(1<<14)*2,
(1<<14),
(1<<14)/2,
(1<<14)/3,
(1<<14)/4,
(1<<14)/5,
(1<<14)/6,
(1<<14)/7
};
struct snd snd;
#define RATE (snd.rate)
#define WAVE (snd.wave) /* ram.hi+0x30 */
#define S1 (snd.ch[0])
#define S2 (snd.ch[1])
#define S3 (snd.ch[2])
#define S4 (snd.ch[3])
rcvar_t sound_exports[] =
{
RCV_END
};
static void s1_freq_d(int d)
{
if (RATE > (d<<4)) S1.freq = 0;
else S1.freq = (RATE << 17)/d;
}
static void s1_freq()
{
s1_freq_d(2048 - (((R_NR14&7)<<8) + R_NR13));
}
static void s2_freq()
{
int d = 2048 - (((R_NR24&7)<<8) + R_NR23);
if (RATE > (d<<4)) S2.freq = 0;
else S2.freq = (RATE << 17)/d;
}
static void s3_freq()
{
int d = 2048 - (((R_NR34&7)<<8) + R_NR33);
if (RATE > (d<<3)) S3.freq = 0;
else S3.freq = (RATE << 21)/d;
}
static void s4_freq()
{
S4.freq = (freqtab[R_NR43&7] >> (R_NR43 >> 4)) * RATE;
if (S4.freq >> 18) S4.freq = 1<<18;
}
void sound_dirty()
{
S1.swlen = ((R_NR10>>4) & 7) << 14;
S1.len = (64-(R_NR11&63)) << 13;
S1.envol = R_NR12 >> 4;
S1.endir = (R_NR12>>3) & 1;
S1.endir |= S1.endir - 1;
S1.enlen = (R_NR12 & 7) << 15;
s1_freq();
S2.len = (64-(R_NR21&63)) << 13;
S2.envol = R_NR22 >> 4;
S2.endir = (R_NR22>>3) & 1;
S2.endir |= S2.endir - 1;
S2.enlen = (R_NR22 & 7) << 15;
s2_freq();
S3.len = (256-R_NR31) << 20;
s3_freq();
S4.len = (64-(R_NR41&63)) << 13;
S4.envol = R_NR42 >> 4;
S4.endir = (R_NR42>>3) & 1;
S4.endir |= S4.endir - 1;
S4.enlen = (R_NR42 & 7) << 15;
s4_freq();
}
void sound_off()
{
memset(&S1, 0, sizeof S1);
memset(&S2, 0, sizeof S2);
memset(&S3, 0, sizeof S3);
memset(&S4, 0, sizeof S4);
R_NR10 = 0x80;
R_NR11 = 0xBF;
R_NR12 = 0xF3;
R_NR14 = 0xBF;
R_NR21 = 0x3F;
R_NR22 = 0x00;
R_NR24 = 0xBF;
R_NR30 = 0x7F;
R_NR31 = 0xFF;
R_NR32 = 0x9F;
R_NR33 = 0xBF;
R_NR41 = 0xFF;
R_NR42 = 0x00;
R_NR43 = 0x00;
R_NR44 = 0xBF;
R_NR50 = 0x77;
R_NR51 = 0xF3;
R_NR52 = 0xF1;
sound_dirty();
}
void sound_reset()
{
memset(&snd, 0, sizeof snd);
if (pcm.hz) snd.rate = (1<<21) / pcm.hz;
else snd.rate = 0;
memcpy(WAVE, hw.cgb ? cgbwave : dmgwave, 16);
memcpy(ram.hi+0x30, WAVE, 16);
sound_off();
}
void sound_mix()
{
int s, l, r, f, n;
if (!RATE || cpu.snd < RATE) return;
for (; cpu.snd >= RATE; cpu.snd -= RATE)
{
l = r = 0;
if (S1.on)
{
s = sqwave[R_NR11>>6][(S1.pos>>18)&7] & S1.envol;
S1.pos += S1.freq;
if ((R_NR14 & 64) && ((S1.cnt += RATE) >= S1.len))
S1.on = 0;
if (S1.enlen && (S1.encnt += RATE) >= S1.enlen)
{
S1.encnt -= S1.enlen;
S1.envol += S1.endir;
if (S1.envol < 0) S1.envol = 0;
if (S1.envol > 15) S1.envol = 15;
}
if (S1.swlen && (S1.swcnt += RATE) >= S1.swlen)
{
S1.swcnt -= S1.swlen;
f = S1.swfreq;
n = (R_NR10 & 7);
if (R_NR10 & 8) f -= (f >> n);
else f += (f >> n);
if (f > 2047)
S1.on = 0;
else
{
S1.swfreq = f;
R_NR13 = f;
R_NR14 = (R_NR14 & 0xF8) | (f>>8);
s1_freq_d(2048 - f);
}
}
s <<= 2;
if (R_NR51 & 1) r += s;
if (R_NR51 & 16) l += s;
}
if (S2.on)
{
s = sqwave[R_NR21>>6][(S2.pos>>18)&7] & S2.envol;
S2.pos += S2.freq;
if ((R_NR24 & 64) && ((S2.cnt += RATE) >= S2.len))
S2.on = 0;
if (S2.enlen && (S2.encnt += RATE) >= S2.enlen)
{
S2.encnt -= S2.enlen;
S2.envol += S2.endir;
if (S2.envol < 0) S2.envol = 0;
if (S2.envol > 15) S2.envol = 15;
}
s <<= 2;
if (R_NR51 & 2) r += s;
if (R_NR51 & 32) l += s;
}
if (S3.on)
{
s = WAVE[(S3.pos>>22) & 15];
if (S3.pos & (1<<21)) s &= 15;
else s >>= 4;
s -= 8;
S3.pos += S3.freq;
if ((R_NR34 & 64) && ((S3.cnt += RATE) >= S3.len))
S3.on = 0;
if (R_NR32 & 96) s <<= (3 - ((R_NR32>>5)&3));
else s = 0;
if (R_NR51 & 4) r += s;
if (R_NR51 & 64) l += s;
}
if (S4.on)
{
if (R_NR43 & 8) s = 1 & (noise7[
(S4.pos>>20)&15] >> (7-((S4.pos>>17)&7)));
else s = 1 & (noise15[
(S4.pos>>20)&4095] >> (7-((S4.pos>>17)&7)));
s = (-s) & S4.envol;
S4.pos += S4.freq;
if ((R_NR44 & 64) && ((S4.cnt += RATE) >= S4.len))
S4.on = 0;
if (S4.enlen && (S4.encnt += RATE) >= S4.enlen)
{
S4.encnt -= S4.enlen;
S4.envol += S4.endir;
if (S4.envol < 0) S4.envol = 0;
if (S4.envol > 15) S4.envol = 15;
}
s += s << 1;
if (R_NR51 & 8) r += s;
if (R_NR51 & 128) l += s;
}
l *= (R_NR50 & 0x07);
r *= ((R_NR50 & 0x70)>>4);
l >>= 4;
r >>= 4;
if (l > 127) l = 127;
else if (l < -128) l = -128;
if (r > 127) r = 127;
else if (r < -128) r = -128;
if (pcm.buf)
{
if (pcm.pos >= pcm.len)
pcm_submit();
if (pcm.stereo)
{
pcm.buf[pcm.pos++] = l+128;
pcm.buf[pcm.pos++] = r+128;
}
else pcm.buf[pcm.pos++] = ((l+r)>>1)+128;
}
}
R_NR52 = (R_NR52&0xf0) | S1.on | (S2.on<<1) | (S3.on<<2) | (S4.on<<3);
}
byte sound_read(byte r)
{
sound_mix();
/* printf("read %02X: %02X\n", r, REG(r)); */
return REG(r);
}
void s1_init()
{
S1.swcnt = 0;
S1.swfreq = ((R_NR14&7)<<8) + R_NR13;
S1.envol = R_NR12 >> 4;
S1.endir = (R_NR12>>3) & 1;
S1.endir |= S1.endir - 1;
S1.enlen = (R_NR12 & 7) << 15;
if (!S1.on) S1.pos = 0;
S1.on = 1;
S1.cnt = 0;
S1.encnt = 0;
}
void s2_init()
{
S2.envol = R_NR22 >> 4;
S2.endir = (R_NR22>>3) & 1;
S2.endir |= S2.endir - 1;
S2.enlen = (R_NR22 & 7) << 15;
if (!S2.on) S2.pos = 0;
S2.on = 1;
S2.cnt = 0;
S2.encnt = 0;
}
void s3_init()
{
int i;
if (!S3.on) S3.pos = 0;
S3.cnt = 0;
S3.on = R_NR30 >> 7;
if (S3.on) for (i = 0; i < 16; i++)
ram.hi[i+0x30] = 0x13 ^ ram.hi[i+0x31];
}
void s4_init()
{
S4.envol = R_NR42 >> 4;
S4.endir = (R_NR42>>3) & 1;
S4.endir |= S4.endir - 1;
S4.enlen = (R_NR42 & 7) << 15;
S4.on = 1;
S4.pos = 0;
S4.cnt = 0;
S4.encnt = 0;
}
void sound_write(byte r, byte b)
{
#if 0
static void *timer;
if (!timer) timer = sys_timer();
printf("write %02X: %02X @ %d\n", r, b, sys_elapsed(timer));
#endif
if (!(R_NR52 & 128) && r != RI_NR52) return;
if ((r & 0xF0) == 0x30)
{
if (S3.on) sound_mix();
if (!S3.on)
WAVE[r-0x30] = ram.hi[r] = b;
return;
}
sound_mix();
switch (r)
{
case RI_NR10:
R_NR10 = b;
S1.swlen = ((R_NR10>>4) & 7) << 14;
S1.swfreq = ((R_NR14&7)<<8) + R_NR13;
break;
case RI_NR11:
R_NR11 = b;
S1.len = (64-(R_NR11&63)) << 13;
break;
case RI_NR12:
R_NR12 = b;
S1.envol = R_NR12 >> 4;
S1.endir = (R_NR12>>3) & 1;
S1.endir |= S1.endir - 1;
S1.enlen = (R_NR12 & 7) << 15;
break;
case RI_NR13:
R_NR13 = b;
s1_freq();
break;
case RI_NR14:
R_NR14 = b;
s1_freq();
if (b & 128) s1_init();
break;
case RI_NR21:
R_NR21 = b;
S2.len = (64-(R_NR21&63)) << 13;
break;
case RI_NR22:
R_NR22 = b;
S2.envol = R_NR22 >> 4;
S2.endir = (R_NR22>>3) & 1;
S2.endir |= S2.endir - 1;
S2.enlen = (R_NR22 & 7) << 15;
break;
case RI_NR23:
R_NR23 = b;
s2_freq();
break;
case RI_NR24:
R_NR24 = b;
s2_freq();
if (b & 128) s2_init();
break;
case RI_NR30:
R_NR30 = b;
if (!(b & 128)) S3.on = 0;
break;
case RI_NR31:
R_NR31 = b;
S3.len = (256-R_NR31) << 13;
break;
case RI_NR32:
R_NR32 = b;
break;
case RI_NR33:
R_NR33 = b;
s3_freq();
break;
case RI_NR34:
R_NR34 = b;
s3_freq();
if (b & 128) s3_init();
break;
case RI_NR41:
R_NR41 = b;
S4.len = (64-(R_NR41&63)) << 13;
break;
case RI_NR42:
R_NR42 = b;
S4.envol = R_NR42 >> 4;
S4.endir = (R_NR42>>3) & 1;
S4.endir |= S4.endir - 1;
S4.enlen = (R_NR42 & 7) << 15;
break;
case RI_NR43:
R_NR43 = b;
s4_freq();
break;
case RI_NR44:
R_NR44 = b;
if (b & 128) s4_init();
break;
case RI_NR50:
R_NR50 = b;
break;
case RI_NR51:
R_NR51 = b;
break;
case RI_NR52:
R_NR52 = b;
if (!(R_NR52 & 128))
sound_off();
break;
default:
return;
}
}

36
sound.h Normal file
View File

@ -0,0 +1,36 @@
#ifndef __SOUND_H__
#define __SOUND_H__
struct sndchan
{
int on;
unsigned pos;
int cnt, encnt, swcnt;
int len, enlen, swlen;
int swfreq;
int freq;
int envol, endir;
};
struct snd
{
int rate;
struct sndchan ch[4];
byte wave[16];
};
extern struct snd snd;
#endif

58
split.c Normal file
View File

@ -0,0 +1,58 @@
/*
* splitline is a destructive argument parser, much like a very primitive
* form of a shell parser. it supports quotes for embedded spaces and
* literal quotes with the backslash escape.
*/
char *splitnext(char **pos)
{
char *a, *d, *s;
d = s = *pos;
while (*s == ' ' || *s == '\t') s++;
a = s;
while (*s && *s != ' ' && *s != '\t')
{
if (*s == '"')
{
s++;
while (*s && *s != '"')
{
if (*s == '\\')
s++;
if (*s)
*(d++) = *(s++);
}
if (*s == '"') s++;
}
else
{
if (*s == '\\')
s++;
*(d++) = *(s++);
}
}
while (*s == ' ' || *s == '\t') s++;
*d = 0;
*pos = s;
return a;
}
int splitline(char **argv, int max, char *line)
{
char *s;
int i;
s = line;
for (i = 0; *s && i < max + 1; i++)
argv[i] = splitnext(&s);
argv[i] = 0;
return i;
}

85
sys/dos/dos.c Normal file
View File

@ -0,0 +1,85 @@
/*
* dos.c
*
* System interface for DOS.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *strdup();
#include <stdarg.h>
#include <sys/time.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#define US(n) ( ((long long)(n)) * 1000000 / UCLOCKS_PER_SEC )
void *sys_timer()
{
uclock_t *cl;
cl = malloc(sizeof *cl);
*cl = uclock();
return cl;
}
int sys_elapsed(uclock_t *cl)
{
uclock_t now;
int usecs;
now = uclock();
usecs = US(now - *cl);
*cl = now;
return usecs;
}
void sys_sleep(int us)
{
uclock_t start;
if (us <= 0) return;
start = uclock();
while(US(uclock()-start) < us);
}
void sys_checkdir(char *path, int wr)
{
}
void sys_initpath(char *exe)
{
char *buf, *home, *p;
home = strdup(exe);
p = strrchr(home, '/');
if (p) *p = 0;
else
{
buf = ".";
rc_setvar("rcpath", 1, &buf);
rc_setvar("savedir", 1, &buf);
return;
}
buf = malloc(strlen(home) + 8);
sprintf(buf, ".;%s/", home);
rc_setvar("rcpath", 1, &buf);
sprintf(buf, ".");
rc_setvar("savedir", 1, &buf);
free(buf);
}
void sys_sanitize(char *s)
{
int i;
for (i = 0; s[i]; i++)
if (s[i] == '\\') s[i] = '/';
}

22
sys/dummy/nojoy.c Normal file
View File

@ -0,0 +1,22 @@
#include "rc.h"
rcvar_t joy_exports[] =
{
RCV_END
};
void joy_init()
{
}
void joy_close()
{
}
void joy_poll()
{
}

43
sys/dummy/nosound.c Normal file
View File

@ -0,0 +1,43 @@
#include "defs.h"
#include "pcm.h"
#include "rc.h"
struct pcm pcm;
static byte buf[4096];
rcvar_t pcm_exports[] =
{
RCV_END
};
void pcm_init()
{
pcm.hz = 11025;
pcm.buf = buf;
pcm.len = sizeof buf;
pcm.pos = 0;
}
void pcm_close()
{
memset(&pcm, 0, sizeof pcm);
}
int pcm_submit()
{
pcm.pos = 0;
return 0;
}

283
sys/linux/fbdev.c Normal file
View File

@ -0,0 +1,283 @@
/*
* Support for the Linux framebuffer device
* Copyright 2001 Laguna
* MGA BES code derived from fbtv
* Copyright Gerd Knorr
* This file may be distributed under the terms of the GNU GPL.
*/
#include <stdlib.h>
#include <string.h>
char *strdup();
#include <unistd.h>
#include <sys/mman.h>
#include <linux/fb.h>
#include <sys/ioctl.h>
#include <sys/fcntl.h>
#include "defs.h"
#include "fb.h"
#include "rc.h"
#include "matrox.h"
struct fb fb;
#define FBSET_CMD "fbset"
static char *fb_mode;
static int fb_depth;
static int vmode[3];
#define FB_DEVICE "/dev/fb0"
static char *fb_device;
static int fbfd = -1;
static byte *fbmap;
static int maplen;
static byte *mmio;
static int bes;
static int base;
static int use_yuv = -1;
static int use_interp = 1;
static struct fb_fix_screeninfo fi;
static struct fb_var_screeninfo vi, initial_vi;
rcvar_t vid_exports[] =
{
RCV_VECTOR("vmode", &vmode, 3),
RCV_STRING("fb_device", &fb_device),
RCV_STRING("fb_mode", &fb_mode),
RCV_INT("fb_depth", &fb_depth),
RCV_BOOL("yuv", &use_yuv),
RCV_BOOL("yuvinterp", &use_interp),
RCV_END
};
static void wrio4(int a, int v)
{
#ifndef IS_LITTLE_ENDIAN
v = (v<<24) | ((v&0xff00)<<8) | ((v&0xff0000)>>8) | (v>>24);
#endif
*(int*)(mmio+a) = v;
}
static void overlay_switch()
{
int a, b;
if (!fb.yuv) return;
if (!fb.enabled)
{
if (bes) wrio4(BESCTL, 0);
bes = 0;
return;
}
if (bes) return;
bes = 1;
memset(fbmap, 0, maplen);
/* color keying (turn it off) */
mmio[PALWTADD] = XKEYOPMODE;
mmio[X_DATAREG] = 0;
/* src */
wrio4(BESA1ORG, base);
wrio4(BESA2ORG, base);
wrio4(BESB1ORG, base);
wrio4(BESB2ORG, base);
wrio4(BESPITCH, 320);
/* dest */
a = (vi.xres - vmode[0])>>1;
b = vi.xres - a - 1;
wrio4(BESHCOORD, (a << 16) | (b - 1));
/* scale horiz */
wrio4(BESHISCAL, 320*131072/(b-a) & 0x001ffffc);
wrio4(BESHSRCST, 0 << 16);
wrio4(BESHSRCEND, 320 << 16);
wrio4(BESHSRCLST, 319 << 16);
/* dest */
a = (vi.yres - vmode[1])>>1;
b = vi.yres - a - 1;
wrio4(BESVCOORD, (a << 16) | (b - 1));
/* scale vert */
wrio4(BESVISCAL, 144*65536/(b-a) & 0x001ffffc);
wrio4(BESV1WGHT, 0);
wrio4(BESV2WGHT, 0);
wrio4(BESV1SRCLST, 143);
wrio4(BESV2SRCLST, 143);
/* turn on (enable, horizontal+vertical interpolation filters */
if (use_interp)
wrio4(BESCTL, 0x50c01);
else
wrio4(BESCTL, 1);
wrio4(BESGLOBCTL, 0x83);
}
static void overlay_init()
{
if (!mmio | !use_yuv) return;
if (use_yuv < 0) if ((vmode[0] < 320) || (vmode[1] < 288)) return;
switch (fi.accel)
{
#ifdef FB_ACCEL_MATROX_MGAG200
case FB_ACCEL_MATROX_MGAG200:
#endif
#ifdef FB_ACCEL_MATROX_MGAG400
case FB_ACCEL_MATROX_MGAG400:
#endif
break;
default:
return;
}
fb.w = 160;
fb.h = 144;
fb.pitch = 640;
fb.pelsize = 4;
fb.yuv = 1;
fb.cc[0].r = fb.cc[1].r = fb.cc[2].r = fb.cc[3].r = 0;
fb.cc[0].l = 0;
fb.cc[1].l = 24;
fb.cc[2].l = 8;
fb.cc[3].l = 16;
base = vi.yres * vi.xres_virtual * ((vi.bits_per_pixel+7)>>3);
maplen = base + fb.pitch * fb.h;
}
static void plain_init()
{
fb.w = vi.xres;
fb.h = vi.yres;
fb.pelsize = (vi.bits_per_pixel+7)>>3;
fb.pitch = vi.xres_virtual * fb.pelsize;
fb.indexed = fi.visual == FB_VISUAL_PSEUDOCOLOR;
fb.cc[0].r = 8 - vi.red.length;
fb.cc[1].r = 8 - vi.green.length;
fb.cc[2].r = 8 - vi.blue.length;
fb.cc[0].l = vi.red.offset;
fb.cc[1].l = vi.green.offset;
fb.cc[2].l = vi.blue.offset;
maplen = fb.pitch * fb.h;
}
void vid_init()
{
char cmd[256];
kb_init();
joy_init();
if (!fb_device)
if (!(fb_device = getenv("FRAMEBUFFER")))
fb_device = strdup(FB_DEVICE);
fbfd = open(fb_device, O_RDWR);
if (fbfd < 0) die("cannot open %s\n", fb_device);
ioctl(fbfd, FBIOGET_VSCREENINFO, &initial_vi);
initial_vi.xoffset = initial_vi.yoffset = 0;
if (fb_mode)
{
sprintf(cmd, FBSET_CMD " %.80s", fb_mode);
system(cmd);
}
ioctl(fbfd, FBIOGET_VSCREENINFO, &vi);
if (fb_depth) vi.bits_per_pixel = fb_depth;
vi.xoffset = vi.yoffset = 0;
vi.accel_flags = 0;
vi.activate = FB_ACTIVATE_NOW;
ioctl(fbfd, FBIOPUT_VSCREENINFO, &vi);
ioctl(fbfd, FBIOGET_VSCREENINFO, &vi);
ioctl(fbfd, FBIOGET_FSCREENINFO, &fi);
if (!vmode[0] || !vmode[1])
{
int scale = rc_getint("scale");
if (scale < 1) scale = 1;
vmode[0] = 160 * scale;
vmode[1] = 144 * scale;
}
if (vmode[0] > vi.xres) vmode[0] = vi.xres;
if (vmode[1] > vi.yres) vmode[1] = vi.yres;
mmio = mmap(0, fi.mmio_len, PROT_READ|PROT_WRITE, MAP_SHARED, fbfd, fi.smem_len);
if ((int)mmio == -1) mmio = 0;
overlay_init();
if (!fb.yuv) plain_init();
fbmap = mmap(0, maplen, PROT_READ|PROT_WRITE, MAP_SHARED, fbfd, 0);
if (!fbmap) die("cannot mmap %s (%d bytes)\n", fb_device, maplen);
fb.ptr = fbmap + base;
memset(fbmap, 0, maplen);
fb.dirty = 0;
fb.enabled = 1;
overlay_switch();
}
void vid_close()
{
fb.enabled = 0;
overlay_switch();
joy_close();
kb_close();
ioctl(fbfd, FBIOPUT_VSCREENINFO, &initial_vi);
memset(fbmap, 0, maplen);
}
void vid_preinit()
{
}
void vid_settitle(char *title)
{
}
void vid_setpal(int i, int r, int g, int b)
{
unsigned short rr = r<<8, gg = g<<8, bb = b<<8;
struct fb_cmap cmap;
memset(&cmap, 0, sizeof cmap);
cmap.start = i;
cmap.len = 1;
cmap.red = &rr;
cmap.green = &gg;
cmap.blue = &bb;
ioctl(fbfd, FBIOPUTCMAP, &cmap);
}
void vid_begin()
{
overlay_switch();
}
void vid_end()
{
overlay_switch();
}
void ev_poll()
{
kb_poll();
joy_poll();
}

93
sys/linux/joy.c Normal file
View File

@ -0,0 +1,93 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *strdup();
#include <linux/joystick.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "input.h"
#include "rc.h"
static int usejoy = 1;
static char *joydev;
static int joyfd = -1;
static int pos[2], max[2], min[2];
static const int axis[2][2] =
{
{ K_JOYLEFT, K_JOYRIGHT },
{ K_JOYUP, K_JOYDOWN }
};
rcvar_t joy_exports[] =
{
RCV_BOOL("joy", &usejoy),
RCV_STRING("joy_device", &joydev),
RCV_END
};
void joy_init()
{
if (!usejoy) return;
if (!joydev) joydev = strdup("/dev/js0");
joyfd = open(joydev, O_RDONLY|O_NONBLOCK);
}
void joy_close()
{
close(joyfd);
}
void joy_poll()
{
struct js_event js;
event_t ev;
int n;
if (joyfd < 0) return;
while (read(joyfd,&js,sizeof(struct js_event)) == sizeof(struct js_event))
{
switch(js.type)
{
case JS_EVENT_BUTTON:
ev.type = js.value ? EV_PRESS : EV_RELEASE;
ev.code = K_JOY0 + js.number;
ev_postevent(&ev);
break;
case JS_EVENT_AXIS:
n = js.number & 1;
if (js.value < min[n]) min[n] = js.value;
else if(js.value > max[n]) max[n] = js.value;
ev.code = axis[n][0];
if(js.value < (min[n]>>2) && js.value < pos[n])
{
ev.type = EV_PRESS;
ev_postevent(&ev);
}
else if (js.value > pos[n])
{
ev.type = EV_RELEASE;
ev_postevent(&ev);
}
ev.code = axis[n][1];
if(js.value > (max[n]>>2) && js.value > pos[n])
{
ev.type = EV_PRESS;
ev_postevent(&ev);
}
else if (js.value < pos[n])
{
ev.type = EV_RELEASE;
ev_postevent(&ev);
}
pos[n] = js.value;
}
}
}

135
sys/linux/kb.c Normal file
View File

@ -0,0 +1,135 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <linux/kd.h>
#include <sys/signal.h>
#include <sys/ioctl.h>
#include <sys/fcntl.h>
#include <sys/vt.h>
#include <termios.h>
#undef K_NUMLOCK
#include "defs.h"
#include "rc.h"
#include "fb.h"
#include "input.h"
#define TTY_DEVICE "/dev/tty"
static int kbfd = -1;
static int initial_kbmode;
static struct termios initial_term, term;
#define SCAN_ALT 56
#define SCAN_FBASE 58
static int alt;
static struct vt_mode vtm, initial_vtm;
extern int keymap[][2];
rcvar_t kb_exports[] =
{
RCV_END
};
static void vcrelease(int s)
{
signal(s, vcrelease);
ioctl(kbfd, VT_RELDISP, VT_ACKACQ);
}
static void vcacquire(int s)
{
signal(s, vcacquire);
ioctl(kbfd, VT_RELDISP, VT_ACKACQ);
fb.enabled = 1;
}
void kb_init()
{
kbfd = open(TTY_DEVICE, O_RDWR);
if (!kbfd) die("no controlling terminal\n");
fcntl(kbfd, F_SETFL, O_NONBLOCK);
if (ioctl(kbfd, KDSETMODE, KD_GRAPHICS) < 0)
die("controlling terminal is not the graphics console\n");
ioctl(kbfd, KDGKBMODE, &initial_kbmode);
tcgetattr(kbfd, &initial_term);
term = initial_term;
term.c_lflag &= ~(ICANON | ECHO | ISIG);
term.c_iflag &= ~(ISTRIP | IGNCR | ICRNL | INLCR | IXOFF | IXON);
term.c_cc[VMIN] = 0;
term.c_cc[VTIME] = 0;
tcsetattr(kbfd, TCSAFLUSH, &term);
ioctl(kbfd, KDSKBMODE, K_MEDIUMRAW);
ioctl(kbfd, VT_GETMODE, &initial_vtm);
signal(SIGUSR1, vcrelease);
signal(SIGUSR2, vcacquire);
vtm = initial_vtm;
vtm.mode = VT_PROCESS;
vtm.relsig = SIGUSR1;
vtm.acqsig = SIGUSR2;
ioctl(kbfd, VT_SETMODE, &vtm);
}
void kb_close()
{
ioctl(kbfd, VT_SETMODE, &initial_vtm);
ioctl(kbfd, KDSKBMODE, initial_kbmode);
tcsetattr(kbfd, TCSAFLUSH, &initial_term);
ioctl(kbfd, KDSETMODE, KD_TEXT);
}
static void vcswitch(int c)
{
struct vt_stat vts;
ioctl(kbfd, VT_GETSTATE, &vts);
if (c != vts.v_active)
{
ioctl(kbfd, VT_ACTIVATE, c);
fb.enabled = 0;
fb.dirty = 1;
}
}
void kb_poll()
{
int i;
event_t ev;
byte k;
int st;
while (read(kbfd, &k, 1) > 0)
{
st = !(k & 0x80);
k &= 0x7f;
if (k == SCAN_ALT) alt = st;
if (alt && k > SCAN_FBASE && k < SCAN_FBASE + 10)
vcswitch(k - SCAN_FBASE);
ev.type = st ? EV_PRESS : EV_RELEASE;
for (i = 0; keymap[i][0]; i++)
if (keymap[i][0] == k)
break;
if (!keymap[i][0]) continue;
ev.code = keymap[i][1];
ev_postevent(&ev);
}
}

36
sys/linux/matrox.h Normal file
View File

@ -0,0 +1,36 @@
/* taken from fbtv */
#define BES_BASE 0x3d00
#define BESA1ORG (BES_BASE+0x00)
#define BESA2ORG (BES_BASE+0x04)
#define BESB1ORG (BES_BASE+0x08)
#define BESB2ORG (BES_BASE+0x0c)
#define BESA1CORG (BES_BASE+0x10)
#define BESA2CORG (BES_BASE+0x14)
#define BESB1CORG (BES_BASE+0x18)
#define BESB2CORG (BES_BASE+0x1c)
#define BESCTL (BES_BASE+0x20)
#define BESPITCH (BES_BASE+0x24)
#define BESHCOORD (BES_BASE+0x28)
#define BESVCOORD (BES_BASE+0x2c)
#define BESHISCAL (BES_BASE+0x30)
#define BESVISCAL (BES_BASE+0x34)
#define BESHSRCST (BES_BASE+0x38)
#define BESHSRCEND (BES_BASE+0x3c)
#define BESV1WGHT (BES_BASE+0x48)
#define BESV2WGHT (BES_BASE+0x4c)
#define BESHSRCLST (BES_BASE+0x50)
#define BESV1SRCLST (BES_BASE+0x54)
#define BESV2SRCLST (BES_BASE+0x58)
#define BESGLOBCTL (BES_BASE+0xc0)
#define BESSTATUS (BES_BASE+0xc4)
#define PALWTADD 0x3c00
#define X_DATAREG 0x3c0a
#define XKEYOPMODE 0x51

22
sys/nix/config.h.in Normal file
View File

@ -0,0 +1,22 @@
#undef WORDS_BIGENDIAN
#undef SIZEOF_SHORT
#undef SIZEOF_INT
#undef SIZEOF_LONG
#undef HAVE_USLEEP
#undef HAVE_SELECT
#undef HAVE_MMAP
#undef HAVE_LIBXEXT
#undef HAVE_X11_EXTENSIONS_XSHM_H
#undef HAVE_SYS_IPC_H
#undef HAVE_SYS_SHM_H
#undef HAVE_SYS_SOUNDCARD_H
#undef HAVE_SOUNDCARD_H

85
sys/nix/map.c Normal file
View File

@ -0,0 +1,85 @@
/*
* this code is not yet used. eventually we want to support using mmap
* to map rom and sram into memory, so we don't waste virtual memory.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>
#include "defs.h"
#define DOTDIR ".gnuboy"
static char *home, *saves;
static char *romfile, *sramfile, *saveprefix;
static int mmapped_rom, mmaped_sram;
byte *map_rom()
{
int fd, len;
byte code;
byte *mem;
fd = open(romfile, O_READ);
lseek(fd, 0x0148, SEEK_SET);
read(fd, &code, 1);
len = loader_romsize(code);
#ifdef HAVE_MMAP
mem = mmap(0, len, PROT_READ,
#endif
}
int map_checkdirs()
{
home = malloc(strlen(getenv("HOME")) + strlen(DOTDIR) + 2);
sprintf(home, "%s/" DOTDIR, getenv(HOME));
saves = malloc(strlen(home) + 6);
sprintf(saves, "%s/saves", home);
if (access(saves, X_OK|W_OK))
{
if (access(home, X_OK|W_OK))
{
if (!access(home, F_OK))
die("cannot access %s (%s)\n", home, strerror(errno));
if (mkdir(home, 0777))
die("cannot create %s (%s)\n", home, strerror(errno));
}
if (!access(saves, F_OK))
die("cannot access %s (%s)\n", home, strerror(errno));
if (mkdir(saves, 0777))
die("cannot create %s (%s)\n", saves, strerror(errno));
}
return 0;
}

104
sys/nix/nix.c Normal file
View File

@ -0,0 +1,104 @@
/*
* nix.c
*
* System interface for *nix systems.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#define DOTDIR ".gnuboy"
#ifndef HAVE_USLEEP
static void my_usleep(unsigned int us)
{
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = us;
select(0, NULL, NULL, NULL, &tv);
}
#endif
void *sys_timer()
{
struct timeval *tv;
tv = malloc(sizeof(struct timeval));
gettimeofday(tv, NULL);
return tv;
}
int sys_elapsed(struct timeval *prev)
{
struct timeval tv;
int secs, usecs;
gettimeofday(&tv, NULL);
secs = tv.tv_sec - prev->tv_sec;
usecs = tv.tv_usec - prev->tv_usec;
*prev = tv;
if (!secs) return usecs;
return 1000000 + usecs;
}
void sys_sleep(int us)
{
if (us <= 0) return;
#ifdef HAVE_USLEEP
usleep(us);
#else
my_usleep(us);
#endif
}
void sys_checkdir(char *path, int wr)
{
char *p;
if (access(path, X_OK | (wr ? W_OK : 0)))
{
if (!access(path, F_OK))
die("cannot access %s: %s\n", path, strerror(errno));
p = strrchr(path, '/');
if (!p) die("descended to root trying to create dirs\n");
*p = 0;
sys_checkdir(path, wr);
*p = '/';
if (mkdir(path, 0777))
die("cannot create %s: %s\n", path, strerror(errno));
}
}
void sys_initpath()
{
char *buf, *home = getenv("HOME");
if (!home)
{
buf = ".";
rc_setvar("rcpath", 1, &buf);
rc_setvar("savedir", 1, &buf);
return;
}
buf = malloc(strlen(home) + strlen(DOTDIR) + 8);
sprintf(buf, "%s/" DOTDIR ":.", home);
rc_setvar("rcpath", 1, &buf);
sprintf(buf, "%s/" DOTDIR "/saves" , home);
rc_setvar("savedir", 1, &buf);
free(buf);
}
void sys_sanitize(char *s)
{
}

104
sys/oss/oss.c Normal file
View File

@ -0,0 +1,104 @@
#include <stdlib.h>
#include <string.h>
char *strdup();
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#ifdef IS_FBSD
#include "machine/soundcard.h"
#define DSP_DEVICE "/dev/dsp"
#endif
#ifdef IS_OBSD
#include "soundcard.h"
#define DSP_DEVICE "/dev/sound"
#endif
#ifdef IS_LINUX
#include <sys/soundcard.h>
#define DSP_DEVICE "/dev/dsp"
#endif
#include "defs.h"
#include "pcm.h"
#include "rc.h"
/* FIXME - all this code is VERY basic, improve it! */
struct pcm pcm;
static int dsp;
static char *dsp_device;
static int stereo = 1;
static int samplerate = 44100;
static int sound = 1;
rcvar_t pcm_exports[] =
{
RCV_BOOL("sound", &sound),
RCV_INT("stereo", &stereo),
RCV_INT("samplerate", &samplerate),
RCV_STRING("oss_device", &dsp_device),
RCV_END
};
void pcm_init()
{
int n;
if (!sound)
{
pcm.hz = 11025;
pcm.len = 4096;
pcm.buf = malloc(pcm.len);
pcm.pos = 0;
dsp = -1;
return;
}
if (!dsp_device) dsp_device = strdup(DSP_DEVICE);
dsp = open(dsp_device, O_WRONLY);
n = 0x80009;
ioctl(dsp, SNDCTL_DSP_SETFRAGMENT, &n);
n = AFMT_U8;
ioctl(dsp, SNDCTL_DSP_SETFMT, &n);
n = stereo;
ioctl(dsp, SNDCTL_DSP_STEREO, &n);
pcm.stereo = n;
n = samplerate;
ioctl(dsp, SNDCTL_DSP_SPEED, &n);
pcm.hz = n;
pcm.len = n / 60;
pcm.buf = malloc(pcm.len);
}
void pcm_close()
{
if (pcm.buf) free(pcm.buf);
memset(&pcm, 0, sizeof pcm);
close(dsp);
}
int pcm_submit()
{
if (dsp < 0)
{
pcm.pos = 0;
return 0;
}
if (pcm.buf) write(dsp, pcm.buf, pcm.pos);
pcm.pos = 0;
return 1;
}

141
sys/pc/keymap.c Normal file
View File

@ -0,0 +1,141 @@
/*
* pckeymap.c
*
* Mappings from IBM-PC scancodes to local key codes.
*/
#include "input.h"
int keymap[][2] =
{
{ 1, K_ESC },
{ 2, '1' },
{ 3, '2' },
{ 4, '3' },
{ 5, '4' },
{ 6, '5' },
{ 7, '6' },
{ 8, '7' },
{ 9, '8' },
{ 10, '9' },
{ 11, '0' },
{ 12, K_MINUS },
{ 13, K_EQUALS },
{ 14, K_BS },
{ 15, K_TAB },
{ 16, 'q' },
{ 17, 'w' },
{ 18, 'e' },
{ 19, 'r' },
{ 20, 't' },
{ 21, 'y' },
{ 22, 'u' },
{ 23, 'i' },
{ 24, 'o' },
{ 25, 'p' },
{ 26, '[' },
{ 27, ']' },
{ 28, K_ENTER },
{ 29, K_CTRL },
{ 30, 'a' },
{ 31, 's' },
{ 32, 'd' },
{ 33, 'f' },
{ 34, 'g' },
{ 35, 'h' },
{ 36, 'j' },
{ 37, 'k' },
{ 38, 'l' },
{ 39, K_SEMI },
{ 40, '\'' },
{ 41, K_TILDE },
{ 42, K_SHIFT },
{ 43, K_BSLASH },
{ 44, 'z' },
{ 45, 'x' },
{ 46, 'c' },
{ 47, 'v' },
{ 48, 'b' },
{ 49, 'n' },
{ 50, 'm' },
{ 51, ',' },
{ 52, '.' },
{ 53, '/' },
{ 54, K_SHIFT },
{ 55, K_NUMMUL },
{ 56, K_ALT },
{ 57, ' ' },
{ 58, K_CAPS },
{ 59, K_F1 },
{ 60, K_F2 },
{ 61, K_F3 },
{ 62, K_F4 },
{ 63, K_F5 },
{ 64, K_F6 },
{ 65, K_F7 },
{ 66, K_F8 },
{ 67, K_F9 },
{ 68, K_F10 },
{ 69, K_NUMLOCK },
{ 70, K_SCROLL },
{ 71, K_NUM7 },
{ 72, K_NUM8 },
{ 73, K_NUM9 },
{ 74, K_NUMMINUS },
{ 75, K_NUM4 },
{ 76, K_NUM5 },
{ 77, K_NUM6 },
{ 78, K_NUMPLUS },
{ 79, K_NUM1 },
{ 80, K_NUM2 },
{ 81, K_NUM3 },
{ 82, K_NUM0 },
{ 83, K_NUMDOT },
{ 87, K_F11 },
{ 88, K_F12 },
{ 96, K_NUMENTER },
{ 97, K_CTRL },
{ 98, K_NUMDIV },
{ 99, K_SYSRQ },
{ 100, K_ALT },
{ 101, K_PAUSE },
{ 119, K_PAUSE },
{ 102, K_HOME },
{ 103, K_UP },
{ 104, K_PRIOR },
{ 105, K_LEFT },
{ 106, K_RIGHT },
{ 107, K_END },
{ 108, K_DOWN },
{ 109, K_NEXT },
{ 110, K_INS },
{ 111, K_DEL },
{ 0, 0 }
};

87
sys/sdl/keymap.c Normal file
View File

@ -0,0 +1,87 @@
/*
* sdl_keymap.c
*
* Mappings from SDL keycode to local key codes.
* Stolen from xkeymap.c
*
*/
#include <SDL/SDL_keysym.h>
#include "input.h"
int keymap[][2] = {
{ SDLK_LSHIFT, K_SHIFT },
{ SDLK_RSHIFT, K_SHIFT },
{ SDLK_LCTRL, K_CTRL },
{ SDLK_RCTRL, K_CTRL },
{ SDLK_LALT, K_ALT },
{ SDLK_RALT, K_ALT },
{ SDLK_LMETA, K_ALT },
{ SDLK_RMETA, K_ALT },
{ SDLK_UP, K_UP },
{ SDLK_DOWN, K_DOWN },
{ SDLK_RIGHT, K_RIGHT },
{ SDLK_LEFT, K_LEFT },
{ SDLK_RETURN, K_ENTER },
{ SDLK_SPACE, K_SPACE },
{ SDLK_TAB, K_TAB },
{ SDLK_BACKSPACE, K_BS },
{ SDLK_DELETE, K_DEL },
{ SDLK_INSERT, K_INS },
{ SDLK_HOME, K_HOME },
{ SDLK_END, K_END },
{ SDLK_ESCAPE, K_ESC },
{ SDLK_PAUSE, K_PAUSE },
{ SDLK_BREAK, K_PAUSE },
{ SDLK_CAPSLOCK, K_CAPS },
{ SDLK_NUMLOCK, K_NUMLOCK },
{ SDLK_SCROLLOCK, K_SCROLL },
{ SDLK_MINUS, K_MINUS },
{ SDLK_EQUALS, K_EQUALS },
{ SDLK_LEFTBRACKET, '[' },
{ SDLK_RIGHTBRACKET, ']' },
{ SDLK_BACKSLASH, K_BSLASH },
{ SDLK_BACKQUOTE, K_TILDE },
{ SDLK_SEMICOLON, K_SEMI },
{ SDLK_QUOTE, K_QUOTE },
{ SDLK_QUOTEDBL, K_QUOTE },
{ SDLK_COMMA, ',' },
{ SDLK_PERIOD, '.' },
{ SDLK_SLASH, '/' },
{ SDLK_F1, K_F1 },
{ SDLK_F2, K_F2 },
{ SDLK_F3, K_F3 },
{ SDLK_F4, K_F4 },
{ SDLK_F5, K_F5 },
{ SDLK_F6, K_F6 },
{ SDLK_F7, K_F7 },
{ SDLK_F8, K_F8 },
{ SDLK_F9, K_F9 },
{ SDLK_F10, K_F10 },
{ SDLK_F11, K_F11 },
{ SDLK_F12, K_F12 },
{ SDLK_KP0, K_NUM0 },
{ SDLK_KP1, K_NUM1 },
{ SDLK_KP2, K_NUM2 },
{ SDLK_KP3, K_NUM3 },
{ SDLK_KP4, K_NUM4 },
{ SDLK_KP5, K_NUM5 },
{ SDLK_KP6, K_NUM6 },
{ SDLK_KP7, K_NUM7 },
{ SDLK_KP8, K_NUM8 },
{ SDLK_KP9, K_NUM9 },
{ SDLK_KP_PLUS, K_NUMPLUS },
{ SDLK_KP_MINUS, K_NUMMINUS },
{ SDLK_KP_MULTIPLY, K_NUMMUL },
{ SDLK_KP_DIVIDE, K_NUMDIV },
{ SDLK_KP_PERIOD, K_NUMDOT },
{ SDLK_KP_ENTER, K_NUMENTER },
{ 0, 0 }
};

510
sys/sdl/sdl.c Normal file
View File

@ -0,0 +1,510 @@
/*
* sdl.c
* sdl interfaces -- based on svga.c
*
* (C) 2001 Damian Gryski <dgryski@uwaterloo.ca>
* Joystick code contributed by David Lau
* Sound code added by Laguna
*
* Licensed under the GPLv2, or later.
*/
#include <stdlib.h>
#include <stdio.h>
#include <SDL/SDL.h>
#include "fb.h"
#include "input.h"
#include "rc.h"
struct fb fb;
static int use_yuv = -1;
static int fullscreen = 1;
static int use_altenter = 1;
static int use_joy = 1, sdl_joy_num;
static SDL_Joystick * sdl_joy = NULL;
static const int joy_commit_range = 3276;
static char Xstatus, Ystatus;
static SDL_Surface *screen;
static SDL_Overlay *overlay;
static SDL_Rect overlay_rect;
static int vmode[3] = { 0, 0, 16 };
rcvar_t vid_exports[] =
{
RCV_VECTOR("vmode", &vmode, 3),
RCV_BOOL("yuv", &use_yuv),
RCV_BOOL("fullscreen", &fullscreen),
RCV_BOOL("altenter", &use_altenter),
RCV_END
};
rcvar_t joy_exports[] =
{
RCV_BOOL("joy", &use_joy),
RCV_END
};
/* keymap - mappings of the form { scancode, localcode } - from sdl/keymap.c */
extern int keymap[][2];
static int mapscancode(SDLKey sym)
{
/* this could be faster: */
/* build keymap as int keymap[256], then ``return keymap[sym]'' */
int i;
for (i = 0; keymap[i][0]; i++)
if (keymap[i][0] == sym)
return keymap[i][1];
if (sym >= '0' && sym <= '9')
return sym;
if (sym >= 'a' && sym <= 'z')
return sym;
return 0;
}
static void joy_init()
{
int i;
int joy_count;
/* Initilize the Joystick, and disable all later joystick code if an error occured */
if (!use_joy) return;
if (SDL_InitSubSystem(SDL_INIT_JOYSTICK))
return;
joy_count = SDL_NumJoysticks();
if (!joy_count)
return;
/* now try and open one. If, for some reason it fails, move on to the next one */
for (i = 0; i < joy_count; i++)
{
sdl_joy = SDL_JoystickOpen(i);
if (sdl_joy)
{
sdl_joy_num = i;
break;
}
}
/* make sure that Joystick event polling is a go */
SDL_JoystickEventState(SDL_ENABLE);
}
static void overlay_init()
{
if (!use_yuv) return;
if (use_yuv < 0)
if (vmode[0] < 320 || vmode[1] < 288)
return;
overlay = SDL_CreateYUVOverlay(320, 144, SDL_YUY2_OVERLAY, screen);
if (!overlay) return;
if (!overlay->hw_overlay || overlay->planes > 1)
{
SDL_FreeYUVOverlay(overlay);
overlay = 0;
return;
}
SDL_LockYUVOverlay(overlay);
fb.w = 160;
fb.h = 144;
fb.pelsize = 4;
fb.pitch = overlay->pitches[0];
fb.ptr = overlay->pixels[0];
fb.yuv = 1;
fb.cc[0].r = fb.cc[1].r = fb.cc[2].r = fb.cc[3].r = 0;
fb.dirty = 1;
fb.enabled = 1;
overlay_rect.x = 0;
overlay_rect.y = 0;
overlay_rect.w = vmode[0];
overlay_rect.h = vmode[1];
/* Color channels are 0=Y, 1=U, 2=V, 3=Y1 */
switch (overlay->format)
{
/* FIXME - support more formats */
case SDL_YUY2_OVERLAY:
default:
fb.cc[0].l = 0;
fb.cc[1].l = 24;
fb.cc[2].l = 8;
fb.cc[3].l = 16;
break;
}
SDL_UnlockYUVOverlay(overlay);
}
void vid_init()
{
int flags;
if (!vmode[0] || !vmode[1])
{
int scale = rc_getint("scale");
if (scale < 1) scale = 1;
vmode[0] = 160 * scale;
vmode[1] = 144 * scale;
}
flags = SDL_ANYFORMAT | SDL_HWPALETTE | SDL_HWSURFACE;
if (fullscreen)
flags |= SDL_FULLSCREEN;
if (SDL_Init(SDL_INIT_VIDEO))
die("SDL: Couldn't initialize SDL: %s\n", SDL_GetError());
if (!(screen = SDL_SetVideoMode(vmode[0], vmode[1], vmode[2], flags)))
die("SDL: can't set video mode: %s\n", SDL_GetError());
SDL_ShowCursor(0);
joy_init();
overlay_init();
if (fb.yuv) return;
SDL_LockSurface(screen);
fb.w = screen->w;
fb.h = screen->h;
fb.pelsize = screen->format->BytesPerPixel;
fb.pitch = screen->pitch;
fb.indexed = fb.pelsize == 1;
fb.ptr = screen->pixels;
fb.cc[0].r = screen->format->Rloss;
fb.cc[0].l = screen->format->Rshift;
fb.cc[1].r = screen->format->Gloss;
fb.cc[1].l = screen->format->Gshift;
fb.cc[2].r = screen->format->Bloss;
fb.cc[2].l = screen->format->Bshift;
SDL_UnlockSurface(screen);
fb.enabled = 1;
fb.dirty = 0;
}
void ev_poll()
{
event_t ev;
SDL_Event event;
int axisval;
while (SDL_PollEvent(&event))
{
switch(event.type)
{
case SDL_ACTIVEEVENT:
if (event.active.state == SDL_APPACTIVE)
fb.enabled = event.active.gain;
break;
case SDL_KEYDOWN:
if ((event.key.keysym.sym == SDLK_RETURN) && (event.key.keysym.mod & KMOD_ALT))
SDL_WM_ToggleFullScreen(screen);
ev.type = EV_PRESS;
ev.code = mapscancode(event.key.keysym.sym);
ev_postevent(&ev);
break;
case SDL_KEYUP:
ev.type = EV_RELEASE;
ev.code = mapscancode(event.key.keysym.sym);
ev_postevent(&ev);
break;
case SDL_JOYAXISMOTION:
switch (event.jaxis.axis)
{
case 0: /* X axis */
axisval = event.jaxis.value;
if (axisval > joy_commit_range)
{
if (Xstatus==2) break;
if (Xstatus==0)
{
ev.type = EV_RELEASE;
ev.code = K_JOYLEFT;
ev_postevent(&ev);
}
ev.type = EV_PRESS;
ev.code = K_JOYRIGHT;
ev_postevent(&ev);
Xstatus=2;
break;
}
if (axisval < -(joy_commit_range))
{
if (Xstatus==0) break;
if (Xstatus==2)
{
ev.type = EV_RELEASE;
ev.code = K_JOYRIGHT;
ev_postevent(&ev);
}
ev.type = EV_PRESS;
ev.code = K_JOYLEFT;
ev_postevent(&ev);
Xstatus=0;
break;
}
/* if control reaches here, the axis is centered,
* so just send a release signal if necisary */
if (Xstatus==2)
{
ev.type = EV_RELEASE;
ev.code = K_JOYRIGHT;
ev_postevent(&ev);
}
if (Xstatus==0)
{
ev.type = EV_RELEASE;
ev.code = K_JOYLEFT;
ev_postevent(&ev);
}
Xstatus=1;
break;
case 1: /* Y axis*/
axisval = event.jaxis.value;
if (axisval > joy_commit_range)
{
if (Ystatus==2) break;
if (Ystatus==0)
{
ev.type = EV_RELEASE;
ev.code = K_JOYUP;
ev_postevent(&ev);
}
ev.type = EV_PRESS;
ev.code = K_JOYDOWN;
ev_postevent(&ev);
Ystatus=2;
break;
}
if (axisval < -joy_commit_range)
{
if (Ystatus==0) break;
if (Ystatus==2)
{
ev.type = EV_RELEASE;
ev.code = K_JOYDOWN;
ev_postevent(&ev);
}
ev.type = EV_PRESS;
ev.code = K_JOYUP;
ev_postevent(&ev);
Ystatus=0;
break;
}
/* if control reaches here, the axis is centered,
* so just send a release signal if necisary */
if (Ystatus==2)
{
ev.type = EV_RELEASE;
ev.code = K_JOYDOWN;
ev_postevent(&ev);
}
if (Ystatus==0)
{
ev.type = EV_RELEASE;
ev.code = K_JOYUP;
ev_postevent(&ev);
}
Ystatus=1;
break;
}
break;
case SDL_JOYBUTTONUP:
if (event.jbutton.button>15) break;
ev.type = EV_RELEASE;
ev.code = K_JOY0 + event.jbutton.button;
ev_postevent(&ev);
break;
case SDL_JOYBUTTONDOWN:
if (event.jbutton.button>15) break;
ev.type = EV_PRESS;
ev.code = K_JOY0+event.jbutton.button;
ev_postevent(&ev);
break;
case SDL_QUIT:
exit(1);
break;
default:
break;
}
}
}
void vid_setpal(int i, int r, int g, int b)
{
SDL_Color col;
col.r = r; col.g = g; col.b = b;
SDL_SetColors(screen, &col, i, 1);
}
void vid_preinit()
{
}
void vid_close()
{
if (overlay)
{
SDL_UnlockYUVOverlay(overlay);
SDL_FreeYUVOverlay(overlay);
}
else SDL_UnlockSurface(screen);
SDL_Quit();
fb.enabled = 0;
}
void vid_settitle(char *title)
{
SDL_WM_SetCaption(title, title);
}
void vid_begin()
{
if (overlay)
{
SDL_LockYUVOverlay(overlay);
fb.ptr = overlay->pixels[0];
return;
}
SDL_LockSurface(screen);
fb.ptr = screen->pixels;
}
void vid_end()
{
if (overlay)
{
SDL_UnlockYUVOverlay(overlay);
if (fb.enabled)
SDL_DisplayYUVOverlay(overlay, &overlay_rect);
return;
}
SDL_UnlockSurface(screen);
if (fb.enabled) SDL_Flip(screen);
}
#include "pcm.h"
struct pcm pcm;
static int sound = 1;
static int samplerate = 44100;
static int stereo = 1;
static volatile int audio_done;
rcvar_t pcm_exports[] =
{
RCV_BOOL("sound", &sound),
RCV_INT("stereo", &stereo),
RCV_INT("samplerate", &samplerate),
RCV_END
};
static void audio_callback(void *blah, byte *stream, int len)
{
memcpy(stream, pcm.buf, len);
audio_done = 1;
}
void pcm_init()
{
int i;
SDL_AudioSpec as;
if (!sound) return;
SDL_InitSubSystem(SDL_INIT_AUDIO);
as.freq = samplerate;
as.format = AUDIO_U8;
as.channels = 1 + stereo;
as.samples = samplerate / 60;
for (i = 1; i < as.samples; i<<=1);
as.samples = i;
as.callback = audio_callback;
as.userdata = 0;
if (SDL_OpenAudio(&as, 0) == -1)
return;
pcm.hz = as.freq;
pcm.stereo = as.channels - 1;
pcm.len = as.size;
pcm.buf = malloc(pcm.len);
pcm.pos = 0;
memset(pcm.buf, 0, pcm.len);
SDL_PauseAudio(0);
}
int pcm_submit()
{
if (!pcm.buf) return 0;
if (pcm.pos < pcm.len) return 1;
while (!audio_done)
SDL_Delay(4);
audio_done = 0;
pcm.pos = 0;
return 1;
}
void pcm_close()
{
if (sound) SDL_CloseAudio();
}

246
sys/svga/svgalib.c Normal file
View File

@ -0,0 +1,246 @@
/*
* svgalib.c
*
* svgalib interface.
*/
#include <stdlib.h>
#include <stdio.h>
#include <vga.h>
#include <vgakeyboard.h>
#include "fb.h"
#include "input.h"
#include "rc.h"
struct fb fb;
static int vmode[3] = { 0, 0, 8 };
static int svga_mode;
static int svga_vsync = 1;
rcvar_t vid_exports[] =
{
RCV_VECTOR("vmode", vmode, 3),
RCV_INT("vsync", &svga_vsync),
RCV_INT("svga_mode", &svga_mode),
RCV_END
};
/* keymap - mappings of the form { scancode, localcode } - from pc/keymap.c */
extern int keymap[][2];
static int mapscancode(int scan)
{
int i;
for (i = 0; keymap[i][0]; i++)
if (keymap[i][0] == scan)
return keymap[i][1];
return 0;
}
static void kbhandler(int scan, int state)
{
event_t ev;
ev.type = state ? EV_PRESS : EV_RELEASE;
ev.code = mapscancode(scan);
ev_postevent(&ev);
}
int *rc_getvec();
static int selectmode()
{
int i;
int stop;
vga_modeinfo *mi;
int best = -1;
int besterr = 1<<24;
int err;
int *vd;
vd = vmode;
stop = vga_lastmodenumber();
for (i = 0; i <= stop; i++)
{
if (!vga_hasmode(i)) continue;
mi = vga_getmodeinfo(i);
/* modex is too crappy to deal with */
if (!mi->bytesperpixel) continue;
/* so are banked modes */
if (mi->width * mi->height * mi->bytesperpixel > 65536)
if (!(mi->flags & (IS_LINEAR))) continue;
/* we can't use modes that are too small */
if (mi->colors < 256) continue;
if (mi->width < vd[0]) continue;
if (mi->height < vd[1]) continue;
/* perfect matches always win */
if (mi->width == vd[0] && mi->height == vd[1]
&& (mi->bytesperpixel<<3) == vd[2])
{
best = i;
break;
}
/* compare error */
err = mi->width * mi->height - vd[0] * vd[1]
+ abs((mi->bytesperpixel<<3)-vd[2]);
if (err < besterr)
{
best = i;
besterr = err;
}
}
if (best < 0)
die("no suitable modes available\n");
return best;
}
void vid_preinit()
{
vga_init();
}
void vid_init()
{
int m;
vga_modeinfo *mi;
if (!vmode[0] || !vmode[1])
{
int scale = rc_getint("scale");
if (scale < 1) scale = 1;
vmode[0] = 160 * scale;
vmode[1] = 144 * scale;
}
m = svga_mode;
if (!m) m = selectmode();
if (!vga_hasmode(m))
die("no such video mode: %d\n", m);
vga_setmode(m);
mi = vga_getmodeinfo(m);
fb.w = mi->width;
fb.h = mi->height;
fb.pelsize = mi->bytesperpixel;
fb.pitch = mi->linewidth;
fb.ptr = vga_getgraphmem();
fb.enabled = 1;
fb.dirty = 0;
switch (mi->colors)
{
case 256:
fb.indexed = 1;
fb.cc[0].r = fb.cc[1].r = fb.cc[2].r = 8;
fb.cc[0].l = fb.cc[1].l = fb.cc[2].l = 0;
break;
case 32768:
fb.indexed = 0;
fb.cc[0].r = fb.cc[1].r = fb.cc[2].r = 3;
fb.cc[0].l = 10;
fb.cc[1].l = 5;
fb.cc[2].l = 0;
break;
case 65536:
fb.indexed = 0;
fb.cc[0].r = fb.cc[2].r = 3;
fb.cc[1].r = 2;
fb.cc[0].l = 11;
fb.cc[1].l = 5;
fb.cc[2].l = 0;
break;
case 16384*1024:
fb.indexed = 0;
fb.cc[0].r = fb.cc[1].r = fb.cc[2].r = 0;
fb.cc[0].l = 16;
fb.cc[1].l = 8;
fb.cc[2].l = 0;
break;
}
keyboard_init();
keyboard_seteventhandler(kbhandler);
joy_init();
}
void vid_close()
{
if (!fb.ptr) return;
memset(&fb, 0, sizeof fb);
joy_close();
keyboard_close();
vga_setmode(TEXT);
}
void vid_settitle(char *title)
{
}
void vid_setpal(int i, int r, int g, int b)
{
vga_setpalette(i, r>>2, g>>2, b>>2);
}
void vid_begin()
{
if (svga_vsync) vga_waitretrace();
}
void vid_end()
{
}
void kb_init()
{
}
void kb_close()
{
}
void kb_poll()
{
keyboard_update();
}
void ev_poll()
{
kb_poll();
joy_poll();
}

141
sys/thinlib/keymap.c Normal file
View File

@ -0,0 +1,141 @@
/*
* pckeymap.c
*
* Mappings from IBM-PC scancodes to local key codes.
*/
#include "input.h"
#include "thinlib.h"
int keymap[][2] =
{
{ THIN_KEY_ESC, K_ESC },
{ THIN_KEY_1, '1' },
{ THIN_KEY_2, '2' },
{ THIN_KEY_3, '3' },
{ THIN_KEY_4, '4' },
{ THIN_KEY_5, '5' },
{ THIN_KEY_6, '6' },
{ THIN_KEY_7, '7' },
{ THIN_KEY_8, '8' },
{ THIN_KEY_9, '9' },
{ THIN_KEY_0, '0' },
{ THIN_KEY_MINUS, K_MINUS },
{ THIN_KEY_EQUALS, K_EQUALS },
{ THIN_KEY_BACKSPACE, K_BS },
{ THIN_KEY_TAB, K_TAB },
{ THIN_KEY_Q, 'q' },
{ THIN_KEY_W, 'w' },
{ THIN_KEY_E, 'e' },
{ THIN_KEY_R, 'r' },
{ THIN_KEY_T, 't' },
{ THIN_KEY_Y, 'y' },
{ THIN_KEY_U, 'u' },
{ THIN_KEY_I, 'i' },
{ THIN_KEY_O, 'o' },
{ THIN_KEY_P, 'p' },
{ THIN_KEY_OPEN_BRACE, '[' },
{ THIN_KEY_CLOSE_BRACE, ']' },
{ THIN_KEY_ENTER, K_ENTER },
{ THIN_KEY_LEFT_CTRL, K_CTRL },
{ THIN_KEY_A, 'a' },
{ THIN_KEY_S, 's' },
{ THIN_KEY_D, 'd' },
{ THIN_KEY_F, 'f' },
{ THIN_KEY_G, 'g' },
{ THIN_KEY_H, 'h' },
{ THIN_KEY_J, 'j' },
{ THIN_KEY_K, 'k' },
{ THIN_KEY_L, 'l' },
{ THIN_KEY_SEMICOLON, K_SEMI },
{ THIN_KEY_QUOTE, '\'' },
{ THIN_KEY_TILDE, K_TILDE },
{ THIN_KEY_LEFT_SHIFT, K_SHIFT },
{ THIN_KEY_BACKSLASH, K_BSLASH },
{ THIN_KEY_Z, 'z' },
{ THIN_KEY_X, 'x' },
{ THIN_KEY_C, 'c' },
{ THIN_KEY_V, 'v' },
{ THIN_KEY_B, 'b' },
{ THIN_KEY_N, 'n' },
{ THIN_KEY_M, 'm' },
{ THIN_KEY_COMMA, ',' },
{ THIN_KEY_PERIOD, '.' },
{ THIN_KEY_SLASH, '/' },
{ THIN_KEY_RIGHT_SHIFT, K_SHIFT },
{ THIN_KEY_NUMPAD_MULT, K_NUMMUL },
{ THIN_KEY_LEFT_ALT, K_ALT },
{ THIN_KEY_SPACE, ' ' },
{ THIN_KEY_CAPS_LOCK, K_CAPS },
{ THIN_KEY_F1, K_F1 },
{ THIN_KEY_F2, K_F2 },
{ THIN_KEY_F3, K_F3 },
{ THIN_KEY_F4, K_F4 },
{ THIN_KEY_F5, K_F5 },
{ THIN_KEY_F6, K_F6 },
{ THIN_KEY_F7, K_F7 },
{ THIN_KEY_F8, K_F8 },
{ THIN_KEY_F9, K_F9 },
{ THIN_KEY_F10, K_F10 },
{ THIN_KEY_NUM_LOCK, K_NUMLOCK },
{ THIN_KEY_SCROLL_LOCK, K_SCROLL },
{ THIN_KEY_NUMPAD_7, K_NUM7 },
{ THIN_KEY_NUMPAD_8, K_NUM8 },
{ THIN_KEY_NUMPAD_9, K_NUM9 },
{ THIN_KEY_NUMPAD_MINUS, K_NUMMINUS },
{ THIN_KEY_NUMPAD_4, K_NUM4 },
{ THIN_KEY_NUMPAD_5, K_NUM5 },
{ THIN_KEY_NUMPAD_6, K_NUM6 },
{ THIN_KEY_NUMPAD_PLUS, K_NUMPLUS },
{ THIN_KEY_NUMPAD_1, K_NUM1 },
{ THIN_KEY_NUMPAD_2, K_NUM2 },
{ THIN_KEY_NUMPAD_3, K_NUM3 },
{ THIN_KEY_NUMPAD_0, K_NUM0 },
{ THIN_KEY_NUMPAD_DECIMAL, K_NUMDOT },
{ THIN_KEY_F11, K_F11 },
{ THIN_KEY_F12, K_F12 },
{ THIN_KEY_NUMPAD_ENTER, K_NUMENTER },
{ THIN_KEY_RIGHT_CTRL, K_CTRL },
{ THIN_KEY_NUMPAD_DIV, K_NUMDIV },
{ THIN_KEY_SYSRQ, K_SYSRQ },
{ THIN_KEY_RIGHT_ALT, K_ALT },
//{ THIN_KEY_PAUSE, K_PAUSE },
{ THIN_KEY_HOME, K_HOME },
{ THIN_KEY_UP, K_UP },
{ THIN_KEY_PGUP, K_PRIOR },
{ THIN_KEY_LEFT, K_LEFT },
{ THIN_KEY_RIGHT, K_RIGHT },
{ THIN_KEY_END, K_END },
{ THIN_KEY_DOWN, K_DOWN },
{ THIN_KEY_PGDN, K_NEXT },
{ THIN_KEY_INSERT, K_INS },
{ THIN_KEY_DELETE, K_DEL },
{ 0, 0 }
};

100
sys/thinlib/lib/Makefile Normal file
View File

@ -0,0 +1,100 @@
#
# Makefile
#
# thinlib library makefile
#
# Copyright (C) 2001 Matthew Conte (matt@conte.com)
#
# $Id: $
################################
# Configuration
CFLAGS = -W -Wall -Werror
DBGCFLAGS = -ggdb -DTHINLIB_DEBUG
OPTCFLAGS = -O3 -fomit-frame-pointer -ffast-math
# Assembler
ASM = nasm
ASMFLAGS = -f coff
DBGASMFLAGS = -g
################################
# WANT_DEBUG = TRUE
################################
ifeq "$(WANT_DEBUG)" "TRUE"
CFLAGS += $(DBGCFLAGS)
ASMFLAGS += $(DBGASMFLAGS)
else
CFLAGS += $(OPTCFLAGS)
endif
################################
CFILES = tl_main tl_log tl_timer tl_int tl_key tl_mouse tl_joy \
tl_dpp tl_bmp tl_vesa tl_vga tl_video tl_sb tl_sound \
tl_event tl_prof
CSRCS = $(addsuffix .c, $(CFILES))
OBJS = $(addsuffix .o, $(CFILES))
################################
.PHONY = all dep clean
all: libthin.a thintest.exe
clean:
rm -f libthin.a thintest.exe $(OBJS) _dep
thintest.exe: thintest.cpp libthin.a
$(CXX) -o $@ thintest.cpp -L. -lthin
libthin.a: $(OBJS)
rm -f $@
ar scru $@ $(OBJS)
dep: rmdep _dep
################################
rmdep:
@rm -f _dep
@echo "# dep file" > _dep
ifneq "$(CSRCS)" ""
@$(foreach .a, $(CSRCS), $(CC) $(CFLAGS) -MM $(.a) >> _dep;)
endif
ifneq "$(ASMSRCS)" ""
@$(foreach .a, $(ASMSRCS), $(ASM) $(ASMFLAGS) -M $(.a) >> _dep;)
endif
_dep:
# this is done so that we don't get all the no such file warnings
@echo "# dep file" > _dep
ifneq "$(CSRCS)" ""
@$(foreach .a, $(CSRCS), $(CC) $(CFLAGS) -MM $(.a) >> _dep;)
endif
ifneq "$(ASMSRCS)" ""
@$(foreach .a, $(ASMSRCS), $(ASM) $(ASMFLAGS) -M $(.a) >> _dep;)
endif
include _dep
################################
%.o: %.cpp
$(CXX) $(CFLAGS) -o $@ -c $<
%.o: %.c
$(CC) $(CFLAGS) -o $@ -c $<
%.o: %.asm
$(ASM) $(ASMFLAGS) -o $@ $<
################################
# $Log: $

67
sys/thinlib/lib/thinlib.h Normal file
View File

@ -0,0 +1,67 @@
/*
** thinlib (c) 2001 Matthew Conte (matt@conte.com)
**
**
** thinlib.h
**
** main library header
**
** $Id: $
*/
#ifndef _THINLIB_H_
#define _THINLIB_H_
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/* types */
#include "tl_types.h"
#include "tl_log.h"
#include "tl_prof.h"
/* system */
#include "tl_djgpp.h"
#include "tl_int.h"
#include "tl_timer.h"
/* input/events */
#include "tl_event.h"
#include "tl_key.h"
#include "tl_mouse.h"
#include "tl_joy.h"
#include "tl_dpp.h"
/* video */
#include "tl_bmp.h"
#include "tl_video.h"
/* audio */
#include "tl_sound.h"
#define THIN_KEY 0x0001
#define THIN_MOUSE 0x0002
#define THIN_JOY 0x0004
#define THIN_DPP 0x0008
#define THIN_TIMER 0x0010
#define THIN_VIDEO 0x0020
#define THIN_SOUND 0x0040
/* main interface */
extern int thin_init(int devices);
extern void thin_shutdown(void);
extern void thin_add_exit(void (*func)(void));
extern void thin_remove_exit(void (*func)(void));
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* !_THINLIB_H */
/*
** $Log: $
*/

View File

@ -0,0 +1,287 @@
/*
** thinlib (c) 2001 Matthew Conte (matt@conte.com)
**
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of version 2 of the GNU Library General
** Public License as published by the Free Software Foundation.
**
** 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
** Library General Public License for more details. To obtain a
** copy of the GNU Library General Public License, write to the Free
** Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
**
** Any permitted reproduction of these routines, in whole or in part,
** must bear this legend.
**
**
** thintest.cpp
**
** thinlib test
** $Id: thintest.cpp,v 1.3 2001/03/12 06:06:55 matt Exp $
*/
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "thinlib.h"
//#define TEST_STEREO
#define TEST_DPP
class test
{
public:
test();
~test();
void run();
private:
enum
{
SAMPLE_RATE = 44100,
FRAGSIZE = 256,
VID_WIDTH = 320,
VID_HEIGHT = 240,
VID_BPP = 8
};
int testSound();
int testVideo();
int testTimer();
int testEvents();
};
test::test()
{
int ret = thin_init(THIN_KEY | THIN_MOUSE | THIN_TIMER
| THIN_VIDEO | THIN_SOUND);
THIN_ASSERT(-1 != ret);
}
test::~test()
{
thin_shutdown();
}
static void fillbuf(void *user_data, void *buf, int size)
{
static int pos = 0;
UNUSED(user_data);
while (size--)
{
*((uint8 *) buf)++ = 127 + (int8)(127.0 * sin(2 * PI * pos / 128));
#ifdef TEST_STEREO
*((uint8 *) buf)++ = 127 + (int8)(127.0 * cos(2 * PI * pos / 128));
#endif
pos = (pos + 1) & 1023;
}
}
int test::testSound()
{
thinsound_t params;
params.sample_rate = SAMPLE_RATE;
params.frag_size = FRAGSIZE;
#ifdef TEST_STEREO
params.format = THIN_SOUND_STEREO | THIN_SOUND_8BIT;
#else
params.format = THIN_SOUND_MONO | THIN_SOUND_8BIT;
#endif
params.callback = fillbuf;
params.user_data = NULL;
if (thin_sound_init(&params))
return -1;
thin_sound_start();
thin_sound_stop();
return 0;
}
int test::testVideo()
{
int i, x, y;
bitmap_t *screen;
bitmap_t *buffer;
/* set up video */
if (thin_vid_init(VID_WIDTH, VID_HEIGHT, VID_BPP, 0/*THIN_VIDEO_HWSURFACE*/))
return -1;
buffer = thin_bmp_create(VID_WIDTH, VID_HEIGHT, VID_BPP, 0);
if (NULL == buffer)
return -1;
/* fill it up with something interesting */
for (y = 0; y < buffer->height; y++)
for (x = 0; x < buffer->width; x++)
buffer->line[y][x] = x ^ y;
/* blit it out 1000 times */
for (i = 0; i < 1000; i++)
{
screen = thin_vid_lockwrite();
if (NULL == screen)
return -1;
for (y = 0; y < screen->height; y++)
memcpy(screen->line[y], buffer->line[y], screen->width);
thin_vid_freewrite(-1, NULL);
}
thin_vid_shutdown();
if (1000 != i)
return -1;
return 0;
}
static volatile int timer_ticks = 0;
static void timer_handler(void *param)
{
(void) param;
timer_ticks++;
}
THIN_LOCKED_STATIC_FUNC(timer_handler)
int test::testTimer()
{
int last_ticks;
THIN_LOCK_FUNC(timer_handler);
THIN_LOCK_VAR(timer_ticks);
/* one second intervals... */
if (thin_timer_init(60, timer_handler, NULL))
return -1;
timer_ticks = last_ticks = 0;
while (timer_ticks <= 60)
{
if (last_ticks != timer_ticks)
{
last_ticks = timer_ticks;
thin_printf("%d 60 hz tick\n", last_ticks);
}
}
thin_timer_shutdown();
return 0;
}
int test::testEvents()
{
thin_event_t event;
bool done = false;
thin_mouse_init(80, 20, 1);
thin_joy_init();
thin_dpp_init();
thin_dpp_add(0x378, 0);
thin_printf("event test: press ESC...");
while (!done)
{
thin_event_gather();
while (thin_event_get(&event))
{
switch (event.type)
{
case THIN_KEY_PRESS:
if (event.data.keysym == THIN_KEY_ESC)
done = true;
thin_printf("key press\n");
break;
case THIN_KEY_RELEASE:
thin_printf("key release\n");
break;
case THIN_MOUSE_MOTION:
thin_printf("mouse motion\n");
break;
case THIN_MOUSE_BUTTON_PRESS:
thin_printf("mouse button press\n");
break;
case THIN_MOUSE_BUTTON_RELEASE:
thin_printf("mouse button release\n");
break;
case THIN_JOY_MOTION:
thin_printf("joy motion\n");
break;
case THIN_JOY_BUTTON_PRESS:
thin_printf("joy button press\n");
break;
case THIN_JOY_BUTTON_RELEASE:
thin_printf("joy button release\n");
break;
default:
break;
}
}
}
thin_dpp_shutdown();
thin_joy_shutdown();
thin_mouse_shutdown();
return 0;
}
void test::run()
{
if (testSound())
return;
if (testVideo())
return;
if (testTimer())
return;
if (testEvents())
return;
thin_printf("\ntest complete.\n");
}
int main(void)
{
test *pTest = new test;
pTest->run();
delete pTest;
return 0;
}
/*
** $Log: thintest.cpp,v $
** Revision 1.3 2001/03/12 06:06:55 matt
** better keyboard driver, support for bit depths other than 8bpp
**
** Revision 1.2 2001/02/01 06:28:26 matt
** thinlib now works under NT/2000
**
** Revision 1.1 2001/01/15 05:27:43 matt
** initial revision
**
*/

99
sys/thinlib/lib/tl_bmp.c Normal file
View File

@ -0,0 +1,99 @@
/*
** thinlib (c) 2001 Matthew Conte (matt@conte.com)
**
**
** tl_bmp.c
**
** Bitmap object manipulation routines
**
** $Id: $
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "tl_types.h"
#include "tl_bmp.h"
#define BPP_PITCH(pitch, bpp) ((pitch) * (((bpp) + 7) / 8))
/* TODO: you can make this faster. */
void thin_bmp_clear(const bitmap_t *bitmap, uint8 color)
{
memset(bitmap->data, color, bitmap->pitch * bitmap->height);
}
static bitmap_t *_make_bitmap(uint8 *data_addr, bool hw, int width,
int height, int bpp, int pitch, int overdraw)
{
bitmap_t *bitmap;
int i;
/* sometimes our data address is zero; for instance, setting
** video selectors for VESA mode with far pointers. so we
** don't want to bail out if we're passed a zero address.
*/
/* Make sure to add in space for line pointers */
bitmap = malloc(sizeof(bitmap_t) + (sizeof(uint8 *) * height));
if (NULL == bitmap)
return NULL;
bitmap->hardware = hw;
bitmap->height = height;
bitmap->width = width;
bitmap->bpp = bpp;
bitmap->data = data_addr;
bitmap->pitch = BPP_PITCH(pitch, bpp);
/* Set up line pointers */
bitmap->line[0] = bitmap->data + overdraw;
for (i = 1; i < height; i++)
bitmap->line[i] = bitmap->line[i - 1] + bitmap->pitch;
return bitmap;
}
/* Allocate and initialize a bitmap structure */
bitmap_t *thin_bmp_create(int width, int height, int bpp, int overdraw)
{
uint8 *addr;
/* left and right overdraw */
int pitch = width + (overdraw * 2);
/* dword align */
pitch = (pitch + 3) & ~3;
addr = malloc(height * BPP_PITCH(pitch, bpp));
if (NULL == addr)
return NULL;
return _make_bitmap(addr, false, width, height, bpp, pitch, overdraw);
}
/* allocate and initialize a hardware bitmap */
bitmap_t *thin_bmp_createhw(uint8 *addr, int width, int height, int bpp, int pitch)
{
return _make_bitmap(addr, true, width, height, bpp, pitch, 0); /* zero overdraw */
}
/* Deallocate space for a bitmap structure */
void thin_bmp_destroy(bitmap_t **bitmap)
{
if (*bitmap)
{
if ((*bitmap)->data && false == (*bitmap)->hardware)
free((*bitmap)->data);
free(*bitmap);
*bitmap = NULL;
}
}
/*
** $Log: $
*/

47
sys/thinlib/lib/tl_bmp.h Normal file
View File

@ -0,0 +1,47 @@
/*
** thinlib (c) 2001 Matthew Conte (matt@conte.com)
**
**
** tl_bmp.h
**
** Bitmap object defines / prototypes
**
** $Id: $
*/
#ifndef _TL_BMP_H_
#define _TL_BMP_H_
#include "tl_types.h"
/* a bitmap rectangle */
typedef struct rect_s
{
int16 x, y;
uint16 w, h;
} rect_t;
typedef struct rgb_s
{
int r, g, b;
} rgb_t;
typedef struct bitmap_s
{
int width, height, pitch;
int bpp;
bool hardware; /* is data a hardware region? */
uint8 *data; /* protected */
uint8 *line[0]; /* will hold line pointers */
} bitmap_t;
extern void thin_bmp_clear(const bitmap_t *bitmap, uint8 color);
extern bitmap_t *thin_bmp_create(int width, int height, int bpp, int overdraw);
extern bitmap_t *thin_bmp_createhw(uint8 *addr, int width, int height, int bpp, int pitch);
extern void thin_bmp_destroy(bitmap_t **bitmap);
#endif /* !_TL_BMP_H_ */
/*
** $Log: $
*/

View File

@ -0,0 +1,34 @@
/*
** thinlib (c) 2001 Matthew Conte (matt@conte.com)
**
**
** tl_djgpp.h
**
** djgpp frigs
**
** $Id: $
*/
#ifndef _TL_DJGPP_H_
#define _TL_DJGPP_H_
#include <dpmi.h>
/* interface to lock code and data */
#define THIN_LOCKED_FUNC(x) void x##_end(void) { }
#define THIN_LOCKED_STATIC_FUNC(x) static void x##_end(void) { }
#define THIN_LOCK_DATA(d, s) _go32_dpmi_lock_data(d, s)
#define THIN_LOCK_CODE(c, s) _go32_dpmi_lock_code(c, s)
#define THIN_LOCK_VAR(x) THIN_LOCK_DATA((void *) &x, sizeof(x))
#define THIN_LOCK_FUNC(x) THIN_LOCK_CODE((void *) x, (long) x##_end - (long) x)
#include <sys/nearptr.h>
#define THIN_PHYSICAL_ADDR(x) ((x) + __djgpp_conventional_base)
extern int thinlib_nearptr;
#endif /* !_TL_DJGPP_H_ */
/*
** $Log: $
*/

191
sys/thinlib/lib/tl_dpp.c Normal file
View File

@ -0,0 +1,191 @@
/*
** thinlib (c) 2001 Matthew Conte (matt@conte.com)
**
**
** tl_dpp.c
**
** DOS DirectPad Pro scanning code, based on code from
** DirectPad Pro (www.ziplabel.com), written by Earle F. Philhower, III
**
** $Id: $
*/
#include <pc.h>
#include "tl_types.h"
#include "tl_dpp.h"
#include "tl_event.h"
#define MAX_PADS 5
#define NES_PWR (0x80 + 0x40 + 0x20 + 0x10 + 0x08)
#define NES_CLK 1
#define NES_LAT 2
#define NES_IN ((inportb(port + 1) & nes_din) ^ xor_val)
#define NES_OUT(v) (outportb(port, (v)))
static const uint8 din_table[MAX_PADS] = { 0x40, 0x20, 0x10, 0x08, 0x80 };
static const int xor_table[MAX_PADS] = { 1, 1, 1, 1, 0 };
static dpp_t dpp[MAX_PADS];
static event_id dpp_id; /* event callback id */
void _dpp_poll(void)
{
int i;
int nes_din, port, xor_val;
dpp_t *pad, old;
thin_event_t event;
for (i = 0; i < MAX_PADS; i++)
{
if (0 == dpp[i].port)
continue;
nes_din = din_table[i];
port = dpp[i].port;
xor_val = nes_din * xor_table[i];
pad = &dpp[i];
old = *pad;
NES_OUT(NES_PWR);
NES_OUT(NES_PWR + NES_LAT + NES_CLK);
NES_OUT(NES_PWR);
pad->a = NES_IN;
NES_OUT(NES_PWR);
NES_OUT(NES_PWR + NES_CLK);
NES_OUT(NES_PWR);
pad->b = NES_IN;
NES_OUT(NES_PWR);
NES_OUT(NES_PWR + NES_CLK);
NES_OUT(NES_PWR);
pad->select = NES_IN;
NES_OUT(NES_PWR);
NES_OUT(NES_PWR + NES_CLK);
NES_OUT(NES_PWR);
pad->start = NES_IN;
NES_OUT(NES_PWR);
NES_OUT(NES_PWR + NES_CLK);
NES_OUT(NES_PWR);
pad->up = NES_IN;
NES_OUT(NES_PWR);
NES_OUT(NES_PWR + NES_CLK);
NES_OUT(NES_PWR);
pad->down = NES_IN;
NES_OUT(NES_PWR);
NES_OUT(NES_PWR + NES_CLK);
NES_OUT(NES_PWR);
pad->left = NES_IN;
NES_OUT(NES_PWR);
NES_OUT(NES_PWR + NES_CLK);
NES_OUT(NES_PWR);
pad->right = NES_IN;
NES_OUT(0); /* power down */
/* generate some events if necessary */
if (pad->left != old.left)
{
event.type = THIN_JOY_MOTION;
event.data.joy_motion.dir = THIN_JOY_LEFT;
event.data.joy_motion.state = pad->left;
thin_event_add(&event);
}
if (pad->right != old.right)
{
event.type = THIN_JOY_MOTION;
event.data.joy_motion.dir = THIN_JOY_RIGHT;
event.data.joy_motion.state = pad->right;
thin_event_add(&event);
}
if (pad->up != old.up)
{
event.type = THIN_JOY_MOTION;
event.data.joy_motion.dir = THIN_JOY_UP;
event.data.joy_motion.state = pad->up;
thin_event_add(&event);
}
if (pad->down != old.down)
{
event.type = THIN_JOY_MOTION;
event.data.joy_motion.dir = THIN_JOY_DOWN;
event.data.joy_motion.state = pad->down;
thin_event_add(&event);
}
if (pad->select != old.select)
{
event.type = pad->select ? THIN_JOY_BUTTON_PRESS : THIN_JOY_BUTTON_RELEASE;
event.data.joy_button = 2;
thin_event_add(&event);
}
if (pad->start != old.start)
{
event.type = pad->start ? THIN_JOY_BUTTON_PRESS : THIN_JOY_BUTTON_RELEASE;
event.data.joy_button = 3;
thin_event_add(&event);
}
if (pad->b != old.b)
{
event.type = pad->b ? THIN_JOY_BUTTON_PRESS : THIN_JOY_BUTTON_RELEASE;
event.data.joy_button = 0;
thin_event_add(&event);
}
if (pad->a != old.a)
{
event.type = pad->a ? THIN_JOY_BUTTON_PRESS : THIN_JOY_BUTTON_RELEASE;
event.data.joy_button = 1;
thin_event_add(&event);
}
}
}
void thin_dpp_read(dpp_t *pad, int pad_num)
{
*pad = dpp[pad_num];
}
int thin_dpp_add(uint16 port, int pad_num)
{
dpp[pad_num].port = port;
return 0;
}
int thin_dpp_init(void)
{
dpp_id = thin_event_add_callback((event_callback_t) _dpp_poll);
if (-1 == dpp_id)
return -1;
return 0;
}
void thin_dpp_shutdown(void)
{
if (-1 != dpp_id)
{
thin_event_remove_callback(dpp_id);
dpp_id = -1;
memset(dpp, 0, sizeof(dpp));
}
}
/*
** $Log: $
*/

35
sys/thinlib/lib/tl_dpp.h Normal file
View File

@ -0,0 +1,35 @@
/*
** thinlib (c) 2001 Matthew Conte (matt@conte.com)
**
**
** tl_dpp.h
**
** DOS DirectPad Pro scanning code prototypes
**
** $Id: $
*/
#ifndef _TL_DPP_H_
#define _TL_DPP_H_
#include "tl_types.h"
typedef struct dpp_s
{
/* private: */
uint16 port; /* LPT port */
/* public: */
int down, up, left, right;
int b, a, select, start;
} dpp_t;
extern int thin_dpp_add(uint16 port, int pad_num);
extern int thin_dpp_init(void);
extern void thin_dpp_shutdown(void);
extern void thin_dpp_read(dpp_t *pad, int pad_num);
#endif /* !_TL_DPP_H_ */
/*
** $Log: $
*/

140
sys/thinlib/lib/tl_event.c Normal file
View File

@ -0,0 +1,140 @@
/*
** thinlib (c) 2001 Matthew Conte (matt@conte.com)
**
**
** tl_event.c
**
** event handling routines
**
** $Id: $
*/
#include "tl_types.h"
#include "tl_event.h"
#include "tl_djgpp.h"
/* maximum of 8 event handling callbacks */
#define MAX_CALLBACKS 8
#define EVENT_QUEUE_MAX 256
#define EVENT_QUEUE_MASK (EVENT_QUEUE_MAX - 1)
#define EVENT_QUEUE_EMPTY (event_queue.head == event_queue.tail)
typedef struct event_queue_s
{
int head;
int tail;
thin_event_t event[EVENT_QUEUE_MAX];
} event_queue_t;
static event_queue_t event_queue;
static event_callback_t event_callback[MAX_CALLBACKS];
/* add an event. */
void thin_event_add(thin_event_t *event)
{
event_queue.event[event_queue.head] = *event;
event_queue.head = (event_queue.head + 1) & EVENT_QUEUE_MASK;
}
THIN_LOCKED_FUNC(thin_event_add)
/* get an event from the event queue. returns 0 if no events. */
int thin_event_get(thin_event_t *event)
{
if (EVENT_QUEUE_EMPTY)
{
event->type = THIN_NOEVENT;
return 0;
}
*event = event_queue.event[event_queue.tail];
event_queue.tail = (event_queue.tail + 1) & EVENT_QUEUE_MASK;
return 1;
}
/* gather up all pollable events */
void thin_event_gather(void)
{
int i;
for (i = 0; i < MAX_CALLBACKS; i++)
{
if (NULL == event_callback[i])
return;
event_callback[i]();
}
}
/* return an ID of an event callback */
event_id thin_event_add_callback(event_callback_t callback)
{
event_id id;
for (id = 0; id < MAX_CALLBACKS; id++)
{
if (NULL == event_callback[id])
break;
}
/* no event callbacks available */
if (id == MAX_CALLBACKS)
return (event_id) -1;
event_callback[id] = callback;
return id;
}
/* remove an event callback */
void thin_event_remove_callback(event_id id)
{
THIN_ASSERT(id >= 0 && id < MAX_CALLBACKS);
if (id < 0 || id >= MAX_CALLBACKS)
return;
THIN_ASSERT(NULL != event_callback[id]);
event_callback[id] = NULL;
/* move all other callbacks down */
for (; id < MAX_CALLBACKS - 1; id++)
{
event_callback[id] = event_callback[id + 1];
event_callback[id + 1] = NULL;
}
}
/* set up the event handling system */
void thin_event_init(void)
{
int i;
/* some modules call thin_event_add from an ISR, so we must
** lock everythig that is touched within that function, as
** well as the code itself.
*/
THIN_LOCK_FUNC(thin_event_add);
THIN_LOCK_VAR(event_queue);
for (i = 0; i < MAX_CALLBACKS; i++)
event_callback[i] = NULL;
event_queue.head = event_queue.tail = 0;
}
/*
** $Log: $
*/

View File

@ -0,0 +1,80 @@
/*
** thinlib (c) 2001 Matthew Conte (matt@conte.com)
**
**
** tl_event.h
**
** event handling routines
**
** $Id: $
*/
#ifndef _TL_EVENT_H_
#define _TL_EVENT_H_
typedef void (*event_callback_t)(void);
typedef int event_id;
enum
{
THIN_NOEVENT = 0,
THIN_KEY_PRESS,
THIN_KEY_RELEASE,
THIN_MOUSE_MOTION,
THIN_MOUSE_BUTTON_PRESS,
THIN_MOUSE_BUTTON_RELEASE,
THIN_JOY_MOTION,
THIN_JOY_BUTTON_PRESS,
THIN_JOY_BUTTON_RELEASE,
THIN_USER_EVENT,
};
enum
{
THIN_JOY_LEFT,
THIN_JOY_RIGHT,
THIN_JOY_UP,
THIN_JOY_DOWN,
};
typedef struct thin_event_s
{
int type;
union
{
/* keyboard */
int keysym;
/* mouse motion */
struct
{
int xpos;
int ypos;
} mouse_motion;
/* mouse button */
int mouse_button;
/* joy motion */
struct
{
int dir;
int state;
} joy_motion;
/* joy button */
int joy_button;
/* user event */
int user_data;
} data;
} thin_event_t;
extern void thin_event_add(thin_event_t *event);
extern int thin_event_get(thin_event_t *event);
extern void thin_event_gather(void);
extern event_id thin_event_add_callback(event_callback_t callback);
extern void thin_event_remove_callback(event_id id);
extern void thin_event_init(void);
#endif /* !_TL_EVENT_H_ */
/*
** $Log: $
*/

262
sys/thinlib/lib/tl_int.c Normal file
View File

@ -0,0 +1,262 @@
/*
** thinlib (c) 2001 Matthew Conte (matt@conte.com)
**
**
** tl_int.c
**
** thinlib interrupt handling. Thanks to Shawn Hargreaves
** and the Allegro project for providing adequate interrupt
** documentation.
**
** $Id: $
*/
#include <dos.h>
#include <go32.h>
#include <dpmi.h>
#include "thinlib.h"
#include "tl_types.h"
#include "tl_log.h"
#include "tl_djgpp.h"
#include "tl_int.h"
#define PIC1_PORT 0x21
#define PIC2_PORT 0xA1
/* Interrupt stuff */
typedef struct intr_s
{
_go32_dpmi_seginfo old_interrupt;
_go32_dpmi_seginfo new_interrupt;
uint8 irq_vector;
inthandler_t handler;
} intr_t;
#define MAX_INTR 8
static bool pic_modified = false;
static uint8 pic1_mask, pic2_mask;
static uint8 pic1_orig, pic2_orig;
static intr_t intr[MAX_INTR];
static bool thin_int_locked = false;
#define MAKE_INT_HANDLER(num) \
static void _int_handler_##num##(void) \
{ \
/* chain if necessary */ \
if (intr[(num)].handler()) \
{ \
void (*func)() = (void *) intr[(num)].old_interrupt.pm_offset; \
func(); \
} \
} \
THIN_LOCKED_STATIC_FUNC(_int_handler_##num##);
MAKE_INT_HANDLER(0)
MAKE_INT_HANDLER(1)
MAKE_INT_HANDLER(2)
MAKE_INT_HANDLER(3)
MAKE_INT_HANDLER(4)
MAKE_INT_HANDLER(5)
MAKE_INT_HANDLER(6)
MAKE_INT_HANDLER(7)
int thin_int_install(int chan, inthandler_t handler)
{
int i;
if (chan < 0)// || chan >= MAX_INTR)
return -1;
if (NULL == handler)
return -1;
if (false == thin_int_locked)
{
int i;
for (i = 0; i < MAX_INTR; i++)
{
intr[i].handler = NULL;
intr[i].irq_vector = 0;
}
THIN_LOCK_VAR(intr);
THIN_LOCK_FUNC(_int_handler_0);
THIN_LOCK_FUNC(_int_handler_1);
THIN_LOCK_FUNC(_int_handler_2);
THIN_LOCK_FUNC(_int_handler_3);
THIN_LOCK_FUNC(_int_handler_4);
THIN_LOCK_FUNC(_int_handler_5);
THIN_LOCK_FUNC(_int_handler_6);
THIN_LOCK_FUNC(_int_handler_7);
thin_int_locked = true;
}
/* find a free slot */
for (i = 0; i < MAX_INTR; i++)
{
if (NULL == intr[i].handler)
{
intr_t *pintr = &intr[i];
pintr->new_interrupt.pm_selector = _go32_my_cs();
switch (i)
{
case 0: pintr->new_interrupt.pm_offset = (int) _int_handler_0; break;
case 1: pintr->new_interrupt.pm_offset = (int) _int_handler_1; break;
case 2: pintr->new_interrupt.pm_offset = (int) _int_handler_2; break;
case 3: pintr->new_interrupt.pm_offset = (int) _int_handler_3; break;
case 4: pintr->new_interrupt.pm_offset = (int) _int_handler_4; break;
case 5: pintr->new_interrupt.pm_offset = (int) _int_handler_5; break;
case 6: pintr->new_interrupt.pm_offset = (int) _int_handler_6; break;
case 7: pintr->new_interrupt.pm_offset = (int) _int_handler_7; break;
default: return -1;
}
pintr->new_interrupt.pm_offset = (int) handler;
pintr->handler = handler;
pintr->irq_vector = chan;
_go32_dpmi_get_protected_mode_interrupt_vector(pintr->irq_vector, &pintr->old_interrupt);
_go32_dpmi_allocate_iret_wrapper(&pintr->new_interrupt);
_go32_dpmi_set_protected_mode_interrupt_vector(pintr->irq_vector, &pintr->new_interrupt);
return 0;
}
}
return -1; /* none free */
}
void thin_int_remove(int chan)
{
int i;
for (i = 0; i < MAX_INTR; i++)
{
intr_t *pintr = &intr[i];
if (pintr->irq_vector == chan && pintr->handler != NULL)
{
_go32_dpmi_set_protected_mode_interrupt_vector(pintr->irq_vector, &pintr->old_interrupt);
_go32_dpmi_free_iret_wrapper(&pintr->new_interrupt);
pintr->handler = NULL;
pintr->irq_vector = 0;
break;
}
}
}
/* helper routines for the irq masking */
static void _irq_exit(void)
{
if (pic_modified)
{
pic_modified = false;
outportb(0x21, pic1_orig);
outportb(0xA1, pic2_orig);
thin_remove_exit(_irq_exit);
}
}
static void _irq_init(void)
{
if (false == pic_modified)
{
pic_modified = true;
/* read initial PIC values */
pic1_orig = inportb(0x21);
pic2_orig = inportb(0xA1);
pic1_mask = 0;
pic2_mask = 0;
thin_add_exit(_irq_exit);
}
}
/* restore original mask for interrupt */
void thin_irq_restore(int irq)
{
if (pic_modified)
{
uint8 pic;
if (irq > 7)
{
pic = inportb(0xA1) & ~(1 << (irq - 8));
outportb(0xA1, pic | (pic2_orig & (1 << (irq - 8))));
pic2_mask &= ~(1 << (irq - 8));
if (pic2_mask)
return;
irq = 2; /* restore cascade if no high IRQs remain */
}
pic = inportb(0x21) & ~(1 << irq);
outportb(0x21, pic | (pic1_orig & (1 << irq)));
pic1_mask &= ~(1 << irq);
}
}
/* unmask an interrupt */
void thin_irq_enable(int irq)
{
uint8 pic;
_irq_init();
pic = inportb(0x21);
if (irq > 7)
{
/* unmask cascade (IRQ2) interrupt */
//outportb(0x21, pic & 0xFB);
outportb(0x21, pic & ~(1 << 2));
pic = inportb(0xA1);
outportb(0xA1, pic & ~(1 << (irq - 8)));
pic2_mask |= 1 << (irq - 8);
}
else
{
outportb(0x21, pic & ~(1 << irq));
pic1_mask |= 1 << irq;
}
}
/* mask an interrupt */
void thin_irq_disable(int irq)
{
uint8 pic;
_irq_init();
if (irq > 7)
{
/* PIC 2 */
pic = inportb(0xA1);
outportb(0xA1, pic & (1 << (irq - 8)));
pic2_mask |= 1 << (irq - 8);
}
else
{
/* PIC 1 */
pic = inportb(0x21);
outportb(0x21, pic & (1 << irq));
pic1_mask |= 1 << irq;
}
}
/*
** $Log: $
*/

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