diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..a43ea21 --- /dev/null +++ b/COPYING @@ -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. + + + Copyright (C) 19yy + + 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. + + , 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. diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..afd42ee --- /dev/null +++ b/INSTALL @@ -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. + + + + diff --git a/Makefile.dos b/Makefile.dos new file mode 100644 index 0000000..0b61f20 --- /dev/null +++ b/Makefile.dos @@ -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 \ No newline at end of file diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000..58e49a3 --- /dev/null +++ b/Makefile.in @@ -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 + + + + diff --git a/Makefile.nix b/Makefile.nix new file mode 100644 index 0000000..3f22450 --- /dev/null +++ b/Makefile.nix @@ -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 + + + + + + + + + + + + diff --git a/Makefile.win b/Makefile.win new file mode 100644 index 0000000..c52c905 --- /dev/null +++ b/Makefile.win @@ -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 diff --git a/README b/README new file mode 100644 index 0000000..f10a5f0 --- /dev/null +++ b/README @@ -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 + + + + + + + + + + + diff --git a/Rules b/Rules new file mode 100644 index 0000000..4afd76a --- /dev/null +++ b/Rules @@ -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 $@ + + + + + + + + + + diff --git a/Version b/Version new file mode 100644 index 0000000..0b12c42 --- /dev/null +++ b/Version @@ -0,0 +1,5 @@ +#define VERSION "1.0.3" /* +VERSION = 1.0.3 +# */ + + diff --git a/asm/i386/asm.h b/asm/i386/asm.h new file mode 100644 index 0000000..17f6db2 --- /dev/null +++ b/asm/i386/asm.h @@ -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__ */ + + + diff --git a/asm/i386/asmnames.h b/asm/i386/asmnames.h new file mode 100644 index 0000000..37c6453 --- /dev/null +++ b/asm/i386/asmnames.h @@ -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 + + diff --git a/asm/i386/cpu.s b/asm/i386/cpu.s new file mode 100644 index 0000000..3665019 --- /dev/null +++ b/asm/i386/cpu.s @@ -0,0 +1,2430 @@ + + # i386 asm cpu core + # optimized for 486/pentium/k6 + + # global register usage: + # %bl - flags + # %bh - A + # %bp - PC + # %esi - number of cycles we have left + # %edi - number of cycles used by current instruction + +#include "asmnames.h" + + .set PC, cpu + .set SP, cpu+4 + .set BC, cpu+8 + .set DE, cpu+12 + .set HL, cpu+16 + .set AF, cpu+20 + + .set B, cpu+9 + .set C, cpu+8 + .set D, cpu+13 + .set E, cpu+12 + .set H, cpu+17 + .set L, cpu+16 + .set A, cpu+21 + .set F, cpu+20 + + .set IME, cpu+24 + .set IMA, cpu+28 + + .set speed, cpu+32 + .set halt, cpu+36 + .set div, cpu+40 + .set tim, cpu+44 + .set lcdc, cpu+48 + .set snd, cpu+52 + + .set regs, ram + + .set rmap, mbc+32 + .set wmap, mbc+96 + + .set DIV, ram+4 + .set TIMA, ram+5 + .set TMA, ram+6 + .set TAC, ram+7 + + .set IF, ram+0x0f + .set IE, ram+0xff + .set KEY1, ram+0x4d + + + .text + .p2align 5 + +debug: + .string "debug: 0x%08X\n" +invalid: + .string "invalid opcode 0x%02X\n" + + # x86 flags - 01=carry, 10=half, 40=zero + # (bit 0) (bit 4) (bit 6) + # gbz80 flags - 10=carry, 20=half, 40=neg, 80=zero + # (bit 4) (bit 5) (bit 6) (bit 7) + +addflagtable: + .byte 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10 + .byte 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10 + .byte 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30 + .byte 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30 + .byte 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10 + .byte 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10 + .byte 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30 + .byte 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30 + .byte 0x80, 0x90, 0x80, 0x90, 0x80, 0x90, 0x80, 0x90 + .byte 0x80, 0x90, 0x80, 0x90, 0x80, 0x90, 0x80, 0x90 + .byte 0xA0, 0xB0, 0xA0, 0xB0, 0xA0, 0xB0, 0xA0, 0xB0 + .byte 0xA0, 0xB0, 0xA0, 0xB0, 0xA0, 0xB0, 0xA0, 0xB0 + .byte 0x80, 0x90, 0x80, 0x90, 0x80, 0x90, 0x80, 0x90 + .byte 0x80, 0x90, 0x80, 0x90, 0x80, 0x90, 0x80, 0x90 + .byte 0xA0, 0xB0, 0xA0, 0xB0, 0xA0, 0xB0, 0xA0, 0xB0 + .byte 0xA0, 0xB0, 0xA0, 0xB0, 0xA0, 0xB0, 0xA0, 0xB0 + .byte 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10 + .byte 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10 + .byte 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30 + .byte 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30 + .byte 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10 + .byte 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10 + .byte 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30 + .byte 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30 + .byte 0x80, 0x90, 0x80, 0x90, 0x80, 0x90, 0x80, 0x90 + .byte 0x80, 0x90, 0x80, 0x90, 0x80, 0x90, 0x80, 0x90 + .byte 0xA0, 0xB0, 0xA0, 0xB0, 0xA0, 0xB0, 0xA0, 0xB0 + .byte 0xA0, 0xB0, 0xA0, 0xB0, 0xA0, 0xB0, 0xA0, 0xB0 + .byte 0x80, 0x90, 0x80, 0x90, 0x80, 0x90, 0x80, 0x90 + .byte 0x80, 0x90, 0x80, 0x90, 0x80, 0x90, 0x80, 0x90 + .byte 0xA0, 0xB0, 0xA0, 0xB0, 0xA0, 0xB0, 0xA0, 0xB0 + .byte 0xA0, 0xB0, 0xA0, 0xB0, 0xA0, 0xB0, 0xA0, 0xB0 + +subflagtable: + .byte 0x40, 0x50, 0x40, 0x50, 0x40, 0x50, 0x40, 0x50 + .byte 0x40, 0x50, 0x40, 0x50, 0x40, 0x50, 0x40, 0x50 + .byte 0x60, 0x70, 0x60, 0x70, 0x60, 0x70, 0x60, 0x70 + .byte 0x60, 0x70, 0x60, 0x70, 0x60, 0x70, 0x60, 0x70 + .byte 0x40, 0x50, 0x40, 0x50, 0x40, 0x50, 0x40, 0x50 + .byte 0x40, 0x50, 0x40, 0x50, 0x40, 0x50, 0x40, 0x50 + .byte 0x60, 0x70, 0x60, 0x70, 0x60, 0x70, 0x60, 0x70 + .byte 0x60, 0x70, 0x60, 0x70, 0x60, 0x70, 0x60, 0x70 + .byte 0xC0, 0xD0, 0xC0, 0xD0, 0xC0, 0xD0, 0xC0, 0xD0 + .byte 0xC0, 0xD0, 0xC0, 0xD0, 0xC0, 0xD0, 0xC0, 0xD0 + .byte 0xE0, 0xF0, 0xE0, 0xF0, 0xE0, 0xF0, 0xE0, 0xF0 + .byte 0xE0, 0xF0, 0xE0, 0xF0, 0xE0, 0xF0, 0xE0, 0xF0 + .byte 0xC0, 0xD0, 0xC0, 0xD0, 0xC0, 0xD0, 0xC0, 0xD0 + .byte 0xC0, 0xD0, 0xC0, 0xD0, 0xC0, 0xD0, 0xC0, 0xD0 + .byte 0xE0, 0xF0, 0xE0, 0xF0, 0xE0, 0xF0, 0xE0, 0xF0 + .byte 0xE0, 0xF0, 0xE0, 0xF0, 0xE0, 0xF0, 0xE0, 0xF0 + .byte 0x40, 0x50, 0x40, 0x50, 0x40, 0x50, 0x40, 0x50 + .byte 0x40, 0x50, 0x40, 0x50, 0x40, 0x50, 0x40, 0x50 + .byte 0x60, 0x70, 0x60, 0x70, 0x60, 0x70, 0x60, 0x70 + .byte 0x60, 0x70, 0x60, 0x70, 0x60, 0x70, 0x60, 0x70 + .byte 0x40, 0x50, 0x40, 0x50, 0x40, 0x50, 0x40, 0x50 + .byte 0x40, 0x50, 0x40, 0x50, 0x40, 0x50, 0x40, 0x50 + .byte 0x60, 0x70, 0x60, 0x70, 0x60, 0x70, 0x60, 0x70 + .byte 0x60, 0x70, 0x60, 0x70, 0x60, 0x70, 0x60, 0x70 + .byte 0xC0, 0xD0, 0xC0, 0xD0, 0xC0, 0xD0, 0xC0, 0xD0 + .byte 0xC0, 0xD0, 0xC0, 0xD0, 0xC0, 0xD0, 0xC0, 0xD0 + .byte 0xE0, 0xF0, 0xE0, 0xF0, 0xE0, 0xF0, 0xE0, 0xF0 + .byte 0xE0, 0xF0, 0xE0, 0xF0, 0xE0, 0xF0, 0xE0, 0xF0 + .byte 0xC0, 0xD0, 0xC0, 0xD0, 0xC0, 0xD0, 0xC0, 0xD0 + .byte 0xC0, 0xD0, 0xC0, 0xD0, 0xC0, 0xD0, 0xC0, 0xD0 + .byte 0xE0, 0xF0, 0xE0, 0xF0, 0xE0, 0xF0, 0xE0, 0xF0 + .byte 0xE0, 0xF0, 0xE0, 0xF0, 0xE0, 0xF0, 0xE0, 0xF0 + +incflagtable: + .byte 0xa0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .byte 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .byte 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .byte 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .byte 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .byte 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .byte 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .byte 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .byte 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .byte 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .byte 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .byte 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .byte 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .byte 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .byte 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .byte 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + +decflagtable: + .byte 192, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96 + .byte 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96 + .byte 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96 + .byte 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96 + .byte 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96 + .byte 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96 + .byte 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96 + .byte 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96 + .byte 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96 + .byte 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96 + .byte 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96 + .byte 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96 + .byte 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96 + .byte 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96 + .byte 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96 + .byte 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96 + + +cycles_table: + .byte 1, 3, 2, 2, 1, 1, 2, 1, 5, 2, 2, 2, 1, 1, 2, 1 + .byte 1, 3, 2, 2, 1, 1, 2, 1, 3, 2, 2, 2, 1, 1, 2, 1 + .byte 3, 3, 2, 2, 1, 1, 2, 1, 3, 2, 2, 2, 1, 1, 2, 1 + .byte 3, 3, 2, 2, 1, 3, 3, 3, 3, 2, 2, 2, 1, 1, 2, 1 + .byte 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1 + .byte 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1 + .byte 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1 + .byte 2, 2, 2, 2, 2, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1 + .byte 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1 + .byte 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1 + .byte 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1 + .byte 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1 + .byte 5, 3, 4, 4, 6, 4, 2, 4, 5, 4, 4, 1, 6, 6, 2, 4 + .byte 5, 3, 4, 0, 6, 4, 2, 4, 5, 4, 4, 0, 6, 0, 2, 4 + .byte 3, 3, 2, 0, 0, 4, 2, 4, 4, 1, 4, 0, 0, 0, 2, 4 + .byte 3, 3, 2, 1, 0, 4, 2, 4, 3, 2, 4, 1, 0, 0, 2, 4 + +cb_cycles_table: + .byte 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2 + .byte 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2 + .byte 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2 + .byte 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2 + .byte 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2 + .byte 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2 + .byte 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2 + .byte 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2 + .byte 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2 + .byte 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2 + .byte 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2 + .byte 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2 + .byte 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2 + .byte 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2 + .byte 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2 + .byte 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2 + + + +optable: + # 00 + .long __NOP, __LD_BC_IMM, __LD_$BC_A, __INC_BC + .long __INC_B, __DEC_B, __LD_B_IMM, __RLCA + .long __LD_$IMM_SP, __ADD_BC, __LD_A_$BC, __DEC_BC + .long __INC_C, __DEC_C, __LD_C_IMM, __RRCA + # 10 + .long __STOP, __LD_DE_IMM, __LD_$DE_A, __INC_DE + .long __INC_D, __DEC_D, __LD_D_IMM, __RLA + .long __JR, __ADD_DE, __LD_A_$DE, __DEC_DE + .long __INC_E, __DEC_E, __LD_E_IMM, __RRA + # 20 + .long __JR_NZ, __LD_HL_IMM, __LDI_$HL_A, __INC_HL + .long __INC_H, __DEC_H, __LD_H_IMM, __DAA + .long __JR_Z, __ADD_HL, __LDI_A_$HL, __DEC_HL + .long __INC_L, __DEC_L, __LD_L_IMM, __CPL + # 30 + .long __JR_NC, __LD_SP_IMM, __LDD_$HL_A, __INC_SP + .long __INC_$HL, __DEC_$HL, __LD_$HL_IMM, __SCF + .long __JR_C, __ADD_SP, __LDD_A_$HL, __DEC_SP + .long __INC_A, __DEC_A, __LD_A_IMM, __CCF + # 40 + .long __LD_B_B, __LD_B_C, __LD_B_D, __LD_B_E + .long __LD_B_H, __LD_B_L, __LD_B_$HL, __LD_B_A + .long __LD_C_B, __LD_C_C, __LD_C_D, __LD_C_E + .long __LD_C_H, __LD_C_L, __LD_C_$HL, __LD_C_A + # 50 + .long __LD_D_B, __LD_D_C, __LD_D_D, __LD_D_E + .long __LD_D_H, __LD_D_L, __LD_D_$HL, __LD_D_A + .long __LD_E_B, __LD_E_C, __LD_E_D, __LD_E_E + .long __LD_E_H, __LD_E_L, __LD_E_$HL, __LD_E_A + # 60 + .long __LD_H_B, __LD_H_C, __LD_H_D, __LD_H_E + .long __LD_H_H, __LD_H_L, __LD_H_$HL, __LD_H_A + .long __LD_L_B, __LD_L_C, __LD_L_D, __LD_L_E + .long __LD_L_H, __LD_L_L, __LD_L_$HL, __LD_L_A + # 70 + .long __LD_$HL_B, __LD_$HL_C, __LD_$HL_D, __LD_$HL_E + .long __LD_$HL_H, __LD_$HL_L, __HALT, __LD_$HL_A + .long __LD_A_B, __LD_A_C, __LD_A_D, __LD_A_E + .long __LD_A_H, __LD_A_L, __LD_A_$HL, __LD_A_A + # 80 + .long __ADD_B, __ADD_C, __ADD_D, __ADD_E + .long __ADD_H, __ADD_L, __ADD_$HL, __ADD_A + .long __ADC_B, __ADC_C, __ADC_D, __ADC_E + .long __ADC_H, __ADC_L, __ADC_$HL, __ADC_A + # 90 + .long __SUB_B, __SUB_C, __SUB_D, __SUB_E + .long __SUB_H, __SUB_L, __SUB_$HL, __SUB_A + .long __SBC_B, __SBC_C, __SBC_D, __SBC_E + .long __SBC_H, __SBC_L, __SBC_$HL, __SBC_A + # A0 + .long __AND_B, __AND_C, __AND_D, __AND_E + .long __AND_H, __AND_L, __AND_$HL, __AND_A + .long __XOR_B, __XOR_C, __XOR_D, __XOR_E + .long __XOR_H, __XOR_L, __XOR_$HL, __XOR_A + # B0 + .long __OR_B, __OR_C, __OR_D, __OR_E + .long __OR_H, __OR_L, __OR_$HL, __OR_A + .long __CP_B, __CP_C, __CP_D, __CP_E + .long __CP_H, __CP_L, __CP_$HL, __CP_A + # C0 + .long __RET_NZ, __POP_BC, __JP_NZ, __JP + .long __CALL_NZ, __PUSH_BC, __ADD_IMM, __RST_00 + .long __RET_Z, __RET, __JP_Z, __CB_OPS + .long __CALL_Z, __CALL, __ADC_IMM, __RST_08 + # D0 + .long __RET_NC, __POP_DE, __JP_NC, __INVALID + .long __CALL_NC, __PUSH_DE, __SUB_IMM, __RST_10 + .long __RET_C, __RETI, __JP_C, __INVALID + .long __CALL_C, __INVALID, __SBC_IMM, __RST_18 + # E0 + .long __LDH_$IMM_A, __POP_HL, __LDH_$C_A, __INVALID + .long __INVALID, __PUSH_HL, __AND_IMM, __RST_20 + .long __ADD_SP_IMM, __JP_HL, __LD_$IMM_A, __INVALID + .long __INVALID, __INVALID, __XOR_IMM, __RST_28 + # F0 + .long __LDH_A_$IMM, __POP_AF, __LDH_A_$C, __DI + .long __INVALID, __PUSH_AF, __OR_IMM, __RST_30 + .long __LD_HL_SP_IMM, __LD_SP_HL, __LD_A_$IMM, __EI + .long __INVALID, __INVALID, __CP_IMM, __RST_38 + + +cb_optable: + .long __RLC_B, __RLC_C, __RLC_D, __RLC_E + .long __RLC_H, __RLC_L, __RLC_$HL, __RLC_A + .long __RRC_B, __RRC_C, __RRC_D, __RRC_E + .long __RRC_H, __RRC_L, __RRC_$HL, __RRC_A + .long __RL_B, __RL_C, __RL_D, __RL_E + .long __RL_H, __RL_L, __RL_$HL, __RL_A + .long __RR_B, __RR_C, __RR_D, __RR_E + .long __RR_H, __RR_L, __RR_$HL, __RR_A + .long __SLA_B, __SLA_C, __SLA_D, __SLA_E + .long __SLA_H, __SLA_L, __SLA_$HL, __SLA_A + .long __SRA_B, __SRA_C, __SRA_D, __SRA_E + .long __SRA_H, __SRA_L, __SRA_$HL, __SRA_A + .long __SWAP_B, __SWAP_C, __SWAP_D, __SWAP_E + .long __SWAP_H, __SWAP_L, __SWAP_$HL, __SWAP_A + .long __SRL_B, __SRL_C, __SRL_D, __SRL_E + .long __SRL_H, __SRL_L, __SRL_$HL, __SRL_A + .long __BIT_0_B, __BIT_0_C, __BIT_0_D, __BIT_0_E + .long __BIT_0_H, __BIT_0_L, __BIT_0_$HL, __BIT_0_A + .long __BIT_1_B, __BIT_1_C, __BIT_1_D, __BIT_1_E + .long __BIT_1_H, __BIT_1_L, __BIT_1_$HL, __BIT_1_A + .long __BIT_2_B, __BIT_2_C, __BIT_2_D, __BIT_2_E + .long __BIT_2_H, __BIT_2_L, __BIT_2_$HL, __BIT_2_A + .long __BIT_3_B, __BIT_3_C, __BIT_3_D, __BIT_3_E + .long __BIT_3_H, __BIT_3_L, __BIT_3_$HL, __BIT_3_A + .long __BIT_4_B, __BIT_4_C, __BIT_4_D, __BIT_4_E + .long __BIT_4_H, __BIT_4_L, __BIT_4_$HL, __BIT_4_A + .long __BIT_5_B, __BIT_5_C, __BIT_5_D, __BIT_5_E + .long __BIT_5_H, __BIT_5_L, __BIT_5_$HL, __BIT_5_A + .long __BIT_6_B, __BIT_6_C, __BIT_6_D, __BIT_6_E + .long __BIT_6_H, __BIT_6_L, __BIT_6_$HL, __BIT_6_A + .long __BIT_7_B, __BIT_7_C, __BIT_7_D, __BIT_7_E + .long __BIT_7_H, __BIT_7_L, __BIT_7_$HL, __BIT_7_A + .long __RES_0_B, __RES_0_C, __RES_0_D, __RES_0_E + .long __RES_0_H, __RES_0_L, __RES_0_$HL, __RES_0_A + .long __RES_1_B, __RES_1_C, __RES_1_D, __RES_1_E + .long __RES_1_H, __RES_1_L, __RES_1_$HL, __RES_1_A + .long __RES_2_B, __RES_2_C, __RES_2_D, __RES_2_E + .long __RES_2_H, __RES_2_L, __RES_2_$HL, __RES_2_A + .long __RES_3_B, __RES_3_C, __RES_3_D, __RES_3_E + .long __RES_3_H, __RES_3_L, __RES_3_$HL, __RES_3_A + .long __RES_4_B, __RES_4_C, __RES_4_D, __RES_4_E + .long __RES_4_H, __RES_4_L, __RES_4_$HL, __RES_4_A + .long __RES_5_B, __RES_5_C, __RES_5_D, __RES_5_E + .long __RES_5_H, __RES_5_L, __RES_5_$HL, __RES_5_A + .long __RES_6_B, __RES_6_C, __RES_6_D, __RES_6_E + .long __RES_6_H, __RES_6_L, __RES_6_$HL, __RES_6_A + .long __RES_7_B, __RES_7_C, __RES_7_D, __RES_7_E + .long __RES_7_H, __RES_7_L, __RES_7_$HL, __RES_7_A + .long __SET_0_B, __SET_0_C, __SET_0_D, __SET_0_E + .long __SET_0_H, __SET_0_L, __SET_0_$HL, __SET_0_A + .long __SET_1_B, __SET_1_C, __SET_1_D, __SET_1_E + .long __SET_1_H, __SET_1_L, __SET_1_$HL, __SET_1_A + .long __SET_2_B, __SET_2_C, __SET_2_D, __SET_2_E + .long __SET_2_H, __SET_2_L, __SET_2_$HL, __SET_2_A + .long __SET_3_B, __SET_3_C, __SET_3_D, __SET_3_E + .long __SET_3_H, __SET_3_L, __SET_3_$HL, __SET_3_A + .long __SET_4_B, __SET_4_C, __SET_4_D, __SET_4_E + .long __SET_4_H, __SET_4_L, __SET_4_$HL, __SET_4_A + .long __SET_5_B, __SET_5_C, __SET_5_D, __SET_5_E + .long __SET_5_H, __SET_5_L, __SET_5_$HL, __SET_5_A + .long __SET_6_B, __SET_6_C, __SET_6_D, __SET_6_E + .long __SET_6_H, __SET_6_L, __SET_6_$HL, __SET_6_A + .long __SET_7_B, __SET_7_C, __SET_7_D, __SET_7_E + .long __SET_7_H, __SET_7_L, __SET_7_$HL, __SET_7_A + + + +daa_table: + .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06 + .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06 + .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06 + .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06 + .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06 + .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06 + .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06 + .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06 + .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06 + .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + .byte 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + .byte 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + .byte 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + .byte 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + .byte 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + .byte 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + + .byte 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + .byte 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + .byte 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + .byte 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + .byte 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + .byte 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + .byte 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + .byte 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + .byte 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + .byte 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + .byte 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + .byte 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + .byte 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + .byte 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + .byte 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + .byte 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + + .byte 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06 + .byte 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06 + .byte 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06 + .byte 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06 + .byte 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06 + .byte 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06 + .byte 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06 + .byte 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06 + .byte 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06 + .byte 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06 + .byte 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + .byte 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + .byte 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + .byte 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + .byte 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + .byte 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + + .byte 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + .byte 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + .byte 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + .byte 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + .byte 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + .byte 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + .byte 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + .byte 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + .byte 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + .byte 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + .byte 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + .byte 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + .byte 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + .byte 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + .byte 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + .byte 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 + + .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + + .byte 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0 + .byte 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0 + .byte 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0 + .byte 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0 + .byte 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0 + .byte 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0 + .byte 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0 + .byte 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0 + .byte 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0 + .byte 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0 + .byte 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0 + .byte 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0 + .byte 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0 + .byte 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0 + .byte 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0 + .byte 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0 + + .byte 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA + .byte 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA + .byte 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA + .byte 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA + .byte 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA + .byte 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA + .byte 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA + .byte 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA + .byte 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA + .byte 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA + .byte 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA + .byte 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA + .byte 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA + .byte 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA + .byte 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA + .byte 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA + + .byte 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A + .byte 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A + .byte 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A + .byte 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A + .byte 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A + .byte 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A + .byte 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A + .byte 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A + .byte 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A + .byte 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A + .byte 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A + .byte 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A + .byte 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A + .byte 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A + .byte 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A + .byte 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A + +daa_carry_table: + .byte 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00 + .byte 00, 00, 00, 00, 00, 00, 00, 00, 16, 16, 00, 00, 00, 00, 00, 00 + .byte 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16 + .byte 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 00, 16 + +zflag_table: + .byte 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + +int_mask_table: + .byte ~0, ~1, ~2, ~1, ~4, ~1, ~2, ~1 + .byte ~8, ~1, ~2, ~1, ~4, ~1, ~2, ~1 + .byte ~16, ~1, ~2, ~1, ~4, ~1, ~2, ~1 + .byte ~8, ~1, ~2, ~1, ~4, ~1, ~2, ~1 + +int_vec_table: + .long 0x00, 0x40, 0x48, 0x40, 0x50, 0x40, 0x48, 0x40 + .long 0x58, 0x40, 0x48, 0x40, 0x50, 0x40, 0x48, 0x40 + .long 0x60, 0x40, 0x48, 0x40, 0x50, 0x40, 0x48, 0x40 + .long 0x58, 0x40, 0x48, 0x40, 0x50, 0x40, 0x48, 0x40 + + + .macro _print arg=0 + pushf + pusha + movl \arg, %eax + pushl %eax + pushl $debug + call printf + addl $8, %esp + popa + popf + .endm + + .macro _trace + testb $1, debug_trace + jz .Lnotrace\@ + pushl %eax + pushl %ecx + pushl %edx + movl %ebx, AF + movl %ebp, PC + movl $1, %eax + pushl %eax + pushl %ebp + call debug_disassemble + addl $8, %esp + popl %edx + popl %ecx + popl %eax +.Lnotrace\@: + .endm + + + # all macros preserve %ebp, %ebx, %esi, and %edi + + + # write to addr %eax, upper bytes must be zero + # source byte passed in %dl + .macro _writeb + movl %eax, %ecx + shrl $12, %eax + movl wmap(,%eax,4), %eax + testl %eax,%eax + jnz .Lusemap\@ + pushl %edx + pushl %ecx + call mem_write + addl $8,%esp + jmp .Lfinish\@ +.Lusemap\@: + movb %dl, (%eax,%ecx) +.Lfinish\@: + .endm + + # read from addr %eax, upper bytes must be zero + # result in %al, upper bytes filled with 0 automagically + .macro _readb + movl %eax, %ecx + shrl $12, %eax + movl rmap(,%eax,4), %edx + testl %edx,%edx + jnz .Lusemap\@ + pushl %ecx + call mem_read + popl %ecx + jmp .Lfinish\@ +.Lusemap\@: + movb (%edx,%ecx),%al +.Lfinish\@: + .endm + + # read signed byte from addr %eax, upper bytes must be zero + # result in %eax + .macro _readbs + movl %eax, %ecx + shrl $12, %eax + movl rmap(,%eax,4), %edx + testl %edx,%edx + jnz .Lusemap\@ + pushl %ecx + call mem_read + movsbl %al, %eax + popl %ecx + jmp .Lfinish\@ +.Lusemap\@: + movsbl (%edx,%ecx),%eax +.Lfinish\@: + .endm + + # fetch from PC + # result in %al, upper bytes filled with 0 automagically + .macro _fetch + movl %ebp, %eax + movl %ebp, %ecx + shrl $12, %eax + incw %bp + movl rmap(,%eax,4), %edx + testl %edx,%edx + jnz .Lusemap\@ + pushl %ecx + call mem_read + popl %ecx + jmp .Lfinish\@ +.Lusemap\@: + movb (%edx,%ecx),%al +.Lfinish\@: + .endm + + # fetch signed byte from PC + # result in %eax + .macro _fetchs + movl %ebp, %eax + movl %ebp, %ecx + shrl $12, %eax + incw %bp + movl rmap(,%eax,4), %edx + testl %edx,%edx + jnz .Lusemap\@ + pushl %ecx + call mem_read + popl %ecx + movsbl %al, %eax + jmp .Lfinish\@ +.Lusemap\@: + movsbl (%edx,%ecx),%eax +.Lfinish\@: + .endm + + # fetch word from PC + # result in %ax, padded with zeros + .macro _fetchw + movl %ebp, %eax + movl %ebp, %edx + shrl $12, %eax + incl %edx + movl %ebp, %ecx + testl $0xfff, %edx + jz .Lunaligned\@ + movl rmap(,%eax,4), %edx + testl %edx, %edx + jnz .Lusemap\@ +.Lunaligned\@: + incl %ebp + pushl %ecx + call mem_read + pushl %eax + pushl %ebp + incl %ebp + call mem_read + popl %ecx + popl %ecx + movb %al, %ah + movb %cl, %al + popl %ecx + andl $0xffff, %ebp + jmp .Lfinish\@ +.Lusemap\@: + movw (%edx,%ecx),%ax + addw $2, %bp +.Lfinish\@: + .endm + + # read word from %eax, upper byte must be zero + # result in %ax, padded with zeros + .macro _readw + movl %eax, %edx + shrl $12, %eax + movl %edx, %ecx + incl %edx + testl $0xfff, %edx + jz .Lunaligned\@ + movl rmap(,%eax,4), %edx + testl %edx, %edx + jnz .Lusemap\@ +.Lunaligned\@: + pushl %ecx + pushl %ecx + call mem_read + popl %ecx + popl %ecx + incl %ecx + pushl %eax + pushl %ecx + call mem_read + popl %ecx + popl %ecx + movb %al, %ah + movb %cl, %al + jmp .Lfinish\@ +.Lusemap\@: + movw (%edx,%ecx),%ax +.Lfinish\@: + .endm + + # write word to addr %eax, upper bytes must be zero + # source byte passed in %dx + .macro _writew + movl %eax, %ecx + shrl $12, %eax + pushl %edx + movl %ecx, %edx + incl %edx + testl $0xfff, %edx + jz .Lunaligned\@ + movl wmap(,%eax,4), %edx + testl %edx, %edx + jnz .Lusemap\@ +.Lunaligned\@: + popl %edx + pushl %edx + pushl %ecx + pushl %edx + pushl %ecx + call mem_write + popl %ecx + popl %ecx + popl %ecx + popl %edx + incl %ecx + shrl $8, %edx + pushl %edx + pushl %ecx + call mem_write + addl $8, %esp + jmp .Lfinish\@ +.Lusemap\@: + popl %eax + movw %ax, (%edx,%ecx) +.Lfinish\@: + .endm + + + .macro _end + jmp opdone + .endm + + .macro _endnz + jnz opdone + .endm + + .macro _endz + jz opdone + .endm + + + .macro _LD dst=B, src=B + movb \src, %al + movb %al, \dst + _end + .endm + + .macro _ld_from_hl dst=B + movl HL, %eax + _readb + movb %al, \dst + .endm + + .macro _ld_to_hl src=B + movl HL, %eax + movb \src, %dl + _writeb + .endm + + .macro _ld_from_imm dst=B + _fetch + movb %al, \dst + .endm + + .macro _push + movl %eax, %edx + movl SP, %eax + subw $2, %ax + movl %eax, SP + _writew + .endm + + .macro _pop + movl SP, %eax + _readw + addw $2, SP + .endm + + .macro _JR + movl %ebp, %eax + _readbs + incl %eax + addw %ax, %bp + .endm + + .macro _JP + _fetchw + movl %eax, %ebp + .endm + + .macro _CALL + movl %ebp, %eax + addl $2, %eax + _push + _JP + .endm + + .macro _RET + _pop + movl %eax, %ebp + .endm + + .macro _NOJR + incw %bp + decl %edi + .endm + + .macro _NOJP + addw $2, %bp + decl %edi + .endm + + .macro _NOCALL + addw $2, %bp + subl $3, %edi + .endm + + .macro _NORET + subl $3, %edi + .endm + + # Use %dl to pass memory values to inc + .macro _INC reg=B, instr=incb, table=incflagtable + xorl %eax, %eax + \instr \reg + movb \reg, %al + andb $0x1f, %bl + orb \table(%eax), %bl + .endm + + .macro _DEC reg=B + _INC \reg, decb, decflagtable + .endm + + .macro _INCW reg=HL + incw \reg + _end + .endm + + .macro _DECW reg=HL + decw \reg + _end + .endm + + .macro _ADD instr=addb, table=addflagtable + \instr %al, %bh + lahf + xorl %ecx, %ecx + movb %ah, %cl + movb \table(%ecx), %bl + .endm + + .macro _SUB + _ADD subb, subflagtable + .endm + + .macro _ADC instr=adcb, table=addflagtable + rolb $4, %bl + _ADD \instr, \table + .endm + + .macro _SBC + _ADC sbbb, subflagtable + .endm + + .macro _CP + _ADD cmpb, subflagtable + .endm + + .macro _ADDW + movl HL, %edx + addb %al, %dl + adcb %ah, %dh + lahf + movl %edx, HL + movb $0, %dh + movb %ah, %dl + andb $0x8f, %bl + movb addflagtable(%edx), %al + andb $0x70, %al + orb %al, %bl + .endm + + .macro _ADDSP dst=SP + movl SP, %edx + addb %al, %dl + adcb %ah, %dh + lahf + movl %edx, \dst + movb $0, %dh + movb %ah, %dl + andb $0x0f, %bl + movb addflagtable(%edx), %al + andb $0x70, %al + orb %al, %bl + .endm + + .macro _AND + movb $0x20, %bl + andb %al, %bh + _endnz + orb $0x80, %bl + .endm + + .macro _OR instr=orb + \instr %al, %bh + setzb %bl + rorb $1, %bl + .endm + + .macro _XOR + _OR xorb + .endm + + + + .macro _RLCA instr=rolb + movb $0, %bl + \instr $1, %bh + rcrb $4, %bl + .endm + + .macro _RRCA + _RLCA rorb + .endm + + .macro _RLA instr=rclb + rolb $4, %bl + movb $0, %bl + \instr $1, %bh + rcrb $4, %bl + .endm + + .macro _RRA + _RLA rcrb + .endm + + .macro _RLC reg=B, instr=rolb + xorl %eax, %eax + movb \reg, %al + xorb %bl, %bl + \instr $1, %al + rcrb $4, %bl + movb %al, \reg + orb zflag_table(%eax), %bl + .endm + + .macro _RRC reg=B + _RLC \reg, rorb + .endm + + .macro _RL reg=B, instr=rclb + xorl %eax, %eax + andb $0x10, %bl + movb \reg, %al + rclb $4, %bl + \instr $1, %al + rcrb $4, %bl + movb %al, \reg + orb zflag_table(%eax), %bl + .endm + + .macro _RR reg=B + _RL \reg, rcrb + .endm + + .macro _SLA reg=B instr=shlb + movb $0, %cl + \instr $1, \reg + setz %bl + rcrb $4, %cl + rorb $1, %bl + orb %cl, %bl + .endm + + .macro _SRA reg=B + _SLA \reg, sarb + .endm + + .macro _SRL reg=B + _SLA \reg, shrb + .endm + + .macro _SWAP reg=B + movb \reg, %al + xorb %bl, %bl + rolb $4, %al + testb %al, %al + movb %al, \reg + _endnz + orb $0x80, %bl + _end + .endm + + .macro _SWAP_A + xorb %bl, %bl + rolb $4, %bh + testb %bh, %bh + _endnz + orb $0x80, %bl + _end + .endm + + .macro _SWAP_MEM + movl HL, %eax + _readb + movb %al, %dl + xorb %bl, %bl + rolb $4, %dl + movl HL, %eax + testb %dl, %dl + jnz .Lnonzero\@ + orb $0x80, %bl +.Lnonzero\@: + _writeb + _end + .endm + + .macro _BIT bit=0, reg=B + andb $0x1f, %bl + movb \reg, %al + orb $0x20, %bl + testb $(1<<\bit), %al + _endnz + orb $0x80, %bl + _end + .endm + + .macro _BIT_MEM bit=0 + movl HL, %eax + _readb + _BIT \bit, %al + .endm + + .macro _RES bit=0, reg=B + andb $(~(1<<\bit)), \reg + _end + .endm + + .macro _RES_MEM bit=0 + movl HL, %eax + _readb + movb %al, %dl + movl HL, %eax + andb $(~(1<<\bit)), %dl + _writeb + _end + .endm + + .macro _SET bit=0, reg=B + orb $(1<<\bit), \reg + _end + .endm + + .macro _SET_MEM bit=0 + movl HL, %eax + _readb + movb %al, %dl + movl HL, %eax + orb $(1<<\bit), %dl + _writeb + _end + .endm + + + + .macro _DAA + xorl %eax, %eax + movb %bl, %ah + andb $0x70, %ah + movb %bh, %al + shrb $4, %ah + xorl %ecx, %ecx + movb daa_table(%eax), %cl + andb $0x4f, %bl + addb %cl, %al + shrb $2, %cl + xorb %ah, %ah + movb %al, %bh + orb daa_carry_table(%ecx), %bl + orb zflag_table(%eax), %bl + .endm + + + + + + + + + .macro _make_bit_ops reg=B, op=BIT, bit=0, u=_ +__\op\u\bit\u\reg: + _\op \bit, \reg + .endm + + .macro _make_cb_ops reg=B +__RLC_\reg: + _RLC \reg + _end +__RRC_\reg: + _RRC \reg + _end +__RL_\reg: + _RL \reg + _end +__RR_\reg: + _RR \reg + _end +__SLA_\reg: + _SLA \reg + _end +__SRA_\reg: + _SRA \reg + _end +__SWAP_\reg: + _SWAP \reg +__SRL_\reg: + _SRL \reg + _end + _make_bit_ops \reg, BIT, 0 + _make_bit_ops \reg, BIT, 1 + _make_bit_ops \reg, BIT, 2 + _make_bit_ops \reg, BIT, 3 + _make_bit_ops \reg, BIT, 4 + _make_bit_ops \reg, BIT, 5 + _make_bit_ops \reg, BIT, 6 + _make_bit_ops \reg, BIT, 7 + _make_bit_ops \reg, RES, 0 + _make_bit_ops \reg, RES, 1 + _make_bit_ops \reg, RES, 2 + _make_bit_ops \reg, RES, 3 + _make_bit_ops \reg, RES, 4 + _make_bit_ops \reg, RES, 5 + _make_bit_ops \reg, RES, 6 + _make_bit_ops \reg, RES, 7 + _make_bit_ops \reg, SET, 0 + _make_bit_ops \reg, SET, 1 + _make_bit_ops \reg, SET, 2 + _make_bit_ops \reg, SET, 3 + _make_bit_ops \reg, SET, 4 + _make_bit_ops \reg, SET, 5 + _make_bit_ops \reg, SET, 6 + _make_bit_ops \reg, SET, 7 + .endm + + .macro _make_cb_hl_op op=RLC suf=_$HL mem=_MEM +__\op\suf: + movl HL, %eax + _readb + movb %al, %dl + _\op %dl + movl HL, %eax + _writeb + _end + .endm + + .macro _make_cb_hl_bit_op op=BIT bit=0 u=_ suf=_$HL mem=_MEM +__\op\u\bit\suf: + _\op\mem \bit + .endm + + + + + .text + .p2align 5 + + .globl cpu_emulate + .globl cpu_step + +__INVALID: + pushl %eax + pushl $invalid + call die + + +__LD_B_C: + _LD B,C +__LD_B_D: + _LD B,D +__LD_B_E: + _LD B,E +__LD_B_H: + _LD B,H +__LD_B_L: + _LD B,L +__LD_B_A: + movb %bh, B + _end +__LD_C_B: + _LD C,B +__LD_C_D: + _LD C,D +__LD_C_E: + _LD C,E +__LD_C_H: + _LD C,H +__LD_C_L: + _LD C,L +__LD_C_A: + movb %bh, C + _end +__LD_D_B: + _LD D,B +__LD_D_C: + _LD D,C +__LD_D_E: + _LD D,E +__LD_D_H: + _LD D,H +__LD_D_L: + _LD D,L +__LD_D_A: + movb %bh, D + _end +__LD_E_B: + _LD E,B +__LD_E_C: + _LD E,C +__LD_E_D: + _LD E,D +__LD_E_H: + _LD E,H +__LD_E_L: + _LD E,L +__LD_E_A: + movb %bh, E + _end +__LD_H_B: + _LD H,B +__LD_H_C: + _LD H,C +__LD_H_D: + _LD H,D +__LD_H_E: + _LD H,E +__LD_H_L: + _LD H,L +__LD_H_A: + movb %bh, H + _end +__LD_L_B: + _LD L,B +__LD_L_C: + _LD L,C +__LD_L_D: + _LD L,D +__LD_L_E: + _LD L,E +__LD_L_H: + _LD L,H +__LD_L_A: + movb %bh, L + _end +__LD_A_B: + movb B, %bh + _end +__LD_A_C: + movb C, %bh + _end +__LD_A_D: + movb D, %bh + _end +__LD_A_E: + movb E, %bh + _end +__LD_A_H: + movb H, %bh + _end +__LD_A_L: + movb L, %bh + _end + + +__LD_$HL_B: + _ld_to_hl B + _end +__LD_$HL_C: + _ld_to_hl C + _end +__LD_$HL_D: + _ld_to_hl D + _end +__LD_$HL_E: + _ld_to_hl E + _end +__LD_$HL_H: + _ld_to_hl H + _end +__LD_$HL_L: + _ld_to_hl L + _end +__LD_$HL_A: + _ld_to_hl %bh + _end + +__LD_B_$HL: + _ld_from_hl B + _end +__LD_C_$HL: + _ld_from_hl C + _end +__LD_D_$HL: + _ld_from_hl D + _end +__LD_E_$HL: + _ld_from_hl E + _end +__LD_H_$HL: + _ld_from_hl H + _end +__LD_L_$HL: + _ld_from_hl L + _end +__LD_A_$HL: + _ld_from_hl %bh + _end + + +__LD_$BC_A: + movl BC, %eax + movb %bh, %dl + _writeb + _end +__LD_A_$BC: + movl BC, %eax + _readb + movb %al, %bh + _end +__LD_$DE_A: + movl DE, %eax + movb %bh, %dl + _writeb + _end +__LD_A_$DE: + movl DE, %eax + _readb + movb %al, %bh + _end + +__LDI_$HL_A: + _ld_to_hl %bh + _INCW HL +__LDI_A_$HL: + _ld_from_hl %bh + _INCW HL +__LDD_$HL_A: + _ld_to_hl %bh + _DECW HL +__LDD_A_$HL: + _ld_from_hl %bh + _DECW HL + + +__FETCH: + _fetch + ret + +__LD_B_IMM: + call __FETCH + movb %al, B + _end +__LD_C_IMM: + call __FETCH + movb %al, C + _end +__LD_D_IMM: + call __FETCH + movb %al, D + _end +__LD_E_IMM: + call __FETCH + movb %al, E + _end +__LD_H_IMM: + call __FETCH + movb %al, H + _end +__LD_L_IMM: + call __FETCH + movb %al, L + _end +__LD_$HL_IMM: + call __FETCH + movb %al, %dl + movl HL, %eax + _writeb + _end +__LD_A_IMM: + call __FETCH + movb %al, %bh + _end + + +__FETCHW: + _fetchw + ret + +__LD_BC_IMM: + call __FETCHW + movl %eax, BC + _end +__LD_DE_IMM: + call __FETCHW + movl %eax, DE + _end +__LD_HL_IMM: + call __FETCHW + movl %eax, HL + _end +__LD_SP_IMM: + call __FETCHW + movl %eax, SP + _end + + + +__LD_$IMM_SP: + _fetchw + movl SP, %edx + _writew + _end +__LD_SP_HL: + movl HL, %eax + movl %eax, SP + _end +__LD_HL_SP_IMM: + _fetchs + _ADDSP HL + _end + + + + +__LD_$IMM_A: + _fetchw + movb %bh, %dl + _writeb + _end +__LD_A_$IMM: + _fetchw + _readb + movb %al, %bh + _end + + + +__LDH_$IMM_A: + _fetch + movb $0xff, %ah + movb %bh, %dl + _writeb + _end +__LDH_A_$IMM: + _fetch + movb $0xff, %ah + _readb + movb %al, %bh + _end +__LDH_$C_A: + movl $0xff00, %eax + movb C, %al + movb %bh, %dl + _writeb + _end +__LDH_A_$C: + movl $0xff00, %eax + movb C, %al + _readb + movb %al, %bh + _end + + + +__ADD_IMM: + _fetch + jmp __ADD +__ADD_$HL: + movl HL, %eax + _readb + jmp __ADD +__ADD_B: + movb B, %al + jmp __ADD +__ADD_C: + movb C, %al + jmp __ADD +__ADD_D: + movb D, %al + jmp __ADD +__ADD_E: + movb E, %al + jmp __ADD +__ADD_H: + movb H, %al + jmp __ADD +__ADD_L: + movb L, %al + jmp __ADD +__ADD_A: + movb %bh, %al +__ADD: + _ADD + _end + +__ADC_IMM: + _fetch + jmp __ADC +__ADC_$HL: + movl HL, %eax + _readb + jmp __ADC +__ADC_B: + movb B, %al + jmp __ADC +__ADC_C: + movb C, %al + jmp __ADC +__ADC_D: + movb D, %al + jmp __ADC +__ADC_E: + movb E, %al + jmp __ADC +__ADC_H: + movb H, %al + jmp __ADC +__ADC_L: + movb L, %al + jmp __ADC +__ADC_A: + movb %bh, %al +__ADC: + _ADC + _end + +__SUB_IMM: + _fetch + jmp __SUB +__SUB_$HL: + movl HL, %eax + _readb + jmp __SUB +__SUB_B: + movb B, %al + jmp __SUB +__SUB_C: + movb C, %al + jmp __SUB +__SUB_D: + movb D, %al + jmp __SUB +__SUB_E: + movb E, %al + jmp __SUB +__SUB_H: + movb H, %al + jmp __SUB +__SUB_L: + movb L, %al + jmp __SUB +__SUB_A: + movb %bh, %al +__SUB: + _SUB + _end + +__SBC_IMM: + _fetch + jmp __SBC +__SBC_$HL: + movl HL, %eax + _readb + jmp __SBC +__SBC_B: + movb B, %al + jmp __SBC +__SBC_C: + movb C, %al + jmp __SBC +__SBC_D: + movb D, %al + jmp __SBC +__SBC_E: + movb E, %al + jmp __SBC +__SBC_H: + movb H, %al + jmp __SBC +__SBC_L: + movb L, %al + jmp __SBC +__SBC_A: + movb %bh, %al +__SBC: + _SBC + _end + +__AND_IMM: + _fetch + jmp __AND +__AND_$HL: + movl HL, %eax + _readb + jmp __AND +__AND_B: + movb B, %al + jmp __AND +__AND_C: + movb C, %al + jmp __AND +__AND_D: + movb D, %al + jmp __AND +__AND_E: + movb E, %al + jmp __AND +__AND_H: + movb H, %al + jmp __AND +__AND_L: + movb L, %al + jmp __AND +__AND_A: + movb %bh, %al +__AND: + _AND + _end + +__XOR_IMM: + _fetch + jmp __XOR +__XOR_$HL: + movl HL, %eax + _readb + jmp __XOR +__XOR_B: + movb B, %al + jmp __XOR +__XOR_C: + movb C, %al + jmp __XOR +__XOR_D: + movb D, %al + jmp __XOR +__XOR_E: + movb E, %al + jmp __XOR +__XOR_H: + movb H, %al + jmp __XOR +__XOR_L: + movb L, %al + jmp __XOR +__XOR_A: + movb %bh, %al +__XOR: + _XOR + _end + +__OR_IMM: + _fetch + jmp __OR +__OR_$HL: + movl HL, %eax + _readb + jmp __OR +__OR_B: + movb B, %al + jmp __OR +__OR_C: + movb C, %al + jmp __OR +__OR_D: + movb D, %al + jmp __OR +__OR_E: + movb E, %al + jmp __OR +__OR_H: + movb H, %al + jmp __OR +__OR_L: + movb L, %al + jmp __OR +__OR_A: + movb %bh, %al +__OR: + _OR + _end + +__CP_IMM: + _fetch + jmp __CP +__CP_$HL: + movl HL, %eax + _readb + jmp __CP +__CP_B: + movb B, %al + jmp __CP +__CP_C: + movb C, %al + jmp __CP +__CP_D: + movb D, %al + jmp __CP +__CP_E: + movb E, %al + jmp __CP +__CP_H: + movb H, %al + jmp __CP +__CP_L: + movb L, %al + jmp __CP +__CP_A: + movb %bh, %al +__CP: + _CP + _end + + +__ADD_BC: + movl BC, %eax + jmp __ADDW +__ADD_DE: + movl DE, %eax + jmp __ADDW +__ADD_SP: + movl SP, %eax + jmp __ADDW +__ADD_HL: + movl HL, %eax +__ADDW: + _ADDW + _end + + + +__INC_B: + _INC B + _end +__INC_C: + _INC C + _end +__INC_D: + _INC D + _end +__INC_E: + _INC E + _end +__INC_H: + _INC H + _end +__INC_L: + _INC L + _end +__INC_$HL: + movl HL, %eax + _readb + movb %al, %dl + _INC %dl + movl HL, %eax + _writeb + _end +__INC_A: + _INC %bh + _end + + +__DEC_B: + _DEC B + _end +__DEC_C: + _DEC C + _end +__DEC_D: + _DEC D + _end +__DEC_E: + _DEC E + _end +__DEC_H: + _DEC H + _end +__DEC_L: + _DEC L + _end +__DEC_$HL: + movl HL, %eax + _readb + movb %al, %dl + _DEC %dl + movl HL, %eax + _writeb + _end +__DEC_A: + _DEC %bh + _end + + + +__INC_BC: + _INCW BC +__INC_DE: + _INCW DE +__INC_HL: + _INCW HL +__INC_SP: + _INCW SP +__DEC_BC: + _DECW BC +__DEC_DE: + _DECW DE +__DEC_HL: + _DECW HL +__DEC_SP: + _DECW SP + + + +__ADD_SP_IMM: + _fetchs + _ADDSP + _end + + + +__RLCA: + _RLCA + _end +__RRCA: + _RRCA + _end +__RLA: + _RLA + _end +__RRA: + _RRA + _end + + +__DAA: + _DAA + _end + + +__SCF: + andb $0x8f, %bl + orb $0x10, %bl + _end +__CCF: + andb $0x9f, %bl + xorb $0x10, %bl + _end + +__CPL: + orb $0x60, %bl + xorb $-1, %bh + _end + + +__EI: + movl $1, IMA + _end +__DI: + xorl %eax,%eax + movl %eax, halt + movl %eax, IME + movl %eax, IMA + _end + + +__PUSH_BC: + movl BC, %eax + jmp __PUSH +__PUSH_DE: + movl DE, %eax + jmp __PUSH +__PUSH_HL: + movl HL, %eax + jmp __PUSH +__PUSH_AF: + movl %ebx, %eax +__PUSH: + _push + _end + +__POP_BC: + _pop + movl %eax, BC + _end +__POP_DE: + _pop + movl %eax, DE + _end +__POP_HL: + _pop + movl %eax, HL + _end +__POP_AF: + _pop + movl %eax, %ebx + _end + + + + + +__JR_NZ: + testb $0x80, %bl + jz __JR + _NOJR + _end + +__JR_Z: + testb $0x80, %bl + jnz __JR + _NOJR + _end + +__JR_NC: + testb $0x10, %bl + jz __JR + _NOJR + _end + +__JR_C: + testb $0x10, %bl + jnz __JR + _NOJR + _end + +__JR: + _JR + _end + + + +__JP_HL: + movl HL, %eax + movl %eax, %ebp + _end + +__JP_NZ: + testb $0x80, %bl + jz __JP + _NOJP + _end + +__JP_Z: + testb $0x80, %bl + jnz __JP + _NOJP + _end + +__JP_NC: + testb $0x10, %bl + jz __JP + _NOJP + _end + +__JP_C: + testb $0x10, %bl + jnz __JP + _NOJP + _end + +__JP: + _JP + _end + + + +__CALL_NZ: + testb $0x80, %bl + jz __CALL + _NOCALL + _end + +__CALL_Z: + testb $0x80, %bl + jnz __CALL + _NOCALL + _end + +__CALL_NC: + testb $0x10, %bl + jz __CALL + _NOCALL + _end + +__CALL_C: + testb $0x10, %bl + jnz __CALL + _NOCALL + _end + +__CALL: + _CALL + _end + + + + +__RET_NZ: + testb $0x80, %bl + jz __RET + _NORET + _end + +__RET_Z: + testb $0x80, %bl + jnz __RET + _NORET + _end + +__RET_NC: + testb $0x10, %bl + jz __RET + _NORET + _end + +__RET_C: + testb $0x10, %bl + jnz __RET + _NORET + _end + +__RETI: + movl $1, %eax + movl %eax, IME + movl %eax, IMA +__RET: + _RET + _end + + +__RST_00: + movl $0x00, %eax + jmp __RST +__RST_08: + movl $0x08, %eax + jmp __RST +__RST_10: + movl $0x10, %eax + jmp __RST +__RST_18: + movl $0x18, %eax + jmp __RST +__RST_20: + movl $0x20, %eax + jmp __RST +__RST_28: + movl $0x28, %eax + jmp __RST +__RST_30: + movl $0x30, %eax + jmp __RST +__RST_38: + movl $0x38, %eax +__RST: + pushl %eax + movl %ebp, %eax + _push + popl %ebp + _end + + + + + +__HALT: + movl $1, %eax + movl %eax, halt + _end + +__STOP: + movb KEY1, %al + testb $1, %al + jz .Loldstop + movb speed, %ah + xorb $1, %ah + movb %ah, speed + shlb $7, %ah + andb $0x7e, %al + orb %ah, %al + movb %al, KEY1 +.Loldstop: + incw %bp + _end + + +__CB_OPS: + _fetch + xorl %ecx, %ecx + movb cb_cycles_table(%eax), %cl + movl %ecx, %edi + jmp *cb_optable(,%eax,4) + + + + + + + _make_cb_ops B + _make_cb_ops C + _make_cb_ops D + _make_cb_ops E + _make_cb_ops H + _make_cb_ops L + +__RLC_A: + _RLC %bh + _end +__RRC_A: + _RRC %bh + _end +__RL_A: + _RL %bh + _end +__RR_A: + _RR %bh + _end +__SLA_A: + _SLA %bh + _end +__SRA_A: + _SRA %bh + _end +__SWAP_A: + _SWAP_A +__SRL_A: + _SRL %bh + _end +__BIT_0_A: _BIT 0, %bh +__BIT_1_A: _BIT 1, %bh +__BIT_2_A: _BIT 2, %bh +__BIT_3_A: _BIT 3, %bh +__BIT_4_A: _BIT 4, %bh +__BIT_5_A: _BIT 5, %bh +__BIT_6_A: _BIT 6, %bh +__BIT_7_A: _BIT 7, %bh +__RES_0_A: _RES 0, %bh +__RES_1_A: _RES 1, %bh +__RES_2_A: _RES 2, %bh +__RES_3_A: _RES 3, %bh +__RES_4_A: _RES 4, %bh +__RES_5_A: _RES 5, %bh +__RES_6_A: _RES 6, %bh +__RES_7_A: _RES 7, %bh +__SET_0_A: _SET 0, %bh +__SET_1_A: _SET 1, %bh +__SET_2_A: _SET 2, %bh +__SET_3_A: _SET 3, %bh +__SET_4_A: _SET 4, %bh +__SET_5_A: _SET 5, %bh +__SET_6_A: _SET 6, %bh +__SET_7_A: _SET 7, %bh + + _make_cb_hl_op RLC + _make_cb_hl_op RRC + _make_cb_hl_op RL + _make_cb_hl_op RR + _make_cb_hl_op SLA + _make_cb_hl_op SRA +__SWAP_$HL: + _SWAP_MEM + _make_cb_hl_op SRL + + _make_cb_hl_bit_op BIT, 0 + _make_cb_hl_bit_op BIT, 1 + _make_cb_hl_bit_op BIT, 2 + _make_cb_hl_bit_op BIT, 3 + _make_cb_hl_bit_op BIT, 4 + _make_cb_hl_bit_op BIT, 5 + _make_cb_hl_bit_op BIT, 6 + _make_cb_hl_bit_op BIT, 7 + + _make_cb_hl_bit_op RES, 0 + _make_cb_hl_bit_op RES, 1 + _make_cb_hl_bit_op RES, 2 + _make_cb_hl_bit_op RES, 3 + _make_cb_hl_bit_op RES, 4 + _make_cb_hl_bit_op RES, 5 + _make_cb_hl_bit_op RES, 6 + _make_cb_hl_bit_op RES, 7 + + _make_cb_hl_bit_op SET, 0 + _make_cb_hl_bit_op SET, 1 + _make_cb_hl_bit_op SET, 2 + _make_cb_hl_bit_op SET, 3 + _make_cb_hl_bit_op SET, 4 + _make_cb_hl_bit_op SET, 5 + _make_cb_hl_bit_op SET, 6 + _make_cb_hl_bit_op SET, 7 + + .text + .p2align 5 + +cpu_emulate: + pushl %ebp + pushl %ebx + pushl %esi + pushl %edi + + movl AF, %ebx + movl PC, %ebp + movl 20(%esp), %esi + cmpl $0, %esi + jle .Ldone + +.Lnext: + movl halt, %eax + andl IME, %eax + jnz .Lidle + +.Ldoop: + # check for pending interrupts + movl IME, %eax + testl %eax, %eax + jz .Lnoint + movb IF, %cl + movb IE, %al + andb %cl, %al + jnz .Lint + +.Lnoint: + # update interrupt master enable + movl IMA, %eax + movl %eax, IME +.Lendint: + + _trace + + _fetch + xorl %ecx, %ecx + movb cycles_table(%eax), %cl + movl %ecx, %edi + jmp *optable(,%eax,4) + +__NOP: +__LD_B_B: +__LD_C_C: +__LD_D_D: +__LD_E_E: +__LD_H_H: +__LD_L_L: +__LD_A_A: +opdone: + shll $1, %edi + + # advance div + movl %edi, %ecx + movb div, %al + addl %ecx, %ecx + movb DIV, %ah + addl %ecx, %eax + movb %al, div + movb %ah, DIV + + # advance timer + movb TAC, %cl + testb $0x04, %cl + jnz .Ltimer +.Lendtimer: + + movb speed, %cl + shrl %cl, %edi + + # advance lcdc + subl %edi, lcdc + jg .Lnolcdc + call lcdc_trans +.Lnolcdc: + + # increment sound cycle counter + addl %edi, snd + + # count off cycles used + subl %edi, %esi + jg .Lnext + jmp .Ldone + + +.Lint: + # throw an interrupt + xorl %edx, %edx + movb int_mask_table(%eax), %ch + movl %edx, IMA + andb %ch, %cl + movl %edx, IME + movb %cl, IF + movl int_vec_table(,%eax,4), %edx + movl %ebp, %eax + movl %edx, %ebp + _push + jmp .Lendint + + +.Ltimer: + xorb $0xff, %cl + movl %edi, %eax + incb %cl + movl tim, %edx + andb $0x03, %cl + addb %cl, %cl + + shll %cl, %eax + addl %eax, %edx + movl %edx, tim + cmpl $512, %edx + jl .Lendtimer + + xorl %eax, %eax + movl %edx, %ecx + movb TIMA, %al + shrl $9, %ecx + andl $0x1ff, %edx + addl %ecx, %eax + movl %edx, tim + movb %al, TIMA + cmpl $256, %eax + jl .Lendtimer + + movb $4, %cl + xorl %edx, %edx + orb %cl, IF + movl $256, %ecx + movb TMA, %dl + subl $256, %eax + subl %edx, %ecx +.Ltimermod: + subl %ecx, %eax + jnc .Ltimermod + movb %al, TIMA + jmp .Lendtimer + +.Ldone: + movl %ebx, AF + movl %ebp, PC + movl 20(%esp), %eax + subl %esi, %eax + + popl %edi + popl %esi + popl %ebx + popl %ebp + ret + + +.Lidle: + pushl %esi + call cpu_idle + popl %ecx + testl %eax, %eax + jz .Ldoop + + subl %eax, %esi + jg .Lnext + jmp .Ldone + + + + +cpu_step: + movl 20(%esp), %edx + movl halt, %eax + andl IME, %eax + jz .Lruncpu + pushl %edx + pushl %edx + call cpu_idle + popl %edx + popl %edx + subl %eax, %edx + jnz .Lruncpu + ret +.Lruncpu: + pushl $1 + call cpu_emulate + popl %edx + ret + + + + + + + + diff --git a/asm/i386/lcd.s b/asm/i386/lcd.s new file mode 100644 index 0000000..6e8a87c --- /dev/null +++ b/asm/i386/lcd.s @@ -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 + + + + + + + + + + + + + + + + + + + + + + diff --git a/asm/i386/refresh.s b/asm/i386/refresh.s new file mode 100644 index 0000000..2d09196 --- /dev/null +++ b/asm/i386/refresh.s @@ -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 + + + + + + diff --git a/configure b/configure new file mode 100755 index 0000000..d9272be --- /dev/null +++ b/configure @@ -0,0 +1,4095 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by Autoconf 2.52. +# +# Copyright 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="sed y%*+%pp%;s%[^_$as_cr_alnum]%_%g" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="sed y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g" + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi + +# Name of the executable. +as_me=`echo "$0" |sed 's,.*[\\/],,'` + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +as_executable_p="test -f" + +# Support unset when possible. +if (FOO=FOO; unset FOO) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + +# NLS nuisances. +$as_unset LANG || test "${LANG+set}" != set || { LANG=C; export LANG; } +$as_unset LC_ALL || test "${LC_ALL+set}" != set || { LC_ALL=C; export LC_ALL; } +$as_unset LC_TIME || test "${LC_TIME+set}" != set || { LC_TIME=C; export LC_TIME; } +$as_unset LC_CTYPE || test "${LC_CTYPE+set}" != set || { LC_CTYPE=C; export LC_CTYPE; } +$as_unset LANGUAGE || test "${LANGUAGE+set}" != set || { LANGUAGE=C; export LANGUAGE; } +$as_unset LC_COLLATE || test "${LC_COLLATE+set}" != set || { LC_COLLATE=C; export LC_COLLATE; } +$as_unset LC_NUMERIC || test "${LC_NUMERIC+set}" != set || { LC_NUMERIC=C; export LC_NUMERIC; } +$as_unset LC_MESSAGES || test "${LC_MESSAGES+set}" != set || { LC_MESSAGES=C; export LC_MESSAGES; } + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH || test "${CDPATH+set}" != set || { CDPATH=:; export CDPATH; } + +# Name of the host. +# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +exec 6>&1 + +# +# Initializations. +# +ac_default_prefix=/usr/local +cross_compiling=no +subdirs= +MFLAGS= MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} + +# Maximum number of lines to put in a shell here document. +# This variable seems obsolete. It should probably be removed, and +# only ac_max_sed_lines should be used. +: ${ac_max_here_lines=38} + +ac_unique_file="cpu.c" + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +# Identity of this package. +PACKAGE_NAME= +PACKAGE_TARNAME= +PACKAGE_VERSION= +PACKAGE_STRING= +PACKAGE_BUGREPORT= + +ac_prev= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'` + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_option in + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + eval "enable_$ac_feature=no" ;; + + -enable-* | --enable-*) + ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "enable_$ac_feature='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package| sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "with_$ac_package='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package | sed 's/-/_/g'` + eval "with_$ac_package=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) { echo "$as_me: error: unrecognized option: $ac_option +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 + { (exit 1); exit 1; }; } + ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` + eval "$ac_envvar='$ac_optarg'" + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + { echo "$as_me: error: missing argument to $ac_option" >&2 + { (exit 1); exit 1; }; } +fi + +# Be sure to have absolute paths. +for ac_var in exec_prefix prefix +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* | NONE | '' ) ;; + *) { echo "$as_me: error: expected an absolute path for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# Be sure to have absolute paths. +for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \ + localstatedir libdir includedir oldincludedir infodir mandir +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* ) ;; + *) { echo "$as_me: error: expected an absolute path for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: should be removed in autoconf 3.0. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used." >&2 + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_prog=$0 + ac_confdir=`echo "$ac_prog" | sed 's%[\\/][^\\/][^\\/]*$%%'` + test "x$ac_confdir" = "x$ac_prog" && ac_confdir=. + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "$as_me: error: cannot find sources in $ac_confdir or .." >&2 + { (exit 1); exit 1; }; } + else + { echo "$as_me: error: cannot find sources in $srcdir" >&2 + { (exit 1); exit 1; }; } + fi +fi +srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'` +ac_env_build_alias_set=${build_alias+set} +ac_env_build_alias_value=$build_alias +ac_cv_env_build_alias_set=${build_alias+set} +ac_cv_env_build_alias_value=$build_alias +ac_env_host_alias_set=${host_alias+set} +ac_env_host_alias_value=$host_alias +ac_cv_env_host_alias_set=${host_alias+set} +ac_cv_env_host_alias_value=$host_alias +ac_env_target_alias_set=${target_alias+set} +ac_env_target_alias_value=$target_alias +ac_cv_env_target_alias_set=${target_alias+set} +ac_cv_env_target_alias_value=$target_alias +ac_env_CC_set=${CC+set} +ac_env_CC_value=$CC +ac_cv_env_CC_set=${CC+set} +ac_cv_env_CC_value=$CC +ac_env_CFLAGS_set=${CFLAGS+set} +ac_env_CFLAGS_value=$CFLAGS +ac_cv_env_CFLAGS_set=${CFLAGS+set} +ac_cv_env_CFLAGS_value=$CFLAGS +ac_env_LDFLAGS_set=${LDFLAGS+set} +ac_env_LDFLAGS_value=$LDFLAGS +ac_cv_env_LDFLAGS_set=${LDFLAGS+set} +ac_cv_env_LDFLAGS_value=$LDFLAGS +ac_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_env_CPPFLAGS_value=$CPPFLAGS +ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_cv_env_CPPFLAGS_value=$CPPFLAGS +ac_env_CPP_set=${CPP+set} +ac_env_CPP_value=$CPP +ac_cv_env_CPP_set=${CPP+set} +ac_cv_env_CPP_value=$CPP + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat < if you have libraries in a + nonstandard directory + CPPFLAGS C/C++ preprocessor flags, e.g. -I if you have + headers in a nonstandard directory + CPP C preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +EOF +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + ac_popdir=`pwd` + for ac_subdir in : $ac_subdirs_all; do test "x$ac_subdir" = x: && continue + cd $ac_subdir + # A "../" for each directory in /$ac_subdir. + ac_dots=`echo $ac_subdir | + sed 's,^\./,,;s,[^/]$,&/,;s,[^/]*/,../,g'` + + case $srcdir in + .) # No --srcdir option. We are building in place. + ac_sub_srcdir=$srcdir ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_sub_srcdir=$srcdir/$ac_subdir ;; + *) # Relative path. + ac_sub_srcdir=$ac_dots$srcdir/$ac_subdir ;; + esac + + # Check for guested configure; otherwise get Cygnus style configure. + if test -f $ac_sub_srcdir/configure.gnu; then + echo + $SHELL $ac_sub_srcdir/configure.gnu --help=recursive + elif test -f $ac_sub_srcdir/configure; then + echo + $SHELL $ac_sub_srcdir/configure --help=recursive + elif test -f $ac_sub_srcdir/configure.ac || + test -f $ac_sub_srcdir/configure.in; then + echo + $ac_configure --help + else + echo "$as_me: WARNING: no configuration information is in $ac_subdir" >&2 + fi + cd $ac_popdir + done +fi + +test -n "$ac_init_help" && exit 0 +if $ac_init_version; then + cat <<\EOF + +Copyright 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001 +Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +EOF + exit 0 +fi +exec 5>config.log +cat >&5 </dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +hostinfo = `(hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +PATH = $PATH + +_ASUNAME +} >&5 + +cat >&5 <\?\"\']*) + ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` + ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'" + ac_sep=" " ;; + *) ac_configure_args="$ac_configure_args$ac_sep$ac_arg" + ac_sep=" " ;; + esac + # Get rid of the leading space. +done + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + echo >&5 + echo "## ----------------- ##" >&5 + echo "## Cache variables. ##" >&5 + echo "## ----------------- ##" >&5 + echo >&5 + # The following way of writing the cache mishandles newlines in values, +{ + (set) 2>&1 | + case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in + *ac_space=\ *) + sed -n \ + "s/'"'"'/'"'"'\\\\'"'"''"'"'/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p" + ;; + *) + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} >&5 + sed "/^$/d" confdefs.h >conftest.log + if test -s conftest.log; then + echo >&5 + echo "## ------------ ##" >&5 + echo "## confdefs.h. ##" >&5 + echo "## ------------ ##" >&5 + echo >&5 + cat conftest.log >&5 + fi + (echo; echo) >&5 + test "$ac_signal" != 0 && + echo "$as_me: caught signal $ac_signal" >&5 + echo "$as_me: exit $exit_status" >&5 + rm -rf conftest* confdefs* core core.* *.core conf$$* $ac_clean_files && + exit $exit_status + ' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo >confdefs.h + +# Let the site file select an alternate cache file if it wants to. +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + { echo "$as_me:841: loading site script $ac_site_file" >&5 +echo "$as_me: loading site script $ac_site_file" >&6;} + cat "$ac_site_file" >&5 + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special + # files actually), so we avoid doing that. + if test -f "$cache_file"; then + { echo "$as_me:852: loading cache $cache_file" >&5 +echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . $cache_file;; + *) . ./$cache_file;; + esac + fi +else + { echo "$as_me:860: creating cache $cache_file" >&5 +echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in `(set) 2>&1 | + sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val="\$ac_cv_env_${ac_var}_value" + eval ac_new_val="\$ac_env_${ac_var}_value" + case $ac_old_set,$ac_new_set in + set,) + { echo "$as_me:876: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { echo "$as_me:880: error: \`$ac_var' was not set in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + { echo "$as_me:886: error: \`$ac_var' has changed since the previous run:" >&5 +echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + { echo "$as_me:888: former value: $ac_old_val" >&5 +echo "$as_me: former value: $ac_old_val" >&2;} + { echo "$as_me:890: current value: $ac_new_val" >&5 +echo "$as_me: current value: $ac_new_val" >&2;} + ac_cache_corrupted=: + fi;; + esac + # Pass precious variables to config.status. It doesn't matter if + # we pass some twice (in addition to the command line arguments). + if test "$ac_new_set" = set; then + case $ac_new_val in + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` + ac_configure_args="$ac_configure_args '$ac_arg'" + ;; + *) ac_configure_args="$ac_configure_args $ac_var=$ac_new_val" + ;; + esac + fi +done +if $ac_cache_corrupted; then + { echo "$as_me:909: error: changes in the environment can compromise the build" >&5 +echo "$as_me: error: changes in the environment can compromise the build" >&2;} + { { echo "$as_me:911: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 +echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac +echo "#! $SHELL" >conftest.sh +echo "exit 0" >>conftest.sh +chmod +x conftest.sh +if { (echo "$as_me:931: PATH=\".;.\"; conftest.sh") >&5 + (PATH=".;."; conftest.sh) 2>&5 + ac_status=$? + echo "$as_me:934: \$? = $ac_status" >&5 + (exit $ac_status); }; then + ac_path_separator=';' +else + ac_path_separator=: +fi +PATH_SEPARATOR="$ac_path_separator" +rm -f conftest.sh + +CFLAGS="$CFLAGS" + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +echo "$as_me:953: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_save_IFS=$IFS; IFS=$ac_path_separator +ac_dummy="$PATH" +for ac_dir in $ac_dummy; do + IFS=$ac_save_IFS + test -z "$ac_dir" && ac_dir=. + $as_executable_p "$ac_dir/$ac_word" || continue +ac_cv_prog_CC="${ac_tool_prefix}gcc" +echo "$as_me:968: found $ac_dir/$ac_word" >&5 +break +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:976: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:979: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo "$as_me:988: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else + ac_save_IFS=$IFS; IFS=$ac_path_separator +ac_dummy="$PATH" +for ac_dir in $ac_dummy; do + IFS=$ac_save_IFS + test -z "$ac_dir" && ac_dir=. + $as_executable_p "$ac_dir/$ac_word" || continue +ac_cv_prog_ac_ct_CC="gcc" +echo "$as_me:1003: found $ac_dir/$ac_word" >&5 +break +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:1011: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:1014: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +echo "$as_me:1027: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_save_IFS=$IFS; IFS=$ac_path_separator +ac_dummy="$PATH" +for ac_dir in $ac_dummy; do + IFS=$ac_save_IFS + test -z "$ac_dir" && ac_dir=. + $as_executable_p "$ac_dir/$ac_word" || continue +ac_cv_prog_CC="${ac_tool_prefix}cc" +echo "$as_me:1042: found $ac_dir/$ac_word" >&5 +break +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:1050: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:1053: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo "$as_me:1062: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else + ac_save_IFS=$IFS; IFS=$ac_path_separator +ac_dummy="$PATH" +for ac_dir in $ac_dummy; do + IFS=$ac_save_IFS + test -z "$ac_dir" && ac_dir=. + $as_executable_p "$ac_dir/$ac_word" || continue +ac_cv_prog_ac_ct_CC="cc" +echo "$as_me:1077: found $ac_dir/$ac_word" >&5 +break +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:1085: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:1088: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo "$as_me:1101: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no + ac_save_IFS=$IFS; IFS=$ac_path_separator +ac_dummy="$PATH" +for ac_dir in $ac_dummy; do + IFS=$ac_save_IFS + test -z "$ac_dir" && ac_dir=. + $as_executable_p "$ac_dir/$ac_word" || continue +if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue +fi +ac_cv_prog_CC="cc" +echo "$as_me:1121: found $ac_dir/$ac_word" >&5 +break +done + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + set dummy "$ac_dir/$ac_word" ${1+"$@"} + shift + ac_cv_prog_CC="$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:1143: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:1146: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +echo "$as_me:1157: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_save_IFS=$IFS; IFS=$ac_path_separator +ac_dummy="$PATH" +for ac_dir in $ac_dummy; do + IFS=$ac_save_IFS + test -z "$ac_dir" && ac_dir=. + $as_executable_p "$ac_dir/$ac_word" || continue +ac_cv_prog_CC="$ac_tool_prefix$ac_prog" +echo "$as_me:1172: found $ac_dir/$ac_word" >&5 +break +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:1180: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:1183: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:1196: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else + ac_save_IFS=$IFS; IFS=$ac_path_separator +ac_dummy="$PATH" +for ac_dir in $ac_dummy; do + IFS=$ac_save_IFS + test -z "$ac_dir" && ac_dir=. + $as_executable_p "$ac_dir/$ac_word" || continue +ac_cv_prog_ac_ct_CC="$ac_prog" +echo "$as_me:1211: found $ac_dir/$ac_word" >&5 +break +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:1219: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:1222: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$ac_ct_CC" && break +done + + CC=$ac_ct_CC +fi + +fi + +test -z "$CC" && { { echo "$as_me:1234: error: no acceptable cc found in \$PATH" >&5 +echo "$as_me: error: no acceptable cc found in \$PATH" >&2;} + { (exit 1); exit 1; }; } + +# Provide some information about the compiler. +echo "$as_me:1239:" \ + "checking for C compiler version" >&5 +ac_compiler=`set X $ac_compile; echo $2` +{ (eval echo "$as_me:1242: \"$ac_compiler --version &5\"") >&5 + (eval $ac_compiler --version &5) 2>&5 + ac_status=$? + echo "$as_me:1245: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:1247: \"$ac_compiler -v &5\"") >&5 + (eval $ac_compiler -v &5) 2>&5 + ac_status=$? + echo "$as_me:1250: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:1252: \"$ac_compiler -V &5\"") >&5 + (eval $ac_compiler -V &5) 2>&5 + ac_status=$? + echo "$as_me:1255: \$? = $ac_status" >&5 + (exit $ac_status); } + +cat >conftest.$ac_ext <<_ACEOF +#line 1259 "configure" +#include "confdefs.h" + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.exe" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +echo "$as_me:1275: checking for C compiler default output" >&5 +echo $ECHO_N "checking for C compiler default output... $ECHO_C" >&6 +ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` +if { (eval echo "$as_me:1278: \"$ac_link_default\"") >&5 + (eval $ac_link_default) 2>&5 + ac_status=$? + echo "$as_me:1281: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # Find the output, starting from the most likely. This scheme is +# not robust to junk in `.', hence go to wildcards (a.*) only as a last +# resort. +for ac_file in `ls a.exe conftest.exe 2>/dev/null; + ls a.out conftest 2>/dev/null; + ls a.* conftest.* 2>/dev/null`; do + case $ac_file in + *.$ac_ext | *.o | *.obj | *.xcoff | *.tds | *.d | *.pdb ) ;; + a.out ) # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + # FIXME: I believe we export ac_cv_exeext for Libtool --akim. + export ac_cv_exeext + break;; + * ) break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +{ { echo "$as_me:1304: error: C compiler cannot create executables" >&5 +echo "$as_me: error: C compiler cannot create executables" >&2;} + { (exit 77); exit 77; }; } +fi + +ac_exeext=$ac_cv_exeext +echo "$as_me:1310: result: $ac_file" >&5 +echo "${ECHO_T}$ac_file" >&6 + +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:1315: checking whether the C compiler works" >&5 +echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6 +# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 +# If not cross compiling, check that we can run a simple program. +if test "$cross_compiling" != yes; then + if { ac_try='./$ac_file' + { (eval echo "$as_me:1321: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:1324: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { echo "$as_me:1331: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'." >&5 +echo "$as_me: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'." >&2;} + { (exit 1); exit 1; }; } + fi + fi +fi +echo "$as_me:1339: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +rm -f a.out a.exe conftest$ac_cv_exeext +ac_clean_files=$ac_clean_files_save +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:1346: checking whether we are cross compiling" >&5 +echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6 +echo "$as_me:1348: result: $cross_compiling" >&5 +echo "${ECHO_T}$cross_compiling" >&6 + +echo "$as_me:1351: checking for executable suffix" >&5 +echo $ECHO_N "checking for executable suffix... $ECHO_C" >&6 +if { (eval echo "$as_me:1353: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:1356: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in `(ls conftest.exe; ls conftest; ls conftest.*) 2>/dev/null`; do + case $ac_file in + *.$ac_ext | *.o | *.obj | *.xcoff | *.tds | *.d | *.pdb ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + export ac_cv_exeext + break;; + * ) break;; + esac +done +else + { { echo "$as_me:1372: error: cannot compute EXEEXT: cannot compile and link" >&5 +echo "$as_me: error: cannot compute EXEEXT: cannot compile and link" >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest$ac_cv_exeext +echo "$as_me:1378: result: $ac_cv_exeext" >&5 +echo "${ECHO_T}$ac_cv_exeext" >&6 + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +echo "$as_me:1384: checking for object suffix" >&5 +echo $ECHO_N "checking for object suffix... $ECHO_C" >&6 +if test "${ac_cv_objext+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 1390 "configure" +#include "confdefs.h" + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { (eval echo "$as_me:1402: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:1405: \$? = $ac_status" >&5 + (exit $ac_status); }; then + for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +{ { echo "$as_me:1417: error: cannot compute OBJEXT: cannot compile" >&5 +echo "$as_me: error: cannot compute OBJEXT: cannot compile" >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +echo "$as_me:1424: result: $ac_cv_objext" >&5 +echo "${ECHO_T}$ac_cv_objext" >&6 +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +echo "$as_me:1428: checking whether we are using the GNU C compiler" >&5 +echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6 +if test "${ac_cv_c_compiler_gnu+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 1434 "configure" +#include "confdefs.h" + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:1449: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:1452: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:1455: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:1458: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_compiler_gnu=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_compiler_gnu=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +echo "$as_me:1470: result: $ac_cv_c_compiler_gnu" >&5 +echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6 +GCC=`test $ac_compiler_gnu = yes && echo yes` +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +CFLAGS="-g" +echo "$as_me:1476: checking whether $CC accepts -g" >&5 +echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_g+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 1482 "configure" +#include "confdefs.h" + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:1494: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:1497: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:1500: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:1503: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_g=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_prog_cc_g=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:1513: result: $ac_cv_prog_cc_g" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_g" >&6 +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +# Some people use a C++ compiler to compile C. Since we use `exit', +# in C++ we need to declare it. In case someone uses the same compiler +# for both compiling C and C++ we need to have the C++ compiler decide +# the declaration of exit, since it's the most demanding environment. +cat >conftest.$ac_ext <<_ACEOF +#ifndef __cplusplus + choke me +#endif +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:1540: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:1543: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:1546: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:1549: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + for ac_declaration in \ + ''\ + '#include ' \ + 'extern "C" void std::exit (int) throw (); using std::exit;' \ + 'extern "C" void std::exit (int); using std::exit;' \ + 'extern "C" void exit (int) throw ();' \ + 'extern "C" void exit (int);' \ + 'void exit (int);' +do + cat >conftest.$ac_ext <<_ACEOF +#line 1561 "configure" +#include "confdefs.h" +#include +$ac_declaration +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:1574: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:1577: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:1580: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:1583: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +continue +fi +rm -f conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +#line 1593 "configure" +#include "confdefs.h" +$ac_declaration +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:1605: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:1608: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:1611: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:1614: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +fi +rm -f conftest.$ac_objext conftest.$ac_ext +done +rm -f conftest* +if test -n "$ac_declaration"; then + echo '#ifdef __cplusplus' >>confdefs.h + echo $ac_declaration >>confdefs.h + echo '#endif' >>confdefs.h +fi + +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +fi +rm -f conftest.$ac_objext conftest.$ac_ext +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +echo "$as_me:1646: checking how to run the C preprocessor" >&5 +echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6 +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if test "${ac_cv_prog_CPP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +#line 1667 "configure" +#include "confdefs.h" +#include + Syntax error +_ACEOF +if { (eval echo "$as_me:1672: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:1678: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +#line 1701 "configure" +#include "confdefs.h" +#include +_ACEOF +if { (eval echo "$as_me:1705: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:1711: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +echo "$as_me:1748: result: $CPP" >&5 +echo "${ECHO_T}$CPP" >&6 +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +#line 1758 "configure" +#include "confdefs.h" +#include + Syntax error +_ACEOF +if { (eval echo "$as_me:1763: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:1769: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +#line 1792 "configure" +#include "confdefs.h" +#include +_ACEOF +if { (eval echo "$as_me:1796: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:1802: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + : +else + { { echo "$as_me:1830: error: C preprocessor \"$CPP\" fails sanity check" >&5 +echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check" >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_aux_dir= +for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do + if test -f $ac_dir/install-sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f $ac_dir/install.sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f $ac_dir/shtool; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { { echo "$as_me:1858: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&5 +echo "$as_me: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&2;} + { (exit 1); exit 1; }; } +fi +ac_config_guess="$SHELL $ac_aux_dir/config.guess" +ac_config_sub="$SHELL $ac_aux_dir/config.sub" +ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure. + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# ./install, which can be erroneously created by make from ./install.sh. +echo "$as_me:1878: checking for a BSD compatible install" >&5 +echo $ECHO_N "checking for a BSD compatible install... $ECHO_C" >&6 +if test -z "$INSTALL"; then +if test "${ac_cv_path_install+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_save_IFS=$IFS; IFS=$ac_path_separator + for ac_dir in $PATH; do + IFS=$ac_save_IFS + # Account for people who put trailing slashes in PATH elements. + case $ac_dir/ in + / | ./ | .// | /cC/* \ + | /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* \ + | /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + if $as_executable_p "$ac_dir/$ac_prog"; then + if test $ac_prog = install && + grep dspmsg "$ac_dir/$ac_prog" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$ac_dir/$ac_prog" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + ac_cv_path_install="$ac_dir/$ac_prog -c" + break 2 + fi + fi + done + ;; + esac + done + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. We don't cache a + # path for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the path is relative. + INSTALL=$ac_install_sh + fi +fi +echo "$as_me:1927: result: $INSTALL" >&5 +echo "${ECHO_T}$INSTALL" >&6 + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +test "$cross_compiling" = "yes" || +echo "$as_me:1939: checking whether byte ordering is bigendian" >&5 +echo $ECHO_N "checking whether byte ordering is bigendian... $ECHO_C" >&6 +if test "${ac_cv_c_bigendian+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_c_bigendian=unknown +# See if sys/param.h defines the BYTE_ORDER macro. +cat >conftest.$ac_ext <<_ACEOF +#line 1947 "configure" +#include "confdefs.h" +#include +#include + +int +main () +{ +#if !BYTE_ORDER || !BIG_ENDIAN || !LITTLE_ENDIAN + bogus endian macros +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:1964: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:1967: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:1970: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:1973: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + # It does; now see whether it defined to BIG_ENDIAN or not. +cat >conftest.$ac_ext <<_ACEOF +#line 1977 "configure" +#include "confdefs.h" +#include +#include + +int +main () +{ +#if BYTE_ORDER != BIG_ENDIAN + not big endian +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:1994: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:1997: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:2000: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:2003: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_c_bigendian=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_c_bigendian=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +fi +rm -f conftest.$ac_objext conftest.$ac_ext +if test $ac_cv_c_bigendian = unknown; then +if test "$cross_compiling" = yes; then + { { echo "$as_me:2019: error: cannot run test program while cross compiling" >&5 +echo "$as_me: error: cannot run test program while cross compiling" >&2;} + { (exit 1); exit 1; }; } +else + cat >conftest.$ac_ext <<_ACEOF +#line 2024 "configure" +#include "confdefs.h" +int +main () +{ + /* Are we little or big endian? From Harbison&Steele. */ + union + { + long l; + char c[sizeof (long)]; + } u; + u.l = 1; + exit (u.c[sizeof (long) - 1] == 1); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:2040: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:2043: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:2045: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:2048: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_c_bigendian=no +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_c_bigendian=yes +fi +rm -f core core.* *.core conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +fi +echo "$as_me:2061: result: $ac_cv_c_bigendian" >&5 +echo "${ECHO_T}$ac_cv_c_bigendian" >&6 +if test $ac_cv_c_bigendian = yes; then + +cat >>confdefs.h <<\EOF +#define WORDS_BIGENDIAN 1 +EOF + +fi + +test "$ac_cv_c_bigendian" = "no" && ENDIAN="-DIS_LITTLE_ENDIAN" + +for ac_func in usleep +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:2076: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 2082 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. */ +#include +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +char (*f) (); + +int +main () +{ +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +f = $ac_func; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:2113: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:2116: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:2119: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:2122: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +eval "$as_ac_var=no" +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:2132: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 2150 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. */ +#include +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +char (*f) (); + +int +main () +{ +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +f = $ac_func; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:2181: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:2184: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:2187: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:2190: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +eval "$as_ac_var=no" +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:2200: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <&5 +echo "$as_me: error: your system must support either usleep or select" >&2;} + { (exit 1); exit 1; }; } + +fi +done + +fi +done + +LIBS="$LIBS -L/usr/local/lib -L/usr/X11R6/lib" + +# Check whether --with-fb or --without-fb was given. +if test "${with_fb+set}" = set; then + withval="$with_fb" + +else + with_fb=yes +fi; + +# Check whether --with-svgalib or --without-svgalib was given. +if test "${with_svgalib+set}" = set; then + withval="$with_svgalib" + +else + with_svgalib=yes +fi; + +# Check whether --with-sdl or --without-sdl was given. +if test "${with_sdl+set}" = set; then + withval="$with_sdl" + +else + with_sdl=yes +fi; + +SOUND="" +JOY="" + +case `uname -s` in +Linux) +SYS_DEFS=-DIS_LINUX + +for ac_header in sys/soundcard.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +echo "$as_me:2255: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 2261 "configure" +#include "confdefs.h" +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:2265: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:2271: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + eval "$as_ac_Header=no" +fi +rm -f conftest.err conftest.$ac_ext +fi +echo "$as_me:2290: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 2309 "configure" +#include "confdefs.h" +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:2313: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:2319: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + eval "$as_ac_Header=no" +fi +rm -f conftest.err conftest.$ac_ext +fi +echo "$as_me:2338: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 2358 "configure" +#include "confdefs.h" +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:2362: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:2368: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + eval "$as_ac_Header=no" +fi +rm -f conftest.err conftest.$ac_ext +fi +echo "$as_me:2387: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 2410 "configure" +#include "confdefs.h" +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:2414: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:2420: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + eval "$as_ac_Header=no" +fi +rm -f conftest.err conftest.$ac_ext +fi +echo "$as_me:2439: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 2462 "configure" +#include "confdefs.h" +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:2466: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:2472: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + eval "$as_ac_Header=no" +fi +rm -f conftest.err conftest.$ac_ext +fi +echo "$as_me:2491: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <&5 +echo $ECHO_N "checking for vga_init in -lvga... $ECHO_C" >&6 +if test "${ac_cv_lib_vga_vga_init+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lvga $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line 2521 "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char vga_init (); +int +main () +{ +vga_init (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:2540: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:2543: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:2546: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:2549: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_vga_vga_init=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_lib_vga_vga_init=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:2560: result: $ac_cv_lib_vga_vga_init" >&5 +echo "${ECHO_T}$ac_cv_lib_vga_vga_init" >&6 +if test $ac_cv_lib_vga_vga_init = yes; then + +for ac_header in vga.h vgakeyboard.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +echo "$as_me:2567: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 2573 "configure" +#include "confdefs.h" +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:2577: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:2583: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + eval "$as_ac_Header=no" +fi +rm -f conftest.err conftest.$ac_ext +fi +echo "$as_me:2602: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <&5 +echo "$as_me: WARNING: svgalib found but headers are missing!!" >&2;} +with_svgalib=no + +fi +done + +else + with_svgalib=no +fi + +fi + +if test "$with_sdl" != "no" ; then +# Extract the first word of "sdl-config", so it can be a program name with args. +set dummy sdl-config; ac_word=$2 +echo "$as_me:2627: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_SDL_CONFIG+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$SDL_CONFIG"; then + ac_cv_prog_SDL_CONFIG="$SDL_CONFIG" # Let the user override the test. +else + ac_save_IFS=$IFS; IFS=$ac_path_separator +ac_dummy="$PATH" +for ac_dir in $ac_dummy; do + IFS=$ac_save_IFS + test -z "$ac_dir" && ac_dir=. + $as_executable_p "$ac_dir/$ac_word" || continue +ac_cv_prog_SDL_CONFIG="yes" +echo "$as_me:2642: found $ac_dir/$ac_word" >&5 +break +done + +fi +fi +SDL_CONFIG=$ac_cv_prog_SDL_CONFIG +if test -n "$SDL_CONFIG"; then + echo "$as_me:2650: result: $SDL_CONFIG" >&5 +echo "${ECHO_T}$SDL_CONFIG" >&6 +else + echo "$as_me:2653: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +if test "$SDL_CONFIG" ; then +SDL_LIBS="`sdl-config --libs`" +SDL_CFLAGS="`sdl-config --cflags`" +old_incs="$INCS" +INCS="$INCS $SDL_CFLAGS" +echo "$as_me:2662: checking for SDL_Init in -lSDL" >&5 +echo $ECHO_N "checking for SDL_Init in -lSDL... $ECHO_C" >&6 +if test "${ac_cv_lib_SDL_SDL_Init+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lSDL $SDL_LIBS $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line 2670 "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char SDL_Init (); +int +main () +{ +SDL_Init (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:2689: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:2692: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:2695: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:2698: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_SDL_SDL_Init=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_lib_SDL_SDL_Init=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:2709: result: $ac_cv_lib_SDL_SDL_Init" >&5 +echo "${ECHO_T}$ac_cv_lib_SDL_SDL_Init" >&6 +if test $ac_cv_lib_SDL_SDL_Init = yes; then + +for ac_header in SDL/SDL.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +echo "$as_me:2716: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 2722 "configure" +#include "confdefs.h" +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:2726: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:2732: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + eval "$as_ac_Header=no" +fi +rm -f conftest.err conftest.$ac_ext +fi +echo "$as_me:2751: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <&5 +echo "$as_me: WARNING: SDL found but headers are missing!!" >&2;} +with_sdl=no + +fi +done + +else + with_sdl=no +fi + +INCS="$old_incs" +else +with_sdl=no +fi +fi + +echo "$as_me:2777: checking for X" >&5 +echo $ECHO_N "checking for X... $ECHO_C" >&6 + +# Check whether --with-x or --without-x was given. +if test "${with_x+set}" = set; then + withval="$with_x" + +fi; +# $have_x is `yes', `no', `disabled', or empty when we do not yet know. +if test "x$with_x" = xno; then + # The user explicitly disabled X. + have_x=disabled +else + if test "x$x_includes" != xNONE && test "x$x_libraries" != xNONE; then + # Both variables are already set. + have_x=yes + else + if test "${ac_cv_have_x+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # One or both of the vars are not set, and there is no cached value. +ac_x_includes=no ac_x_libraries=no +rm -fr conftest.dir +if mkdir conftest.dir; then + cd conftest.dir + # Make sure to not put "make" in the Imakefile rules, since we grep it out. + cat >Imakefile <<'EOF' +acfindx: + @echo 'ac_im_incroot="${INCROOT}"; ac_im_usrlibdir="${USRLIBDIR}"; ac_im_libdir="${LIBDIR}"' +EOF + if (xmkmf) >/dev/null 2>/dev/null && test -f Makefile; then + # GNU make sometimes prints "make[1]: Entering...", which would confuse us. + eval `${MAKE-make} acfindx 2>/dev/null | grep -v make` + # Open Windows xmkmf reportedly sets LIBDIR instead of USRLIBDIR. + for ac_extension in a so sl; do + if test ! -f $ac_im_usrlibdir/libX11.$ac_extension && + test -f $ac_im_libdir/libX11.$ac_extension; then + ac_im_usrlibdir=$ac_im_libdir; break + fi + done + # Screen out bogus values from the imake configuration. They are + # bogus both because they are the default anyway, and because + # using them would break gcc on systems where it needs fixed includes. + case $ac_im_incroot in + /usr/include) ;; + *) test -f "$ac_im_incroot/X11/Xos.h" && ac_x_includes=$ac_im_incroot;; + esac + case $ac_im_usrlibdir in + /usr/lib | /lib) ;; + *) test -d "$ac_im_usrlibdir" && ac_x_libraries=$ac_im_usrlibdir ;; + esac + fi + cd .. + rm -fr conftest.dir +fi + +# Standard set of common directories for X headers. +# Check X11 before X11Rn because it is often a symlink to the current release. +ac_x_header_dirs=' +/usr/X11/include +/usr/X11R6/include +/usr/X11R5/include +/usr/X11R4/include + +/usr/include/X11 +/usr/include/X11R6 +/usr/include/X11R5 +/usr/include/X11R4 + +/usr/local/X11/include +/usr/local/X11R6/include +/usr/local/X11R5/include +/usr/local/X11R4/include + +/usr/local/include/X11 +/usr/local/include/X11R6 +/usr/local/include/X11R5 +/usr/local/include/X11R4 + +/usr/X386/include +/usr/x386/include +/usr/XFree86/include/X11 + +/usr/include +/usr/local/include +/usr/unsupported/include +/usr/athena/include +/usr/local/x11r5/include +/usr/lpp/Xamples/include + +/usr/openwin/include +/usr/openwin/share/include' + +if test "$ac_x_includes" = no; then + # Guess where to find include files, by looking for Intrinsic.h. + # First, try using that file with no special directory specified. + cat >conftest.$ac_ext <<_ACEOF +#line 2874 "configure" +#include "confdefs.h" +#include +_ACEOF +if { (eval echo "$as_me:2878: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:2884: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # We can compile using X headers with no special include directory. +ac_x_includes= +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + for ac_dir in $ac_x_header_dirs; do + if test -r "$ac_dir/X11/Intrinsic.h"; then + ac_x_includes=$ac_dir + break + fi +done +fi +rm -f conftest.err conftest.$ac_ext +fi # $ac_x_includes = no + +if test "$ac_x_libraries" = no; then + # Check for the libraries. + # See if we find them without any special options. + # Don't add to $LIBS permanently. + ac_save_LIBS=$LIBS + LIBS="-lXt $LIBS" + cat >conftest.$ac_ext <<_ACEOF +#line 2917 "configure" +#include "confdefs.h" +#include +int +main () +{ +XtMalloc (0) + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:2929: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:2932: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:2935: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:2938: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + LIBS=$ac_save_LIBS +# We can link X programs with no special library path. +ac_x_libraries= +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +LIBS=$ac_save_LIBS +for ac_dir in `echo "$ac_x_includes $ac_x_header_dirs" | sed s/include/lib/g` +do + # Don't even attempt the hair of trying to link an X program! + for ac_extension in a so sl; do + if test -r $ac_dir/libXt.$ac_extension; then + ac_x_libraries=$ac_dir + break 2 + fi + done +done +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +fi # $ac_x_libraries = no + +if test "$ac_x_includes" = no || test "$ac_x_libraries" = no; then + # Didn't find X anywhere. Cache the known absence of X. + ac_cv_have_x="have_x=no" +else + # Record where we found X for the cache. + ac_cv_have_x="have_x=yes \ + ac_x_includes=$ac_x_includes ac_x_libraries=$ac_x_libraries" +fi +fi + + fi + eval "$ac_cv_have_x" +fi # $with_x != no + +if test "$have_x" != yes; then + echo "$as_me:2976: result: $have_x" >&5 +echo "${ECHO_T}$have_x" >&6 + no_x=yes +else + # If each of the values was on the command line, it overrides each guess. + test "x$x_includes" = xNONE && x_includes=$ac_x_includes + test "x$x_libraries" = xNONE && x_libraries=$ac_x_libraries + # Update the cache value to reflect the command line values. + ac_cv_have_x="have_x=yes \ + ac_x_includes=$x_includes ac_x_libraries=$x_libraries" + echo "$as_me:2986: result: libraries $x_libraries, headers $x_includes" >&5 +echo "${ECHO_T}libraries $x_libraries, headers $x_includes" >&6 +fi + +if test "$no_x" != "yes" ; then +with_x=yes + +echo "$as_me:2993: checking for XShmCreateImage in -lXext" >&5 +echo $ECHO_N "checking for XShmCreateImage in -lXext... $ECHO_C" >&6 +if test "${ac_cv_lib_Xext_XShmCreateImage+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lXext $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line 3001 "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char XShmCreateImage (); +int +main () +{ +XShmCreateImage (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:3020: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:3023: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:3026: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:3029: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_Xext_XShmCreateImage=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_lib_Xext_XShmCreateImage=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:3040: result: $ac_cv_lib_Xext_XShmCreateImage" >&5 +echo "${ECHO_T}$ac_cv_lib_Xext_XShmCreateImage" >&6 +if test $ac_cv_lib_Xext_XShmCreateImage = yes; then + cat >>confdefs.h <&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 3060 "configure" +#include "confdefs.h" +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:3064: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:3070: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + eval "$as_ac_Header=no" +fi +rm -f conftest.err conftest.$ac_ext +fi +echo "$as_me:3089: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <&5 +echo "${ECHO_T}enabling selected compiler warnings" >&6 +CFLAGS="$CFLAGS -ansi -pedantic -Wall -Wno-implicit -Wno-long-long" ;; +*) +echo "$as_me:3155: result: disabling warnings for non-gcc compiler" >&5 +echo "${ECHO_T}disabling warnings for non-gcc compiler" >&6 ;; +esac +fi + +if test "$enable_debug" = yes ; then +echo "$as_me:3161: result: including debugging symbols" >&5 +echo "${ECHO_T}including debugging symbols" >&6 +CFLAGS="$CFLAGS -g" +fi + +if test "$enable_profile" = yes ; then +echo "$as_me:3167: result: enabling performance profiling" >&5 +echo "${ECHO_T}enabling performance profiling" >&6 +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) ;; +*) { echo "$as_me:3201: WARNING: unknown architecture $enable_arch" >&5 +echo "$as_me: WARNING: unknown architecture $enable_arch" >&2;} ;; +esac ;; + +*) + +case "$enable_arch" in +k6|i686|i586) { echo "$as_me:3208: WARNING: your compiler is too old to support $enable_arch optimizations" >&5 +echo "$as_me: WARNING: your compiler is too old to support $enable_arch optimizations" >&2;} ;; +i486) CFLAGS="$CFLAGS -m486" ;; +i386) CFLAGS="$CFLAGS -m386" ;; +no) ;; +*) { echo "$as_me:3213: WARNING: unknown architecture $enable_arch" >&5 +echo "$as_me: WARNING: unknown architecture $enable_arch" >&2;} ;; +esac ;; + +esac + +case "$enable_optimize" in +yes|full) +echo "$as_me:3221: result: producing heavily optimized code" >&5 +echo "${ECHO_T}producing heavily optimized code" >&6 + +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) + +echo "$as_me:3248: result: using minimal optimizations" >&5 +echo "${ECHO_T}using minimal optimizations" >&6 +CFLAGS="$CFLAGS -O3" ;; + +esac + +if test "$enable_asm" = yes ; then +case `uname -m` in +i?86) +echo "$as_me:3257: result: using optimized i386 cores" >&5 +echo "${ECHO_T}using optimized i386 cores" >&6 +ASM="-DUSE_ASM -I./asm/i386" ; ASM_OBJS="asm/i386/cpu.o asm/i386/lcd.o asm/i386/refresh.s" ;; +*) +echo "$as_me:3261: result: no optimized asm core available for \`uname -m\`" >&5 +echo "${ECHO_T}no optimized asm core available for \`uname -m\`" >&6 ;; +esac +fi + +ac_config_headers="$ac_config_headers sys/nix/config.h" + +ac_config_files="$ac_config_files Makefile" +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overriden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, don't put newlines in cache variables' values. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +{ + (set) 2>&1 | + case `(ac_space=' '; set | grep ac_space) 2>&1` in + *ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} | + sed ' + t clear + : clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + : end' >>confcache +if cmp -s $cache_file confcache; then :; else + if test -w $cache_file; then + test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file" + cat confcache >$cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# VPATH may cause trouble with some makes, so we remove $(srcdir), +# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=/{ +s/:*\$(srcdir):*/:/; +s/:*\${srcdir}:*/:/; +s/:*@srcdir@:*/:/; +s/^\([^=]*=[ ]*\):*/\1/; +s/:*$//; +s/^[^=]*=[ ]*$//; +}' +fi + +DEFS=-DHAVE_CONFIG_H + +: ${CONFIG_STATUS=./config.status} +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ echo "$as_me:3348: creating $CONFIG_STATUS" >&5 +echo "$as_me: creating $CONFIG_STATUS" >&6;} +cat >$CONFIG_STATUS <<_ACEOF +#! $SHELL +# Generated automatically by configure. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +SHELL=\${CONFIG_SHELL-$SHELL} +ac_cs_invocation="\$0 \$@" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi + +# Name of the executable. +as_me=`echo "$0" |sed 's,.*[\\/],,'` + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +as_executable_p="test -f" + +# Support unset when possible. +if (FOO=FOO; unset FOO) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + +# NLS nuisances. +$as_unset LANG || test "${LANG+set}" != set || { LANG=C; export LANG; } +$as_unset LC_ALL || test "${LC_ALL+set}" != set || { LC_ALL=C; export LC_ALL; } +$as_unset LC_TIME || test "${LC_TIME+set}" != set || { LC_TIME=C; export LC_TIME; } +$as_unset LC_CTYPE || test "${LC_CTYPE+set}" != set || { LC_CTYPE=C; export LC_CTYPE; } +$as_unset LANGUAGE || test "${LANGUAGE+set}" != set || { LANGUAGE=C; export LANGUAGE; } +$as_unset LC_COLLATE || test "${LC_COLLATE+set}" != set || { LC_COLLATE=C; export LC_COLLATE; } +$as_unset LC_NUMERIC || test "${LC_NUMERIC+set}" != set || { LC_NUMERIC=C; export LC_NUMERIC; } +$as_unset LC_MESSAGES || test "${LC_MESSAGES+set}" != set || { LC_MESSAGES=C; export LC_MESSAGES; } + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH || test "${CDPATH+set}" != set || { CDPATH=:; export CDPATH; } + +exec 6>&1 + +_ACEOF + +# Files that config.status was made for. +if test -n "$ac_config_files"; then + echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_headers"; then + echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_links"; then + echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_commands"; then + echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS +fi + +cat >>$CONFIG_STATUS <<\EOF + +ac_cs_usage="\ +\`$as_me' instantiates files from templates according to the +current configuration. + +Usage: $0 [OPTIONS] [FILE]... + + -h, --help print this help, then exit + -V, --version print version number, then exit + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Report bugs to ." +EOF + +cat >>$CONFIG_STATUS <>$CONFIG_STATUS <<\EOF +# If no file are specified by the user, then we need to provide default +# value. By we need to know if files were specified by the user. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=*) + ac_option=`expr "x$1" : 'x\([^=]*\)='` + ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'` + shift + set dummy "$ac_option" "$ac_optarg" ${1+"$@"} + shift + ;; + -*);; + *) # This is not an option, so the user has probably given explicit + # arguments. + ac_need_defaults=false;; + esac + + case $1 in + # Handling of the options. +EOF +cat >>$CONFIG_STATUS <>$CONFIG_STATUS <<\EOF + --version | --vers* | -V ) + echo "$ac_cs_version"; exit 0 ;; + --he | --h) + # Conflict between --help and --header + { { echo "$as_me:3521: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; };; + --help | --hel | -h ) + echo "$ac_cs_usage"; exit 0 ;; + --debug | --d* | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + shift + CONFIG_FILES="$CONFIG_FILES $1" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + shift + CONFIG_HEADERS="$CONFIG_HEADERS $1" + ac_need_defaults=false;; + + # This is an error. + -*) { { echo "$as_me:3540: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; } ;; + + *) ac_config_targets="$ac_config_targets $1" ;; + + esac + shift +done + +exec 5>>config.log +cat >&5 << _ACEOF + +## ----------------------- ## +## Running config.status. ## +## ----------------------- ## + +This file was extended by $as_me 2.52, executed with + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + > $ac_cs_invocation +on `(hostname || uname -n) 2>/dev/null | sed 1q` + +_ACEOF +EOF + +cat >>$CONFIG_STATUS <<\EOF +for ac_config_target in $ac_config_targets +do + case "$ac_config_target" in + # Handling of arguments. + "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "sys/nix/config.h" ) CONFIG_HEADERS="$CONFIG_HEADERS sys/nix/config.h" ;; + *) { { echo "$as_me:3577: error: invalid argument: $ac_config_target" >&5 +echo "$as_me: error: invalid argument: $ac_config_target" >&2;} + { (exit 1); exit 1; }; };; + esac +done + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers +fi + +# Create a temporary directory, and hook for its removal unless debugging. +$debug || +{ + trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0 + trap '{ (exit 1); exit 1; }' 1 2 13 15 +} + +# Create a (secure) tmp directory for tmp files. +: ${TMPDIR=/tmp} +{ + tmp=`(umask 077 && mktemp -d -q "$TMPDIR/csXXXXXX") 2>/dev/null` && + test -n "$tmp" && test -d "$tmp" +} || +{ + tmp=$TMPDIR/cs$$-$RANDOM + (umask 077 && mkdir $tmp) +} || +{ + echo "$me: cannot create a temporary directory in $TMPDIR" >&2 + { (exit 1); exit 1; } +} + +EOF + +cat >>$CONFIG_STATUS <\$tmp/subs.sed <<\\CEOF +s,@SHELL@,$SHELL,;t t +s,@exec_prefix@,$exec_prefix,;t t +s,@prefix@,$prefix,;t t +s,@program_transform_name@,$program_transform_name,;t t +s,@bindir@,$bindir,;t t +s,@sbindir@,$sbindir,;t t +s,@libexecdir@,$libexecdir,;t t +s,@datadir@,$datadir,;t t +s,@sysconfdir@,$sysconfdir,;t t +s,@sharedstatedir@,$sharedstatedir,;t t +s,@localstatedir@,$localstatedir,;t t +s,@libdir@,$libdir,;t t +s,@includedir@,$includedir,;t t +s,@oldincludedir@,$oldincludedir,;t t +s,@infodir@,$infodir,;t t +s,@mandir@,$mandir,;t t +s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t +s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t +s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t +s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t +s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t +s,@build_alias@,$build_alias,;t t +s,@host_alias@,$host_alias,;t t +s,@target_alias@,$target_alias,;t t +s,@ECHO_C@,$ECHO_C,;t t +s,@ECHO_N@,$ECHO_N,;t t +s,@ECHO_T@,$ECHO_T,;t t +s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t +s,@DEFS@,$DEFS,;t t +s,@LIBS@,$LIBS,;t t +s,@CC@,$CC,;t t +s,@CFLAGS@,$CFLAGS,;t t +s,@LDFLAGS@,$LDFLAGS,;t t +s,@CPPFLAGS@,$CPPFLAGS,;t t +s,@ac_ct_CC@,$ac_ct_CC,;t t +s,@EXEEXT@,$EXEEXT,;t t +s,@OBJEXT@,$OBJEXT,;t t +s,@CPP@,$CPP,;t t +s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t +s,@INSTALL_SCRIPT@,$INSTALL_SCRIPT,;t t +s,@INSTALL_DATA@,$INSTALL_DATA,;t t +s,@SDL_CONFIG@,$SDL_CONFIG,;t t +s,@SYS_DEFS@,$SYS_DEFS,;t t +s,@ENDIAN@,$ENDIAN,;t t +s,@SOUND@,$SOUND,;t t +s,@JOY@,$JOY,;t t +s,@ASM@,$ASM,;t t +s,@ASM_OBJS@,$ASM_OBJS,;t t +s,@FB_OBJS@,$FB_OBJS,;t t +s,@SDL_CFLAGS@,$SDL_CFLAGS,;t t +s,@SDL_LIBS@,$SDL_LIBS,;t t +s,@TARGETS@,$TARGETS,;t t +s,@XINCS@,$XINCS,;t t +s,@XLIBS@,$XLIBS,;t t +CEOF + +EOF + + cat >>$CONFIG_STATUS <<\EOF + # Split the substitutions into bite-sized pieces for seds with + # small command number limits, like on Digital OSF/1 and HP-UX. + ac_max_sed_lines=48 + ac_sed_frag=1 # Number of current file. + ac_beg=1 # First line for current file. + ac_end=$ac_max_sed_lines # Line after last line for current file. + ac_more_lines=: + ac_sed_cmds= + while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + else + sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + fi + if test ! -s $tmp/subs.frag; then + ac_more_lines=false + else + # The purpose of the label and of the branching condition is to + # speed up the sed processing (if there are no `@' at all, there + # is no need to browse any of the substitutions). + # These are the two extra sed commands mentioned above. + (echo ':t + /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed" + else + ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed" + fi + ac_sed_frag=`expr $ac_sed_frag + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_lines` + fi + done + if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat + fi +fi # test -n "$CONFIG_FILES" + +EOF +cat >>$CONFIG_STATUS <<\EOF +for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case $ac_file in + - | *:- | *:-:* ) # input from stdin + cat >$tmp/stdin + ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + * ) ac_file_in=$ac_file.in ;; + esac + + # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories. + ac_dir=`$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + { case "$ac_dir" in + [\\/]* | ?:[\\/]* ) as_incr_dir=;; + *) as_incr_dir=.;; +esac +as_dummy="$ac_dir" +for as_mkdir_dir in `IFS='/\\'; set X $as_dummy; shift; echo "$@"`; do + case $as_mkdir_dir in + # Skip DOS drivespec + ?:) as_incr_dir=$as_mkdir_dir ;; + *) + as_incr_dir=$as_incr_dir/$as_mkdir_dir + test -d "$as_incr_dir" || mkdir "$as_incr_dir" + ;; + esac +done; } + + ac_dir_suffix="/`echo $ac_dir|sed 's,^\./,,'`" + # A "../" for each directory in $ac_dir_suffix. + ac_dots=`echo "$ac_dir_suffix" | sed 's,/[^/]*,../,g'` + else + ac_dir_suffix= ac_dots= + fi + + case $srcdir in + .) ac_srcdir=. + if test -z "$ac_dots"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_dots | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_dots$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_dots$srcdir ;; + esac + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_dots$INSTALL ;; + esac + + if test x"$ac_file" != x-; then + { echo "$as_me:3796: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + rm -f "$ac_file" + fi + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated automatically by config.status. */ + configure_input="Generated automatically from `echo $ac_file_in | + sed 's,.*/,,'` by configure." + + # First look for the input files in the build tree, otherwise in the + # src tree. + ac_file_inputs=`IFS=: + for f in $ac_file_in; do + case $f in + -) echo $tmp/stdin ;; + [\\/$]*) + # Absolute (can't be DOS-style, as IFS=:) + test -f "$f" || { { echo "$as_me:3814: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + echo $f;; + *) # Relative + if test -f "$f"; then + # Build tree + echo $f + elif test -f "$srcdir/$f"; then + # Source tree + echo $srcdir/$f + else + # /dev/null tree + { { echo "$as_me:3827: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + fi;; + esac + done` || { (exit 1); exit 1; } +EOF +cat >>$CONFIG_STATUS <>$CONFIG_STATUS <<\EOF +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s,@configure_input@,$configure_input,;t t +s,@srcdir@,$ac_srcdir,;t t +s,@top_srcdir@,$ac_top_srcdir,;t t +s,@INSTALL@,$ac_INSTALL,;t t +" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out + rm -f $tmp/stdin + if test x"$ac_file" != x-; then + mv $tmp/out $ac_file + else + cat $tmp/out + rm -f $tmp/out + fi + +done +EOF +cat >>$CONFIG_STATUS <<\EOF + +# +# CONFIG_HEADER section. +# + +# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where +# NAME is the cpp macro being defined and VALUE is the value it is being given. +# +# ac_d sets the value in "#define NAME VALUE" lines. +ac_dA='s,^\([ ]*\)#\([ ]*define[ ][ ]*\)' +ac_dB='[ ].*$,\1#\2' +ac_dC=' ' +ac_dD=',;t' +# ac_u turns "#undef NAME" without trailing blanks into "#define NAME VALUE". +ac_uA='s,^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_uB='$,\1#\2define\3' +ac_uC=' ' +ac_uD=',;t' + +for ac_file in : $CONFIG_HEADERS; do test "x$ac_file" = x: && continue + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case $ac_file in + - | *:- | *:-:* ) # input from stdin + cat >$tmp/stdin + ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + * ) ac_file_in=$ac_file.in ;; + esac + + test x"$ac_file" != x- && { echo "$as_me:3888: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + + # First look for the input files in the build tree, otherwise in the + # src tree. + ac_file_inputs=`IFS=: + for f in $ac_file_in; do + case $f in + -) echo $tmp/stdin ;; + [\\/$]*) + # Absolute (can't be DOS-style, as IFS=:) + test -f "$f" || { { echo "$as_me:3899: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + echo $f;; + *) # Relative + if test -f "$f"; then + # Build tree + echo $f + elif test -f "$srcdir/$f"; then + # Source tree + echo $srcdir/$f + else + # /dev/null tree + { { echo "$as_me:3912: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + fi;; + esac + done` || { (exit 1); exit 1; } + # Remove the trailing spaces. + sed 's/[ ]*$//' $ac_file_inputs >$tmp/in + +EOF + +# Transform confdefs.h into two sed scripts, `conftest.defines' and +# `conftest.undefs', that substitutes the proper values into +# config.h.in to produce config.h. The first handles `#define' +# templates, and the second `#undef' templates. +# And first: Protect against being on the right side of a sed subst in +# config.status. Protect against being in an unquoted here document +# in config.status. +rm -f conftest.defines conftest.undefs +# Using a here document instead of a string reduces the quoting nightmare. +# Putting comments in sed scripts is not portable. +# +# `end' is used to avoid that the second main sed command (meant for +# 0-ary CPP macros) applies to n-ary macro definitions. +# See the Autoconf documentation for `clear'. +cat >confdef2sed.sed <<\EOF +s/[\\&,]/\\&/g +s,[\\$`],\\&,g +t clear +: clear +s,^[ ]*#[ ]*define[ ][ ]*\(\([^ (][^ (]*\)([^)]*)\)[ ]*\(.*\)$,${ac_dA}\2${ac_dB}\1${ac_dC}\3${ac_dD},gp +t end +s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD},gp +: end +EOF +# If some macros were called several times there might be several times +# the same #defines, which is useless. Nevertheless, we may not want to +# sort them, since we want the *last* AC-DEFINE to be honored. +uniq confdefs.h | sed -n -f confdef2sed.sed >conftest.defines +sed 's/ac_d/ac_u/g' conftest.defines >conftest.undefs +rm -f confdef2sed.sed + +# This sed command replaces #undef with comments. This is necessary, for +# example, in the case of _POSIX_SOURCE, which is predefined and required +# on some systems where configure will not decide to define it. +cat >>conftest.undefs <<\EOF +s,^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*,/* & */, +EOF + +# Break up conftest.defines because some shells have a limit on the size +# of here documents, and old seds have small limits too (100 cmds). +echo ' # Handle all the #define templates only if necessary.' >>$CONFIG_STATUS +echo ' if egrep "^[ ]*#[ ]*define" $tmp/in >/dev/null; then' >>$CONFIG_STATUS +echo ' # If there are no defines, we may have an empty if/fi' >>$CONFIG_STATUS +echo ' :' >>$CONFIG_STATUS +rm -f conftest.tail +while grep . conftest.defines >/dev/null +do + # Write a limited-size here document to $tmp/defines.sed. + echo ' cat >$tmp/defines.sed <>$CONFIG_STATUS + # Speed up: don't consider the non `#define' lines. + echo '/^[ ]*#[ ]*define/!b' >>$CONFIG_STATUS + # Work around the forget-to-reset-the-flag bug. + echo 't clr' >>$CONFIG_STATUS + echo ': clr' >>$CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.defines >>$CONFIG_STATUS + echo 'CEOF + sed -f $tmp/defines.sed $tmp/in >$tmp/out + rm -f $tmp/in + mv $tmp/out $tmp/in +' >>$CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.defines >conftest.tail + rm -f conftest.defines + mv conftest.tail conftest.defines +done +rm -f conftest.defines +echo ' fi # egrep' >>$CONFIG_STATUS +echo >>$CONFIG_STATUS + +# Break up conftest.undefs because some shells have a limit on the size +# of here documents, and old seds have small limits too (100 cmds). +echo ' # Handle all the #undef templates' >>$CONFIG_STATUS +rm -f conftest.tail +while grep . conftest.undefs >/dev/null +do + # Write a limited-size here document to $tmp/undefs.sed. + echo ' cat >$tmp/undefs.sed <>$CONFIG_STATUS + # Speed up: don't consider the non `#undef' + echo '/^[ ]*#[ ]*undef/!b' >>$CONFIG_STATUS + # Work around the forget-to-reset-the-flag bug. + echo 't clr' >>$CONFIG_STATUS + echo ': clr' >>$CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.undefs >>$CONFIG_STATUS + echo 'CEOF + sed -f $tmp/undefs.sed $tmp/in >$tmp/out + rm -f $tmp/in + mv $tmp/out $tmp/in +' >>$CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.undefs >conftest.tail + rm -f conftest.undefs + mv conftest.tail conftest.undefs +done +rm -f conftest.undefs + +cat >>$CONFIG_STATUS <<\EOF + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated automatically by config.status. */ + if test x"$ac_file" = x-; then + echo "/* Generated automatically by configure. */" >$tmp/config.h + else + echo "/* $ac_file. Generated automatically by configure. */" >$tmp/config.h + fi + cat $tmp/in >>$tmp/config.h + rm -f $tmp/in + if test x"$ac_file" != x-; then + if cmp -s $ac_file $tmp/config.h 2>/dev/null; then + { echo "$as_me:4029: $ac_file is unchanged" >&5 +echo "$as_me: $ac_file is unchanged" >&6;} + else + ac_dir=`$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + { case "$ac_dir" in + [\\/]* | ?:[\\/]* ) as_incr_dir=;; + *) as_incr_dir=.;; +esac +as_dummy="$ac_dir" +for as_mkdir_dir in `IFS='/\\'; set X $as_dummy; shift; echo "$@"`; do + case $as_mkdir_dir in + # Skip DOS drivespec + ?:) as_incr_dir=$as_mkdir_dir ;; + *) + as_incr_dir=$as_incr_dir/$as_mkdir_dir + test -d "$as_incr_dir" || mkdir "$as_incr_dir" + ;; + esac +done; } + + fi + rm -f $ac_file + mv $tmp/config.h $ac_file + fi + else + cat $tmp/config.h + rm -f $tmp/config.h + fi +done +EOF + +cat >>$CONFIG_STATUS <<\EOF + +{ (exit 0); exit 0; } +EOF +chmod +x $CONFIG_STATUS +ac_clean_files=$ac_clean_files_save + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + exec 5>/dev/null + $SHELL $CONFIG_STATUS || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || { (exit 1); exit 1; } +fi + diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..f42907a --- /dev/null +++ b/configure.in @@ -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) + + + diff --git a/cpu.c b/cpu.c new file mode 100644 index 0000000..8dd5559 --- /dev/null +++ b/cpu.c @@ -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<= 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; + 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 */ + + + + + + + + + + + + diff --git a/cpu.h b/cpu.h new file mode 100644 index 0000000..085181c --- /dev/null +++ b/cpu.h @@ -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 + + diff --git a/cpucore.h b/cpucore.h new file mode 100644 index 0000000..934bd1e --- /dev/null +++ b/cpucore.h @@ -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, +}; + + + + + + + + + + + + diff --git a/cpuregs.h b/cpuregs.h new file mode 100644 index 0000000..0d09908 --- /dev/null +++ b/cpuregs.h @@ -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__ */ + + diff --git a/debug.c b/debug.c new file mode 100644 index 0000000..2569891 --- /dev/null +++ b/debug.c @@ -0,0 +1,689 @@ + + +#include +#include + +#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--; + } +} + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/defs.h b/defs.h new file mode 100644 index 0000000..ddbeae3 --- /dev/null +++ b/defs.h @@ -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 + diff --git a/docs/CHANGES b/docs/CHANGES new file mode 100644 index 0000000..2d934c1 --- /dev/null +++ b/docs/CHANGES @@ -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 + + diff --git a/docs/CONFIG b/docs/CONFIG new file mode 100644 index 0000000..a25a941 --- /dev/null +++ b/docs/CONFIG @@ -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 diff --git a/docs/CREDITS b/docs/CREDITS new file mode 100644 index 0000000..cbabf76 --- /dev/null +++ b/docs/CREDITS @@ -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 + diff --git a/docs/FAQ b/docs/FAQ new file mode 100644 index 0000000..479121d --- /dev/null +++ b/docs/FAQ @@ -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. + + + diff --git a/docs/HACKING b/docs/HACKING new file mode 100644 index 0000000..3efd85e --- /dev/null +++ b/docs/HACKING @@ -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! + + + + + + + + + + + + + diff --git a/docs/LIBERTY b/docs/LIBERTY new file mode 100644 index 0000000..07b6ee3 --- /dev/null +++ b/docs/LIBERTY @@ -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. + diff --git a/docs/README.old b/docs/README.old new file mode 100644 index 0000000..7e25655 --- /dev/null +++ b/docs/README.old @@ -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 + + + + + + diff --git a/docs/WHATSNEW b/docs/WHATSNEW new file mode 100644 index 0000000..75e964e --- /dev/null +++ b/docs/WHATSNEW @@ -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. + + + + + + diff --git a/emu.c b/emu.c new file mode 100644 index 0000000..544526d --- /dev/null +++ b/emu.c @@ -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(); + } +} + + + + + + + + + + + + diff --git a/etc/classic.rc b/etc/classic.rc new file mode 100644 index 0000000..f9a9618 --- /dev/null +++ b/etc/classic.rc @@ -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 diff --git a/etc/filters.rc b/etc/filters.rc new file mode 100644 index 0000000..5fbdc35 --- /dev/null +++ b/etc/filters.rc @@ -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 + diff --git a/etc/laguna.pgp b/etc/laguna.pgp new file mode 100644 index 0000000..b6bb9d3 --- /dev/null +++ b/etc/laguna.pgp @@ -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----- diff --git a/etc/palettes.rc b/etc/palettes.rc new file mode 100644 index 0000000..c77d925 --- /dev/null +++ b/etc/palettes.rc @@ -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 + + + + + + + + diff --git a/etc/sample.rc b/etc/sample.rc new file mode 100644 index 0000000..8cbd699 --- /dev/null +++ b/etc/sample.rc @@ -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. +# + diff --git a/events.c b/events.c new file mode 100644 index 0000000..c6ee954 --- /dev/null +++ b/events.c @@ -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; +} + + + + + + + + diff --git a/exports.c b/exports.c new file mode 100644 index 0000000..d958143 --- /dev/null +++ b/exports.c @@ -0,0 +1,43 @@ + + +#include + +#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); +} diff --git a/fastmem.c b/fastmem.c new file mode 100644 index 0000000..531ead7 --- /dev/null +++ b/fastmem.c @@ -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() +{ +} + diff --git a/fastmem.h b/fastmem.h new file mode 100644 index 0000000..33c93e4 --- /dev/null +++ b/fastmem.h @@ -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 diff --git a/fb.h b/fb.h new file mode 100644 index 0000000..97d16ae --- /dev/null +++ b/fb.h @@ -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 + + + + diff --git a/hw.c b/hw.c new file mode 100644 index 0000000..b507f4d --- /dev/null +++ b/hw.c @@ -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; +} + + + + + + + diff --git a/hw.h b/hw.h new file mode 100644 index 0000000..f425efc --- /dev/null +++ b/hw.h @@ -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 + + diff --git a/inflate.c b/inflate.c new file mode 100644 index 0000000..5079f49 --- /dev/null +++ b/inflate.c @@ -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 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 ) + lb = q-cnt; + answer |= ((unsigned int)((data[(p+cnt)/8]>>ob)&((1U<>(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= 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 +#include + +#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; +} + + + + + + + diff --git a/lcd.c b/lcd.c new file mode 100644 index 0000000..71d138e --- /dev/null +++ b/lcd.c @@ -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<> 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].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(); +} + + + + + + + + + + + + + + + + diff --git a/lcd.h b/lcd.h new file mode 100644 index 0000000..b062534 --- /dev/null +++ b/lcd.h @@ -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 + + + diff --git a/lcdc.c b/lcdc.c new file mode 100644 index 0000000..b0baa10 --- /dev/null +++ b/lcdc.c @@ -0,0 +1,179 @@ + + +#include + +#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; + } + } +} + + + + + + + diff --git a/loader.c b/loader.c new file mode 100644 index 0000000..b884048 --- /dev/null +++ b/loader.c @@ -0,0 +1,395 @@ + + +#include "defs.h" +#include "regs.h" +#include "mem.h" +#include "hw.h" +#include "rtc.h" +#include "rc.h" + +#include +#include +#include + +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 +}; + + + + + + + + + diff --git a/loader.h b/loader.h new file mode 100644 index 0000000..fd68f44 --- /dev/null +++ b/loader.h @@ -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 + + diff --git a/main.c b/main.c new file mode 100644 index 0000000..3c2c84c --- /dev/null +++ b/main.c @@ -0,0 +1,323 @@ + + + + + + +#include +#include +#include + +char *strdup(); + +#include +#include + +#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; +} + + + + + + + + + + + diff --git a/mem.c b/mem.c new file mode 100644 index 0000000..edcb4f1 --- /dev/null +++ b/mem.c @@ -0,0 +1,576 @@ + + +#include + +#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(); +} + + + + + + + diff --git a/mem.h b/mem.h new file mode 100644 index 0000000..29caa68 --- /dev/null +++ b/mem.h @@ -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 + + + diff --git a/newsound.c b/newsound.c new file mode 100644 index 0000000..0ae04da --- /dev/null +++ b/newsound.c @@ -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; + + } +} + + + + + + + + diff --git a/noise.h b/noise.h new file mode 100644 index 0000000..037499e --- /dev/null +++ b/noise.h @@ -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 + diff --git a/palette.c b/palette.c new file mode 100644 index 0000000..7a335f8 --- /dev/null +++ b/palette.c @@ -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); +} + + + + + diff --git a/path.c b/path.c new file mode 100644 index 0000000..13a87bd --- /dev/null +++ b/path.c @@ -0,0 +1,52 @@ + + + +#include +#include +#include + +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; +} + + + + + + + diff --git a/pcm.h b/pcm.h new file mode 100644 index 0000000..3719933 --- /dev/null +++ b/pcm.h @@ -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 + + diff --git a/rc.h b/rc.h new file mode 100644 index 0000000..7c2be30 --- /dev/null +++ b/rc.h @@ -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 + + + + diff --git a/rccmds.c b/rccmds.c new file mode 100644 index 0000000..2ea68cf --- /dev/null +++ b/rccmds.c @@ -0,0 +1,194 @@ + + + + +#include + +#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; +} + + + + + + + + + + + + + + + + + + + + + + + diff --git a/rcfile.c b/rcfile.c new file mode 100644 index 0000000..9c7d1a3 --- /dev/null +++ b/rcfile.c @@ -0,0 +1,46 @@ + + + +#include +#include +#include + +#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 +}; + + diff --git a/rckeys.c b/rckeys.c new file mode 100644 index 0000000..8dc2eba --- /dev/null +++ b/rckeys.c @@ -0,0 +1,86 @@ + + + +#include +#include + +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] = '+'; + } +} + + + + diff --git a/rcvars.c b/rcvars.c new file mode 100644 index 0000000..8364e89 --- /dev/null +++ b/rcvars.c @@ -0,0 +1,235 @@ + + + + +#include +#include + +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)); +} + + + + + + + + diff --git a/refresh.c b/refresh.c new file mode 100644 index 0000000..ef932e7 --- /dev/null +++ b/refresh.c @@ -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 + diff --git a/regs.h b/regs.h new file mode 100644 index 0000000..4457fd9 --- /dev/null +++ b/regs.h @@ -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 + + + + + diff --git a/rtc.c b/rtc.c new file mode 100644 index 0000000..c3f96ec --- /dev/null +++ b/rtc.c @@ -0,0 +1,130 @@ + + + +#include + +#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(); +} + + + + + + + + + + + + diff --git a/rtc.h b/rtc.h new file mode 100644 index 0000000..76f5017 --- /dev/null +++ b/rtc.h @@ -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 + + + diff --git a/save.c b/save.c new file mode 100644 index 0000000..1dc7905 --- /dev/null +++ b/save.c @@ -0,0 +1,286 @@ + + +#include + +#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); +} + + + + + + + + + + + + + + + + + + + diff --git a/sound.c b/sound.c new file mode 100644 index 0000000..6e2545a --- /dev/null +++ b/sound.c @@ -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; + } +} + + + + diff --git a/sound.h b/sound.h new file mode 100644 index 0000000..2056f80 --- /dev/null +++ b/sound.h @@ -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 + + diff --git a/split.c b/split.c new file mode 100644 index 0000000..af53d73 --- /dev/null +++ b/split.c @@ -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; +} + + + + + diff --git a/sys/dos/dos.c b/sys/dos/dos.c new file mode 100644 index 0000000..d6b3179 --- /dev/null +++ b/sys/dos/dos.c @@ -0,0 +1,85 @@ +/* + * dos.c + * + * System interface for DOS. + */ + +#include +#include +#include +char *strdup(); +#include +#include +#include +#include +#include + +#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] = '/'; +} + + + + + + diff --git a/sys/dummy/nojoy.c b/sys/dummy/nojoy.c new file mode 100644 index 0000000..1252194 --- /dev/null +++ b/sys/dummy/nojoy.c @@ -0,0 +1,22 @@ + + +#include "rc.h" + +rcvar_t joy_exports[] = +{ + RCV_END +}; + +void joy_init() +{ +} + +void joy_close() +{ +} + +void joy_poll() +{ +} + + diff --git a/sys/dummy/nosound.c b/sys/dummy/nosound.c new file mode 100644 index 0000000..c899204 --- /dev/null +++ b/sys/dummy/nosound.c @@ -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; +} + + + + + diff --git a/sys/linux/fbdev.c b/sys/linux/fbdev.c new file mode 100644 index 0000000..5fcd1a8 --- /dev/null +++ b/sys/linux/fbdev.c @@ -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 +#include +char *strdup(); +#include +#include +#include +#include +#include + +#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 = ≫ + cmap.blue = &bb; + ioctl(fbfd, FBIOPUTCMAP, &cmap); +} + +void vid_begin() +{ + overlay_switch(); +} + +void vid_end() +{ + overlay_switch(); +} + +void ev_poll() +{ + kb_poll(); + joy_poll(); +} + + + diff --git a/sys/linux/joy.c b/sys/linux/joy.c new file mode 100644 index 0000000..eacb2a1 --- /dev/null +++ b/sys/linux/joy.c @@ -0,0 +1,93 @@ + +#include +#include +#include +char *strdup(); +#include +#include +#include +#include +#include + +#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; + } + } +} diff --git a/sys/linux/kb.c b/sys/linux/kb.c new file mode 100644 index 0000000..70661b9 --- /dev/null +++ b/sys/linux/kb.c @@ -0,0 +1,135 @@ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); + } +} + diff --git a/sys/linux/matrox.h b/sys/linux/matrox.h new file mode 100644 index 0000000..a1d8911 --- /dev/null +++ b/sys/linux/matrox.h @@ -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 + + + diff --git a/sys/nix/config.h.in b/sys/nix/config.h.in new file mode 100644 index 0000000..e528ae6 --- /dev/null +++ b/sys/nix/config.h.in @@ -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 + + + diff --git a/sys/nix/map.c b/sys/nix/map.c new file mode 100644 index 0000000..1d63e2b --- /dev/null +++ b/sys/nix/map.c @@ -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 +#include +#include +#include + + +#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; +} + + + + + + + + + + diff --git a/sys/nix/nix.c b/sys/nix/nix.c new file mode 100644 index 0000000..dbb024c --- /dev/null +++ b/sys/nix/nix.c @@ -0,0 +1,104 @@ +/* + * nix.c + * + * System interface for *nix systems. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#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) +{ +} + + + + diff --git a/sys/oss/oss.c b/sys/oss/oss.c new file mode 100644 index 0000000..b8e4d6a --- /dev/null +++ b/sys/oss/oss.c @@ -0,0 +1,104 @@ + + +#include +#include +char *strdup(); +#include +#include +#include + +#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 +#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; +} + + + + + + diff --git a/sys/pc/keymap.c b/sys/pc/keymap.c new file mode 100644 index 0000000..7fe201c --- /dev/null +++ b/sys/pc/keymap.c @@ -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 } +}; + + + + + + + + + diff --git a/sys/sdl/keymap.c b/sys/sdl/keymap.c new file mode 100644 index 0000000..c5c049b --- /dev/null +++ b/sys/sdl/keymap.c @@ -0,0 +1,87 @@ +/* + * sdl_keymap.c + * + * Mappings from SDL keycode to local key codes. + * Stolen from xkeymap.c + * + */ + +#include +#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 } +}; + diff --git a/sys/sdl/sdl.c b/sys/sdl/sdl.c new file mode 100644 index 0000000..2350ebd --- /dev/null +++ b/sys/sdl/sdl.c @@ -0,0 +1,510 @@ +/* + * sdl.c + * sdl interfaces -- based on svga.c + * + * (C) 2001 Damian Gryski + * Joystick code contributed by David Lau + * Sound code added by Laguna + * + * Licensed under the GPLv2, or later. + */ + +#include +#include + +#include + + +#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(); +} + + + + + + diff --git a/sys/svga/svgalib.c b/sys/svga/svgalib.c new file mode 100644 index 0000000..3da1f01 --- /dev/null +++ b/sys/svga/svgalib.c @@ -0,0 +1,246 @@ +/* + * svgalib.c + * + * svgalib interface. + */ + + +#include +#include + +#include +#include + +#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(); +} + + + + + + + + + + + + + + diff --git a/sys/thinlib/keymap.c b/sys/thinlib/keymap.c new file mode 100644 index 0000000..0ad1870 --- /dev/null +++ b/sys/thinlib/keymap.c @@ -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 } +}; + + + + + + + + + diff --git a/sys/thinlib/lib/Makefile b/sys/thinlib/lib/Makefile new file mode 100644 index 0000000..8c2f994 --- /dev/null +++ b/sys/thinlib/lib/Makefile @@ -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: $ diff --git a/sys/thinlib/lib/thinlib.h b/sys/thinlib/lib/thinlib.h new file mode 100644 index 0000000..a33b617 --- /dev/null +++ b/sys/thinlib/lib/thinlib.h @@ -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: $ +*/ diff --git a/sys/thinlib/lib/thintest.cpp b/sys/thinlib/lib/thintest.cpp new file mode 100644 index 0000000..f8d414c --- /dev/null +++ b/sys/thinlib/lib/thintest.cpp @@ -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 +#include +#include +#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(¶ms)) + 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 +** +*/ diff --git a/sys/thinlib/lib/tl_bmp.c b/sys/thinlib/lib/tl_bmp.c new file mode 100644 index 0000000..536f725 --- /dev/null +++ b/sys/thinlib/lib/tl_bmp.c @@ -0,0 +1,99 @@ +/* +** thinlib (c) 2001 Matthew Conte (matt@conte.com) +** +** +** tl_bmp.c +** +** Bitmap object manipulation routines +** +** $Id: $ +*/ + +#include +#include +#include + +#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: $ +*/ diff --git a/sys/thinlib/lib/tl_bmp.h b/sys/thinlib/lib/tl_bmp.h new file mode 100644 index 0000000..ec2467c --- /dev/null +++ b/sys/thinlib/lib/tl_bmp.h @@ -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: $ +*/ diff --git a/sys/thinlib/lib/tl_djgpp.h b/sys/thinlib/lib/tl_djgpp.h new file mode 100644 index 0000000..7aa914c --- /dev/null +++ b/sys/thinlib/lib/tl_djgpp.h @@ -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 + +/* 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 +#define THIN_PHYSICAL_ADDR(x) ((x) + __djgpp_conventional_base) + +extern int thinlib_nearptr; + +#endif /* !_TL_DJGPP_H_ */ + +/* +** $Log: $ +*/ diff --git a/sys/thinlib/lib/tl_dpp.c b/sys/thinlib/lib/tl_dpp.c new file mode 100644 index 0000000..f9cacf2 --- /dev/null +++ b/sys/thinlib/lib/tl_dpp.c @@ -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 + +#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: $ +*/ diff --git a/sys/thinlib/lib/tl_dpp.h b/sys/thinlib/lib/tl_dpp.h new file mode 100644 index 0000000..97cebdc --- /dev/null +++ b/sys/thinlib/lib/tl_dpp.h @@ -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: $ +*/ diff --git a/sys/thinlib/lib/tl_event.c b/sys/thinlib/lib/tl_event.c new file mode 100644 index 0000000..5590b32 --- /dev/null +++ b/sys/thinlib/lib/tl_event.c @@ -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: $ +*/ diff --git a/sys/thinlib/lib/tl_event.h b/sys/thinlib/lib/tl_event.h new file mode 100644 index 0000000..432f7e7 --- /dev/null +++ b/sys/thinlib/lib/tl_event.h @@ -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: $ +*/ diff --git a/sys/thinlib/lib/tl_int.c b/sys/thinlib/lib/tl_int.c new file mode 100644 index 0000000..cfa771f --- /dev/null +++ b/sys/thinlib/lib/tl_int.c @@ -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 +#include +#include +#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: $ +*/ diff --git a/sys/thinlib/lib/tl_int.h b/sys/thinlib/lib/tl_int.h new file mode 100644 index 0000000..fa4d6e0 --- /dev/null +++ b/sys/thinlib/lib/tl_int.h @@ -0,0 +1,31 @@ +/* +** thinlib (c) 2001 Matthew Conte (matt@conte.com) +** +** +** tl_int.h +** +** interrupt handling stuff +** +** $Id: $ +*/ + +#ifndef _TL_INT_H_ +#define _TL_INT_H_ + +#define THIN_DISABLE_INTS() __asm__ __volatile__ ("cli") +#define THIN_ENABLE_INTS() __asm__ __volatile__ ("sti") + +typedef int (*inthandler_t)(void); + +extern int thin_int_install(int chan, inthandler_t handler); +extern void thin_int_remove(int chan); + +extern void thin_irq_restore(int irq); +extern void thin_irq_enable(int irq); +extern void thin_irq_disable(int irq); + +#endif /* !_TL_INT_H_ */ + +/* +** $Log: $ +*/ diff --git a/sys/thinlib/lib/tl_joy.c b/sys/thinlib/lib/tl_joy.c new file mode 100644 index 0000000..ba220e4 --- /dev/null +++ b/sys/thinlib/lib/tl_joy.c @@ -0,0 +1,204 @@ +/* +** thinlib (c) 2001 Matthew Conte (matt@conte.com) +** +** +** tl_joy.c +** +** DOS joystick reading routines +** +** $Id: $ +*/ + +#include + +#include "tl_types.h" +#include "tl_int.h" +#include "tl_joy.h" +#include "tl_event.h" + +#define JOY_PORT 0x201 +#define JOY_TIMEOUT 10000 + +#define J1_A 0x10 +#define J1_B 0x20 +#define J2_A 0x40 +#define J2_B 0x80 + +#define J1_X 0x01 +#define J1_Y 0x02 +#define J2_X 0x04 +#define J2_Y 0x08 + +#define JOY_CENTER 0xAA +#define JOY_MIN_THRESH 0.7 +#define JOY_MAX_THRESH 1.3 + +static struct joyinfo_s +{ + int id; /* callback ID */ + int x_minthresh, x_maxthresh; + int y_minthresh, y_maxthresh; + int x_read, y_read; + uint8 button_state; + bool disconnected; +} joyinfo; + +static joy_t joystick; + +/* Read data in from joy port */ +static int _portread(void) +{ + /* Set timeout to max number of samples */ + int timeout = JOY_TIMEOUT; + uint8 port_val; + + joyinfo.x_read = 0; + joyinfo.y_read = 0; + + THIN_DISABLE_INTS(); + + /* Clear the latch and request a sample */ + port_val = inportb(JOY_PORT); + outportb(JOY_PORT, port_val); + + do + { + port_val = inportb(JOY_PORT); + if (port_val & J1_X) + joyinfo.x_read++; + if (port_val & J1_Y) + joyinfo.y_read++; + } + while (--timeout && (port_val & 3)); + + joyinfo.button_state = port_val; + + THIN_ENABLE_INTS(); + + if (0 == timeout) + return -1; + else + return 0; +} + +void _poll_joystick(void) +{ + int i; + joy_t old; + thin_event_t event; + + old = joystick; + + if (_portread()) + { + joyinfo.disconnected = true; + return; + } + + /* Calc X axis */ + joystick.left = (joyinfo.x_read < joyinfo.x_minthresh) ? true : false; + joystick.right = (joyinfo.x_read > joyinfo.x_maxthresh) ? true : false; + + /* Calc Y axis */ + joystick.up = (joyinfo.y_read < joyinfo.y_minthresh) ? true : false; + joystick.down = (joyinfo.y_read > joyinfo.y_maxthresh) ? true : false; + + /* Get button status */ + /* note that buttons returned by hardware are inverted logic */ + joystick.button[0] = (joyinfo.button_state & J1_A) ? false : true; + joystick.button[1] = (joyinfo.button_state & J2_A) ? false : true; + joystick.button[2] = (joyinfo.button_state & J1_B) ? false : true; + joystick.button[3] = (joyinfo.button_state & J2_B) ? false : true; + + /* generate some events if necessary */ + if (joystick.left != old.left) + { + event.type = THIN_JOY_MOTION; + event.data.joy_motion.dir = THIN_JOY_LEFT; + event.data.joy_motion.state = joystick.left; + thin_event_add(&event); + } + + if (joystick.right != old.right) + { + event.type = THIN_JOY_MOTION; + event.data.joy_motion.dir = THIN_JOY_RIGHT; + event.data.joy_motion.state = joystick.right; + thin_event_add(&event); + } + + if (joystick.up != old.up) + { + event.type = THIN_JOY_MOTION; + event.data.joy_motion.dir = THIN_JOY_UP; + event.data.joy_motion.state = joystick.up; + thin_event_add(&event); + } + + if (joystick.down != old.down) + { + event.type = THIN_JOY_MOTION; + event.data.joy_motion.dir = THIN_JOY_DOWN; + event.data.joy_motion.state = joystick.down; + thin_event_add(&event); + } + + for (i = 0; i < JOY_MAX_BUTTONS; i++) + { + if (joystick.button[i] != old.button[i]) + { + event.type = joystick.button[i] ? THIN_JOY_BUTTON_PRESS : THIN_JOY_BUTTON_RELEASE; + event.data.joy_button = i; + + thin_event_add(&event); + } + } +} + +int thin_joy_read(joy_t *joy) +{ + if (joyinfo.disconnected) + return -1; + + *joy = joystick; + + return 0; +} + +/* Detect presence of joystick */ +int thin_joy_init(void) +{ + joyinfo.disconnected = true; + + if (_portread()) + return -1; + + joyinfo.id = thin_event_add_callback((event_callback_t) _poll_joystick); + if (-1 == joyinfo.id) + return -1; + + joyinfo.disconnected = false; + + /* Set the threshhold */ + joyinfo.x_minthresh = JOY_MIN_THRESH * joyinfo.x_read; + joyinfo.x_maxthresh = JOY_MAX_THRESH * joyinfo.x_read; + joyinfo.y_minthresh = JOY_MIN_THRESH * joyinfo.y_read; + joyinfo.y_maxthresh = JOY_MAX_THRESH * joyinfo.y_read; + + return 0; +} + +void thin_joy_shutdown(void) +{ + joyinfo.disconnected = true; + + if (-1 != joyinfo.id) + { + thin_event_remove_callback(joyinfo.id); + joyinfo.id = -1; + } +} + +/* +** $Log: $ +*/ diff --git a/sys/thinlib/lib/tl_joy.h b/sys/thinlib/lib/tl_joy.h new file mode 100644 index 0000000..e0656f8 --- /dev/null +++ b/sys/thinlib/lib/tl_joy.h @@ -0,0 +1,31 @@ +/* +** thinlib (c) 2001 Matthew Conte (matt@conte.com) +** +** +** tl_joy.h +** +** DOS joystick reading defines / protos +** +** $Id: $ +*/ + +#ifndef _TL_JOY_H_ +#define _TL_JOY_H_ + +#define JOY_MAX_BUTTONS 4 + +typedef struct joy_s +{ + int left, right, up, down; + int button[JOY_MAX_BUTTONS]; +} joy_t; + +extern void thin_joy_shutdown(void); +extern int thin_joy_init(void); +extern int thin_joy_read(joy_t *joy); + +#endif /* !_TL_JOY_H_ */ + +/* +** $Log: $ +*/ diff --git a/sys/thinlib/lib/tl_key.c b/sys/thinlib/lib/tl_key.c new file mode 100644 index 0000000..12b89d0 --- /dev/null +++ b/sys/thinlib/lib/tl_key.c @@ -0,0 +1,125 @@ +/* +** thinlib (c) 2001 Matthew Conte (matt@conte.com) +** +** +** tl_key.c +** +** DOS keyboard handler +** +** $Id: $ +*/ + +#include +#include +#include +#include + +#include "tl_types.h" +#include "tl_djgpp.h" +#include "tl_key.h" +#include "tl_event.h" + +#define KEYBOARD_INT 0x09 + +/* maybe make this globally accessible? */ +static int key_status[THIN_MAX_KEYS]; +static bool key_repeat = false; +static bool ext_key = false; + +static _go32_dpmi_seginfo old_key_handler; +static _go32_dpmi_seginfo new_key_handler; + + +static const uint8 ext_tab[0x80] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 - 0x07 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x08 - 0x0F */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x17 */ + 0, 0, 0, 0, THIN_KEY_NUMPAD_ENTER, THIN_KEY_RIGHT_CTRL, 0, 0, /* 0x10 - 0x1F */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 - 0x27 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x28 - 0x2F */ + 0, 0, 0, 0, 0, THIN_KEY_NUMPAD_DIV, 0, THIN_KEY_SYSRQ, /* 0x30 - 0x37 */ + THIN_KEY_RIGHT_ALT, 0, 0, 0, 0, 0, 0, 0, /* 0x38 - 0x3F */ + 0, 0, 0, 0, 0, 0, THIN_KEY_BREAK, THIN_KEY_HOME, /* 0x40 - 0x47 */ + THIN_KEY_UP, THIN_KEY_PGUP, 0, THIN_KEY_LEFT, 0, THIN_KEY_RIGHT, 0, THIN_KEY_END, /* 0x48 - 0x4F */ + THIN_KEY_DOWN, THIN_KEY_PGDN, THIN_KEY_INSERT, THIN_KEY_DELETE, /* 0x50 - 0x57 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x58 - 0x5F */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60 - 0x67 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x68 - 0x6F */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 - 0x77 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x78 - 0x7F */ +}; + +/* keyboard ISR */ +static void key_handler(void) +{ + thin_event_t event; + uint8 raw_code, ack_code; + + /* read the key */ + raw_code = inportb(0x60); + ack_code = inportb(0x61); + outportb(0x61, ack_code | 0x80); + outportb(0x61, ack_code); + outportb(0x20, 0x20); + + if (0xE0 == raw_code) + { + ext_key = true; + } + else + { + if (ext_key) + { + ext_key = false; + event.data.keysym = ext_tab[raw_code & 0x7F]; + } + else + { + event.data.keysym = raw_code & 0x7F; + } + + event.type = (raw_code & 0x80) ? THIN_KEY_RELEASE : THIN_KEY_PRESS; + + if (key_repeat || (event.type != key_status[event.data.keysym])) + { + key_status[event.data.keysym] = event.type; + thin_event_add(&event); + } + } +} +THIN_LOCKED_STATIC_FUNC(key_handler) + +void thin_key_set_repeat(bool state) +{ + key_repeat = state; +} + +/* set up variables, lock code/data, set the new handler and save old one */ +int thin_key_init(void) +{ + THIN_LOCK_FUNC(key_handler); + THIN_LOCK_VAR(key_status); + THIN_LOCK_VAR(ext_key); + + _go32_dpmi_get_protected_mode_interrupt_vector(KEYBOARD_INT, &old_key_handler); + new_key_handler.pm_offset = (uint32) key_handler; + new_key_handler.pm_selector = _go32_my_cs(); + _go32_dpmi_allocate_iret_wrapper(&new_key_handler); + _go32_dpmi_set_protected_mode_interrupt_vector(KEYBOARD_INT, &new_key_handler); + + memset(key_status, THIN_KEY_RELEASE, sizeof(key_status)); + + return 0; /* can't fail */ +} + +/* restore old keyboard handler */ +void thin_key_shutdown(void) +{ + _go32_dpmi_set_protected_mode_interrupt_vector(KEYBOARD_INT, &old_key_handler); + _go32_dpmi_free_iret_wrapper(&new_key_handler); +} + +/* +** $Log: $ +*/ diff --git a/sys/thinlib/lib/tl_key.h b/sys/thinlib/lib/tl_key.h new file mode 100644 index 0000000..ab34032 --- /dev/null +++ b/sys/thinlib/lib/tl_key.h @@ -0,0 +1,146 @@ +/* +** thinlib (c) 2001 Matthew Conte (matt@conte.com) +** +** +** tl_key.h +** +** DOS keyboard handling defines / protos +** +** $Id: $ +*/ + +#ifndef _TL_KEY_H_ +#define _TL_KEY_H_ + +/* Keyboard scancodes */ +#define THIN_KEY_ESC 1 +#define THIN_KEY_1 2 +#define THIN_KEY_2 3 +#define THIN_KEY_3 4 +#define THIN_KEY_4 5 +#define THIN_KEY_5 6 +#define THIN_KEY_6 7 +#define THIN_KEY_7 8 +#define THIN_KEY_8 9 +#define THIN_KEY_9 10 +#define THIN_KEY_0 11 +#define THIN_KEY_MINUS 12 +#define THIN_KEY_EQUALS 13 +#define THIN_KEY_BACKSPACE 14 +#define THIN_KEY_TAB 15 +#define THIN_KEY_Q 16 +#define THIN_KEY_W 17 +#define THIN_KEY_E 18 +#define THIN_KEY_R 19 +#define THIN_KEY_T 20 +#define THIN_KEY_Y 21 +#define THIN_KEY_U 22 +#define THIN_KEY_I 23 +#define THIN_KEY_O 24 +#define THIN_KEY_P 25 +#define THIN_KEY_OPEN_BRACE 26 +#define THIN_KEY_CLOSE_BRACE 27 +#define THIN_KEY_ENTER 28 +#define THIN_KEY_LEFT_CTRL 29 +#define THIN_KEY_A 30 +#define THIN_KEY_S 31 +#define THIN_KEY_D 32 +#define THIN_KEY_F 33 +#define THIN_KEY_G 34 +#define THIN_KEY_H 35 +#define THIN_KEY_J 36 +#define THIN_KEY_K 37 +#define THIN_KEY_L 38 +#define THIN_KEY_SEMICOLON 39 +#define THIN_KEY_QUOTE 40 +#define THIN_KEY_TILDE 41 +#define THIN_KEY_LEFT_SHIFT 42 +#define THIN_KEY_BACKSLASH 43 +#define THIN_KEY_Z 44 +#define THIN_KEY_X 45 +#define THIN_KEY_C 46 +#define THIN_KEY_V 47 +#define THIN_KEY_B 48 +#define THIN_KEY_N 49 +#define THIN_KEY_M 50 +#define THIN_KEY_COMMA 51 +#define THIN_KEY_PERIOD 52 +#define THIN_KEY_SLASH 53 +#define THIN_KEY_RIGHT_SHIFT 54 +#define THIN_KEY_NUMPAD_MULT 55 +#define THIN_KEY_LEFT_ALT 56 +#define THIN_KEY_SPACE 57 +#define THIN_KEY_CAPS_LOCK 58 +#define THIN_KEY_F1 59 +#define THIN_KEY_F2 60 +#define THIN_KEY_F3 61 +#define THIN_KEY_F4 62 +#define THIN_KEY_F5 63 +#define THIN_KEY_F6 64 +#define THIN_KEY_F7 65 +#define THIN_KEY_F8 66 +#define THIN_KEY_F9 67 +#define THIN_KEY_F10 68 +#define THIN_KEY_NUM_LOCK 69 +#define THIN_KEY_SCROLL_LOCK 70 + +#define THIN_KEY_F11 87 +#define THIN_KEY_F12 88 + +#define THIN_KEY_NUMPAD_7 71 +#define THIN_KEY_NUMPAD_8 72 +#define THIN_KEY_NUMPAD_9 73 +#define THIN_KEY_NUMPAD_MINUS 74 +#define THIN_KEY_NUMPAD_4 75 +#define THIN_KEY_NUMPAD_5 76 +#define THIN_KEY_NUMPAD_6 77 +#define THIN_KEY_NUMPAD_PLUS 78 +#define THIN_KEY_NUMPAD_1 79 +#define THIN_KEY_NUMPAD_2 80 +#define THIN_KEY_NUMPAD_3 81 +#define THIN_KEY_NUMPAD_0 82 +#define THIN_KEY_NUMPAD_DECIMAL 83 + +/* TODO: investigate */ +/*#define THIN_KEY_PRINT_SCREEN 84*/ + +/* TODO: are these correct? +** I don't have one of them new winders keyboards. +*/ +#define THIN_KEY_LEFT_WINDOWS 91 +#define THIN_KEY_RIGHT_WINDOWS 92 +#define THIN_KEY_MENU 93 + +/* extended keys */ +#define THIN_KEY_RIGHT_ALT 100 +#define THIN_KEY_RIGHT_CTRL 101 +#define THIN_KEY_NUMPAD_DIV 102 +#define THIN_KEY_NUMPAD_ENTER 103 +#define THIN_KEY_UP 104 +#define THIN_KEY_DOWN 105 +#define THIN_KEY_LEFT 106 +#define THIN_KEY_RIGHT 107 +#define THIN_KEY_INSERT 108 +#define THIN_KEY_HOME 109 +#define THIN_KEY_PGUP 110 +#define THIN_KEY_DELETE 111 +#define THIN_KEY_END 112 +#define THIN_KEY_PGDN 113 + +#define THIN_KEY_BREAK 114 +#define THIN_KEY_SYSRQ 116 + +/* PAUSE generates a ^X sequence... bleh */ +/*#define THIN_KEY_PAUSE 115*/ + +#define THIN_MAX_KEYS 128 + +extern int thin_key_init(void); +extern void thin_key_shutdown(void); +extern void thin_key_set_repeat(bool state); + +#endif /* !_TL_KEY_H_ */ + +/* +** $Log: $ +*/ diff --git a/sys/thinlib/lib/tl_log.c b/sys/thinlib/lib/tl_log.c new file mode 100644 index 0000000..2a92da0 --- /dev/null +++ b/sys/thinlib/lib/tl_log.c @@ -0,0 +1,59 @@ +/* +** thinlib (c) 2001 Matthew Conte (matt@conte.com) +** +** +** tl_log.c +** +** Error logging functions +** +** $Id: $ +*/ + +#include +#include +#include +#include "tl_types.h" +#include "tl_log.h" + +#define MAX_LOG_BUF_SIZE 1024 + +static int (*log_func)(const char *format, ... ) = printf; + +void thin_printf(const char *format, ... ) +{ + /* don't allocate on stack every call */ + static char buffer[MAX_LOG_BUF_SIZE + 1]; + va_list arg; + + va_start(arg, format); + + if (NULL != log_func) + { + vsprintf(buffer, format, arg); + log_func(buffer); + } + + va_end(arg); +} + +void thin_setlogfunc(int (*func)(const char *format, ... )) +{ + log_func = func; +} + +void thin_assert(int expr, int line, const char *file, char *msg) +{ + if (expr) + return; + + if (NULL != msg) + thin_printf("THIN_ASSERT: line %d of %s, %s\n", line, file, msg); + else + thin_printf("THIN_ASSERT: line %d of %s\n", line, file); + + exit(-1); +} + +/* +** $Log: $ +*/ diff --git a/sys/thinlib/lib/tl_log.h b/sys/thinlib/lib/tl_log.h new file mode 100644 index 0000000..c5d1115 --- /dev/null +++ b/sys/thinlib/lib/tl_log.h @@ -0,0 +1,23 @@ +/* +** thinlib (c) 2001 Matthew Conte (matt@conte.com) +** +** +** tl_log.h +** +** Error logging header file +** +** $Id: $ +*/ + +#ifndef _TL_LOG_H_ +#define _TL_LOG_H_ + +extern void thin_printf(const char *format, ... ); +extern void thin_setlogfunc(int (*logfunc)(const char *string, ... )); +extern void thin_assert(int expr, int line, const char *file, char *msg); + +#endif /* !_TL_LOG_H_ */ + +/* +** $Log: $ +*/ diff --git a/sys/thinlib/lib/tl_main.c b/sys/thinlib/lib/tl_main.c new file mode 100644 index 0000000..8095dc6 --- /dev/null +++ b/sys/thinlib/lib/tl_main.c @@ -0,0 +1,179 @@ +/* +** thinlib (c) 2001 Matthew Conte (matt@conte.com) +** +** +** tl_main.c +** +** main library init / shutdown code +** +** $Id: $ +*/ + +#include "tl_types.h" +#include "thinlib.h" + +#include "tl_timer.h" + +#include "tl_event.h" +#include "tl_key.h" +#include "tl_joy.h" +#include "tl_dpp.h" + +#include "tl_sound.h" +#include "tl_video.h" +#include "tl_djgpp.h" + +#include +#include + +/* our global "near pointer" flag. */ +int thinlib_nearptr = 0; +static int initialized_flags = 0; +static bool shutdown_called = false; + +typedef void (*funcptr_t)(void); +#define MAX_EXIT_FUNCS 32 + +static funcptr_t exit_func[MAX_EXIT_FUNCS]; + +void thin_add_exit(void (*func)(void)) +{ + int i; + + for (i = 0; i < MAX_EXIT_FUNCS; i++) + { + if (NULL == exit_func[i] || func == exit_func[i]) + { + exit_func[i] = func; + break; + } + } +} + +void thin_remove_exit(void (*func)(void)) +{ + int i; + + for (i = 0; i < MAX_EXIT_FUNCS; i++) + { + if (func == exit_func[i]) + { + while (i < MAX_EXIT_FUNCS - 1) + { + exit_func[i] = exit_func[i - 1]; + i++; + } + exit_func[MAX_EXIT_FUNCS - 1] = NULL; + break; + } + } +} + +int thin_init(int devices) +{ + int success = devices; + + /* set up our crt0 flags the way we want them. This might be a + ** bit too late (i.e. crt0.s/stub has already been executed), but + ** we might as well try. + */ + _crt0_startup_flags &= ~_CRT0_FLAG_UNIX_SBRK; + _crt0_startup_flags |= _CRT0_FLAG_NONMOVE_SBRK; + + /* Try to enable near pointers through djgpp's default mechanism. + ** This allows us to manipulate memory-mapped devices (sound cards, + ** video cards, etc.) as if they were regular memory addresses, at + ** the cost of disabling memory protection. Win NT and 2000 strictly + ** disallow near pointers, so we need to flag this. + */ + thinlib_nearptr = __djgpp_nearptr_enable(); + + /* open up event handler */ + thin_event_init(); + + if (devices & THIN_KEY) + { + if (thin_key_init()) + success &= ~THIN_KEY; + } + + if (devices & THIN_JOY) + { + if (thin_joy_init()) + success &= ~THIN_JOY; + } + + if (devices & THIN_DPP) + { + if (thin_dpp_init()) + success &= ~THIN_DPP; + } + + /* THIN_SOUND, THIN_VIDEO, THIN_TIMER implicitly successful.. */ + + initialized_flags = success; + + /* set up our atexit routine */ + atexit(thin_shutdown); + shutdown_called = false; + + return success; +} + +void thin_shutdown(void) +{ + int i; + + /* thin_shutdown is an atexit() registered routine, so make sure + ** that we don't get called multiple times. + */ + if (shutdown_called) + return; + + for (i = MAX_EXIT_FUNCS - 1; i >= 0; i--) + { + if (exit_func[i]) + { + exit_func[i](); + exit_func[i] = NULL; + } + } + + /* not started from thin_init */ + thin_sound_shutdown(); + thin_timer_shutdown(); + thin_vid_shutdown(); + + /* started from thin_init... */ + if (initialized_flags & THIN_KEY) + thin_key_shutdown(); + + if (initialized_flags & THIN_JOY) + thin_joy_shutdown(); + + if (initialized_flags & THIN_DPP) + thin_dpp_shutdown(); + + /* back to memory protection, if need be */ + if (thinlib_nearptr) + __djgpp_nearptr_disable(); + + shutdown_called = true; +} + +/* Reduce size of djgpp executable */ +char **__crt0_glob_function(char *_argument) +{ + UNUSED(_argument); + return (char **) 0; +} + +/* Reduce size of djgpp executable */ +void __crt0_load_environment_file(char *_app_name) +{ + UNUSED(_app_name); +} + +/* +** $Log: $ +*/ diff --git a/sys/thinlib/lib/tl_mouse.c b/sys/thinlib/lib/tl_mouse.c new file mode 100644 index 0000000..d9a6782 --- /dev/null +++ b/sys/thinlib/lib/tl_mouse.c @@ -0,0 +1,223 @@ +/* +** thinlib (c) 2001 Matthew Conte (matt@conte.com) +** +** +** tl_mouse.c +** +** DOS mouse handling routines +** +** $Id: $ +*/ + +/* TODO: add events to motion/button presses. */ +/* TODO: mouse interrupt based? */ + +#include +#include +#include + +#include "tl_types.h" +#include "tl_mouse.h" +#include "tl_event.h" + +#define MOUSE_FIX 8 // 24.8 fixpoint + +#define MOUSE_INT 0x33 +#define INT_GET_MICKEYS 0x0B +#define INT_GET_BUTTONS 0x03 + +static struct mouse_s +{ + int xpos, ypos; + int xdelta, ydelta; + int maxwidth, maxheight; + int num_buttons; + int delta_shift; + uint8 button; + bool enabled; + event_id id; +} mouse; + +static void _get_mickeys(int *dx, int *dy) +{ + __dpmi_regs r; + + /* get mickeys */ + r.x.ax = INT_GET_MICKEYS; + __dpmi_int(MOUSE_INT, &r); + *dx = (int16) r.x.cx; + *dy = (int16) r.x.dx; +} + +static uint8 _get_buttons(void) +{ + __dpmi_regs r; + uint8 left, middle, right; + + r.x.ax = INT_GET_BUTTONS; + __dpmi_int(MOUSE_INT, &r); + + left = (r.x.bx & 1); + right = ((r.x.bx >> 1) & 1); + middle = ((r.x.bx >> 2) & 1); + + return (right << THIN_MOUSE_RIGHT + | middle << THIN_MOUSE_MIDDLE + | left << THIN_MOUSE_LEFT); +} + +static void _mouse_poll(void) +{ + int mick_x, mick_y; + int old_x, old_y; + int old_button; + + if (false == mouse.enabled) + return; + + _get_mickeys(&mick_x, &mick_y); + + mick_x <<= (MOUSE_FIX - mouse.delta_shift); + mick_y <<= (MOUSE_FIX - mouse.delta_shift); + + old_x = mouse.xpos; + old_y = mouse.ypos; + mouse.xpos += mick_x; + mouse.ypos += mick_y; + + if (mouse.xpos < 0) + mouse.xpos = 0; + else if (mouse.xpos > mouse.maxwidth) + mouse.xpos = mouse.maxwidth; + + if (mouse.ypos < 0) + mouse.ypos = 0; + else if (mouse.ypos > mouse.maxheight) + mouse.ypos = mouse.maxheight; + + mick_x = mouse.xpos - old_x; + mick_y = mouse.ypos - old_y; + mouse.xdelta += mick_x; + mouse.ydelta += mick_y; + + old_button = mouse.button; + mouse.button = _get_buttons(); + + /* if our delta really changed, add an event */ + if (0 != mick_x || 0 != mick_y) + { + thin_event_t event; + + event.type = THIN_MOUSE_MOTION; + event.data.mouse_motion.xpos = mouse.xpos; + event.data.mouse_motion.ypos = mouse.ypos; + + thin_event_add(&event); + } + + /* if button state changed, add applicable events */ + if (old_button != mouse.button) + { + thin_event_t event; + int i; + + for (i = 0; i < THIN_MOUSE_MAX_BUTTONS; i++) + { + /* TODO: this is kind of krunky. a separate event for + ** every button, but return the state of all buttons? + ** bleh. + */ + if ((old_button & (1 << i)) != (mouse.button & (1 << i))) + { + event.type = (mouse.button & (1 << i)) + ? THIN_MOUSE_BUTTON_PRESS : THIN_MOUSE_BUTTON_RELEASE; + event.data.mouse_button = mouse.button; + + thin_event_add(&event); + } + } + } +} + +uint8 thin_mouse_getmotion(int *dx, int *dy) +{ + *dx = mouse.xdelta >> MOUSE_FIX; + *dy = mouse.ydelta >> MOUSE_FIX; + mouse.xdelta = 0; + mouse.ydelta = 0; + return mouse.button; +} + +uint8 thin_mouse_getpos(int *x, int *y) +{ + *x = mouse.xpos >> MOUSE_FIX; + *y = mouse.ypos >> MOUSE_FIX; + return mouse.button; +} + + +void thin_mouse_setrange(int width, int height) +{ + mouse.maxwidth = (width - 1) << MOUSE_FIX; + mouse.maxheight = (height - 1) << MOUSE_FIX; + mouse.xpos = (width / 2) << MOUSE_FIX; + mouse.ypos = (height / 2) << MOUSE_FIX; + mouse.xdelta = 0; + mouse.ydelta = 0; +} + + +void thin_mouse_shutdown(void) +{ + if (-1 != mouse.id) + { + thin_event_remove_callback(mouse.id); + mouse.id = -1; + mouse.enabled = false; + } +} + + +/* Set up mouse, center pointer */ +int thin_mouse_init(int width, int height, int delta_shift) +{ + __dpmi_regs r; + + r.x.ax = 0x00; + __dpmi_int(MOUSE_INT, &r); + + if (0 == r.x.ax) + { + mouse.enabled = false; + mouse.id = -1; + return -1; + } + + mouse.enabled = true; + + mouse.num_buttons = r.x.bx; + if (r.x.bx == 0xFFFF) + mouse.num_buttons = 2; + else if (mouse.num_buttons > 3) + mouse.num_buttons = 3; + + mouse.delta_shift = delta_shift; + + mouse.button = 0; + + thin_mouse_setrange(width, height); + + /* set it up for the event handling */ + mouse.id = thin_event_add_callback((event_callback_t) _mouse_poll); + if (-1 == mouse.id) + { + mouse.enabled = false; + return -1; + } + + return 0; +} + +/* +** $Log: $ +*/ diff --git a/sys/thinlib/lib/tl_mouse.h b/sys/thinlib/lib/tl_mouse.h new file mode 100644 index 0000000..29a87b7 --- /dev/null +++ b/sys/thinlib/lib/tl_mouse.h @@ -0,0 +1,40 @@ +/* +** thinlib (c) 2001 Matthew Conte (matt@conte.com) +** +** +** tl_mouse.h +** +** DOS mouse handling defines / prototypes +** +** $Id: $ +*/ + +#ifndef _TL_MOUSE_H_ +#define _TL_MOUSE_H_ + +#include "tl_types.h" + +/* mouse buttons */ +enum +{ + THIN_MOUSE_LEFT = 0, + THIN_MOUSE_MIDDLE, + THIN_MOUSE_RIGHT, + THIN_MOUSE_MAX_BUTTONS, +}; + +#define THIN_MOUSE_BUTTON_MASK(x) (1 << (x)) + +extern int thin_mouse_init(int width, int height, int delta_shift); +extern void thin_mouse_shutdown(); + +extern void thin_mouse_setrange(int width, int height); + +extern uint8 thin_mouse_getmotion(int *dx, int *dy); +extern uint8 thin_mouse_getpos(int *x, int *y); + +#endif /* !_TL_MOUSE_H_ */ + +/* +** $Log: $ +*/ diff --git a/sys/thinlib/lib/tl_prof.c b/sys/thinlib/lib/tl_prof.c new file mode 100644 index 0000000..13dc7e2 --- /dev/null +++ b/sys/thinlib/lib/tl_prof.c @@ -0,0 +1,25 @@ +/* +** thinlib (c) 2001 Matthew Conte (matt@conte.com) +** +** +** tl_prof.c +** +** Screen border color profiler nastiness. +** +** $Id: $ +*/ + +#include +#include "tl_types.h" +#include "tl_prof.h" + +void thin_prof_setborder(int pal_index) +{ + inportb(0x3DA); + outportb(0x3C0, 0x31); + outportb(0x3C0, (uint8) pal_index); +} + +/* +** $Log: $ +*/ diff --git a/sys/thinlib/lib/tl_prof.h b/sys/thinlib/lib/tl_prof.h new file mode 100644 index 0000000..c0e647b --- /dev/null +++ b/sys/thinlib/lib/tl_prof.h @@ -0,0 +1,21 @@ +/* +** thinlib (c) 2001 Matthew Conte (matt@conte.com) +** +** +** tl_prof.h +** +** Screen border color profiler. +** +** $Id: $ +*/ + +#ifndef _TL_PROF_H_ +#define _TL_PROF_H_ + +extern void thin_prof_setborder(int pal_index); + +#endif /* !_TL_PROF_H_ */ + +/* +** $Log: $ +*/ diff --git a/sys/thinlib/lib/tl_sb.c b/sys/thinlib/lib/tl_sb.c new file mode 100644 index 0000000..5d0f49b --- /dev/null +++ b/sys/thinlib/lib/tl_sb.c @@ -0,0 +1,1050 @@ +/* +** thinlib (c) 2001 Matthew Conte (matt@conte.com) +** +** +** tl_sb.c +** +** DOS Sound Blaster routines +** +** Note: the information in this file has been gathered from many +** Internet documents, and from source code written by Ethan Brodsky. +** +** $Id: $ +*/ + +#include +#include +#include +#include +#include + +#include "tl_types.h" +#include "tl_djgpp.h" +#include "tl_int.h" +#include "tl_sb.h" +#include "tl_log.h" + + +/* General defines */ +#define LOW_BYTE(x) (uint8) ((x) & 0xFF) +#define HIGH_BYTE(x) (uint8) ((x) >> 8) + +#define INVALID 0xFFFFFFFF +#define DEFAULT_TIMEOUT 20000 + +#define DETECT_POLL_REPS 1000 + +#define DSP_VERSION_SB_15 0x0200 +#define DSP_VERSION_SB_20 0x0201 +#define DSP_VERSION_SB_PRO 0x0300 +#define DSP_VERSION_SB16 0x0400 + +/* DSP register offsets */ +#define DSP_RESET 0x06 +#define DSP_READ 0x0A +#define DSP_READ_READY 0x0E +#define DSP_WRITE 0x0C +#define DSP_WRITE_BUSY 0x0C +#define DSP_DMA_ACK_8BIT 0x0E +#define DSP_DMA_ACK_16BIT 0x0F +#define DSP_RESET_SUCCESS 0xAA + +/* SB 1.0 commands */ +#define DSP_DMA_TIME_CONST 0x40 +#define DSP_DMA_DAC_8BIT 0x14 +#define DSP_DMA_PAUSE_8BIT 0xD0 +#define DSP_DMA_CONT_8BIT 0xD4 +#define DSP_SPEAKER_ON 0xD1 +#define DSP_SPEAKER_OFF 0xD3 +#define DSP_GET_VERSION 0xE1 + +/* SB 1.5 - Pro commands */ +#define DSP_DMA_BLOCK_SIZE 0x48 +#define DSP_DMA_DAC_AI_8BIT 0x1C /* low-speed autoinit */ +#define DSP_DMA_DAC_HS_8BIT 0x90 /* high-speed autoinit */ + +/* SB16 commands */ +#define DSP_DMA_DAC_RATE 0x41 +#define DSP_DMA_START_16BIT 0xB0 +#define DSP_DMA_START_8BIT 0xC0 +#define DSP_DMA_DAC_MODE 0x06 +#define DSP_DMA_PAUSE_16BIT 0xD5 +#define DSP_DMA_CONT_16BIT 0xD6 +#define DSP_DMA_STOP_8BIT 0xDA + +/* DMA flags */ +#define DSP_DMA_UNSIGNED 0x00 +#define DSP_DMA_SIGNED 0x10 +#define DSP_DMA_MONO 0x00 +#define DSP_DMA_STEREO 0x20 + +/* DMA address/port/command defines */ +#define DMA_MASKPORT_16BIT 0xD4 +#define DMA_MODEPORT_16BIT 0xD6 +#define DMA_CLRPTRPORT_16BIT 0xD8 +#define DMA_ADDRBASE_16BIT 0xC0 +#define DMA_COUNTBASE_16BIT 0XC2 +#define DMA_MASKPORT_8BIT 0x0A +#define DMA_MODEPORT_8BIT 0x0B +#define DMA_CLRPTRPORT_8BIT 0x0C +#define DMA_ADDRBASE_8BIT 0x00 +#define DMA_COUNTBASE_8BIT 0x01 +#define DMA_STOPMASK_BASE 0x04 +#define DMA_STARTMASK_BASE 0x00 +#define DMA_AUTOINIT_MODE 0x58 +#define DMA_ONESHOT_MODE 0x48 + +/* centerline */ +#define SILENCE_SIGNED 0x00 +#define SILENCE_UNSIGNED 0x80 + +/* get the irq vector number from an irq channel */ +#define SB_IRQVEC(chan) ((chan < 8) ? (0x08 + (chan)) : (0x70 + ((chan) - 8))) + + +/* DOS low-memory buffer info */ +static struct +{ + _go32_dpmi_seginfo buffer; + uint32 bufaddr; /* linear address */ + uint32 offset; + uint32 page; +} dos; + + +/* DMA information */ +static struct +{ + volatile int count; + uint16 addrport; + uint16 ackport; + bool autoinit; +} dma; + +/* 8 and 16 bit DMA ports */ +static const uint8 dma8_ports[4] = { 0x87, 0x83, 0x81, 0x82 }; +static const uint8 dma16_ports[4] = { 0xFF, 0x8B, 0x89, 0x8A }; + +/* Sound Blaster context */ +static struct +{ + bool initialized; + + uint16 baseio; + uint16 dsp_version; + + uint16 sample_rate; + uint8 format; + + uint8 irq, dma, dma16; + + uint8 *buffer; + uint32 buf_size; + uint32 buf_chunk; + + sbmix_t callback; + void *user_data; +} sb; + + +/* +** Basic DSP routines +*/ +static void dsp_write(uint8 value) +{ + int timeout = DEFAULT_TIMEOUT; + + /* wait until DSP is ready... */ + while (timeout-- && (inportb(sb.baseio + DSP_WRITE_BUSY) & 0x80)) + ; /* loop */ + + outportb(sb.baseio + DSP_WRITE, value); +} + +static uint8 dsp_read(void) +{ + int timeout = DEFAULT_TIMEOUT; + + while (timeout-- && (0 == (inportb(sb.baseio + DSP_READ_READY) & 0x80))) + ; /* loop */ + + return inportb(sb.baseio + DSP_READ); +} + +/* returns zero if DSP found and successfully reset, nonzero otherwise */ +static int dsp_reset(void) +{ + outportb(sb.baseio + DSP_RESET, 1); /* reset command */ + delay(5); /* 5 usec delay */ + outportb(sb.baseio + DSP_RESET, 0); /* clear */ + delay(5); /* 5 usec delay */ + + if (DSP_RESET_SUCCESS == dsp_read()) + return 0; + + /* BLEH, we failed */ + return -1; +} + +/* return DSP version in 8:8 major:minor format */ +static uint16 dsp_getversion(void) +{ + uint8 major, minor; + + dsp_write(DSP_GET_VERSION); + + major = dsp_read(); + minor = dsp_read(); + + return ((uint16) (major << 8) | minor); +} + +/* +** BLASTER environment variable parsing +*/ +static int get_env_item(char *env, void *ptr, char find, int base, int width) +{ + char *item; + int value; + + item = strrchr(env, find); + if (NULL == item) + return -1; + + item++; + value = strtol(item, NULL, base); + + switch (width) + { + case 32: + *(uint32 *) ptr = value; + break; + + case 16: + *(uint16 *) ptr = value; + break; + + case 8: + *(uint8 *) ptr = value; + break; + + default: + break; + } + + return 0; +} + +/* parse the BLASTER environment variable */ +static int parse_blaster_env(void) +{ + char blaster[255 + 1], *penv; + + penv = getenv("BLASTER"); + + /* bail out if we can't find it... */ + if (NULL == penv) + return -1; + + /* copy it, normalize case */ + strncpy(blaster, penv, 255); + strupr(blaster); + + if (get_env_item(blaster, &sb.baseio, 'A', 16, 16)) + return -1; + if (get_env_item(blaster, &sb.irq, 'I', 10, 8)) + return -1; + if (get_env_item(blaster, &sb.dma, 'D', 10, 8)) + return -1; + if (get_env_item(blaster, &sb.dma16, 'H', 10, 8)) + sb.dma16 = (uint8) INVALID; + + return 0; +} + +/* +** Brute force autodetection code +*/ + +/* detect the base IO by attempting to +** reset the DSP at known addresses +*/ +static uint16 detect_baseio(void) +{ + int i; + static const uint16 port_val[] = + { + 0x210, 0x220, 0x230, 0x240, + 0x250, 0x260, 0x280, (uint16) INVALID + }; + + for (i = 0; (uint16) INVALID != port_val[i]; i++) + { + sb.baseio = port_val[i]; + if (0 == dsp_reset()) + break; + } + + /* will return INVALID if not found */ + return port_val[i]; +} + +/* stop all DSP activity */ +static void dsp_stop(void) +{ + /* pause 8/16 bit DMA mode digitized sound IO */ + dsp_reset(); + dsp_write(DSP_DMA_PAUSE_8BIT); + dsp_write(DSP_DMA_PAUSE_16BIT); +} + +/* return number of set bits in byte x */ +static int bitcount(uint8 x) +{ + int i, set_count = 0; + + for (i = 0; i < 8; i++) + if (x & (1 << i)) + set_count++; + + return set_count; +} + +/* returns position of lowest bit set in byte x (INVALID if none) */ +static int bitpos(uint8 x) +{ + int i; + + for (i = 0; i < 8; i++) + if (x & (1 << i)) + return i; + + return INVALID; +} + +static uint8 detect_dma(bool high_dma) +{ + uint8 dma_maskout, dma_mask; + int i; + + /* stop DSP activity */ + dsp_stop(); + + dma_maskout = ~0x10; /* initially mask only DMA4 */ + + /* poll to find out which dma channels are in use */ + for (i = 0; i < DETECT_POLL_REPS; i++) + dma_maskout &= ~(inportb(0xD0) & 0xF0) | (inportb(0x08) >> 4); + + /* TODO: this causes a pretty nasty sound */ + /* program card, see whch channel becomes active */ + if (false == high_dma) + { + /* 8 bit */ + dsp_write(DSP_DMA_DAC_8BIT); + } + else + { + dsp_write(DSP_DMA_START_16BIT); /* 16-bit, D/A, S/C, FIFO off */ + dsp_write(DSP_DMA_SIGNED | DSP_DMA_MONO); /* 16-bit mono signed PCM */ + } + + dsp_write(0xF0); /* send some default length */ + dsp_write(0xFF); + + /* poll to find out which DMA channels are in use with sound */ + dma_mask = 0; /* dma channels active during audio, minus masked out */ + for (i = 0; i < DETECT_POLL_REPS; i++) + dma_mask |= (((inportb(0xD0) & 0xF0) | (inportb(0x08) >> 4)) & dma_maskout); + + /* stop all DSP activity */ + dsp_stop(); + + if (1 == bitcount(dma_mask)) + return (uint8) bitpos(dma_mask); + else + return (uint8) INVALID; +} + +static void dsp_transfer(uint8 dma) +{ + outportb(DMA_MASKPORT_8BIT, DMA_STOPMASK_BASE | dma); + + /* write DMA mode: single-cycle read transfer */ + outportb(DMA_MODEPORT_8BIT, DMA_ONESHOT_MODE | dma); + outportb(DMA_CLRPTRPORT_8BIT, 0x00); + + /* one transfer */ + outportb(DMA_COUNTBASE_8BIT + (2 * dma), 0x00); /* low */ + outportb(DMA_COUNTBASE_8BIT + (2 * dma), 0x00); /* high */ + + /* address */ + outportb(DMA_ADDRBASE_8BIT + (2 * dma), 0x00); + outportb(DMA_ADDRBASE_8BIT + (2 * dma), 0x00); + outportb(dma8_ports[dma], 0x00); + + /* unmask DMA channel */ + outportb(DMA_MASKPORT_8BIT, DMA_STARTMASK_BASE | dma); + + /* 8-bit single cycle DMA mode */ + dsp_write(DSP_DMA_DAC_8BIT); + dsp_write(0x00); + dsp_write(0x00); +} + +/* +** IRQ autodetection +*/ +#define NUM_IRQ_CHANNELS 5 + +static const uint8 irq_channels[NUM_IRQ_CHANNELS] = { 2, 3, 5, 7, 10 }; +static volatile bool irq_hit[NUM_IRQ_CHANNELS]; + +#define MAKE_IRQ_HANDLER(num) \ +static int chan##num##_handler(void) { irq_hit[num] = true; return 0; } \ +THIN_LOCKED_STATIC_FUNC(chan##num##_handler) + +MAKE_IRQ_HANDLER(0) +MAKE_IRQ_HANDLER(1) +MAKE_IRQ_HANDLER(2) +MAKE_IRQ_HANDLER(3) +MAKE_IRQ_HANDLER(4) + +static void ack_interrupt(uint8 irq) +{ + /* acknowledge the interrupts! */ + inportb(sb.baseio + 0x0E); + if (irq > 7) + outportb(0xA0, 0x20); + outportb(0x20, 0x20); +} + +static uint8 detect_irq(void) +{ + bool irq_mask[NUM_IRQ_CHANNELS]; + uint8 irq = (uint8) INVALID; + int i; + + THIN_LOCK_FUNC(chan0_handler); + THIN_LOCK_FUNC(chan1_handler); + THIN_LOCK_FUNC(chan2_handler); + THIN_LOCK_FUNC(chan3_handler); + THIN_LOCK_FUNC(chan4_handler); + THIN_LOCK_VAR(irq_hit); + + THIN_DISABLE_INTS(); + + /* install temp handlers */ + thin_int_install(SB_IRQVEC(irq_channels[0]), chan0_handler); + thin_int_install(SB_IRQVEC(irq_channels[1]), chan1_handler); + thin_int_install(SB_IRQVEC(irq_channels[2]), chan2_handler); + thin_int_install(SB_IRQVEC(irq_channels[3]), chan3_handler); + thin_int_install(SB_IRQVEC(irq_channels[4]), chan4_handler); + + /* enable IRQs */ + for (i = 0; i < NUM_IRQ_CHANNELS; i++) + { + thin_irq_enable(irq_channels[i]); + irq_hit[i] = false; + } + + THIN_ENABLE_INTS(); + + /* wait to see which interrupts are triggered without sound */ + delay(100); + + /* mask out any interrupts triggered without sound */ + for (i = 0; i < NUM_IRQ_CHANNELS; i++) + { + irq_mask[i] = irq_hit[i]; + irq_hit[i] = false; + } + + /* try to trigger an interrupt using DSP command F2 */ + dsp_write(0xF2); + + delay(100); + + /* detect triggered interrupts */ + for (i = 0; i < NUM_IRQ_CHANNELS; i++) + { + if (true == irq_hit[i] && false == irq_mask[i]) + { + irq = irq_channels[i]; + ack_interrupt(irq); + break; + } + } + + /* if F2 fails to trigger an int, run a short transfer */ + if ((uint8) INVALID == irq) + { + dsp_reset(); + dsp_transfer(sb.dma); + + delay(100); + + /* detect triggered interrupts */ + for (i = 0; i < NUM_IRQ_CHANNELS; i++) + { + if (true == irq_hit[i] && false == irq_mask[i]) + { + irq = irq_channels[i]; + ack_interrupt(irq); + break; + } + } + } + + /* reset DSP just in case */ + dsp_reset(); + + THIN_DISABLE_INTS(); + + /* restore IRQs to previous state, uninstall handlers */ + for (i = 0; i < NUM_IRQ_CHANNELS; i++) + { + thin_irq_restore(irq_channels[i]); + thin_int_remove(SB_IRQVEC(irq_channels[i])); + } + + THIN_ENABLE_INTS(); + + return irq; +} + +/* try and detect an SB without environment variables */ +static int sb_detect(void) +{ + sb.baseio = detect_baseio(); + if ((uint16) INVALID == sb.baseio) + return -1; + + sb.irq = detect_irq(); + if ((uint8) INVALID == sb.irq) + return -1; + + sb.dma = detect_dma(false); + if ((uint8) INVALID == sb.dma) + return -1; + + /* may or may not exist */ + sb.dma16 = detect_dma(true); + + return 0; +} + +/* +** Probe for an SB +*/ +static int sb_probe(void) +{ + int retval; + + retval = parse_blaster_env(); + + /* if environment parse failed, try brute force autodetection */ + if (-1 == retval) + retval = sb_detect(); + + /* no blaster found */ + if (-1 == retval) + { + thin_printf("thinlib.sb: no sound blaster found\n"); + return -1; + } + + if (dsp_reset()) + { + thin_printf("thinlib.sb: could not reset SB DSP: check BLASTER= variable\n"); + return -1; + } + + sb.dsp_version = dsp_getversion(); + return 0; +} + +/* +** Interrupt handler for 8/16-bit audio +*/ + +static int sb_isr(void) +{ + uint32 address, offset; + + dma.count++; + + /* NOTE: this only works with 8-bit, as one-shot mode + ** does not seem to work with 16-bit transfers + */ + if (false == dma.autoinit) + { + dsp_write(DSP_DMA_DAC_8BIT); + dsp_write(LOW_BYTE(sb.buf_size - 1)); + dsp_write(HIGH_BYTE(sb.buf_size - 1)); + } + + /* indicate we got the interrupt */ + inportb(dma.ackport); + + /* determine the current playback position */ + address = inportb(dma.addrport); + address |= (inportb(dma.addrport) << 8); + address -= dos.offset; + + if (address < sb.buf_size) + offset = sb.buf_chunk; + else + offset = 0; + + sb.callback(sb.user_data, sb.buffer + offset, sb.buf_size); + + /* if we haven't enabled near pointers, we've written to a double + ** buffer, so transfer it to low DOS memory area + */ + if (0 == thinlib_nearptr) + dosmemput(sb.buffer + offset, sb.buf_chunk, dos.bufaddr + offset); + + /* acknowledge interrupt was taken */ + if (sb.irq > 7) + outportb(0xA0, 0x20); + outportb(0x20, 0x20); + + return 0; +} +THIN_LOCKED_STATIC_FUNC(sb_isr) + + +/* install the SB ISR */ +static void sb_setisr(void) +{ + THIN_DISABLE_INTS(); + + thin_int_install(SB_IRQVEC(sb.irq), sb_isr); + + /* enable IRQ */ + thin_irq_enable(sb.irq); + + THIN_ENABLE_INTS(); +} + + +static void sb_restoreisr(void) +{ + THIN_DISABLE_INTS(); + + /* restore IRQ to previous state */ + thin_irq_restore(sb.irq); + + thin_int_remove(SB_IRQVEC(sb.irq)); + + THIN_ENABLE_INTS(); +} + +/* allocate sound buffers */ +static int sb_allocate_buffers(int buf_size) +{ + int double_bufsize; + + sb.buf_size = buf_size; + +// if (sb.format & SB_FORMAT_STEREO) +// sb.buf_size *= 2; + + if (sb.format & SB_FORMAT_16BIT) + sb.buf_chunk = sb.buf_size * sizeof(uint16); + else + sb.buf_chunk = sb.buf_size * sizeof(uint8); + + double_bufsize = 2 * sb.buf_chunk; + + dos.buffer.size = (double_bufsize + 15) >> 4; + if (_go32_dpmi_allocate_dos_memory(&dos.buffer)) + return -1; + + /* calc linear address */ + dos.bufaddr = dos.buffer.rm_segment << 4; + if (sb.format & SB_FORMAT_16BIT) + { + dos.page = (dos.bufaddr >> 16) & 0xFF; + dos.offset = (dos.bufaddr >> 1) & 0xFFFF; + } + else + { + dos.page = (dos.bufaddr >> 16) & 0xFF; + dos.offset = dos.bufaddr & 0xFFFF; + } + + if (thinlib_nearptr) + { + sb.buffer = (uint8 *) THIN_PHYSICAL_ADDR(dos.bufaddr); + } + else + { + sb.buffer = malloc(double_bufsize); + if (NULL == sb.buffer) + return -1; + } + + /* clear out the buffers */ + if (sb.format & SB_FORMAT_SIGNED) + memset(sb.buffer, SILENCE_SIGNED, double_bufsize); + else + memset(sb.buffer, SILENCE_UNSIGNED, double_bufsize); + + if (0 == thinlib_nearptr) + dosmemput(sb.buffer, double_bufsize, dos.bufaddr); + + return 0; +} + +/* free buffers */ +static void sb_free_buffers(void) +{ + sb.callback = NULL; + + _go32_dpmi_free_dos_memory(&dos.buffer); + + if (0 == thinlib_nearptr) + { + free(sb.buffer); + sb.buffer = NULL; + } + + sb.buffer = NULL; +} + +/* get rid of all things SB */ +void thin_sb_shutdown(void) +{ + if (true == sb.initialized) + { + sb.initialized = false; + + dsp_reset(); + + sb_restoreisr(); + + sb_free_buffers(); + } +} + +/* initialize sound bastard */ +int thin_sb_init(int *sample_rate, int *buf_size, int *format) +{ +#define CLAMP_RATE(in_rate, min_rate, max_rate) \ + (in_rate < min_rate ? min_rate : \ + (in_rate > max_rate ? max_rate : in_rate)) + + /* don't init twice! */ + if (true == sb.initialized) + return 0; + + /* lock variables, routines */ + THIN_LOCK_VAR(dma); + THIN_LOCK_VAR(dos); + THIN_LOCK_VAR(sb); + THIN_LOCK_FUNC(sb_isr); + + memset(&sb, 0, sizeof(sb)); + + if (sb_probe()) + return -1; + + /* try autoinit DMA first */ + dma.autoinit = true; + sb.format = (uint8) *format; + + /* determine which SB model we have, and act accordingly */ + if (sb.dsp_version < DSP_VERSION_SB_15) + { + /* SB 1.0 */ + sb.sample_rate = CLAMP_RATE(*sample_rate, 4000, 22050); + sb.format &= ~(SB_FORMAT_16BIT | SB_FORMAT_STEREO); + dma.autoinit = false; + } + else if (sb.dsp_version < DSP_VERSION_SB_20) + { + /* SB 1.5 */ + sb.sample_rate = CLAMP_RATE(*sample_rate, 5000, 22050); + sb.format &= ~(SB_FORMAT_16BIT | SB_FORMAT_STEREO); + } + else if (sb.dsp_version < DSP_VERSION_SB_PRO) + { + /* SB 2.0 */ + sb.sample_rate = CLAMP_RATE(*sample_rate, 5000, 44100); + sb.format &= ~(SB_FORMAT_16BIT | SB_FORMAT_STEREO); + } + else if (sb.dsp_version < DSP_VERSION_SB16) + { + /* SB Pro */ + if (sb.format & SB_FORMAT_STEREO) + sb.sample_rate = CLAMP_RATE(*sample_rate, 5000, 22050); + else + sb.sample_rate = CLAMP_RATE(*sample_rate, 5000, 44100); + sb.format &= ~SB_FORMAT_16BIT; + } + else + { + /* SB 16 */ + sb.sample_rate = CLAMP_RATE(*sample_rate, 5000, 44100); + } + + /* sanity check for 16-bit */ + if ((sb.format & SB_FORMAT_16BIT) && ((uint8) INVALID == sb.dma16)) + { + sb.format &= ~SB_FORMAT_16BIT; + thin_printf("thinlib.sb: 16-bit DMA channel not available, dropping to 8-bit\n"); + } + + /* clamp buffer size to something sane */ + if ((uint16) *buf_size > sb.sample_rate) + { + *buf_size = sb.sample_rate; + thin_printf("thinlib.sb: buffer size too big, dropping to %d bytes\n", *buf_size); + } + + /* allocate buffer / DOS memory */ + if (sb_allocate_buffers(*buf_size)) + { + thin_printf("thinlib.sb: failed allocating sound buffers\n"); + return -1; + } + + /* set the new IRQ vector! */ + sb_setisr(); + + sb.initialized = true; + + /* return the actual values */ + *sample_rate = sb.sample_rate; + *buf_size = sb.buf_size; + *format = sb.format; + + return 0; +} + +void thin_sb_stop(void) +{ + if (true == sb.initialized) + { + if (sb.format & SB_FORMAT_16BIT) + { + dsp_write(DSP_DMA_PAUSE_16BIT); /* pause 16-bit DMA */ + dsp_write(DSP_DMA_STOP_8BIT); + dsp_write(DSP_DMA_PAUSE_16BIT); + } + else + { + dsp_write(DSP_DMA_PAUSE_8BIT); /* pause 8-bit DMA */ + dsp_write(DSP_SPEAKER_OFF); + } + } +} + +/* return time constant for older sound bastards */ +static uint8 get_time_constant(int rate) +{ + return ((65536 - (256000000L / rate)) / 256); +} + +static void init_samplerate(int rate) +{ + if ((sb.format & SB_FORMAT_16BIT) || sb.dsp_version >= DSP_VERSION_SB16) + { + dsp_write(DSP_DMA_DAC_RATE); + dsp_write(HIGH_BYTE(rate)); + dsp_write(LOW_BYTE(rate)); + } + else + { + dsp_write(DSP_DMA_TIME_CONST); + dsp_write(get_time_constant(rate)); + } +} + +/* set the sample rate */ +void thin_sb_setrate(int rate) +{ + if (sb.format & SB_FORMAT_16BIT) + { + dsp_write(DSP_DMA_PAUSE_16BIT); /* pause 16-bit DMA */ + init_samplerate(rate); + dsp_write(DSP_DMA_CONT_16BIT); /* continue 16-bit DMA */ + } + else + { + dsp_write(DSP_DMA_PAUSE_8BIT); /* pause 8-bit DMA */ + init_samplerate(rate); + dsp_write(DSP_DMA_CONT_8BIT); /* continue 8-bit DMA */ + } + + sb.sample_rate = rate; +} + +/* start SB DMA transfer */ +static void start_transfer(void) +{ + uint8 dma_mode, start_command, mode_command; + int dma_length; + + /* reset DMA count */ + dma.count = 0; + + dma_length = sb.buf_size * 2; + + if (true == dma.autoinit) + { + start_command = DSP_DMA_DAC_MODE; /* autoinit DMA */ + dma_mode = DMA_AUTOINIT_MODE; + } + else + { + start_command = 0; + dma_mode = DMA_ONESHOT_MODE; + } + + /* things get a little bit nasty here, look out */ + if (sb.format & SB_FORMAT_16BIT) + { + uint8 dma_base = sb.dma16 - 4; + + dma_mode |= dma_base; + start_command |= DSP_DMA_START_16BIT; + + outportb(DMA_MASKPORT_16BIT, DMA_STOPMASK_BASE | dma_base); + outportb(DMA_MODEPORT_16BIT, dma_mode); + outportb(DMA_CLRPTRPORT_16BIT, 0x00); + outportb(DMA_ADDRBASE_16BIT + (4 * dma_base), LOW_BYTE(dos.offset)); + outportb(DMA_ADDRBASE_16BIT + (4 * dma_base), HIGH_BYTE(dos.offset)); + outportb(DMA_COUNTBASE_16BIT + (4 * dma_base), LOW_BYTE(dma_length - 1)); + outportb(DMA_COUNTBASE_16BIT + (4 * dma_base), HIGH_BYTE(dma_length - 1)); + outportb(dma16_ports[dma_base], dos.page); + outportb(DMA_MASKPORT_16BIT, DMA_STARTMASK_BASE | dma_base); + + dma.ackport = sb.baseio + DSP_DMA_ACK_16BIT; + dma.addrport = DMA_ADDRBASE_16BIT + (4 * (sb.dma16 - 4)); + } + else + { + dma_mode |= sb.dma; + start_command |= DSP_DMA_START_8BIT; + + outportb(DMA_MASKPORT_8BIT, DMA_STOPMASK_BASE + sb.dma); + outportb(DMA_MODEPORT_8BIT, dma_mode); + outportb(DMA_CLRPTRPORT_8BIT, 0x00); + outportb(DMA_ADDRBASE_8BIT + (2 * sb.dma), LOW_BYTE(dos.offset)); + outportb(DMA_ADDRBASE_8BIT + (2 * sb.dma), HIGH_BYTE(dos.offset)); + outportb(DMA_COUNTBASE_8BIT + (2 * sb.dma), LOW_BYTE(dma_length - 1)); + outportb(DMA_COUNTBASE_8BIT + (2 * sb.dma), HIGH_BYTE(dma_length - 1)); + outportb(dma8_ports[sb.dma], dos.page); + outportb(DMA_MASKPORT_8BIT, DMA_STARTMASK_BASE + sb.dma); + + dma.ackport = sb.baseio + DSP_DMA_ACK_8BIT; + dma.addrport = DMA_ADDRBASE_8BIT + (2 * sb.dma); + } + + /* check signed/unsigned */ + if (sb.format & SB_FORMAT_SIGNED) + mode_command = DSP_DMA_SIGNED; + else + mode_command = DSP_DMA_UNSIGNED; + + /* check stereo */ + if (sb.format & SB_FORMAT_STEREO) + mode_command |= DSP_DMA_STEREO; + else + mode_command |= DSP_DMA_MONO; + + init_samplerate(sb.sample_rate); + + /* start things going */ + if ((sb.format & SB_FORMAT_16BIT) || sb.dsp_version >= DSP_VERSION_SB16) + { + dsp_write(start_command); + dsp_write(mode_command); + dsp_write(LOW_BYTE(sb.buf_size - 1)); + dsp_write(HIGH_BYTE(sb.buf_size - 1)); + } + else + { + /* turn on speaker */ + dsp_write(DSP_SPEAKER_ON); + + if (true == dma.autoinit) + { + dsp_write(DSP_DMA_BLOCK_SIZE); /* set buffer size */ + dsp_write(LOW_BYTE(sb.buf_size - 1)); + dsp_write(HIGH_BYTE(sb.buf_size - 1)); + + if (sb.dsp_version < DSP_VERSION_SB_20) + dsp_write(DSP_DMA_DAC_AI_8BIT); /* low speed autoinit */ + else + dsp_write(DSP_DMA_DAC_HS_8BIT); + } + else + { + dsp_write(DSP_DMA_DAC_8BIT); + dsp_write(LOW_BYTE(sb.buf_size - 1)); + dsp_write(HIGH_BYTE(sb.buf_size - 1)); + } + } +} + +/* TODO: this gets totally wacked when we change the timer rate!!! */ +/* start playing the output buffer */ +int thin_sb_start(sbmix_t fillbuf, void *user_data) +{ + clock_t count; + int projected_dmacount; + + /* make sure we really should be here... */ + if (false == sb.initialized || NULL == fillbuf) + return -1; + + /* stop any current processing */ + thin_sb_stop(); + + /* set the callback routine */ + sb.callback = fillbuf; + sb.user_data = user_data; + + /* calculate how many DMAs we should have in one second + ** and scale it down just a tad + */ + projected_dmacount = (int) ((0.8 * sb.sample_rate) / sb.buf_size); + if (projected_dmacount < 1) + projected_dmacount = 1; + + /* get the transfer going, so we can ensure interrupts are firing */ + start_transfer(); + count = clock(); + while ((clock() - count) < CLOCKS_PER_SEC && dma.count < projected_dmacount) + ; /* spin */ + + if (dma.count < projected_dmacount) + { + if (true == dma.autoinit) + { + thin_printf("thinlib.sb: Autoinit DMA failed, trying one-shot mode.\n"); + dsp_reset(); + dma.autoinit = false; + dma.count = 0; + return (thin_sb_start(fillbuf, user_data)); + } + else + { + thin_printf("thinlib.sb: One-shot DMA mode failed, sound will not be heard.\n"); + thin_printf("thinlib.sb: DSP version: %d.%d baseio: %X IRQ: %d DMA: %d High: %d\n", + sb.dsp_version >> 8, sb.dsp_version & 0xFF, + sb.baseio, sb.irq, sb.dma, sb.dma16); + return -1; + } + } + + return 0; +} + +/* +** $Log: $ +*/ diff --git a/sys/thinlib/lib/tl_sb.h b/sys/thinlib/lib/tl_sb.h new file mode 100644 index 0000000..74889ad --- /dev/null +++ b/sys/thinlib/lib/tl_sb.h @@ -0,0 +1,35 @@ +/* +** thinlib (c) 2001 Matthew Conte (matt@conte.com) +** +** +** tl_sb.h +** +** DOS Sound Blaster header file +** +** $Id: $ +*/ + +#ifndef _TL_SB_H_ +#define _TL_SB_H_ + +typedef void (*sbmix_t)(void *user_data, void *buffer, int size); + +/* Sample format bitfields */ +#define SB_FORMAT_8BIT 0x00 +#define SB_FORMAT_16BIT 0x01 +#define SB_FORMAT_MONO 0x00 +#define SB_FORMAT_STEREO 0x02 +#define SB_FORMAT_UNSIGNED 0x00 +#define SB_FORMAT_SIGNED 0x04 + +extern int thin_sb_init(int *sample_rate, int *buf_size, int *format); +extern void thin_sb_shutdown(void); +extern int thin_sb_start(sbmix_t fillbuf, void *user_data); +extern void thin_sb_stop(void); +extern void thin_sb_setrate(int rate); + +#endif /* !_TL_SB_H_ */ + +/* +** $Log: $ +*/ diff --git a/sys/thinlib/lib/tl_sound.c b/sys/thinlib/lib/tl_sound.c new file mode 100644 index 0000000..35b7bab --- /dev/null +++ b/sys/thinlib/lib/tl_sound.c @@ -0,0 +1,120 @@ +/* +** thinlib (c) 2001 Matthew Conte (matt@conte.com) +** +** +** tl_sound.c +** +** sound driver +** +** $Id: $ +*/ + +#include "tl_types.h" +#include "tl_sound.h" +#include "tl_sb.h" +#include "tl_log.h" + + +typedef struct snddriver_s +{ + const char *name; + int (*init)(int *sample_rate, int *frag_size, int *format); + void (*shutdown)(void); + int (*start)(audio_callback_t callback, void *user_data); + void (*stop)(void); + void (*setrate)(int sample_rate); + audio_callback_t callback; + void *user_data; +} snddriver_t; + +static snddriver_t sb = +{ + "Sound Blaster", + thin_sb_init, + thin_sb_shutdown, + thin_sb_start, + thin_sb_stop, + thin_sb_setrate, + NULL, + NULL +}; + +static snddriver_t *driver_list[] = +{ + &sb, + NULL +}; + +static snddriver_t snddriver; + +int thin_sound_init(thinsound_t *sound_params) +{ + snddriver_t **iter; + int sample_rate, frag_size, format; + + THIN_ASSERT(sound_params); + + sample_rate = sound_params->sample_rate; + frag_size = sound_params->frag_size; + format = sound_params->format; + + for (iter = driver_list; *iter != NULL; iter++) + { + if (0 == (*iter)->init(&sample_rate, &frag_size, &format)) + { + snddriver = **iter; + + /* copy the parameters back */ + sound_params->sample_rate = sample_rate; + sound_params->frag_size = frag_size; + sound_params->format = format; + + /* and set the callback */ + snddriver.callback = sound_params->callback; + snddriver.user_data = sound_params->user_data; + + return 0; + } + } + + snddriver.name = NULL; + + thin_printf("thin: could not find any sound drivers.\n"); + return -1; +} + +void thin_sound_shutdown(void) +{ + if (NULL == snddriver.name) + return; + + snddriver.shutdown(); + memset(&snddriver, 0, sizeof(snddriver_t)); +} + +void thin_sound_start(void) +{ + if (NULL == snddriver.name) + return; + + THIN_ASSERT(snddriver.callback); + snddriver.start(snddriver.callback, snddriver.user_data); +} + +void thin_sound_stop(void) +{ + if (NULL == snddriver.name) + return; + + snddriver.stop(); +} + +void thin_sound_setrate(int sample_rate) +{ + if (snddriver.setrate) + snddriver.setrate(sample_rate); +} + +/* +** $Log: $ +*/ diff --git a/sys/thinlib/lib/tl_sound.h b/sys/thinlib/lib/tl_sound.h new file mode 100644 index 0000000..6e7f28e --- /dev/null +++ b/sys/thinlib/lib/tl_sound.h @@ -0,0 +1,48 @@ +/* +** thinlib (c) 2001 Matthew Conte (matt@conte.com) +** +** +** tl_sound.h +** +** thinlib sound routines +** +** $Id: $ +*/ + +#ifndef _TL_SOUND_H_ +#define _TL_SOUND_H_ + +#include "tl_types.h" + +#define THIN_SOUND_8BIT 0x00 +#define THIN_SOUND_16BIT 0x01 +#define THIN_SOUND_MONO 0x00 +#define THIN_SOUND_STEREO 0x02 +#define THIN_SOUND_UNSIGNED 0x00 +#define THIN_SOUND_SIGNED 0x04 + +typedef void (*audio_callback_t)(void *user_data, void *buffer, int samples); + +typedef struct thinsound_s +{ + int sample_rate; + int frag_size; + int format; + audio_callback_t callback; + void *user_data; +} thinsound_t; + +extern int thin_sound_init(thinsound_t *sound_params); +extern void thin_sound_shutdown(void); + +// TODO: roll into one pause() function +extern void thin_sound_start(void); +extern void thin_sound_stop(void); + +extern void thin_sound_setrate(int sample_rate); + +#endif /* !_TL_SOUND_H_ */ + +/* +** $Log: $ +*/ diff --git a/sys/thinlib/lib/tl_timer.c b/sys/thinlib/lib/tl_timer.c new file mode 100644 index 0000000..2c235aa --- /dev/null +++ b/sys/thinlib/lib/tl_timer.c @@ -0,0 +1,138 @@ +/* +** thinlib (c) 2001 Matthew Conte (matt@conte.com) +** +** +** tl_timer.c +** +** DOS timer routines +** +** $Id: $ +*/ + +#include +#include +#include + +#include "tl_types.h" +#include "tl_djgpp.h" +#include "tl_timer.h" + +#define TIMER_INT 0x08 +#define TIMER_TICKS 1193182L + +#define TIMER_CONTROL 0x43 +#define TIMER_ACCESS 0x40 +#define TIMER_DEFAULT_MODE 0x36 + +static _go32_dpmi_seginfo old_handler, new_handler; + +static struct +{ + timerhandler_t handler; + void *param; + int interval, frac, current; + uint8 current_mode; + bool initialized; +} timer; + +/* port 0x43 - write control word +** port 0x40 - read/write timer count +** control word composition: +** D7-D6 - 0=counter0, 1=counter1, 2=counter2, 3=read-back +** D5-D4 - 0=latch, 1=r/w lsb, 2=r/w msb, 3=r/2 lsb then msb +** D3-D1 - 0=m0, 1=m1, 2/6=m2, 3/7=m3, 4=m4, 5=m5 +** D0 - 0=16-bit binary counter, 1=BCD counter +** +** mode 0 - interrupt on terminal count +** mode 1 - hardware retriggerable one-shot +** mode 2 - rate generator +** mode 3 - square wave mode +** mode 4 - software triggered mode +** mode 5 - hardware triggered strobe (retriggerable) +*/ + + +/* Reprogram the PIT timer to fire at a specified value */ +void thin_timer_setrate(int hertz) +{ + int time; + + if (0 == hertz) + time = 0; + else + time = TIMER_TICKS / (long) hertz; + + timer.interval = time; + timer.frac = time & 0xFFFF; + + if (0 == time) + timer.current_mode = TIMER_DEFAULT_MODE; /* reset to standard */ + + outportb(TIMER_CONTROL, timer.current_mode); + outportb(TIMER_ACCESS, timer.frac & 0xFF); + outportb(TIMER_ACCESS, timer.frac >> 8); +} + +static void _timer_int_handler(void) +{ + timer.current += timer.frac; + if (timer.current >= timer.interval) + { + timer.current -= timer.interval; + timer.handler(timer.param); + } + + outportb(0x20, 0x20); +} +THIN_LOCKED_STATIC_FUNC(_timer_int_handler); + +/* Lock code, data, and chain an interrupt handler */ +int thin_timer_init(int hertz, timerhandler_t func_ptr, void *param) +{ + if (false == timer.initialized) + { + THIN_LOCK_FUNC(_timer_int_handler); + THIN_LOCK_VAR(timer); + + timer.handler = func_ptr; + timer.param = param; + + /* chain onto old timer interrupt */ + _go32_dpmi_get_protected_mode_interrupt_vector(TIMER_INT, &old_handler); + new_handler.pm_offset = (int) _timer_int_handler; + new_handler.pm_selector = _go32_my_cs(); + _go32_dpmi_chain_protected_mode_interrupt_vector(TIMER_INT, &new_handler); + + timer.current = 0; + + /* Set PIC to fire at desired refresh rate */ + + /* counter 0, lsb+msb, mode 3, binary counter */ + timer.current_mode = 0x36; + + timer.initialized = true; + } + + thin_timer_setrate(hertz); + + return 0; +} + +/* Remove the timer handler */ +void thin_timer_shutdown(void) +{ + if (false == timer.initialized) + return; + + /* Restore previous timer setting */ + thin_timer_setrate(0); + + /* Remove the interrupt handler */ + _go32_dpmi_set_protected_mode_interrupt_vector(TIMER_INT, &old_handler); + + timer.initialized = false; +} + +/* +** $Log: $ +*/ diff --git a/sys/thinlib/lib/tl_timer.h b/sys/thinlib/lib/tl_timer.h new file mode 100644 index 0000000..0926eeb --- /dev/null +++ b/sys/thinlib/lib/tl_timer.h @@ -0,0 +1,25 @@ +/* +** thinlib (c) 2001 Matthew Conte (matt@conte.com) +** +** +** tl_timer.h +** +** DOS timer routine defines / prototypes +** +** $Id: $ +*/ + +#ifndef _TL_TIMER_H_ +#define _TL_TIMER_H_ + +typedef void (*timerhandler_t)(void *param); + +extern int thin_timer_init(int hertz, timerhandler_t func_ptr, void *param); +extern void thin_timer_shutdown(void); +extern void thin_timer_setrate(int hertz); + +#endif /* !_TL_TIMER_H_ */ + +/* +** $Log: $ +*/ diff --git a/sys/thinlib/lib/tl_types.h b/sys/thinlib/lib/tl_types.h new file mode 100644 index 0000000..b0862b4 --- /dev/null +++ b/sys/thinlib/lib/tl_types.h @@ -0,0 +1,61 @@ +/* +** thinlib (c) 2001 Matthew Conte (matt@conte.com) +** +** +** tl_types.h +** +** type definitions for thinlib +** +** $Id: $ +*/ + +#ifndef _TL_TYPES_ +#define _TL_TYPES_ + +/* TODO: rethink putting these here. */ +#ifdef THINLIB_DEBUG + +#include "tl_log.h" + +#define THIN_ASSERT(expr) thin_assert((int) (expr), __LINE__, __FILE__, NULL) +#define THIN_ASSERT_MSG(msg) thin_assert(false, __LINE__, __FILE__, (msg)) + +#else /* !THINLIB_DEBUG */ + +#define THIN_ASSERT(expr) +#define THIN_ASSERT_MSG(msg) + +#endif /* !THINLIB_DEBUG */ + +/* quell stupid compiler warnings */ +#define UNUSED(x) ((x) = (x)) + +typedef signed char int8; +typedef signed short int16; +typedef signed int int32; +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint32; + +#ifndef __cplusplus +#undef false +#undef true +#undef NULL + +typedef enum +{ + false = 0, + true = 1 +} bool; + +#ifndef NULL +#define NULL ((void *) 0) +#endif + +#endif /* !__cplusplus */ + +#endif /* !_TL_TYPES_ */ + +/* +** $Log: $ +*/ diff --git a/sys/thinlib/lib/tl_vesa.c b/sys/thinlib/lib/tl_vesa.c new file mode 100644 index 0000000..799f923 --- /dev/null +++ b/sys/thinlib/lib/tl_vesa.c @@ -0,0 +1,401 @@ +/* +** thinlib (c) 2001 Matthew Conte (matt@conte.com) +** +** +** tl_vesa.c +** +** VESA code. +** +** $Id: $ +*/ + +#include +#include +#include +#include +#include +#include + +#include "tl_types.h" +#include "tl_log.h" +#include "tl_bmp.h" + +#include "tl_djgpp.h" + +#include "tl_video.h" +#include "tl_vesa.h" + +#define __PACKED__ __attribute__ ((packed)) + +/* VESA information block structure */ +typedef struct vesainfo_s +{ + char VESASignature[4] __PACKED__; + uint16 VESAVersion __PACKED__; + uint32 OEMStringPtr __PACKED__; + char Capabilities[4] __PACKED__; + uint32 VideoModePtr __PACKED__; + uint16 TotalMemory __PACKED__; + uint16 OemSoftwareRev __PACKED__; + uint32 OemVendorNamePtr __PACKED__; + uint32 OemProductNamePtr __PACKED__; + uint32 OemProductRevPtr __PACKED__; + uint8 Reserved[222] __PACKED__; +} vesainfo_t; + +/* SuperVGA mode information block */ +typedef struct modeinfo_s +{ + uint16 ModeAttributes __PACKED__; + uint8 WinAAttributes __PACKED__; + uint8 WinBAttributes __PACKED__; + uint16 WinGranularity __PACKED__; + uint16 WinSize __PACKED__; + uint16 WinASegment __PACKED__; + uint16 WinBSegment __PACKED__; + uint32 WinFuncPtr __PACKED__; + uint16 BytesPerScanLine __PACKED__; + uint16 XResolution __PACKED__; + uint16 YResolution __PACKED__; + uint8 XCharSize __PACKED__; + uint8 YCharSize __PACKED__; + uint8 NumberOfPlanes __PACKED__; + uint8 BitsPerPixel __PACKED__; + uint8 NumberOfBanks __PACKED__; + uint8 MemoryModel __PACKED__; + uint8 BankSize __PACKED__; + uint8 NumberOfImagePages __PACKED__; + uint8 Reserved_page __PACKED__; + uint8 RedMaskSize __PACKED__; + uint8 RedMaskPos __PACKED__; + uint8 GreenMaskSize __PACKED__; + uint8 GreenMaskPos __PACKED__; + uint8 BlueMaskSize __PACKED__; + uint8 BlueMaskPos __PACKED__; + uint8 ReservedMaskSize __PACKED__; + uint8 ReservedMaskPos __PACKED__; + uint8 DirectColorModeInfo __PACKED__; + + /* VBE 2.0 extensions */ + uint32 PhysBasePtr __PACKED__; + uint32 OffScreenMemOffset __PACKED__; + uint16 OffScreenMemSize __PACKED__; + + /* VBE 3.0 extensions */ + uint16 LinBytesPerScanLine __PACKED__; + uint8 BnkNumberOfPages __PACKED__; + uint8 LinNumberOfPages __PACKED__; + uint8 LinRedMaskSize __PACKED__; + uint8 LinRedFieldPos __PACKED__; + uint8 LinGreenMaskSize __PACKED__; + uint8 LinGreenFieldPos __PACKED__; + uint8 LinBlueMaskSize __PACKED__; + uint8 LinBlueFieldPos __PACKED__; + uint8 LinRsvdMaskSize __PACKED__; + uint8 LinRsvdFieldPos __PACKED__; + uint32 MaxPixelClock __PACKED__; + + uint8 Reserved[190] __PACKED__; +} modeinfo_t; + + +#define MASK_LINEAR(addr) (addr & 0x000FFFFF) +#define RM_TO_LINEAR(addr) (((addr & 0xFFFF0000) >> 12) + (addr & 0xFFFF)) +#define RM_OFFSET(addr) (addr & 0xF) +#define RM_SEGMENT(addr) ((addr >> 4) & 0xFFFF) + +#define VBE_LINEAR_ADDR 0x4000 +#define VBE_LINEAR_AVAIL 0x0080 + +#define VBE_INT 0x10 +#define VBE_SUCCESS 0x004F +#define VBE_FUNC_DETECT 0x4F00 +#define VBE_FUNC_GETMODEINFO 0x4F01 +#define VBE_FUNC_SETMODE 0x4F02 +#define VBE_FUNC_GETMODE 0x4F03 +#define VBE_FUNC_FLIPPAGE 0x4F07 + +#define MAX_NUM_MODES 256 + +short int vid_selector = -1; +static uint16 modelist[MAX_NUM_MODES]; +static bitmap_t *screen = NULL; +static bitmap_t *hardware = NULL; +static int total_memory = 0; +static bool vesa_hardware = false; + +/* look for vesa */ +static int vesa_detect(void) +{ + vesainfo_t vesa_info; + __dpmi_regs regs; + long list_ptr; + int mode_pos; + + /* Use DOS transfer buffer to hold VBE info */ + THIN_ASSERT(sizeof(vesainfo_t) < _go32_info_block.size_of_transfer_buffer); + memset(®s, 0, sizeof(__dpmi_regs)); + + strncpy(vesa_info.VESASignature, "VBE2", 4); + dosmemput(&vesa_info, sizeof(vesainfo_t), MASK_LINEAR(__tb)); + + regs.x.ax = VBE_FUNC_DETECT; + regs.x.es = RM_SEGMENT(__tb); + regs.x.di = RM_OFFSET(__tb); + + __dpmi_int(VBE_INT, ®s); + if (VBE_SUCCESS != regs.x.ax) + return -1; + + dosmemget(MASK_LINEAR(__tb), sizeof(vesainfo_t), &vesa_info); + if (strncmp(vesa_info.VESASignature, "VESA", 4) != 0) + return -1; + + /* check to see if linear framebuffer is available */ + if ((vesa_info.VESAVersion >> 8) < 2) + { + thin_printf("thinlib.vesa: no linear framebuffer available\n"); + return -1; + } + + /* build list of available modes */ + memset(&modelist, 0, MAX_NUM_MODES * sizeof(uint16)); + mode_pos = 0; + + list_ptr = RM_TO_LINEAR(vesa_info.VideoModePtr); + while (1) + { + uint16 mode; + + dosmemget(list_ptr + mode_pos * 2, 2, &mode); + + if (0xFFFF == mode) + { + modelist[mode_pos] = 0; + break; + } + + modelist[mode_pos++] = mode; + } + + total_memory = vesa_info.TotalMemory; + + return 0; +} + +static int vesa_getmodeinfo(uint16 mode, modeinfo_t *modeinfo) +{ + __dpmi_regs regs; + + THIN_ASSERT(sizeof(modeinfo_t) < _go32_info_block.size_of_transfer_buffer); + + memset(®s, 0, sizeof(regs)); + regs.x.ax = VBE_FUNC_GETMODEINFO; + regs.x.cx = mode; + regs.x.es = RM_SEGMENT(__tb); + regs.x.di = RM_OFFSET(__tb); + + __dpmi_int(VBE_INT, ®s); + if (VBE_SUCCESS != regs.x.ax) + return -1; + + dosmemget(MASK_LINEAR(__tb), sizeof(modeinfo_t), modeinfo); + return 0; +} + +static uint16 vesa_findmode(int width, int height, int bpp) +{ + modeinfo_t mode_info; + uint16 mode; + int mode_pos; + + for (mode_pos = 0; ; mode_pos++) + { + mode = modelist[mode_pos]; + + if (0 == mode) + break; + + if (vesa_getmodeinfo(mode, &mode_info)) + break; /* we are definitely screwed */ + + if (mode_info.XResolution == width && mode_info.YResolution == height + && mode_info.BitsPerPixel == bpp) + { + return mode; + } + } + + return 0; +} + +int thin_vesa_setmode(int width, int height, int bpp) +{ + uint16 mode; + __dpmi_regs regs; + __dpmi_meminfo mi; + modeinfo_t mode_info; + unsigned int address; + + mode = vesa_findmode(width, height, bpp); + if (0 == mode) + { + thin_printf("thinlib.vesa: yikes, couldn't find mode\n"); + return -1; + } + + if (vesa_getmodeinfo(mode, &mode_info)) + { + thin_printf("thinlib.vesa: error in vesa_getmodeinfo\n"); + return -1; + } + + mi.size = mode_info.BytesPerScanLine * mode_info.YResolution; + mi.address = mode_info.PhysBasePtr; + if (-1 == __dpmi_physical_address_mapping(&mi)) + { + thin_printf("thinlib.vesa: error in __dpmi_physical_address_mapping\n"); + return -1; + } + + if (false == vesa_hardware) + { + vid_selector = __dpmi_allocate_ldt_descriptors(1); + if (-1 == vid_selector) + { + thin_printf("thinlib.vesa: error in __dpmi_allocate_ldt_descriptors\n"); + return -1; + } + + /* paranoid */ + if (-1 == __dpmi_set_descriptor_access_rights(vid_selector, 0x40f3)) + { + thin_printf("thinlib.vesa: error in __dpmi_set_descriptor_access_rights\n"); + return -1; + } + + if (-1 == __dpmi_set_segment_base_address(vid_selector, mi.address)) + { + thin_printf("thinlib.vesa: error in __dpmi_set_segment_base_address\n"); + return -1; + } + + if (-1 == __dpmi_set_segment_limit(vid_selector, total_memory << 16 | 0xfff)) + { + thin_printf("thinlib.vesa: error in __dpmi_set_segment_limit\n"); + return -1; + } + } + + memset(®s, 0, sizeof(regs)); + regs.x.ax = VBE_FUNC_SETMODE; + regs.x.bx = mode | VBE_LINEAR_ADDR; + + __dpmi_int(VBE_INT, ®s); + if (VBE_SUCCESS != regs.x.ax) + { + thin_printf("thinlib.vesa: vesa dpmi int failed\n"); + return -1; + } + + if (false == vesa_hardware) + address = 0; + else + address = mi.address; + + if (NULL != screen) + thin_bmp_destroy(&screen); + + if (vesa_hardware) + { + screen = thin_bmp_createhw((uint8 *) THIN_PHYSICAL_ADDR(address), + mode_info.XResolution, mode_info.YResolution, + mode_info.BitsPerPixel, mode_info.XResolution); + if (NULL == screen) + { + thin_printf("thinlib.vesa: failed creating hardware surface\n"); + return -1; + } + } + else + { + if (NULL != hardware) + thin_bmp_destroy(&hardware); + + hardware = thin_bmp_createhw((uint8 *) address, + mode_info.XResolution, mode_info.YResolution, + mode_info.BitsPerPixel, mode_info.XResolution); + if (NULL == hardware) + { + thin_printf("thinlib.vesa: failed creating hardware surface\n"); + return -1; + } + + screen = thin_bmp_create(mode_info.XResolution, mode_info.YResolution, + mode_info.BitsPerPixel, 0); + if (NULL == screen) + { + thin_printf("thinlib.vesa: failed creating software surface\n"); + return -1; + } + } + + return 0; +} + +void thin_vesa_shutdown(void) +{ + __dpmi_regs regs; + + /* set text mode */ + memset(®s, 0, sizeof(regs)); + regs.x.ax = 0x0003; + __dpmi_int(VBE_INT, ®s); + + if (NULL != screen) + thin_bmp_destroy(&screen); + + if (NULL != hardware) + thin_bmp_destroy(&hardware); +} + +int thin_vesa_init(int width, int height, int bpp, int param) +{ + screen = NULL; + hardware = NULL; + + /* check to see if VESA card is present */ + if (vesa_detect()) + return -1; + + if (thinlib_nearptr && (param & THIN_VIDEO_HWSURFACE)) + vesa_hardware = true; + else + vesa_hardware = false; + + return thin_vesa_setmode(width, height, bpp); +} + +bitmap_t *thin_vesa_lockwrite(void) +{ + return screen; +} + + +void thin_vesa_freewrite(int num_dirties, rect_t *dirty_rects) +{ + UNUSED(num_dirties); + UNUSED(dirty_rects); + + /* if we don't have a hardware surface, blat it out */ + if (false == vesa_hardware) + { + _movedatal(_my_ds(), (unsigned) screen->line[0], + vid_selector, (unsigned) hardware->line[0], + hardware->pitch * hardware->height / 4); + } +} + +/* +** $Log: $ +*/ diff --git a/sys/thinlib/lib/tl_vesa.h b/sys/thinlib/lib/tl_vesa.h new file mode 100644 index 0000000..3bcba07 --- /dev/null +++ b/sys/thinlib/lib/tl_vesa.h @@ -0,0 +1,29 @@ +/* +** thinlib (c) 2001 Matthew Conte (matt@conte.com) +** +** +** tl_vesa.h +** +** VESA code header +** +** $Id: $ +*/ + +#ifndef _TL_VESA_H_ +#define _TL_VESA_H_ + +#include "tl_bmp.h" + +extern int thin_vesa_init(int width, int height, int bpp, int param); +extern void thin_vesa_shutdown(void); + +extern int thin_vesa_setmode(int width, int height, int bpp); + +extern bitmap_t *thin_vesa_lockwrite(void); +extern void thin_vesa_freewrite(int num_dirties, rect_t *dirty_rects); + +#endif /* !_TL_VESA_H_ */ + +/* +** $Log: $ +*/ diff --git a/sys/thinlib/lib/tl_vga.c b/sys/thinlib/lib/tl_vga.c new file mode 100644 index 0000000..ba851b3 --- /dev/null +++ b/sys/thinlib/lib/tl_vga.c @@ -0,0 +1,381 @@ +/* +** thinlib (c) 2001 Matthew Conte (matt@conte.com) +** +** +** tl_vga.c +** +** VGA-related functions +** +** $Id: $ +*/ + +#include +#include +#include +#include +#include "tl_types.h" +#include "tl_bmp.h" +#include "tl_video.h" +#include "tl_vga.h" +#include "tl_djgpp.h" +#include "tl_int.h" + +#define DEFAULT_OVERSCAN 0 + +#define MODE_TEXT 0x03 +#define MODE_13H 0x13 + +#define VGA_ADDRESS 0xA0000 /* we love segments! */ + +/* VGA card register addresses */ +#define VGA_ATTR 0x3C0 /* Attribute reg */ +#define VGA_MISC 0x3C2 /* Misc. output register */ +#define VGA_SEQ_ADDR 0x3C4 /* Base port of sequencer */ +#define VGA_SEQ_DATA 0x3C5 /* Data port of sequencer */ +#define VGA_CRTC_ADDR 0x3D4 /* Base port of CRT controller */ +#define VGA_CRTC_DATA 0x3D5 /* Data port of CRT controller */ +#define VGA_STATUS 0x3DA /* Input status #1 register */ + +#define VGA_PAL_READ 0x3C7 /* Palette read address */ +#define VGA_PAL_WRITE 0x3C8 /* Palette write address */ +#define VGA_PAL_DATA 0x3C9 /* Palette data register */ + +/* generic VGA CRTC register indexes */ +#define HZ_DISPLAY_TOTAL 0x00 +#define HZ_DISPLAY_END 0x01 +#define CRTC_OVERFLOW 0x07 +#define VT_DISPLAY_END 0x12 +#define MEM_OFFSET 0x13 + +/* indices into our register array */ +#define CLOCK_INDEX 0 +#define H_TOTAL_INDEX 1 +#define H_DISPLAY_INDEX 2 +#define H_BLANKING_START_INDEX 3 +#define H_BLANKING_END_INDEX 4 +#define H_RETRACE_START_INDEX 5 +#define H_RETRACE_END_INDEX 6 +#define V_TOTAL_INDEX 7 +#define OVERFLOW_INDEX 8 +#define MAXIMUM_SCANLINE_INDEX 10 +#define V_RETRACE_START_INDEX 11 +#define V_RETRACE_END_INDEX 12 +#define V_END_INDEX 13 +#define MEM_OFFSET_INDEX 14 +#define UNDERLINE_LOC_INDEX 15 +#define V_BLANKING_START_INDEX 16 +#define V_BLANKING_END_INDEX 17 +#define MODE_CONTROL_INDEX 18 +#define MEMORY_MODE_INDEX 20 + + +typedef struct vgareg_s +{ + int port; + int index; + uint8 value; +} vgareg_t; + +typedef struct vgamode_s +{ + int width; + int height; + char *name; + vgareg_t *regs; +} vgamode_t; + +/* 60 Hz */ +static vgareg_t mode_256x224[] = +{ + { 0x3C2, 0x00, 0xE3 }, { 0x3D4, 0x00, 0x5F }, { 0x3D4, 0x01, 0x3F }, + { 0x3D4, 0x02, 0x40 }, { 0x3D4, 0x03, 0x82 }, { 0x3D4, 0x04, 0x4A }, + { 0x3D4, 0x05, 0x9A }, { 0x3D4, 0x06, 0x0B }, { 0x3D4, 0x07, 0xB2 }, + { 0x3D4, 0x08, 0x00 }, { 0x3D4, 0x09, 0x61 }, { 0x3d4, 0x10, 0x00 }, + { 0x3D4, 0x11, 0xAC }, { 0x3D4, 0x12, 0xBF }, { 0x3D4, 0x13, 0x20 }, + { 0x3D4, 0x14, 0x40 }, { 0x3D4, 0x15, 0x01 }, { 0x3D4, 0x16, 0x0A }, + { 0x3D4, 0x17, 0xA3 }, { 0x3C4, 0x01, 0x01 }, { 0x3C4, 0x04, 0x0E }, + { 0, 0, 0 } +}; + +static vgareg_t mode_256x240[] = +{ + { 0x3c2, 0x00, 0xe3},{ 0x3d4, 0x00, 0x55},{ 0x3d4, 0x01, 0x3f}, + { 0x3d4, 0x02, 0x80},{ 0x3d4, 0x03, 0x90},{ 0x3d4, 0x04, 0x49}, + { 0x3d4, 0x05, 0x80},{ 0x3D4, 0x06, 0x43},{ 0x3d4, 0x07, 0xb2}, + { 0x3d4, 0x08, 0x00},{ 0x3D4, 0x09, 0x61},{ 0x3d4, 0x10, 0x04}, + { 0x3d4, 0x11, 0xac},{ 0x3D4, 0x12, 0xdf},{ 0x3d4, 0x13, 0x20}, + { 0x3d4, 0x14, 0x40},{ 0x3d4, 0x15, 0x07},{ 0x3D4, 0x16, 0x11}, + { 0x3d4, 0x17, 0xa3},{ 0x3c4, 0x01, 0x01},{ 0x3c4, 0x04, 0x0e}, + { 0, 0, 0 } +}; + +static vgareg_t mode_256x256[] = +{ + { 0x3C2, 0x00, 0xE3 }, { 0x3D4, 0x00, 0x5F }, { 0x3D4, 0x01, 0x3F }, + { 0x3D4, 0x02, 0x40 }, { 0x3D4, 0x03, 0x82 }, { 0x3D4, 0x04, 0x4A }, + { 0x3D4, 0x05, 0x9A }, { 0x3D4, 0x06, 0x23 }, { 0x3D4, 0x07, 0xB2 }, + { 0x3D4, 0x08, 0x00 }, { 0x3D4, 0x09, 0x61 }, { 0x3D4, 0x10, 0x0A }, + { 0x3D4, 0x11, 0xAC }, { 0x3D4, 0x12, 0xFF }, { 0x3D4, 0x13, 0x20 }, + { 0x3D4, 0x14, 0x40 }, { 0x3D4, 0x15, 0x07 }, { 0x3D4, 0x16, 0x1A }, + { 0x3D4, 0x17, 0xA3 }, { 0x3C4, 0x01, 0x01 }, { 0x3C4, 0x04, 0x0E }, + { 0, 0, 0 } +}; + +/* 60 Hz */ +static vgareg_t mode_256x256wide[] = +{ + { 0x3C2, 0x00, 0xE3 }, { 0x3D4, 0x00, 0x52 }, { 0x3D4, 0x01, 0x3F }, + { 0x3D4, 0x02, 0x80 }, { 0x3D4, 0x03, 0x90 }, { 0x3D4, 0x04, 0x49 }, + { 0x3D4, 0x05, 0x80 }, { 0x3D4, 0x06, 0x55 }, { 0x3D4, 0x07, 0xB2 }, + { 0x3D4, 0x08, 0x00 }, { 0x3D4, 0x09, 0x61 }, { 0x3D4, 0x10, 0x20 }, + { 0x3D4, 0x11, 0xAC }, { 0x3D4, 0x12, 0xFF }, { 0x3D4, 0x13, 0x20 }, + { 0x3D4, 0x14, 0x40 }, { 0x3D4, 0x15, 0x07 }, { 0x3D4, 0x16, 0x1A }, + { 0x3D4, 0x17, 0xA3 }, { 0x3C4, 0x01, 0x01 }, { 0x3C4, 0x04, 0x0E }, + { 0, 0, 0 } +}; + +/* 60 Hz */ +static vgareg_t mode_288x224[] = +{ + { 0x3C2, 0x00, 0xE3 }, { 0x3D4, 0x00, 0x5F }, { 0x3D4, 0x01, 0x47 }, + { 0x3D4, 0x02, 0x50 }, { 0x3D4, 0x03, 0x82 }, { 0x3D4, 0x04, 0x50 }, + { 0x3D4, 0x05, 0x80 }, { 0x3D4, 0x06, 0x08 }, { 0x3D4, 0x07, 0x3E }, + { 0x3D4, 0x08, 0x00 }, { 0x3D4, 0x09, 0x41 }, { 0x3D4, 0x10, 0xDA }, + { 0x3D4, 0x11, 0x9C }, { 0x3D4, 0x12, 0xBF }, { 0x3D4, 0x13, 0x24 }, + { 0x3D4, 0x14, 0x40 }, { 0x3D4, 0x15, 0xC7 }, { 0x3D4, 0x16, 0x04 }, + { 0x3D4, 0x17, 0xA3 }, { 0x3C4, 0x01, 0x01 }, { 0x3C4, 0x04, 0x0E }, + { 0, 0, 0 } +}; + +static vgareg_t mode_320x200[] = +{ + { 0, 0, 0 } +}; + +static vgamode_t vidmodes[] = +{ + { 288, 224, "288x224", mode_288x224 }, + { 256, 224, "256x224", mode_256x224 }, + { 256, 240, "256x240", mode_256x240 }, + { 256, 256, "256x256 (wide)", mode_256x256wide }, + { 256, 256, "256x256", mode_256x256 }, + { 320, 200, "320x200", mode_320x200 }, + { 0, 0, NULL, 0 } +}; + + +static bitmap_t *screen = NULL; +static bitmap_t *hardware = NULL; + +/* current VGA mode */ +static vgamode_t *vga_mode = NULL; +static bool vga_hardware = false; + +/* Set a VGA mode */ +static void vga_setvgamode(uint8 mode) +{ + __dpmi_regs r; + r.x.ax = mode; + __dpmi_int(0x10, &r); +} + +static void vga_set_overscan(int index) +{ + outportb(VGA_ATTR, 0x31); + outportb(VGA_ATTR, index); +} + +static void vga_outregs(vgareg_t *reg) +{ + uint8 crtc_val; + + /* Disable interrupts, wait for vertical retrace */ +// thin_vga_waitvsync(); + THIN_DISABLE_INTS(); + + /* Sequencer reset */ + outportb(VGA_SEQ_ADDR, 0x00); + outportb(VGA_SEQ_DATA, 0x01); + crtc_val = inportb(VGA_CRTC_DATA) & 0x7F; + + /* Unprotect registers 0-7 */ + outportb(VGA_CRTC_ADDR, 0x11); + outportb(VGA_CRTC_DATA, crtc_val); + + /* Reset read/write flip-flop */ + inportb(VGA_STATUS); + + /* Do the icky register stuff */ + while (reg->port) + { + switch(reg->port) + { + case VGA_ATTR: + /* Reset read/write flip-flop */ + inportb(VGA_STATUS); + /* Ensure VGA output is enabled - bit 5 */ + outportb(VGA_ATTR, reg->index | 0x20); + outportb(VGA_ATTR, reg->value); + break; + + case VGA_MISC: + /* Write directly to port */ + outportb(reg->port, reg->value); + break; + + case VGA_SEQ_ADDR: + case VGA_CRTC_ADDR: + default: + /* Index to port, value to port + 1 */ + outportb(reg->port, reg->index); + outportb(reg->port + 1, reg->value); + break; + } + + reg++; + } + + /* Set overscan color */ + vga_set_overscan(DEFAULT_OVERSCAN); + + /* Clear sequencer reset */ + outportb(VGA_SEQ_ADDR, 0x00); + outportb(VGA_SEQ_DATA, 0x03); + + THIN_ENABLE_INTS(); +} + +/* Set up VGA mode 13h, then tweak it appropriately */ +int thin_vga_setmode(int width, int height, int bpp) +{ + if (8 != bpp) + return -1; + + vga_mode = vidmodes; + + /* Search for the video mode */ + while ((vga_mode->width != width) || (vga_mode->height != height)) + { + if (NULL == vga_mode->regs) + { + vga_mode = NULL; + return -1; + } + vga_mode++; + } + + /* Set up our standard mode 13h */ + vga_setvgamode(MODE_13H); + + vga_outregs(vga_mode->regs); + + return 0; +} + +/* Destroy VGA */ +void thin_vga_shutdown(void) +{ + /* set textmode */ + vga_setvgamode(MODE_TEXT); + + if (screen) + thin_bmp_destroy(&screen); + + if (hardware) + thin_bmp_destroy(&hardware); +} + +/* Initialize VGA */ +int thin_vga_init(int width, int height, int bpp, int param) +{ + if (8 != bpp) + return -1; + + /* ensure we really want a hardware surface */ + if (thinlib_nearptr && (param & THIN_VIDEO_HWSURFACE)) + { + vga_hardware = true; + screen = thin_bmp_createhw((uint8 *) THIN_PHYSICAL_ADDR(VGA_ADDRESS), + width, height, bpp, width); + if (NULL == screen) + return -1; + } + else + { + vga_hardware = false; + hardware = thin_bmp_createhw((uint8 *) VGA_ADDRESS, + width, height, bpp, width); + if (NULL == hardware) + return -1; + + screen = thin_bmp_create(width, height, bpp, 0); + if (NULL == screen) + return -1; + } + + /* Set the initial video mode, no scanlines */ + if (thin_vga_setmode(width, height, bpp)) + { + thin_vga_shutdown(); + return -1; + } + + return 0; +} + +/* cram an 8-bit, 256 entry rgb palette into 6-bit vga */ +void thin_vga_setpalette(rgb_t *palette, int index, int length) +{ + int i; + + /* we also want to find the closest color index to black, + ** and set that as our overscan color + */ + int overscan_index = 0; + int overscan_sum = 255 * 3; + + outportb(VGA_PAL_WRITE, index); + + for (i = 0; i < length; i++) + { + if (palette[i].r + palette[i].g + palette[i].b < overscan_sum) + { + overscan_sum = palette[i].r + palette[i].g + palette[i].b; + overscan_index = index + i; + } + + outportb(VGA_PAL_DATA, palette[i].r >> 2); + outportb(VGA_PAL_DATA, palette[i].g >> 2); + outportb(VGA_PAL_DATA, palette[i].b >> 2); + } + + vga_set_overscan(overscan_index); +} + +void thin_vga_waitvsync(void) +{ + while (0 == (inportb(VGA_STATUS) & 0x08)); + //while (inportb(VGA_STATUS) & 0x08); +} + +bitmap_t *thin_vga_lockwrite(void) +{ + /* always return screen */ + return screen; +} + +void thin_vga_freewrite(int num_dirties, rect_t *dirty_rects) +{ + UNUSED(num_dirties); + UNUSED(dirty_rects); + + if (false == vga_hardware) + { + dosmemput(screen->line[0], hardware->pitch * hardware->height, + (int) hardware->line[0]); + } +} + +/* +** $Log: $ +*/ diff --git a/sys/thinlib/lib/tl_vga.h b/sys/thinlib/lib/tl_vga.h new file mode 100644 index 0000000..ce53812 --- /dev/null +++ b/sys/thinlib/lib/tl_vga.h @@ -0,0 +1,32 @@ +/* +** thinlib (c) 2001 Matthew Conte (matt@conte.com) +** +** +** tl_vga.h +** +** VGA-specific thinlib routines +** +** $Id: $ +*/ + +#ifndef _TL_VGA_H_ +#define _TL_VGA_H_ + +#include "tl_types.h" +#include "tl_bmp.h" + +extern int thin_vga_init(int width, int height, int bpp, int param); +extern void thin_vga_shutdown(void); + +extern int thin_vga_setmode(int width, int height, int bpp); +extern void thin_vga_setpalette(rgb_t *palette, int index, int length); +extern void thin_vga_waitvsync(void); + +extern bitmap_t *thin_vga_lockwrite(void); +extern void thin_vga_freewrite(int num_dirties, rect_t *dirty_rects); + +#endif /* !_TL_VGA_H_ */ + +/* +** $Log: $ +*/ diff --git a/sys/thinlib/lib/tl_video.c b/sys/thinlib/lib/tl_video.c new file mode 100644 index 0000000..f4290b7 --- /dev/null +++ b/sys/thinlib/lib/tl_video.c @@ -0,0 +1,140 @@ +/* +** thinlib (c) 2001 Matthew Conte (matt@conte.com) +** +** +** tl_video.h +** +** thinlib video routines +** +** $Id: $ +*/ + +#include "tl_types.h" +#include "tl_video.h" +#include "tl_vesa.h" +#include "tl_vga.h" +#include "tl_log.h" + +typedef struct viddriver_s +{ + const char *name; + int (*init)(int width, int height, int bpp, int param); + void (*shutdown)(void); + int (*setmode)(int width, int height, int bpp); + void (*setpalette)(rgb_t *palette, int index, int length); + void (*waitvsync)(void); + bitmap_t *(*lock)(void); + void (*free)(int num_dirties, rect_t *dirty_rects); + void (*blit)(bitmap_t *primary, int num_dirties, rect_t *dirty_rects); + int caps; +} viddriver_t; + +static viddriver_t vesa = +{ + "VESA 3.0 LFB", + thin_vesa_init, + thin_vesa_shutdown, + thin_vesa_setmode, + thin_vga_setpalette, + thin_vga_waitvsync, + thin_vesa_lockwrite, + thin_vesa_freewrite, + NULL, + 0, +}; + +static viddriver_t vga = +{ + "VGA", + thin_vga_init, + thin_vga_shutdown, + thin_vga_setmode, + thin_vga_setpalette, + thin_vga_waitvsync, + thin_vga_lockwrite, + thin_vga_freewrite, + NULL, + 0, /*THIN_VIDEO_SCANLINES,*/ +}; + +static viddriver_t *driver_list[] = +{ + &vesa, + &vga, + NULL +}; + +static viddriver_t driver; + +int thin_vid_init(int width, int height, int bpp, int param) +{ + /* cascade driver checks by iterating through all drivers */ + viddriver_t **iter; + + for (iter = driver_list; *iter != NULL; iter++) + { + if (0 == (*iter)->init(width, height, bpp, param)) + { + driver = **iter; + return 0; + } + } + + driver.name = NULL; + thin_printf("thinlib.video: could not find any matching video modes.\n"); + return -1; +} + +void thin_vid_shutdown(void) +{ + if (NULL != driver.name) + { + driver.shutdown(); + memset(&driver, 0, sizeof(viddriver_t)); + } +} + +int thin_vid_getcaps(void) +{ + return driver.caps; +} + +int thin_vid_setmode(int width, int height, int bpp) +{ + if (driver.setmode(width, height, bpp)) + { + thin_printf("thinlib.video: could not set %s video mode %dx%d %dbpp\n", + driver.name, width, height, bpp); + return -1; + } + + return 0; +} + +void thin_vid_setpalette(rgb_t *palette, int index, int length) +{ + driver.setpalette(palette, index, length); +} + +bitmap_t *thin_vid_lockwrite(void) +{ + return driver.lock(); +} + +void thin_vid_freewrite(int num_dirties, rect_t *dirty_rects) +{ + if (NULL != driver.free) + driver.free(num_dirties, dirty_rects); +} + +void thin_vid_customblit(bitmap_t *primary, int num_dirties, + rect_t *dirty_rects) +{ + THIN_ASSERT(driver.blit); + + driver.blit(primary, num_dirties, dirty_rects); +} + +/* +** $Log: $ +*/ diff --git a/sys/thinlib/lib/tl_video.h b/sys/thinlib/lib/tl_video.h new file mode 100644 index 0000000..731ad78 --- /dev/null +++ b/sys/thinlib/lib/tl_video.h @@ -0,0 +1,41 @@ +/* +** thinlib (c) 2001 Matthew Conte (matt@conte.com) +** +** +** tl_video.h +** +** thinlib video routines +** +** $Id: $ +*/ + +#ifndef _TL_VIDEO_H_ +#define _TL_VIDEO_H_ + +#include "tl_types.h" +#include "tl_bmp.h" + +/* video driver capabilities */ +#define THIN_VIDEO_CUSTOMBLIT 0x0001 +#define THIN_VIDEO_SCANLINES 0x0002 +#define THIN_VIDEO_HWSURFACE 0x0004 + +extern int thin_vid_getcaps(void); + +extern int thin_vid_init(int width, int height, int bpp, int param); +extern void thin_vid_shutdown(void); + +extern int thin_vid_setmode(int width, int height, int bpp); +extern void thin_vid_setpalette(rgb_t *palette, int index, int length); + +extern bitmap_t *thin_vid_lockwrite(void); +extern void thin_vid_freewrite(int num_dirties, rect_t *dirty_rects); + +extern void thin_vid_customblit(bitmap_t *primary, int num_dirties, + rect_t *dirty_rects); + +#endif /* !_TL_VIDEO_H_ */ + +/* +** $Log: $ +*/ diff --git a/sys/thinlib/thinlib.c b/sys/thinlib/thinlib.c new file mode 100644 index 0000000..bd2d943 --- /dev/null +++ b/sys/thinlib/thinlib.c @@ -0,0 +1,375 @@ +#include +#include +#include "fb.h" +#include "input.h" +#include "rc.h" +#include "pcm.h" +#include "thinlib.h" + +struct pcm pcm; + +static volatile int audio_int; + +static int samplerate = 44100; +static int sound = 1; +static int stereo = 0; + +static int joystick = 1; + +static int dpp = 0; +static int dpp_pad = 0; +static int dpp_port = 0x378; + +struct fb fb; + +static bitmap_t *screen = NULL; +static int vmode[3] = { 320, 200, 8 }; + +rcvar_t vid_exports[] = +{ + RCV_VECTOR("vmode", vmode, 3), + RCV_END +}; + +rcvar_t pcm_exports[] = +{ + RCV_BOOL("sound", &sound), + RCV_INT("samplerate", &samplerate), + RCV_INT("stereo", &stereo), + RCV_END +}; + +rcvar_t joy_exports[] = +{ + RCV_BOOL("joystick", &joystick), + RCV_BOOL("dpp", &dpp), + RCV_INT("dpp_pad", &dpp_pad), + RCV_INT("dpp_port", &dpp_port), + RCV_END +}; + +void joy_init() +{ + if (joystick) + { + if (thin_joy_init()) + joystick = 0; + } + + if (dpp) + { + if (thin_dpp_init()) + dpp = 0; + else + thin_dpp_add(dpp_port, dpp_pad); + } +} + +void joy_close() +{ + if (joystick) + thin_joy_shutdown(); + if (dpp) + thin_dpp_shutdown(); +} + +void joy_poll() +{ + /* handled by event polling */ +} + + +/* hardware audio buffer fill */ +static void _audio_callback(void *user_data, void *buf, int len) +{ + /* user_data unused */ + memcpy(buf, pcm.buf, len); + audio_int = 1; +} + +void pcm_init() +{ + thinsound_t params; + int i; + + if (!sound) + { + pcm.hz = 11025; + pcm.len = 4096; + pcm.buf = (byte *) malloc(pcm.len); + pcm.pos = 0; + pcm.stereo = stereo; + return; + } + + params.sample_rate = samplerate; + params.frag_size = samplerate / 60; + for (i = 1; i < params.frag_size; i <<= 1); + params.frag_size = i; + params.format = THIN_SOUND_8BIT | THIN_SOUND_UNSIGNED; + if (stereo) + params.format |= THIN_SOUND_STEREO; + else + params.format |= THIN_SOUND_MONO; + params.callback = _audio_callback; + + if (thin_sound_init(¶ms)) + { + sound = 0; + return; + } + + pcm.hz = params.sample_rate; + pcm.len = params.frag_size; + pcm.stereo = (params.format & THIN_SOUND_STEREO) ? 1 : 0; + pcm.buf = (byte *) malloc(pcm.len); + if (!pcm.buf) + die("failed to allocate sound buffer\n"); + + memset(pcm.buf, 0, pcm.len); + pcm.pos = 0; + + thin_sound_start(); +} + +void pcm_close() +{ + if (sound) + { + thin_sound_stop(); + thin_shutdown(); + } + + if (pcm.buf) + free(pcm.buf); + + memset(&pcm, 0, sizeof pcm); +} + +int pcm_submit() +{ + if (!sound) + { + pcm.pos = 0; + return 0; + } + + if (pcm.pos < pcm.len) + return 1; + + while (!audio_int) + ; /* spin */ + + audio_int = 0; + pcm.pos = 0; + + return 1; +} + + +/* keyboard stuff... */ + +/* keymap - mappings of the form { scancode, localcode } - from keymap.c */ +extern int keymap[][2]; +static int scanmap[256]; + +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 buildscanmap() +{ + int key, i; + + memset(scanmap, 0, sizeof(scanmap)); + + for (key = 0; key < 256; key++) + scanmap[key] = mapscancode(key); +} + +void ev_poll() +{ + thin_event_t event; + event_t ev; + + thin_event_gather(); + + while (thin_event_get(&event)) + { + switch (event.type) + { + case THIN_KEY_PRESS: + ev.type = EV_PRESS; + ev.code = scanmap[event.data.keysym]; + ev_postevent(&ev); + break; + + case THIN_KEY_RELEASE: + ev.type = EV_RELEASE; + ev.code = scanmap[event.data.keysym]; + ev_postevent(&ev); + break; + + case THIN_JOY_MOTION: + ev.type = event.data.joy_motion.state ? EV_PRESS : EV_RELEASE; + switch (event.data.joy_motion.dir) + { + case THIN_JOY_LEFT: + ev.code = K_JOYLEFT; + break; + + case THIN_JOY_RIGHT: + ev.code = K_JOYRIGHT; + break; + + case THIN_JOY_UP: + ev.code = K_JOYUP; + break; + + case THIN_JOY_DOWN: + ev.code = K_JOYDOWN; + break; + } + + ev_postevent(&ev); + break; + + case THIN_JOY_BUTTON_PRESS: + ev.type = EV_PRESS; + ev.code = K_JOY0 + event.data.joy_button; + ev_postevent(&ev); + break; + + case THIN_JOY_BUTTON_RELEASE: + ev.type = EV_RELEASE; + ev.code = K_JOY0 + event.data.joy_button; + ev_postevent(&ev); + break; + + default: + break; + } + } +} + +void vid_preinit() +{ + int gotmask = thin_init(THIN_VIDEO | THIN_SOUND | THIN_KEY); + if ((THIN_VIDEO | THIN_KEY) != (gotmask & (THIN_VIDEO | THIN_KEY))) + die("thinlib initialization failed."); + thin_key_set_repeat(false); + buildscanmap(); + + /* don't spam the graphics screen if we don't have soundcard */ + thin_setlogfunc(NULL); + + joy_init(); +} + +void vid_init() +{ + int red_length, green_length, blue_length; + int red_offset, green_offset, blue_offset; + + if (thin_vid_init(vmode[0], vmode[1], vmode[2], THIN_VIDEO_HWSURFACE)) + die("could not set video mode"); + + screen = thin_vid_lockwrite(); + if (NULL == screen) + die("could not get ahold of video surface"); + + fb.w = screen->width; + fb.h = screen->height; + fb.pitch = screen->pitch; + fb.ptr = screen->data; + + fb.pelsize = (screen->bpp + 7) / 8; + fb.indexed = (screen->bpp == 8) ? 1 : 0; + + switch (screen->bpp) + { + case 8: + red_length = 0; + green_length = 0; + blue_length = 0; + red_offset = 0; + green_offset = 0; + blue_offset = 0; + break; + + case 16: + red_length = 5; + green_length = 6; + blue_length = 5; + red_offset = 11; + green_offset = 5; + blue_offset = 0; + break; + + case 32: + red_length = 8; + green_length = 8; + blue_length = 8; + red_offset = 16; + green_offset = 8; + blue_offset = 0; + break; + + case 15: + case 24: + default: + die("i don't know what to do with %dbpp mode", screen->bpp); + break; + } + + fb.cc[0].r = 8 - red_length; + fb.cc[1].r = 8 - green_length; + fb.cc[2].r = 8 - blue_length; + fb.cc[0].l = red_offset; + fb.cc[1].l = green_offset; + fb.cc[2].l = blue_offset; + + fb.enabled = 1; + fb.dirty = 0; +} + +void vid_close() +{ + fb.enabled = 0; + joy_close(); + thin_shutdown(); +} + +void vid_settitle(char *title) +{ +} + +void vid_setpal(int i, int r, int g, int b) +{ + rgb_t color; + + color.r = r; + color.g = g; + color.b = b; + thin_vid_setpalette(&color, i, 1); +} + +void vid_begin() +{ + screen = thin_vid_lockwrite(); + + fb.ptr = screen->data; + fb.pitch = screen->pitch; + fb.w = screen->width; + fb.h = screen->height; +} + +void vid_end() +{ + thin_vid_freewrite(-1, NULL); +} diff --git a/sys/windows/gameboy.ico b/sys/windows/gameboy.ico new file mode 100644 index 0000000..22598f6 Binary files /dev/null and b/sys/windows/gameboy.ico differ diff --git a/sys/windows/resource.rc b/sys/windows/resource.rc new file mode 100644 index 0000000..1a33296 --- /dev/null +++ b/sys/windows/resource.rc @@ -0,0 +1,3 @@ +#define IDI_GAMEBOYICO 107 + +IDI_GAMEBOYICO ICON DISCARDABLE "sys/mingw32/gameboy.ico" diff --git a/sys/windows/windows.c b/sys/windows/windows.c new file mode 100644 index 0000000..3df9192 --- /dev/null +++ b/sys/windows/windows.c @@ -0,0 +1,77 @@ +/* + * MinGW32 system file + * based on nix.c and dos.c + * req's SDL + * -Dave Kiddell + */ + +#include +#include +#include +char *strdup(); + +#include + +void *sys_timer() +{ + Uint32 *tv; + + tv = malloc(sizeof *tv); + *tv = SDL_GetTicks() * 1000; + return tv; +} + +int sys_elapsed(Uint32 *cl) +{ + Uint32 now; + Uint32 usecs; + + now = SDL_GetTicks() * 1000; + usecs = now - *cl; + *cl = now; + return usecs; +} + +void sys_sleep(int us) +{ + /* dbk: for some reason 2000 works.. + maybe its just compensation for the time it takes for SDL_Delay to + execute, or maybe sys_timer is too slow */ + SDL_Delay(us/1000); +} + +void sys_sanitize(char *s) +{ + int i; + for (i = 0; s[i]; i++) + if (s[i] == '\\') s[i] = '/'; +} + +void sys_initpath(char *exe) +{ + char *buf, *home, *p; + + home = strdup(exe); + sys_sanitize(home); + 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, ".", home); + rc_setvar("savedir", 1, &buf); + free(buf); +} + +void sys_checkdir(char *path, int wr) +{ +} + + diff --git a/sys/x11/keymap.c b/sys/x11/keymap.c new file mode 100644 index 0000000..93cb4bb --- /dev/null +++ b/sys/x11/keymap.c @@ -0,0 +1,96 @@ +/* + * xkeymap.c + * + * Mappings from X keycode to local key codes. + */ + +#include +#include "input.h" + +int keymap[][2] = +{ + { XK_Shift_L, K_SHIFT }, + { XK_Shift_R, K_SHIFT }, + { XK_Control_L, K_CTRL }, + { XK_Control_R, K_CTRL }, + { XK_Alt_L, K_ALT }, + { XK_Alt_R, K_ALT }, + { XK_Meta_L, K_ALT }, + { XK_Meta_R, K_ALT }, + + { XK_Up, K_UP }, + { XK_Down, K_DOWN }, + { XK_Right, K_RIGHT }, + { XK_Left, K_LEFT }, + { XK_Return, K_ENTER }, + { XK_Tab, K_TAB }, + { XK_BackSpace, K_BS }, + { XK_Delete, K_DEL }, + { XK_Insert, K_INS }, + { XK_Home, K_HOME }, + { XK_End, K_END }, + { XK_Prior, K_PRIOR }, + { XK_Next, K_NEXT }, + { XK_Escape, K_ESC }, + { XK_Pause, K_PAUSE }, + { XK_Caps_Lock, K_CAPS }, + { XK_Num_Lock, K_NUMLOCK }, + { XK_Scroll_Lock, K_SCROLL }, + + { XK_minus, K_MINUS }, + { XK_equal, K_EQUALS }, + { XK_asciitilde, K_TILDE }, + + { XK_F1, K_F1 }, + { XK_F2, K_F2 }, + { XK_F3, K_F3 }, + { XK_F4, K_F4 }, + { XK_F5, K_F5 }, + { XK_F6, K_F6 }, + { XK_F7, K_F7 }, + { XK_F8, K_F8 }, + { XK_F9, K_F9 }, + { XK_F10, K_F10 }, + { XK_F11, K_F11 }, + { XK_F12, K_F12 }, + + { XK_KP_Insert, K_NUM0 }, + { XK_KP_End, K_NUM1 }, + { XK_KP_Down, K_NUM2 }, + { XK_KP_Next, K_NUM3 }, + { XK_KP_Left, K_NUM4 }, + { XK_KP_Begin, K_NUM5 }, + { XK_KP_Space, K_NUM5 }, /* FIXME - ??? */ + { XK_KP_Right, K_NUM6 }, + { XK_KP_Home, K_NUM7 }, + { XK_KP_Up, K_NUM8 }, + { XK_KP_Prior, K_NUM9 }, + { XK_KP_0, K_NUM0 }, + { XK_KP_1, K_NUM1 }, + { XK_KP_2, K_NUM2 }, + { XK_KP_3, K_NUM3 }, + { XK_KP_4, K_NUM4 }, + { XK_KP_5, K_NUM5 }, + { XK_KP_6, K_NUM6 }, + { XK_KP_7, K_NUM7 }, + { XK_KP_8, K_NUM8 }, + { XK_KP_9, K_NUM9 }, + { XK_KP_Add, K_NUMPLUS }, + { XK_KP_Subtract, K_NUMMINUS }, + { XK_KP_Multiply, K_NUMMUL }, + { XK_KP_Divide, K_NUMDIV }, + { XK_KP_Separator, K_NUMDOT }, + { XK_KP_Delete, K_NUMDOT }, + { XK_KP_Enter, K_NUMENTER }, + + { 0, 0 } +}; + + + + + + + + + diff --git a/sys/x11/xlib.c b/sys/x11/xlib.c new file mode 100644 index 0000000..596ac72 --- /dev/null +++ b/sys/x11/xlib.c @@ -0,0 +1,524 @@ +/* + * xlib.c + * + * Xlib interface. + * dist under gnu gpl + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#if defined(HAVE_LIBXEXT) && defined(HAVE_X11_EXTENSIONS_XSHM_H) \ + && defined(HAVE_SYS_IPC_H) && defined(HAVE_SYS_SHM_H) +#define USE_XSHM +#endif +#else +#define USE_XSHM /* assume we have shm if no config.h - is this ok? */ +#endif + +#ifdef USE_XSHM +/* make sure ipc.h and shm.h will work! */ +#define _SVID_SOURCE +#define _XOPEN_SOURCE +#endif + +#include + +#include +#include +#include + +#ifdef USE_XSHM +#include +#include +#include +#endif + +#include "fb.h" +#include "input.h" +#include "rc.h" + + +struct fb fb; + +static int vmode[3] = { 0, 0, 0 }; +static int x_shmsync = 1; + +rcvar_t vid_exports[] = +{ + RCV_VECTOR("vmode", &vmode, 3), + RCV_BOOL("x_shmsync", &x_shmsync), + RCV_END +}; + + +static int initok; + +/* Loads of bogus Xlib crap...bleh */ + +static char *x_displayname; + +static Display *x_display; +static int x_screen; + +static struct +{ + int bits; + int vc; + int bytes; +} x_vissup[] = +{ + { 8, PseudoColor, 1 }, + { 15, TrueColor, 2 }, + { 16, TrueColor, 2 }, + { 32, TrueColor, 4 }, + { 24, TrueColor, 3 }, + { 0, 0, 0 } +}; + +static int x_bits, x_bytes; +static Visual *x_vis; +static XVisualInfo x_visinfo; +static int x_pseudo; +static Colormap x_cmap; +static XColor x_ctable[256]; + +static int x_wattrmask; +static XSetWindowAttributes x_wattr; +static int x_gcvalmask; +static XGCValues x_gcval; + +static Window x_win; +static int x_win_x, x_win_y; +static int x_width, x_height; +static GC x_gc; + +static XSizeHints x_size; +static XWMHints x_wmhints; +/*static XClassHint x_class;*/ + +#ifdef USE_XSHM +static XShmSegmentInfo x_shm; +#endif + +static int x_useshm; +static int x_shmevent; +static int x_shmdone; +static XImage *x_image; +static int x_byteswap; + +static XEvent x_ev; + + + + +static void freescreen() +{ + if (!initok || !x_image) return; + if ((char *)fb.ptr != (char *)x_image->data) + free(fb.ptr); +#ifdef USE_XSHM + if (x_useshm) + { + /* FIXME - is this the right way to free shared mem? */ + XSync(x_display, False); + if (!XShmDetach(x_display, &x_shm)) + die ("XShmDetach failed\n"); + XSync(x_display, False); + shmdt(x_shm.shmaddr); + shmctl(x_shm.shmid, IPC_RMID, 0); + x_image->data = NULL; + } +#endif + free(x_image); + x_image = NULL; + fb.ptr = NULL; +} + +static void allocscreen() +{ + if (initok) freescreen(); +#ifdef USE_XSHM + if (x_useshm) + { + x_image = XShmCreateImage( + x_display, x_vis, x_bits, ZPixmap, 0, + &x_shm, x_width, x_height); + if (x_image) + { + x_shm.shmid = shmget( + IPC_PRIVATE, + x_image->bytes_per_line * x_image->height, + IPC_CREAT | 0777); + if (x_shm.shmid < 0) + die("shmget failed\n"); + x_image->data = x_shm.shmaddr = + shmat(x_shm.shmid, 0, 0); + if (!x_image->data) + die("shmat failed\n"); + if (!XShmAttach(x_display, &x_shm)) + die("XShmAttach failed\n"); + XSync(x_display, False); + x_shmdone = 1; + fb.pitch = x_image->bytes_per_line; + } + else + { + x_useshm = 0; + } + } +#endif + if (!x_useshm) + { + x_image = XCreateImage( + x_display, x_vis, x_bits, ZPixmap, 0, + malloc(x_width*x_height*x_bytes), + x_width, x_height, x_bits, x_width*x_bytes); + if (!x_image) + die("XCreateImage failed\n"); + } + x_byteswap = x_image->byte_order == +#ifdef IS_LITTLE_ENDIAN + MSBFirst +#else + LSBFirst +#endif + ; + if (x_byteswap && x_bytes > 1) + fb.ptr = malloc(x_image->bytes_per_line * x_image->height); + else + fb.ptr = (byte *)x_image->data; +} + + + +void vid_resize() +{ + freescreen(); + x_width = fb.w; + x_height = fb.h; + XResizeWindow(x_display, x_win, x_width, x_height); + x_size.flags = PSize | PMinSize | PMaxSize; + x_size.min_width = x_size.max_width = x_size.base_width = x_width; + x_size.min_height = x_size.max_height = x_size.base_height = x_height; + XSetWMNormalHints(x_display, x_win, &x_size); + XSync(x_display, False); + allocscreen(); +} + + +static void colorshifts() +{ + int i; + int mask[3]; + int l, c; + + mask[0] = x_vis->red_mask; + mask[1] = x_vis->green_mask; + mask[2] = x_vis->blue_mask; + + for (i = 0; i < 3; i++) + { + for (l = 0; l < 32 && !((1< XK_asciitilde) + return 0; + code = sym - XK_space + ' '; + if (code >= 'A' && code <= 'Z') + code = tolower(code);; + return code; +} + + +void vid_end(); + +static int nextevent(int sync) +{ + event_t ev; + + if (!sync && !XPending(x_display)) + return 0; + + XNextEvent(x_display, &x_ev); + switch(x_ev.type) + { + case KeyPress: + ev.type = EV_PRESS; + ev.code = mapxkeycode(x_ev.xkey.keycode); + break; + case KeyRelease: + ev.type = EV_RELEASE; + ev.code = mapxkeycode(x_ev.xkey.keycode); + break; + case Expose: + vid_end(); + return 1; + break; + default: + if (x_ev.type == x_shmevent) x_shmdone = 1; + return 1; + break; + } + return ev_postevent(&ev); /* returns 0 if queue is full */ +} + + + +void ev_poll() +{ + while (nextevent(0)); + joy_poll(); +} + + + +void vid_settitle(char *title) +{ + XStoreName(x_display, x_win, title); + XSetIconName(x_display, x_win, title); +} + +void vid_setpal(int i, int r, int g, int b) +{ + if (!initok) return; + + if (x_pseudo == 1) + { + x_ctable[i].red = r << 8; + x_ctable[i].green = g << 8; + x_ctable[i].blue = b << 8; + XStoreColors(x_display, x_cmap, x_ctable, 256); + } +} + +void vid_begin() +{ + if (!x_useshm) return; + + /* XSync(x_display, False); */ + while (!x_shmdone && x_shmsync) + nextevent(1); +} + +static void endianswap() +{ + int cnt; + un16 t16; + un32 t32; + un16 *src16 = (void *)fb.ptr; + un16 *dst16 = (void *)x_image->data; + un32 *src32 = (void *)fb.ptr; + un32 *dst32 = (void *)x_image->data; + + switch (x_bytes) + { + case 2: + cnt = (x_image->bytes_per_line * x_image->height)>>1; + while (cnt--) + { + t16 = *(src16++); + *(dst16++) = (t16 << 8) | (t16 >> 8); + } + break; + case 4: + cnt = (x_image->bytes_per_line * x_image->height)>>2; + while (cnt--) + { + t32 = *(src32++); + *(dst32++) = (t32 << 24) | ((t32 << 8) & 0x00FF0000) | + ((t32 >> 8) & 0x0000FF00) | (t32 >> 24); + } + break; + } +} + + +void vid_end() +{ + if (!initok) return; + + if (x_byteswap) endianswap(); + if (x_useshm) + { + if (!x_shmdone) return; +#ifdef USE_XSHM + if (!XShmPutImage( + x_display, x_win, x_gc, x_image, + 0, 0, 0, 0, x_width, x_height, True)) + die("XShmPutImage failed\n"); +#endif + x_shmdone = 0; + } + else + { + XPutImage(x_display, x_win, x_gc, x_image, + 0, 0, 0, 0, x_width, x_height); + } +} + + + + +