From: notaz Date: Sat, 7 Apr 2007 13:46:21 +0000 (+0000) Subject: initial fce ultra 0.81 import X-Git-Tag: r1~90 X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?p=fceu.git;a=commitdiff_plain;h=c62d28102c77e19c291c78bf6bf7f0a81abd54b9 initial fce ultra 0.81 import git-svn-id: file:///home/notaz/opt/svn/fceu@90 be3aeb3a-fb24-0410-a615-afba39da0efa --- c62d28102c77e19c291c78bf6bf7f0a81abd54b9 diff --git a/Documentation/AUTHORS b/Documentation/AUTHORS new file mode 100644 index 0000000..c34fcae --- /dev/null +++ b/Documentation/AUTHORS @@ -0,0 +1,25 @@ +A list of people who have contributed code to FCE Ultra follows. +Please note that the "Code Contributions" field may not be all inclusive; +the coder may have done more than what is listed. + +Name/Alias Code Contributions Contact Information +------------------------------------------------------------------------------ +Aaron Oneal Many changes to compile support@pocketgb.com + with MSVC and first frame + skipping code. + +Ben Parnell Most of the FCE Ultra code. xodnizel@users.sourceforge.net + +BERO Base FCE code. bero@geocities.co.jp + +\Firebug\ VGA register setting code. ?? + +LULU SDL network play code. ?? + +Paul Various code for the official kuliniew@purdue.edu + Kuliniewicz SDL port. + +Quietust VRC7 "translation" code. quietust@ircN.org + +Tatsuyuki OPL2 emulator. ?? + Satoh diff --git a/Documentation/COPYING b/Documentation/COPYING new file mode 100644 index 0000000..afd5a94 --- /dev/null +++ b/Documentation/COPYING @@ -0,0 +1,341 @@ + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 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/Documentation/ChangeLog b/Documentation/ChangeLog new file mode 100644 index 0000000..9712202 --- /dev/null +++ b/Documentation/ChangeLog @@ -0,0 +1,823 @@ +.81: +---- + + More SDL goodies. + + Renamed "Makefile.olinuxsdl" to "Makefile.unixsdl" + + More SDL fixes and cleanups. + + BeOS port is now compiled with the "-no-fpic" flag, which allows + me to use my inline assembly. The executable work fine as far as + I can tell. + + Added "Makefile.beos". gcc doesn't like my inline assembly + used in RefreshLine(),so this port will likely be slightly slower than + other x86 ports. + + Added a check to make sure SIGBUS is not equal to SIGSEGV in + drivers/cli/main.c. Needed for compiling under BeOS. + + Renamed the "PI" member of the X6502 structure due to conflicts with + a defined symbol with some math header files. + + Merged fcelineasm.h into fceline.h + + Fixed(possibly) a possible problem in fcelineasm.h with input register + clobbering. + + More SDL changes. + + Added speed throttling code to the CLI code, and added a command + line switch "-nothrottle". + + Lots of restructuring/rewriting/merging of the MMC3 code. + + Updated DOS code to use the generic CLI wrapper. + + Reads from $4090 and $4092 now return the current envelope setting + in FDS sound emulation. I'm not sure if this is correct... Affects + "Ai Senshi Nicole" and "Bio Miracle Bokutte Upa". + + Added native SDL sound support to the SDL code. the "olinuxsdl" + now uses this code by default instead of the unixdsp sound code. + + Modified MMC3 IRQ counter emulation. I'll need to watch out to see + if it breaks any games. Fixes: MegaMan 3, Gun Nac, Klax(Japanese). + + Changed a few memory reads in x6502.c to use RdRAM instead of RdMem, + resulting in a slight speed increase. + + Cleaned up mapper 250 emulation code. + + Added support for iNES mapper 51(thanks to Kevin Horton for the + information). + + Merged some iNES mappers corresponding to bootleg multicarts + based on MMC3s with mbshare/mmc3.c. + + Added support for iNES mapper 52(thanks to Kevin Horton for the + information). + + Made some hacks to the MMC3 emulation code so that I can add support + for pirate MMC3 multicarts more easily. I should clean it up later. + Moved mapper 44 emulation code to mbshare/mmc3.c. + + Saving screen snapshots will no longer corrupt the frame buffer + for one frame(unless memory couldn't be allocated). + + Fixed screen snapshot saving(it was sort of broken due to the + changes made to the driver<->emulator interface code; status + messages were being saved to the image). FCEUI_SaveSnapshot() + no longer returns a value(the request to save a screen snapshot is + serviced before status information would be written in the next frame). + + nosprites is now set to 0 before RefreshSprite() returns, to prevent + problems if a game turns off the bg and sprites when FetchSpriteData() + for the next scanline is called but then turns on sprites when + the actual scanline is drawn. + + PPU_hook() is now called more often if PPU_hook is non-null. + Made changes to mappers 118, 95, 9, and 10 to compensate. + No games seem to be broken, and I added support for mapper 96 + (though the games aren't very playable because the special controller + isn't emulated). + + Romance of the 3 Kingdoms is now recognized to use 16KB ex-WRAM. + + Added support for mapper 185...sort of. I think this is another + instance of incompatible hardware being lumped onto one mapper number. + Sigh. + + Added support for "Famicom Jump 2" as iNES mapper 153. + If a good(as far as I can tell) dump is loaded, FCE Ultra will + automatically fix the mapper number. + I also made some changes to the mapper 16 IRQ emulation code. + + BRK now sets the I flag. + + Reads from $4015 no longer reset DPCM IRQ. + + Changed emulation of RTI instruction slightly. + + X.IRQlow is now set to 0 in PowerNES(). + + The VS Unisystem bit in the iNES header is no longer looked at( + I was having too many problems with this bit being set when it + shouldn't have been). Now, VS Unisystem emulation is enabled + when a known VS Unisystem game is loaded. I also rewrote the VS + Unisystem detection function. + + iNES mapper 1 now supports pageable CHR RAM if no CHR ROM is present. + Fixes "Family School". + + Mapper 70 no longer has a mirroring control emulated, and I extended + the number of 8KB CHR pages supported to 16. + + Cleaned up iNES MMC5 save RAM loading/saving code and added + support for MMC1 games with 16KB of RAM(the second 8KB are saved), + via CRC32s(currently only Genghis Khan(USA) and Nobunaga's Ambition( + USA and Japan) are recognized). + + Added support for the MMC5 Koei game "Ishin no Arashi", in the iNES + format(I added an entry with its CRC32 value and the number of 8KB + WRAM banks it needs). + + Better iNES mapper 33/48 IRQ counter emulation. + + Added the game "Uchuusen - Cosmo Carrier" to this list. I'm + beginning to hate the iNES format more and more...or maybe + just Fanwen. :) + + Added the mapper 32 game "Major League" to the list of games + that usually need iNES header correction...but in this case, the + iNES header cannot specify that this game needs to have one-screen + mirroring. + + iNES header information is now printed before any header corrections + are made based on a database. + + Fixed a bug in mapper 32 emulation. "Ai Sensei no Oshiete" + works now. + + Tried to add support for iNES mappers 245 and 249. + + Fixed the MMC5 read handler to return the data last on the data + bus instead of 0xFF when a read was made to an unmapped address. + This seems to fix the lockup problems in "Bandit Kings of Ancient + China". + + Reversed "Modified the time at which the "y scroll" register is updated + during hblank." The changes broke Klax. + + Added an unsigned 64-bit base timestamp variable "timestampbase". + Adding this to the 32-bit variable "timestamp" will return + the number of cycles executed since emulation started(in the future + I'll probably change it to since reset or power toggle). + This allowed me to replace "lastn" hack in the MMC1 code with + something better. + + Changed my mind and undid the removal of support for old save states. + + Removed support for old save states and in general I won't + try to support save states made with previous versions. + + MMC1: Writes to $8000-$FFFF with D7 set will now cause + the first MMC1 register to be OR'ed with 0x0C. I'm not sure + if this is correct, but it doesn't seem to break anything + and it fixes Robocop 3. I'll see if anyone reports games + not working in .81 that worked in .80. + + Worked on a generic driver interface wrapper very similar + to the driver interface FCE Ultra used to use(I'm getting + tired of all of the duplicated driver code). Eventually, + the DOS, SVGAlib, and SDL ports will use this wrapper. + + Similar change to the argument parsing code. + + Changed configuration file saving-loading routines and the + configuration structure to allow for linking config structures + to each other). + + Small fix to the emulation of the MMC5 split screen mode. + + Made Linux SDL code compilable again. + + Changes to MMC5 EXRAM emulation(read/write). + + Fixes to the emulation of the MMC5's split screen mode, based on + observations while using CastleVania 3 and a Game Genie(on a real + NES). + + Fixed a bug in ines.c that caused any calls to AddExState() from + a mapper initialization function to be effectively "erased"(ResetExState() + was called after the mapper was initialized). Fixes the VRC7 sound + state saving/loading stuff. + + Finished adding support for the MMC5's split screen mode(this does + not mean that the support is complete, but at least the intro in + "Uchuu Keibitai SDF" works correctly now). + + Worked on adding support for the MMC5's split screen mode. Not + completed. + + Reverted to .80's FDS sound code. + + Modified the time at which the "y scroll" register is updated + during hblank. + + NSF playing code will now disable FDS sound output on song init + (fixes some problems with the Zelda no Densetsu NSF rip). + + Increased the emulated clock speed of the FDS sound emulation code + to give better quality output. + + Modified NMI to occur a few cycles later. Fixes BattleToads...but + it may have broken other games. Also modified the way NMI's are + handled in x6502.c. + + Modified ines.c to memset() GameMemBlock to 0 on virtual power toggle. + Also, trainers are now loaded into their own buffer instead of + directly into emulated WRAM and copied into emulated WRAM on + power toggle; I've been meaning to do this for quite some time. + + Changes to the way the zapper cursor is drawn on the screen. + + FCEUD_WriteSoundData(), FCEUD_BlitScreen(), and FCEUD_UpdateInput() + have been combined into one function: FCEUD_Update(). + + More fixes to the network play code, and a fix to the Windows network + play driver code that fixes(hopefully) a rather evil bug that caused + lockups when the remote stopped network play. + + Added code to set the battery-backed bit in RAM if a game needs it, + based on CRC32. + + Added more games to the list of games that commonly have bad iNES + headers, in ines.c + + Updated docs and usage.h for DOS and Linux regarding the new video + mode and the new refresh rates. + + Linux: Fixed a bug with video mode 6(a few upper scanlines were being + cut off). Increased the refresh rate of video mode 3 to 120hz. + + Increased the refresh rate of video mode 2 to 65 hz in the Linux port. + + Screen snapshots can now be taken while playing an NSF. + + Added a new sexy tweaked vga mode that I created to the Linux svgalib + port. It's 256x224 at a refresh rate of 103hz. Hopefully it won't + blow up anyone's monitor. ;) + DOS port will follow eventually. + + Modified Makefile.base to produce an executable named "fceu" instead + of "fce". + + The plans(cycle-accurate ppu emulation) for .90 were a bit ambitious, + and I still need to make other fixes before then. + + Fixed some minor(usually) bugs with setting 256x240 tweaked VGA mode + in DOS and Linux ports. + +.80: +---- + + Cleaned/fixed a few things in the mapper 19 emulation code. + Family Circuit '91 still doesn't work quite right... I wonder if + it's a bad dump. + + Added input override code to Windows port. + + Added code to fix iNES header information in RAM and suggest + changes to the user. + + Added support for iNES mapper 152(to be used with games set to + mapper 70, that use one-screen mirroring instead of h/v mirroring). + + Blits using the DirectX blitting function(method?) to the primary + surface are now done with the asynchronous flag set(if that + fails, a "normal" blit is tried). + + The DirectX blit buffer(secondary surface that FCE Ultra writes to + directly and then uses the DirectDraw blit function on to blit + to the primary buffer) is now created without specifying it + should be in system memory or video memory, except in the case + when no hardware blitting is available, and then DDraw is explicitly + told to create the surface in system memory. + + Added Family Keyboard support to the DOS port. + + Cleaned up the VRC7 sound emulation code. I need to find a way + to save the current sound state in a save state. + + Found out the real name of the "Space Shadow" gun; it's + called the "Hyper Shot". I'm still not sure who made it, though. + Possibly Bandai did. The interesting thing is that Konami + also made a Famicom accessory(dual square boxy things with two buttons + on each) with the same name(though there might not be a space in the + name). + + Only the upper two bits read from $4016/$4017 are undefined. + Bit5 is always 0, though. Fixed the bug in "input.c". + Silly kevtris' old documents. New kevtris' brain is always good. + + Family Keyboard support for the Windows port. + + Added support for the Family Basic Keyboard to the Linux port, other + ports todo. + Might want to add support for the tape recorder at some time. + Also mapped the "Scroll Lock" key to disable/enable command keys + so that the FBK is more useable. It doesn't disable CTRL C, + though... + + Changed a lot of inlined functions in x6502.c to macros so that + I could test out some optimization ideas. + + DOS code updates for game input override support. + + Small optimzation to opcode $4c, and relative jumps. + + Added some code to ines.c to set controller information in + FCEUGameInfo(returned by FCEUI_LoadGame()) based on crc32 values. + + Updated user documentation and usage.h for DOS and SVGAlib input + command-line changes. + + Added an option to disable the four-score(to Windows and Linux ports + so far). + + Updated Windows interface to support the new Famicom expansion + devices. + + (Re)Added support for the Famicom 4-player device. + + Improved Zapper emulation...sort of. It still needs a lot of work. :/ + + Added *partial* support for the "Space Shadow" gun. + + Added support for the Arkanoid controller(both NES and Famicom style). + + Added code to support the extension Famicom input devices. + + Added PAL scanline start/end drawing settings to Windows port. + + Added pause emulation key(F2) to Windows port. + + In the process of rewriting/fixing up input code stuff. + + Minor bug fix to Power Pad emulation code. + + VS Hogan's Alley and VS Duck Hunt automatically select the zapper + now(though it only works on the SVGAlib port). + + Undid some FDS sound code changes introduced in 0.76 that totally + screwed up sound. Oops. + + Added code to allow different settings for first/last scanline + drawn while in PAL emulation mode, to the Linux and DOS ports. + + Added convenience(it's not necessary, but it reduces redundant and + confusing code in the driver code) function + FCEUI_GetCurrentVidSystem(int *slstart, int *slend). + + Updated file "TODO". + + Changed #include to #include "zlib/unzip.h" + in file.c. + + NSF 6502 player now initialized the stack pointer on reset. + + Worked on de-emphasis emulation code quite a bit. + The deemphasized palette calculated at the end of the frame is now + based on what deemphasis bits were set for the longest during + the screen(sampling interval is a scanline) update loop. + Added a "static" deemphasized palette at $40-$7F in the palette table. + This corresponds to the colors when all of the deemphasis bits are set. + I did this to fix the PAL game "Noah's Ark", without breaking + anything else. The only downside is a slight speed loss(~2% on + my system when sound is disabled), but this is acceptable to me, + at least. + Maybe it's time to write hi/true-color ppu drawing code... + + + Fixed an out of bounds array access in svga.c in SetNESDeemph(). + The variable "lastd" in svga.c was being initialized to the wrong value. + Thanks to "Jarod CANAL" for pointing this out. + + Removed FCEUI_SetFirstRenderedLine or whatever it was called and + the function to set the last line. Replaced with: + void FCEUI_SetRenderedLines(int ntscf, int ntscl, int palf, int pall); + + Changed SetVidSys(int w) to ResetVidSys() in fce.c. Reenabled + PAL/NTSC emulation overrides based on game loaded(really only useful + for NSFs and UNIFs now). + + UNIF loading code now recognizes the chunk "CTRL" and tries + to use it. Only the svgalib code supports overriding of input + settings based on game loaded, now, though... + The user is still going to have to configure powerpad settings + on his/her own. + + Fixed return values of FCEU_fseek() and fixed a problem in unif.c + related to it. + + Changed mechanism for how FCE Ultra tells the driver + code what type of system is being emulated. A structure of + type "FCEUGI" is returned from FCEUI_LoadGame(). + + Fixed a major mapper 64 emulation bug introduced in 0.76. + + Modified BlitVidHi() in drivers/win/video.c to speed it up. + + Added support for loading the iNES-format Game Genie ROM image. + + Removed ggrom.h and added code to load the Game Genie ROM + image from a file. + + Added Windows netplay.c. (new: the user exiting the emulator + while stuck in a blocking recv() or send() loop now works.) + + Fixed a vram address register bug in fce.c that I created when I got + rid of the pair/dpair data types. + + Added new mappers/92.c + + Removed mappers/92.c until I can contact LULU or rewrite it. + + drivers/win changes. Removed netplay.c until I can rewrite it. + + Got rid of pair/dpair data types. + + Got rid of silly "TempArray" thing. + + Began adding GPL headers to files. FCE Ultra is going to be in + a state of legal limbo for the next few days... + + Replaced crc32.c and crc32.h, and added some #ifdef's and #defines + to use the crc32 code in zlib instead if zlib is linked with + FCE Ultra. + + More fixes to sb.c. + + Cleaned up drawing.h and ggrom.h(even though ggrom.h will *probably* + be removed before the next release). + + Redid frameskip code. + + Rewrote necessary pieces of sb.c and sb.h and fixed quite a few + bugs. I still need to test it on various other sound cards, though. + + Rewrote(more like "recreated in my own image") DOS keyboard driver. + Removed unused "keyscan.h" from drivers/svgalib + + Rewrote part of(the parts that Marat wrote - the connection and + closing parts) the Linux TCP/IP network play code. I guess it works, + but I haven't tested it very much. In any case, it's still dangerous + to use network play in FCE Ultra with SVGAlib, since recv() or send() + might block and since the keyboard is in raw mode, you have a problem. + Maybe a future SVGAlib will fix the general problem of lockups if + keyboard_update() isn't called, though it is only partly SVGAlib's + problem... + + Fixed FCEU_fseek() when used with a compressed file in the PKZIP + format that has been loaded. Fixes a UNIF loading problem. + Also added a check to the return value of FCEU_fseek() in unif.c. + + Replaced Marat's 6502 emulation core with my own. + It should be fully functional, but as always, I don't know if + I implemented the undocumented instructions correctly. + Several things are correct in this new core that were not in + Marat's(D flag is no longer cleared by interrupts, for example). + + Altered mapper 16 irq counter emulation slightly. + + Fixed the behavior of the SXA, SYA, and XAS opcodes based on the + documentation I have. I'm not sure what happens when page crossing + occurs with those instructions on a real NES, though. + Also CHANGED(not fixed) emulation of opcode $8B("XAA"). + + Changed some of the M* functions(absolute indexed and maybe some others) + to perform dummy reads. + + Changed some of the macros in m6502.c to inlined functions so + that I can modify and examine them more easily. + +.77: +---- + + Fixed a silly network play bug(in the global network play code) + that caused excessive lag. + + Added a "niceness" setting to the sound configuration dialog. + Removed obsolete information from the dialog. + + Fixed speed throttling code in Windows port when PAL emulation is + disabled/enabled and a new game hasn't been loaded yet. + + Commented out a printf() to debug stuff in fds.c(oops). + + Applied PK's joystick patch to the osdl code. It allows the user + to map axes and it fixes a joystick button mapping configuration saving + bug. + + Added two command line options new to Linux port to DOS port. + Just need to test them... + + Added some stuff to unif.c to allow for boards that can support + CHR RAM instead of CHR ROM(darn Sachen boards...). Fixes UNIF + version of "Q-Boy". + + Added command line option "-snapname" to Linux port. I'll add it + to the Windows port as well, but probably not to the DOS port. + + Added clip option to Linux port. + + Fixed sound logging(in Windows port) so that multiple recording + sessions now work. + + Added an option to clip the leftmost and rightmost 8 columns + of graphics in the Windows port. + + Added a submenu that lists recently opened files, in the Windows + port. + +.76: +---- + + Updated porting.txt. + + Added speed throttling code to Windows port that's used when sound is + disabled(and an option in the "Miscellaneous" + configuration window to disable it). + + Added cheat interface to DOS build. + + A few tweaks to the text cheat interface code in the cheat listing code. + Added a command to quickly toggle the status of the cheat(though a + cheat can still be disabled/enabled by (M)odifying the cheat). + + Support for UNIF UNL-TC-U01-1.5M board added. Same credits as below. + + Rewrote mapper 228 code just for the heck of it(actually, I couldn't + figure out why some(about two) of the games in the Action 52 cart + weren't working in .75(and .76). I traced it back to .71, where an apparent bug in + the undocumented 6502 opcode emulation allowed it to work, which + was later fixed for the .72 release(opcode 0x7C)). I'm thinking + that the dump is bad... + + Added a few crc32 checks to ines.c to check for and report when + a known(by me) bad(hacked or bad dump) game is loaded. + + Added support for the following UNIF boards. Thanks to Quietust + and Kevin Horton for the information. Some problems still exist + with a few games that use these boards, though... + + UNL-Sachen-8259B + UNL-Sachen-8259A + UNL-Sachen-74LS374N + UNL-SA-016-1M + UNL-SA-72007 + UNL-SA-72008 + UNL-SA-0036 + UNL-SA-0037 + + Fixes to some stuff in cart.c(for example, calling setprg32() when + only 16kb of prg data is present now works). + + Added support for iNES mapper 189. + + Tried to add support for the UNIF board "UNL-H2288". Failed. + + Updated "cheat.txt" to fix a few typos and added an example of finding + and adding a cheat using the Linux port's text interface. The actual + section on the Linux cheat interface still needs to be written, however. + + Changed network play code in the Windows port and fixed a bug. + Fixed a similar bug in the Linux netplay code....sort of. + + A few cosmetic changes to the dialogs in the Windows port. + + Fixed sound initialization on the Windows port(it was being initialized + when FCE Ultra started even if it was disabled by the user. Oops.). + + Joystick button configuration code in Windows port changed slightly + to be more useable. + + Changed reference to video mode 5 in the linux port to "1 per 4". + It may not be very meaningful, but it is certainly better + than "TV Emulation". I'll change the Windows port reference later. + + Documented video mode 7(320x240) for the Linux port. Also added + a check for the FBDev driver in order to use this mode instead + of a tweaked vga mode if that driver is being used. + + Added/Fixed cheat interface for Linux port. It's still not perfect, + though. The code is ugly... + + Callback function for FCEUI_ListCheats() now receives + status information(enabled/disabled). + + Callback functions for cheat functions now must return 0 to + stop listing cheats or 1 to continue. + + Fixed a problem(the cheat code was reading in cheats + for address $0000 from cheat files if any blank lines were present). + + SDL port zlib changes(linked dynamically to zlib now). + + More changes to envelope decay + looping on code. No longer + depends on value @ $4017. It now sounds correct based on some + tests I did with SMB3 and a GG, but Goonies 2 doesn't sound right( + based on a sound file sent to me by another person). + + Added support for iNES mapper 140. Thanks to Quietust for the + information. + + I need to figure out how to deal with the problem of so many + bad NSFs that most people consider good... + + Changed envelope decays a bit. Their behavior now depends on bit + 7 of $4017. I don't know if this is right... + + Addition of debug.c for some debugging stuff. + + Updated zlib to 1.1.4. + + Modified code in various files to allow UNIF games to override + current selected video system emulation(NTSC or PAL). Need to + make sure this really works. + + Changed sound.c to prevent desynchronization during network play. + This might slow down sound emulation slightly, especially when + sound emulation is disabled. I really don't care... + + Updating network play code. More info later... + + Moved the sound/video/etc output code in EmLoop() to the top of the + for(;;) loop so that initialization prior to calling EmLoop() will + be the same as initialization done during a call to a FCEUD_* function. + + A few very small changes to sound emulation in fds.c. + + Changed unlink() to remove() in cheat.c and removed the including + of the header file unistd.h. + + Split up the cc=... statement in RefreshLine() to make it easier + to read and not ambiguous(to Borland's C compiler). + + Changed a lot of the function declarations in cheat.c. I'll need + to verify that cheat searching still works ok and that cheats still work + ok. I'll also need to update the Windows(and Linux console) cheat code + to prevent compiler warnings. + + Fixed various minor code problems(not minor if you want to use a + compiler besides gcc). This is an ongoing process... + + Removed bit fields after reading about and thinking about possible + portability problems, though I kept some optimizations in fceline.h + + Minor code simplification in drivers/win/joystick.c(replaced + "case 200 ... 207" and "case 208 ... 215". + + Modified some code to use bit-fields in the graphics rendering code + in fce.c and fceline.h. gcc seems to be able to optimize the new + code to run faster. + + Fixed m6502.c and fce.c to initialize variables on virtual power + toggle. This should fix network play on the Windows port. + I'm also in the process of cleaning up fce.c. + +.75: +---- + Fixed directories configuration stuff in Windows port(corresponds + to .75r2). + + More changes to square wave channel envelope/volume emulation... + Changes described in the large paragraph some lines down have been + abandoned. + + Added a command line switch to set the volume in the dos port. + + Changed Windows sound configuration dialog. Now buffer length is + specified in time, not samples. I also added a volume control. + + Reduced the volume on 8-bit sound output on all ports by 1/2. + + Added a function FCEUI_SetSoundVolume(). Added support for setting + the volume via the command line in the Linux port, other ports coming + soon. + + Changed FCEUD_WriteSoundData() again. No longer has a "Check" + argument. All clipping(ugh) is done internally now. + + Added a directories configuration dialog to the Windows port. I worked + on it too long and I became a zombie, so it might have a few bugs. + That's what guine...err...users are for. ;) Don't expect me to do + anything like this for any other port, though. I don't feel like + doing it on the DOS port, and the Linux and other UNIXy ports shouldn't + really need it. + + Made some changes to the rectangle/square wave channel emulation to + fix the pops in SMB. I have no idea if what I did is correct. To + be honest, I'm not sure I know EXACTLY what I did, but it's something + like this: Writes to $4003/$4007 now reset the duty cycle count + and reload the cycle counter with the current wavelength. Writes to + $4003/$4007 now do not update the amplitude output of the channels; they + will be updated after the cycle counter hits 0(or below). + + More information in iNES informational output. + + Minor changes to mappers/16.c. + + Increased the volume of the VRC6's sawtooth wave channel. + + Added more information to the RAM cheat guide. + + Changed the triangle wave generation code slightly. I decided + to remove support for the higher-frequency triangle waves, as they + are too cpu-costly to create and are probably not very audible on a + real NES anyway. + + Major changes to how sound is mixed. This necessitated a high pass + DC-offset removing filter, and a low-pass filter just to make things + sound better. ^_^ + Note: FCEUD_WriteSoundData() no longer needs to clear the waveform + data referenced by the passed pointer; it's done internally now. + + Fixed JMP ($xxxx) - now handles wrapping like it occurs on a real 6502 + (hopefully; I'm assuming that the same holds true for the NES' cpu). + + Added the ability to load a custom global palette in the DOS port. + + Fixed bug in drivers/common/unixdsp.c(wrong return value if sample + rate was out of range). + + Many sound fixes... - Frequency sweeps, length counter stuff,... + + Changed Windows port to use IDirectInput7 and IDirectInputDevice7 + interfaces. + + Fixed a Game Genie bug in the core emulation code. It only appeared + in the Windows port, though. (Enabling gg emulation, loading a game, + and then disabling gg emulation and loading a new game while in the + gg code entry screen would cause the new loaded game to not work + correctly). + + Modified windows port to use the config saving/loading stuff in + drivers/common/config.c + + Mapper 45 cleanups/fixes. + + Added the ability to load a custom global palette in the Linux port. + Yay. + + Fixed a large number of overcomplicated code and silly bugs in + drivers/common/config.c. This changes the format of the configuration + structure, too. Also added support for saving/loading strings with + automatic memory allocation when strings are loaded. + + Minor change in InitNetplay(). + + Fixed bad type conversions for pointers to functions and fixed + some bad declarations of functions. + + Reenabled zlib support for the sdl build. I need to pay attention + to patches that modify lines than don't fit on my screen. + + Fixed vidblit.c to not emit so many warnings when compiling. + +.74: +---- + + Stop sound in Windows port when user clicks l/m/r mouse buttons + in the non-client area of the window. + + Added "Drag and drop" file open support to Windows port. + + Various code cleanups. + + mappers/33.c optimization. + + Rewrote the function "FCEU_MakeFName()". + + Removed crc32.h from mappers directory. + + Modified some of the window resizing code in the Windows port. + + Added support for waiting for vblank/double buffering to the Windows + port. + + Added/Fixed support for iNES mapper 248. + + After an NSF file is loaded, information about its header is now + displayed. + + Fixed a typo in the Namco 106 extra ram handling code. + + Improved the quality of the Namco 106's extra sound channels. + - Thank Mamiya and Applepie(real name?) for info. + + When an NSF file is being played, FCE Ultra will no longer go through + its scanline rendering loop. This speeds up NSF playback considerably. + + Updated "porting.txt". + + Moved some stuff from DriverInterface() to their own functions. + + Fixed some iNES mapper 18 IRQ counter emulation bugs. "Ninja Jajamaru - + Ginga Dai Sakusen" now works. + + Rewrote large pieces of the mapper 64 code. "Skull and Crossbones" + still doesn't work, though. + + Changed format of iNES header information output, added "ROM CRC32" info. + + Modified the way cycle timing is done slightly. No change + for NTSC emulation, but PAL emulation is a little more accurate. + + Changed the behavior of indirect indexed(I hope I got that right ;)) + instructions to behave more like a real 6502(junk reads are now + performed). + + A few optimizations/cleanups in m6502.c. diff --git a/Documentation/FAQ b/Documentation/FAQ new file mode 100644 index 0000000..0ce51b5 --- /dev/null +++ b/Documentation/FAQ @@ -0,0 +1,45 @@ +FCE Ultra General User's FAQ + preliminary version +------------------ + + +Q: Why doesn't the NSF work(correctly) on FCE Ultra? +A: Some NSF rips are bad. Some read from addresses that are not specified + in the NSF specifications, expecting certain values to be returned. + Others execute undocumented instructions that have no affect on + less-accurate software NSF players, but will cause problems on NSF players + that emulate these instructions. Also, the playback rate specified + in the NSF header is currently ignored, though I haven't encountered + any problems in doing this. + + +Q: Why doesn't the game work(correctly) on FCE Ultra? +A: Many factors can make a game not work on FCE Ultra: + + - If the ROM image is in the iNES format(typically files that have + the extension "nes"), its header may be incorrect. This + incorrectness may also be because of garbage in the + header. Certain utilities used to put text in the reserved + bytes of the iNES header, then those reserved bytes were + later assigned functions. FCE Ultra recognizes and + automatically removes(from the ROM image in RAM, not on the + storage medium) SOME header junk. + + If the game has graphical errors while scrolling, chances are + the mirroring is set incorrectly in the header. + + You can try to edit the header with a utility(in the NES + utilities section at http://zophar.net ) or a hex editor. + + - The on-cart hardware the game uses may not be emulated + correctly. + + - Limitations of the ROM image format may prevent a game from + being emulated correctly without special code to recognize that + game. This occurs quite often with many Koei MMC5(iNES mapper 5) + and MMC1(iNES mapper 1) games in the iNES format. FCE Ultra identifies + and emulates some of these games based on the ROM CRC32 value. + + - The ROM image may be encrypted. The author of SMYNES seems to + have done this intentionally to block other emulators from + playing "SMYNES only" games. diff --git a/Documentation/README b/Documentation/README new file mode 100644 index 0000000..71e5c8b --- /dev/null +++ b/Documentation/README @@ -0,0 +1,31 @@ +FCE Ultra was developed with gcc and GNU make in mind. MSVC will probably +compile the Windows source code with a few modifications, but you'll still +need to make a project file. + +Several pre-made makefiles are provided: + Makefile.beos - BeOS(with SDL and SDL_net) + Makefile.unixsdl - UN*X(FreeBSD/Linux/etc, with SDL) + Makefile.linuxvga - Linux(with svgalib) + Makefile.dos - (MS/PC/DR) DOS + Makefile.win - MS Windows 9x/Me/Xp/2000/etc.(with DirectX). + +If you want to use Makefile.beos or Makefile.unixsdl for a cpu type other +than 80x86, you will need to remove "-DC80x86" from the defines line +and also remove "-DLSB_FIRST" if your target cpu uses MSB first ordering. +You'll also need to remove/change "-mcpu=xxx". + +For the Windows port, I use MINGW32. http://www.mingw.org + +To compile the DOS port, you'll need to download the DJGPP package. +Any version of gcc >=2.95.3 should compile the code without changes, BUT +the DJGPP versions of gcc have some problems: + gcc 2.95.3 sometimes breaks when compiling the code. gcc 3.0.2 seems to + produce bad code for sound.c. So, I recommend using gcc 3.0.4 for + compiling the DOS version. + +Modifying the "-mcpu=i686" string in the makefiles, to optimize more effectively +for your cpu type, is a good idea. + +Always do a "make -f Makefile. clean" before compiling for a +different platform or if you update via cvs and if you have stale object +files lying around. diff --git a/Documentation/RELEASE-NOTES b/Documentation/RELEASE-NOTES new file mode 100644 index 0000000..64d343c --- /dev/null +++ b/Documentation/RELEASE-NOTES @@ -0,0 +1,36 @@ +----June 29, 2001: + -Added the source code for zlib. The makefile in the zlib directory should + be included by the platform makefile(such as Makefile.win) or zlib should + be linked in some other way. If you use the zlib that comes with this + source distribution, you'll need to set up your compiler to recognize + the zlib subdirectory as a global include directory(like "-Izlib" with gcc). + -Changed the expected behavior of FCEUD_BlitScreen(). Added two new functions, + FCEUI_SetFirstRenderedLine() and FCEUI_SetLastRenderedLine(). See + porting.txt for details. + + +----May 25, 2001: + -A few deobfuscations of the code(more will come in the future). + -Defined some macros to declare functions using different(faster) calling + conventions. After redeclaring memory map emulation functions, + there was a somewhat significant speed increase. Using the new calling conventions + for PPU_hook and MapIRQHook type functions resulted in a more significant + speed increase. + -Some of the driver<->emulator interface code has changed. Much more + will change in the future. + -Added some stuff in types.h to (possibly) help people with compiling + FCE Ultra with MSVC++. + +----May 5, 2001: + -Optimized RAM reading/writing emulation in fce.c a little. + -Moved the UNIF board emulation code to the "boards" subdir. UNIF support + isn't even near being done, though. Not a high priority either. + +----April 16, 2001: + -Much of the driver interface code was restructured/rewritten, especially the + input-related code. + -All driver functions called by the emulator are now prefixed with "FCEUD_". + (I may do something similar with the names of the driver interface functions). + -The core emulation code has been updated slightly since the Windows .53 + release, but the changes are so minor that another binary release is not + necessary. diff --git a/Documentation/TODO b/Documentation/TODO new file mode 100644 index 0000000..95b8b77 --- /dev/null +++ b/Documentation/TODO @@ -0,0 +1,60 @@ +*** First, things that are not on the TODO list(Don't bug me about these + things if you're an idiot. I don't like listening to idiots. + If you are not an idiot, and you can make decent arguments for why + these should be on the TODO list, then you can bug me.). + + Remappable command keys: Too much work would be involved, demand isn't + very high, and I don't need this feature. Maybe in the distant future, if + input devices have changed significantly and if FCE Ultra is still around. + + High-level Game Genie support(like the RAM cheats): This would be fairly + simple to do, after doing the RAM cheat stuff, but I think that FCE Ultra + already has too many ways to "cheat" and that this would just be code bloat. + + +*** General Features: + + Windows Port: + Support for command-line options(so that one crazy guy will quit bugging + me). + + SDL Port: + Make the code better. + Add a GTK+ interface using GLADE. + + Figure out a good way to add "turbo" button support and then do it. + + Make default svgalib video mode a non-tweaked VGA mode. + + Finish the software video blitting "library", add support for 2xsai, eagle, + interpolation, etc. effects. + + Rewrite network play code. Add security features(such as only allowing + connections from a specified ip address). + + +*** Emulation: + + Fix DPCM playback and IRQ at end of playback. + + Improve the sound filters. They work, but not very well. + + Fix frame IRQ(if it even exists...) and $4017. + + Fix some 6502 emulation bugs(undocumented opcodes might not be implemented + correctly and I'm not sure if the IRQ flag latency is implemented correctly). + + Fix MMC3 IRQ emulation. Check if this fixes the PAL version of "Star Wars". + + Figure out correct timing for when the PPU refresh address register is + updated by the PPU(for the next scanline). + + Vertical blank period might be too short? Or maybe something is wrong + with my mapper 16 IRQ emulation code. Probably the latter. + See SD Gundam Knight 3 or SD GK 2. + + Sound frame count stuff on PAL games(is it correct?). + + Fix FDS sound emulation. + + Fix Zapper emulation(Chiller still doesn't always work correctly). diff --git a/Documentation/cheat.txt b/Documentation/cheat.txt new file mode 100644 index 0000000..55b1129 --- /dev/null +++ b/Documentation/cheat.txt @@ -0,0 +1,249 @@ +FCE Ultra Cheat Guide + version .4 +--------------------------- + +Table of Contents: + + 1: Introduction + 1.0) Introduction + 1.1) Cheat Files + + 2: The Windows Interface + 2.0) The Main Cheat Window + 2.1) The Add Cheat Window + 2.1.1) The Cheat Search Interface + + 3: The DOS/Linux Interface - UNFINISHED(read "The Windows Interface" for now) + 3.0) The Main Cheat Menu + 3.0.1) The Cheat List + 3.1) The Add Cheat Menu + 3.1.1) The Cheat Search Interface + + 4: Finding Cheats + 4.0) "Mega Man 3" Windows Example + 4.1) "Over Horizon" DOS/Linux Example + 4.2) Hints + +/******** Section 1.0: */ + + FCE Ultra allows cheating by the periodic "patching" of arbitrary addresses + in the 6502's memory space with arbitrary values. Currently, only RAM + patching is allowed(trying to patch an address where ROM is will silently + fail). + + The patches are all applied a short time before the emulated + vertical blanking period. This detail shouldn't concern most people, though. + However, this does mean that cheating with games that use + bank-switched RAM may be problematic. Fortunately, such games are not very + common(in relation to the total number of NES and Famicom games). + +/******** Section 1.1: */ + + Cheats are stored in the "cheats" subdirectory under the base FCE Ultra + directory. The files are in a simple plain-text format. Each line represents + a one-byte memory patch. The format is: + + Address(hex):Value(hex):Description + + Example: + + 040e:05:Infinite super power. + + A colon(:) can be prefixed to the beginning of a line to disable that cheat. + + +/******** Section 2.0 */ + + All addresses listed in the Cheats and Add Cheat windows are in a + 16-bit hexadecimal format and all values in these windows are in an + unsigned 8-bit decimal format(the range for values is 0 through 255). + + + The main Cheats window contains the list of cheats for the currently + loaded game, places to view and edit the attributes of the selected cheat, + a button to delete the selected cheat, a button to open the Add Cheat + window, and a button to close the Cheats window. + +/******** Section 2.1 */ + + To the left in the Add Cheat window are text edit boxes for inputting + attributes of a cheat and a button to add that cheat. To the right is + the cheat searching interface. + + /******* Section 2.1.1 */ + + The cheat search interface consists of several components: a list of + addresses and associated data for a search, several command buttons, + and the search paramters. + + The list of addresses is in the format of: + "Address:Original Value:Current Value". + + The address is the location in the 6502's address space, the original + value is the value that was stored at this address when the search was + reset, and the current value is the value that is currently stored at + that address. Selecting an item in this list will automatically cause + the "Address" field in the "Add Cheat" box to be updated with the + selected address. + + The "Reset Search" button resets the search process; all valid addresses + are displayed in the cheat list and the data values at those addresses noted. + + The "Do Search" buttons performs a search based on the search parameters + and removes any non-matching addresses from the address list. + + The "Set Original to Current" button sets the remembered original values + to the current values. It is like the "Reset Search" button, but it does + not affect which addresses are shown in the address list. This command is + especially useful when used in conjunction with the "O!=C" search filter. + + The "Unhide Excluded" button shows all addresses that are excluded as a + result of any previous searches. It is like the "Reset Search" button + except that it does not affect the remembered original values. + + The numbers assigned the names "V1" and "V2" have different meanings based + on which filter is selected. A list of the names of the filters and detailed + information on what they do follows("original value" corresponds to the value + remembered for a given addres and "current value" is the value currently + at that address. Also, if a value is not explicitly said to be shown + under a certain condition, then it is obviously excluded.): + + "O==V1 && C==V2": + Show the address if the original value is equal to "V1" AND + the current value is equal to "V2". + + "O==V1 && |O-C|==V2": + Show the address if the original value is equal to "V1" AND + the difference between the current value and the original + value is equal to "V2". + + "|O-C|==V2": + Show the address if the difference between the current value + and the original value is equal to "V2". + "O!=C": + Show the address if the original value does not equal the + current value. + + +/******** Section 4.0 */ + + This example will give Mega Man unlimited energy. + Immediately after entering the Top Man stage, make your way to the + "Add Cheat" window. Push "Reset Search". + Go back to playing and move right until the first enemy appears. Allow + yourself to be hit twice. Each hit does "2" damage, so you've lost 4 energy + bars. Go to the "Add Cheat" window again and select the third filter + ("|O-C|==V2") and enter the value 4 next to "V2". Then push "Do Search". + + Several addresses will appear in the address list. You can try to find + the address you want through trial and error, or you can narrow the results + down further. We will do the latter. + + Go back to playing MM3 and get hit one more time and make your way back + to the "Add Cheat" window. Your damage is now "6". You can probably + see which address that contains your life(it is 00A2). If now, change + V2 to 6 and push "Do Search" again. This should leave only 00A2. + + Select that entry in the address list. Shift your attention to the "Add + Cheat" box to the left. Type in a meaningful name and the desired value(156; + it was the value when you had no damage, so it's safe to assume it's the + maximum value you can use). Push the "Add" button and a confirmation box + will come up. The cheat has been added. + + +/******** Section 4.1 */ + + This example will give you infinite lives in the NTSC(Japanese) version + of "Over Horizon". + + Start a new game. Notice that when you press "Start" during gameplay, + the number of lives you have left is indicated. With no cheating, you + start with 3 lives(2 lives left). + + Activate the cheat interface immediately after starting a new game. + Select the "New Cheats" menu and "Reset Search". + + I'll assume that the number of lives left shown in the game is the same number + that's stored in RAM. Now, "Do Search". You're going to use the first search + filter. For V1, enter the value 2. For V2, enter the same value. This, + coupled with the fact that you just reset the search, will allow you to search + for a value "absolutely"(as opposed to changes in the value). + + Now, "Show Results". When I did it, I received 11 results: + + 1) $0000:002:002 + 2) $001c:002:002 + 3) $001e:002:002 + 4) $009d:002:002 + 5) $00b9:002:002 + 6) $00e3:002:002 + 7) $0405:002:002 + 8) $0406:002:002 + 9) $0695:002:002 + 10) $07d5:002:002 + 11) $07f8:002:002 + + You really can't do much yet(unless you want to spend time doing trial + and error cheat additions). Return to the game. + + After losing a life, go back to the cheat interface, to the "New Cheats" + menu, and "Show Results". Here are my results: + + 1) $0000:002:002 + 2) $001c:002:002 + 3) $001e:002:002 + 4) $009d:002:002 + 5) $00b9:002:041 + 6) $00e3:002:002 + 7) $0405:002:001 + 8) $0406:002:002 + 9) $0695:002:002 + 10) $07d5:002:001 + 11) $07f8:002:002 + + Notice that two addresses seem to hold the number of lives($0405 and + $07d5). You can lose another life and go "Show Results" again, and you + should see that $07d5 is the address that holds the number of lives. + + Now that you know the address that holds the number of lives, you can + add a cheat. You can either type in the number from the cheat results list + corresponding to the address you want to add a cheat for, or you can + remember the address and select "Add Cheat" from the "New Cheats" menu. + Do the former. + + Now you will need to enter a name for the cheat. I suggest something short, + but descriptive. "Infinite lives" will work fine. Next, a prompt for + the address will show up. Since you selected an item from the list, you + can press enter to use the associated address($07d5). Next, you will + need to enter a value. It doesn't need to be large(in fact, it probably + shouldn't be; abnormally high numbers can cause some games to misbehave). + I suggest a value of 2. After this, you should get a prompt that looks like + this: + + Add cheat "Infinite lives" for address $07d5 with value 002?(Y/N)[N]: + + Answer "Y". You now have infinite lives. + +/******** Section 4.2 */ + + Games store player information in many different ways. For example, + if you have "3" lives in Super Wacky Dodgeball 1989, the game might store + it in memory as 2, 3, or 4, or perhaps a different number all together. + Also, say that you have 69 life points out of 200 in Mole Mashers. The + game might store how many life points you have, or how much damage you have + taken. Relative value searches are very valuable because you probably + don't know the way that the game stores its player data. + + Some games, especially RPGs, deal with individual numbers greater than + 8-bits in size. Most that I've seen seem to store the multiple-byte data + least significant byte(lower byte of number) first in memory, though + conceivably, it could be stored most significant byte first, or the component + bytes of the number could be non-contiguous, though the latter is very unlikely. + For example, say I have 5304 experience points in Boring Quest for the + Overused Plot Device. To split the number into two eight bit decimal numbers, + take 5304 %(modulus) 256. This will give a number that is the lower 8 bits. + Next, take 5304 / 256. The integral component of your answer will be the + upper 8 bits(or the next 8 bits, if the number is or can be larger than 16 + bits) of 5304. Now you will need to search for these numbers. Fortunately, + most(all?) RPGs seem to store large numbers exactly as they are shown in the + game. diff --git a/Documentation/fcs.txt b/Documentation/fcs.txt new file mode 100644 index 0000000..d269301 --- /dev/null +++ b/Documentation/fcs.txt @@ -0,0 +1,143 @@ +FCE Ultra Save State Format +(preliminary document - AKA *very incomplete*) +--------------------------------------- + +FCE Ultra's save state format is now designed to be as forward and backwards +compatible as possible. This is achieved through the (over)use of chunks. +All multiple-byte variables are stored LSB(least significant byte)-first. +Data types: + + (u)int8 - (un)signed 8 bit variable(also referred to as "byte") + (u)int16 - (un)signed 16 bit variable + (u)int32 - (un)signed 32 bit variable + +-- Main File Header: + +The main file header is 16-bytes in length. The first three bytes contain +the string "FCS". The next byte contains the version of FCE Ultra that saved +this save state. This document only applies to version "53"(.53) and higher. +After the version byte, the size of the entire file in bytes(minus the 16 byte +main file header) is stored. The rest of the header is currently unused +and should be nulled out. Graphical example of relevant parts: + + FCS + +-- Section Chunks: + +Sections chunk headers are 5-bytes in length. The first byte defines what +section it is, the next four bytes define the total size of the section +(including the section chunk header). + + + +Section definitions: + + 1 - "CPU" + 2 - "CPUC" + 3 - "PPU" + 4 - "CTLR" + 5 - "SND" + 16 - "EXTRA" + +-- Subsection Chunks + +Subsection chunks are stored within section chunks. They contain the actual +state data. Each subsection chunk is composed of an 8-byte header and the data. +The header contains a description(a name) and the size of the data contained +in the chunk: + + +The name is a four-byte string. It does not need to be null-terminated. +If the string is less than four bytes in length, the remaining unused bytes +must be null. + +-- Subsection Chunk Description Definitions + +Note that not all subsection chunk description definitions listed below +are guaranteed to be in the section chunk. It's just a list of what CAN +be in a section chunk. This especially applies to the "EXTRA" subsection. + +---- Section "CPU" + + Name: Type: Description: + + PC uint16 Program Counter + A uint8 Accumulator + P uint8 Processor status register + X uint8 X register + Y uint8 Y register + S uint8 Stack pointer + RAM uint8[0x800] 2KB work RAM + +---- Section "CPUC" (emulator specific) + + Name: Type: Description: + + JAMM uint8 Non-zero value if CPU in a "jammed" state + IRQL uint8 Non-zero value if IRQs are to be generated constantly + ICoa int32 Temporary cycle counter + ICou int32 Cycle counter + +---- Section "PPU" + + Name: Type: Description: + + NTAR uint8[0x800] 2 KB of name/attribute table RAM + PRAM uint8[32] 32 bytes of palette index RAM + SPRA uint8[0x100] 256 bytes of sprite RAM + PPU uint8[4] Last values written to $2000 and $2001, the PPU + status register, and the last value written to + $2003. + XOFF uint8 Tile X-offset. + VTOG uint8 Toggle used by $2005 and $2006. + RADD uint16 PPU Address Register(address written to/read from + when $2007 is accessed). + TADD uint16 PPU Address Register + VBUF uint8 VRAM Read Buffer + PGEN uint8 PPU "general" latch. See Ki's document. + +---- Section "CTLR" (somewhat emulator specific) + + Name: Type: Description: + + J1RB uint8 Bit to be returned when first joystick is read. + J2RB uint8 Bit to be returned when second joystick is read. + +---- Section "SND" (somewhat emulator specific) + + + +---- Section "EXTRA" (varying emulator specificness) + + For iNES-format games(incomplete): + + Name: Type: Description: + + WRAM uint8[0x2000] 8KB of WRAM at $6000-$7fff + MEXR uint8[0x8000] (very emulator specific) + CHRR uint8[0x2000] 8KB of CHR RAM at $0000-$1fff(in PPU address space). + EXNR uint8[0x800] Extra 2KB of name/attribute table RAM. + MPBY uint8[32] (very emulator specific) + MIRR uint8 Current mirroring: + 0 = "Horizontal" + 1 = "Vertical" + $10 = Mirror from $2000 + $11 = Mirror from $2400 + IRQC uint32 Generic IRQ counter + IQL1 uint32 Generic IRQ latch + IQL2 uint32 Generic IRQ latch + IRQA uint8 Generic IRQ on/off register. + PBL uint8[4] List of 4 8KB ROM banks paged in at $8000-$FFFF + CBL uint8[8] List of 8 1KB VROM banks page in at $0000-$1FFF(PPU). + + For FDS games(incomplete): + + Name: Type: Description: + + DDT uint8[65500] Disk data for side x(0-3). + FDSR uint8[0x8000] 32 KB of work RAM + CHRR uint8[0x2000] 8 KB of CHR RAM + IRQC uint32 IRQ counter + IQL1 uint32 IRQ latch + IRQA uint8 IRQ on/off. + diff --git a/Documentation/porting.txt b/Documentation/porting.txt new file mode 100644 index 0000000..72fefca --- /dev/null +++ b/Documentation/porting.txt @@ -0,0 +1,229 @@ +*Incomplete* + + +***Driver-supplied functions: + These functions will only be called after the driver code calls + FCEUI_LoadGame() or FCEUI_Emulate(). + +void FCEUD_Update(uint8 *XBuf, int32 *Buffer, int Count); + Called by FCE Ultra on every emulated frame. This function should + perform the following three things(in any order): + + 1. + Update the data pointed to by the pointers passed to + FCEUI_SetInput() and FCEUI_SetInputFC(). + 2. + Copy contents of XBuf over to video memory(or whatever needs to be + done to make the contents of XBuf visible on screen). + Each line is 256 pixels(and bytes) in width, and there can be 240 + lines. The pitch for each line is 272 bytes. + XBuf will be 0 if the symbol FRAMESKIP is defined and this frame + was skipped. + 3. + Write the contents of "Buffer" to the sound device. "Count" is the + number of samples to write. Only the lower 16-bits of each + sample are used, so each 32-bit sample in "Buffer" can be converted to + signed 16-bit by dropping the upper 16 bits. + When sound was disabled for the frame, "Count" will be 0. + +void FCEUD_SetPalette(uint8 index, uint8 r, uint8 g, uint8 b); + Set palette entry "index" to specified RGB values(value: min=0, max=255). + +void FCEUD_GetPalette(uint8 index, uint8 *r, uint8 *g, uint8 *b); + Get palette entry "index" data. + +void FCEUD_PrintError(char *s); + Print/Display an error message string pointed to by "s". + +int FCEUD_NetworkConnect(void); + Initialize a network connection. Return 0 if an error occurs. + +int FCEUD_NetworkRecvData(uint8 *data, uint32 len, int block); + Receive "len" bytes of data to "data". If block is 0 and "len" amount + of data is not available, return -1. If block is 1, wait until the + requested amount of data is available. + Return 0 on failure. + +int FCEUD_NetworkSendData(uint8 *data, uint32 len); + Send "len" bytes of "data". Return 0 on failure. + +void FCEUD_NetworkClose(void); + Close the network connection. + + +***FCE Ultra functions(called by the driver code): + The FCEUI_* functions may only be called before FCEUI_Emulate() is + called or after it returns and before it is called again, or after the + following functions are called and before they return: + FCEUD_Update(); + Calling the FCEUI_* functions at any other time may result in + undefined behavior. + +void FCEUI_SetInput(int port, int type, void *ptr, int attrib); +void FCEUI_SetInputFC(int type, void *ptr, int attrib); +void FCEUI_DisableFourScore(int s); + +void FCEUI_SetSnapName(int a); + +void FCEUI_DisableSpriteLimitation(int a); + Disables the 8-sprite-per-scanline limitation of the NES if "a" + is nonzero. The default behavior is the limitation is enabled. + +void FCEUI_SaveExtraDataUnderBase(int a); + If "a" is nonzero, save extra non-volatile game data(battery-backed + RAM) under FCE Ultra's base directory. Otherwise, the behavior is + to save it under the same directory the game is located in(this is + the default behavior). + +FCEUGI *FCEUI_LoadGame(char *name); + Loads a new file. "name" is the full path of the file to load. + Returns 0 on failure, or a pointer to data type "FCEUGI": + See file "git.h" for more details on this structure. + +int FCEUI_Initialize(void); + Allocates and initializes memory. Should only be called once, before + any calls to other FCEU functions. + +void FCEUI_SetBaseDirectory(void); + Specifies the base FCE Ultra directory. This should be called + immediately after FCEUI_Initialize() and any time afterwards. + +void FCEUI_SetDirOverride(int which, char *n); + + FCEUIOD_CHEATS - Cheats + FCEUIOD_MISC - Miscellaneous stuff(custom game palettes) + FCEUIOD_NV - Non-volatile game data(battery backed RAM) + FCEUIOD_SNAPS - Screen snapshots + FCEUIOD_STATE - Save states + +void FCEUI_Emulate(void); + Enters the emulation loop. This loop will be exited when FCEUI_CloseGame() + is called. This function obviously shouldn't be called if FCEUI_LoadGame() + wasn't called or FCEUI_CloseGame() was called after FCEUI_LoadGame(). + +void FCEUI_CloseGame(void); + Closes the loaded game and frees all memory used to load it. + Also causes FCEUI_Emulate() to return. + +void FCEUI_SetRenderedLines(int ntscf, int ntscl, int palf, int pall); + Sets the first(minimum is 0) and last(NOT the last scanline plus one; + maximum is 239) scanlines of background data to draw, for both NTSC + emulation mode and PAL emulation mode. + + Defaults are as if this function were called with the variables set + up as follows: + ntscf=8, ntscl=239, palf=0, pall=239 + +void FCEUI_SetNetworkPlay(int type); + Sets status of network play according to "type". If type is 0, + then network play is disabled. If type is 1, then we are server. + If type is 2, then we are a client. + +void FCEUI_SelectState(int w); + Selects the state "slot" to save to and load from. + +void FCEUI_SaveState(void); + Saves the current virtual NES state from the "slot" selected by + FCEUI_SelectState(). + +void FCEUI_LoadState(void); + Loads the current virtual NES state from the "slot" selected by + FCEUI_SelectState(). + +void FCEUI_SaveSnapshot(void); + Saves a screen snapshot. + +void FCEUI_DispMessage(char *msg); + Displays a short, one-line message using FCE Ultra's built-in + functions and ASCII font data. + +int32 FCEUI_GetDesiredFPS(void); + Returns the desired FPS based on whether NTSC or PAL emulation is + enabled, shifted left by 24 bits(this is necessary because the real + FPS value is not a whole integer). This function should only be + necessary if sound emulation is disabled. + +int FCEUI_GetCurrentVidSystem(int *slstart, int *slend); + Convenience function(not strictly necessary, but reduces excessive code + duplication); returns currently emulated video system + (0=NTSC, 1=PAL). It will also set the variables pointed to by slstart + and slend to the first and last scanlines to be rendered, respectively, + if slstart and slend are not 0. + +int FCEUI_AddCheat(char *name, uint16 addr, uint8 val); + Adds a RAM cheat with the specified name to patch the address "addr" + with the value "val". + +int FCEUI_DelCheat(uint32 which); + Deletes the specified(by number) cheat. + +void FCEUI_ListCheats(void (*callb)(char *name, uint16 a, uint8 v)); + Causes FCE Ultra to go through the list of all cheats loaded for + the current game and call callb() for each cheat with the cheat + information. + +int FCEUI_GetCheat(uint32 which, char **name, int32 *a, int32 *v, int *s); + Gets information on the cheat referenced by "which". + +int FCEUI_SetCheat(uint32 which, char *name, int32 a, int32 v, int s); + Sets information for the cheat referenced by "which". + +void FCEUI_CheatSearchBegin(void); + Begins the cheat search process. Current RAM values are copied + to a buffer to later be processed by the other cheat search functions. + +void FCEUI_CheatSearchEnd(int type, int v1, int v2); + Searches the buffer using the search method specified by "type" + and the parameters "v1" and "v2". + +int32 FCEUI_CheatSearchGetCount(void); + Returns the number of matches from the cheat search. + +void FCEUI_CheatSearchGet(void (*callb)(uint16 a, int last, int current)); + +void FCEUI_CheatSearchGetRange(int first, int last, void (*callb)(uint16 a, int last, int current)); + Like FCEUI_CheatSearchGet(), but you can specify the first and last + matches to get. + +void FCEUI_CheatSearchShowExcluded(void); + Undos any exclusions of valid addresses done by FCEUI_CheatSearchEnd(). + +void FCEUI_CheatSearchSetCurrentAsOriginal(void); + Copies the current values in RAM into the cheat search buffer. + +***Recognized defined symbols: + +The following defined symbols affect the way FCE Ultra is compiled: + + C80x86 + - Include 80x86 inline assembly in AT&T syntax. + + FRAMESKIP + - Include frame skipping code. + + NETWORK + - Include network play code. + + FPS + - Compile code that prints out a number when FCE Ultra exits + that represents the average fps. + + ZLIB + - Compile support for compressed PKZIP-style files AND gzip compressed + files. "unzip.c" will need to be compiled and linked in by you if + this is defined(it's in the zlib subdirectory). + + LSB_FIRST + - Compile code to expect that variables that are greater than 8 bits + in size are stored Least Significant Byte First in memory. + + PSS_STYLE x + - Sets the path separator style to the integer 'x'. Valid styles are: + 1: Only "/" - For UNIX platforms. + 2: Both "/" and "\" - For Windows and MSDOS platforms. + 3: Only "\" - For ???. + 4: Only ":" - For Macintoshes and Apple IIs ^_^. + + + + diff --git a/Documentation/rel/1.0 b/Documentation/rel/1.0 new file mode 100644 index 0000000..d791845 --- /dev/null +++ b/Documentation/rel/1.0 @@ -0,0 +1,178 @@ +/******************************************************************************/ +/* 1.0) What FCE Ultra is: */ +/******************************************************************************/ + + FCE Ultra is an NTSC and PAL Famicom/NES emulator for various + platforms. It is based upon Bero's original FCE source code. Current + features include good PPU, CPU, pAPU, expansion chip, and joystick + emulation. Also a feature unique to this emulator(at the current + time) is authentic Game Genie emulation. Save states and snapshot + features also have been implemented. The VS Unisystem is emulated + as well. FCE Ultra supports iNES format ROM images, UNIF format ROM + images, headerless and FWNES style FDS disk images, and NSF files. + + FCE Ultra currently supports the following iNES mappers(many partially): + +Number: Description: Game Examples: +-------------------------------------------------------------------------------- + 0 No Mapper Donkey Kong, Mario Bros + 1 Nintendo MMC1 MegaMan 2, Final Fantasy + 2 Simple 16KB PRG Switch MegaMan 1, Archon, 1944 + 3 Simple 8KB CHR Switch Spy Hunter, Gradius + 4 Nintendo MMC3 Recca, TMNT 2, Final Fantasy 3 + 5 Nintendo MMC5 Castlevania 3, Just Breed, Uchuu Keibitai SDF + 6 FFE F4 Series(hacked) Saint Seiya, Ganbare Goemon + 7 AOROM Battle Toads, Lion King + 8 FFE F3 Series(hacked) Doraemon Kaitakuhen + 9 Nintendo MMC2 Punchout! + 10 Nintendo MMC4 Fire Emblem, Fire Emblem Gaiden + 11 Color Dreams Crystal Mines, Bible Adventures + 13 CPROM Videomation + 15 Multi-cart(pirate) 100-in-1: Contra Function 16 + 16 Bandai Dragon Ball Z, Gundam Knight + 17 FFE F8 Series(hacked) Parodius, Last Armageddon + 18 Jaleco SS806 Pizza Pop, Plazma Ball + 19 Namco 106 Splatter House, Mappy Kids + 21 Konami VRC4 2A WaiWai World 2, Ganbare Goemon Gaiden 2 + 22 Konami VRC4 1B Twinbee 3 + 23 Konami VRC2B WaiWai World, Getsufuu Maden + 24 Konami VRC6 Akumajo Densetsu(Dracula 3) + 25 Konami VRC4 Gradius 2, Bio Miracle: Boku tte Upa + 26 Konami VRC6 A0-A1 Inverse Esper Dream 2, Madara + 32 Irem G-101 Image Fight 2, Perman + 33 Taito TC0190/TC0350 Don Doko Don 1&2 + 34 NINA-001 and BNROM Impossible Mission 2, Deadly Towers, Bug Honey + 40 (pirate) Super Mario Bros. 2 + 41 Caltron 6-in-1 Caltron 6-in-1 + 42 (pirate) "Mario Baby" + 43 Multi-cart(pirate) Golden Game 150 in 1 + 44 Multi-cart(pirate) Super HiK 7 in 1 + 45 Multi-cart(pirate) Super 1000000 in 1 + 46 Game Station Rumble Station + 47 NES-QJ Nintendo World Cup/Super Spike V.B. + 48 Taito TC190V Flintstones + 49 Multi-cart(pirate) Super HiK 4 in 1 + 51 Multi-cart(pirate) 11 in 1 Ball Games + 52 Multi-cart(pirate) Mario Party 7 in 1 + 64 Tengen RAMBO-1 Shinobi, Klax + 65 Irem H-3001 Daiku no Gensan 2 + 66 GNROM SMB + Duck Hunt + 67 Sunsoft Mapper "3" Fantasy Zone 2 + 68 Sunsoft Mapper "4" After Burner 2, Nantetta Baseball + 69 Sunsoft FME-7 Batman: ROTJ, Gimmick! + 70 ?? Kamen Rider Club + 71 Camerica Fire Hawk, Linus Spacehead + 72 Jaleco ?? Pinball Quest + 73 Konami VRC3 Salamander + 75 Jaleco SS8805/Konami VRC1 Tetsuwan Atom, King Kong 2 + 76 Namco 109 Megami Tenshi 1 + 77 Irem ?? Napoleon Senki + 78 Irem 74HC161/32 Holy Diver + 79 NINA-06 F15 City War, Krazy Kreatures + 80 Taito X-005 Minelvation Saga + 82 Taito ?? Kyuukyoku Harikiri Stadium - Heisei Gannen Ban + 83 Multi-cart(pirate) Dragon Ball Party + 85 Konami VRC7 Lagrange Point + 86 Jaleco ?? More Pro Baseball + 87 ?? Argus + 89 Sunsoft ?? Mito Koumon + 90 Pirate Super Mario World, Mortal Kombat + 92 Jaleco ?? MOERO Pro Soccer, MOERO Pro Yakyuu '88 + 93 ?? Fantasy Zone + 94 ?? Senjou no Ookami + 95 Namco ?? Dragon Buster + 97 ?? Kaiketsu Yanchamaru + 99 VS System 8KB CHR Switch VS SMB, VS Excite Bike +105 NES-EVENT Nintendo World Championships +112 Asder Sango Fighter, Hwang Di +113 MB-91 Deathbots +118 MMC3-TLSROM/TKSROM Board Ys 3, Goal! 2, NES Play Action Football +119 MMC3-TQROM Board High Speed, Pin*Bot +140 Jaleco ?? Bio Senshi Dan +151 Konami VS System Expansion VS The Goonies, VS Gradius +152 ?? Arkanoid 2, Saint Seiya Ougon Densetsu +153 Bandai ?? Famicom Jump 2 +180 ?? Crazy Climber +182 ?? Super Donkey Kong +184 ?? Wing of Madoola, The +189 Micro Genius TXC ?? Thunder Warrior +225 Multi-cart(pirate) 58-in-1/110-in-1/52 Games +226 Multi-cart(pirate) 76-in-1 +227 Multi-cart(pirate) 1200-in-1 +228 Action 52 Action 52, Cheetahmen 2 +229 Multi-cart(pirate) 31-in-1 +232 BIC-48 Quattro Arcade, Quattro Sports +234 Multi-cart ?? Maxi-15 +240 ?? Gen Ke Le Zhuan, Shen Huo Le Zhuan +242 ?? Wai Xing Zhan Shi +246 ?? Fong Shen Ban +248 ?? Bao Qing Tian +250 ?? Time Diver Avenger + + FCE Ultra currently supports the following UNIF boards(minus the + prefixes HVC-, NES-, BTL-, and BMC-, as they are currently ignored): + +Group: Name: Game Examples: +-------------------------------------------------------------------------------- +Bootleg: + MARIO1-MALEE2 Super Mario Bros. Malee 2 + NovelDiamond9999999in1 Novel Diamond 999999 in 1 + Super24in1SC03 Super 24 in 1 + Supervision16in1 Supervision 16-in-1 + +Unlicensed: + Sachen-8259A Super Cartridge Version 1 + Sachen-8259B Silver Eagle + Sachen-74LS374N Auto Upturn + SA-016-1M Master Chu and the Drunkard Hu + SA-72007 Sidewinder + SA-72008 Jovial Race + SA-0036 Mahjong 16 + SA-0037 Mahjong Trap + TC-U01-1.5M Challenge of the Dragon + +MMC1: + SAROM Dragon Warrior + SBROM Dance Aerobics + SCROM Orb 3D + SEROM Boulderdash + SGROM Defender of the Crown + SKROM Dungeon Magic + SLROM Castlevania 2 + SL1ROM Sky Shark + SNROM Shingen the Ruler + SOROM Nobunaga's Ambition + +MMC3: + TFROM Legacy of the Wizard + TGROM Megaman 4 + TKROM Kirby's Adventure + TKSROM Ys 3 + TLROM Super Spike V'Ball + TLSROM Goal! 2 + TR1ROM Gauntlet + TQROM Pinbot + TSROM Super Mario Bros. 3 + TVROM Rad Racer 2 + +MMC5: + EKROM Gemfire + ELROM Castlevania 3 + ETROM Nobunaga's Ambition 2 + EWROM Romance of the Three Kingdoms 2 + +MMC6: + HKROM Star Tropics + +Nintendo +discrete +logic: + CNROM Gotcha + CPROM Videomation + MHROM + NROM-128 Mario Bros. + NROM-256 Super Mario Bros. + RROM-128 + UNROM Megaman + + diff --git a/Documentation/rel/1.1.dos b/Documentation/rel/1.1.dos new file mode 100644 index 0000000..003a7d8 --- /dev/null +++ b/Documentation/rel/1.1.dos @@ -0,0 +1,22 @@ +/******************************************************************************/ +/* 1.1) System requirements: */ +/******************************************************************************/ + + Minimum system requirements: + + Pentium 60 MHz + 8 MB RAM + 400 KB free disk space + Linux 2.0.36 + VGA adapter + + Recommended system specifications(at least): + + Pentium 233 MHz + 16 MB RAM + 5 MB free disk space + Windows 9x/Me/2000/XP(long filename support) + VGA adapter + Sound Blaster Pro or newer sound card(or compatible) + + diff --git a/Documentation/rel/1.1.linux b/Documentation/rel/1.1.linux new file mode 100644 index 0000000..b84d391 --- /dev/null +++ b/Documentation/rel/1.1.linux @@ -0,0 +1,22 @@ +/******************************************************************************/ +/* 1.1) System requirements: */ +/******************************************************************************/ + + Minimum system requirements: + + Pentium 60 MHz + 8 MB RAM + 400 KB free disk space + Linux 2.0.36 + VGA adapter + + Recommended system specifications(at least): + + Pentium 233 MHz + 16 MB RAM + 5 MB free disk space + Linux 2.2.x + SVGA adapter with 512 KB of RAM + Sound device capable of handling a sample rate of about 44100 hz. + + diff --git a/Documentation/rel/1.1.win b/Documentation/rel/1.1.win new file mode 100644 index 0000000..af57dc7 --- /dev/null +++ b/Documentation/rel/1.1.win @@ -0,0 +1,26 @@ +/******************************************************************************/ +/* 1.1) System requirements: */ +/******************************************************************************/ + + Minimum system specifications: + + Pentium 60 MHz + 8 MB RAM + 400 KB free disk space + Windows 95 + DirectX 7.0 + Video adapter + + Recommended minimum system specifications: + + Pentium 233 MHz + 16 MB RAM + 5 MB free disk space + Windows 98SE + Video adapter with 2D acceleration abilities + DirectX 8.0 + Joystick + Mouse + Sound device capable of handling a sample rate of 44100 hz. + + diff --git a/Documentation/rel/2.0.dos b/Documentation/rel/2.0.dos new file mode 100644 index 0000000..c837350 --- /dev/null +++ b/Documentation/rel/2.0.dos @@ -0,0 +1,93 @@ +/******************************************************************************/ +/* 2.0) Starting FCE Ultra */ +/******************************************************************************/ + + Start FCE Ultra using the following format: + + fceu romimage.nes + + can be one or more of the following: + + -vmode x Select video mode(all are 8 bpp). + 1 = 256x240 @ 72 hz + 2 = 256x256 @ 65 hz + 3 = 256x256(with scanlines) @ 120 hz + 6 = 256x224(with scanlines) @ 120 hz + 8 = 256x224 @ 103 hz + -vsync x Wait for the screen's vertical retrace before updating + the screen. This *may* cause sound distortion. + 0 = Disabled. + 1 = Enabled. + -cpalette x Load a custom global palette from file x. + The filename x is saved in the configuration file, + not the data from the file. Note that you should + probably use an absolute("C:\nes\test.pal") filename + rather than a relative("test.pal") filename. If x is 0, + the default (built in) global palette will be used. + -ntsccol Emulate an NTSC's TV's colors based on user-supplied + hue and tint values. + 0 = Disabled. + 1 = Enabled. + -sound x Sound. + 0 = Disabled. + Otherwise, x = playback rate in samples per second. + -soundvol x Sound volume. x is an integral percentage value. + The default value is, obviously, 100. + Setting it higher will increase the likelihood that + sound clipping will occur. This shouldn't be noticeable + if the volume is reasonable(200% or lower; perhaps even + higher for many games). + -f8bit x Force 8-bit sound. Enabling this will decrease + sound quality noticeably without increasing + performance noticeably. Only use when 16-bit sound + is problematic. + 0 = Disabled. + 1 = Enabled. + -nothrottle x Disables speed throttling used when sound is disabled + if x is non-zero. The default value of x is 0. + -joy x Joystick mapped to virtual joystick x. + 0 = Disabled, reset configuration. + -inputx str Select device mapped to virtual NES-style input port + x(1-2). + The default for both virtual input ports is "gamepad". + str may be: none, gamepad, zapper, powerpada, + powerpadb, arkanoid + -fcexp str Select Famicom expansion port device. If you select + a device other than "none", you should enable the + "gamepad" on both NES-style virtual input ports. + The default is "none". + str may be: none, shadow, arkanoid, 4player, fkb + -nofs x Disables Four-Score emulation if x is 1. Default is 0. + Note that Four-Score emulation will only be active + if "gamepad" is mapped to one or both virtual input + ports. + -gg Enable Game Genie emulation. + -pal Emulate a PAL NES. + -no8lim x Disables the 8 sprites per scanline limitation. + 0 = Limitation enabled. + 1 = Limitation disabled. + -subase x Save extra game data files(such as battery-backed RAM) + under the base directory if enabled. + 0 = Disabled. + 1 = Enabled. + -snapname x Selects what type of file name screen snapshots will + have. + 0 = Numeric(0.png) + 1 = File base and numeric(mario-0.png) + -clipsides x Clip leftmost and rightmost 8 columns of pixels of + video output. + 0 = Disabled. + 1 = Enabled. + -slstart x Set the first drawn emulated scanline for NTSC emulation + mode. Valid values for x are 0 through 239. + The default value is 8. + -slend x Set the last drawn emulated scanline for NTSC emulation + mode. Valid values for x are 0 through 239. + The default value is 239. + -slstartp x Set the first drawn emulated scanline for PAL emulation + mode. Valid values for x are 0 through 239. + The default value is 0. + -slendp x Set the last drawn emulated scanline for PAL emulation + mode. Valid values for x are 0 through 239. + The default value is 239. + diff --git a/Documentation/rel/2.0.linux b/Documentation/rel/2.0.linux new file mode 100644 index 0000000..bace00f --- /dev/null +++ b/Documentation/rel/2.0.linux @@ -0,0 +1,109 @@ +/******************************************************************************/ +/* 2.0) Starting FCE Ultra */ +/******************************************************************************/ + + Start FCE Ultra using the following format: + + ./fceu romimage.nes + + can be one or more of the following: + + -vmode x Select video mode(all are 8 bpp). + 1 = 256x240 @ 72 hz + 2 = 256x256 @ 65 hz + 3 = 256x256(with scanlines) @ 120 hz + 4 = 640x480(with scanlines) + 5 = 640x480("1 per 4") + 6 = 256x224(with scanlines) @ 120 hz + 7 = 320x240 + 8 = 256x224 @ 103 hz + -vsync x Wait for the screen's vertical retrace before updating + the screen. If you have sound enabled and enable this, + you may have to increase the number of sound fragments + from the default of 8, to reduce buffer underruns. + Buffer underruns will not be preventable if the + current video refresh rate is < ~60 hz (or < ~50 hz + if PAL emulation is enabled). + 0 = Disabled. + 1 = Enabled. + -cpalette x Load a custom global palette from file x. + The filename x is saved in the configuration file, + not the data from the file. Note that you should + probably use an absolute("/home/jp/test.pal") filename + rather than a relative("test.pal") filename. If x is 0, + the default (built in) global palette will be used. + -ntsccol Emulate an NTSC's TV's colors based on user-supplied + hue and tint values. + 0 = Disabled. + 1 = Enabled. + -sound x Sound. + 0 = Disabled. + Otherwise, x = playback rate in samples per second. + -soundvol x Sound volume. x is an integral percentage value. + The default value is, obviously, 100. + Setting it higher will increase the likelihood that + sound clipping will occur. This shouldn't be noticeable + if the volume is reasonable(200% or lower; perhaps even + higher for many games). + -sfragsize x Set sound fragment size to 2^x(2 to the power of x) + SAMPLES. The default value is 7. + -snfrags x Set number of sound fragments to x. The default value + is 8. + -f8bit x Force 8-bit sound. Enabling this will decrease + sound quality noticeably without increasing + performance noticeably. Only use when 16-bit sound + is problematic. + 0 = Disabled. + 1 = Enabled. + -nothrottle x Disables speed throttling used when sound is disabled + if x is non-zero. The default value of x is 0. + -joyx y Joystick mapped to virtual joystick x(1-4). + 0 = Disabled, reset configuration. + Otherwise, y(1-inf) = joystick number. + -inputx str Select device mapped to virtual NES-style input port + x(1-2). + The default for both virtual input ports is "gamepad". + str may be: none, gamepad, zapper, powerpada, + powerpadb, arkanoid + -fcexp str Select Famicom expansion port device. If you select + a device other than "none", you should enable the + "gamepad" on both NES-style virtual input ports. + The default is "none". + str may be: none, shadow, arkanoid, 4player, fkb + -nofs x Disables Four-Score emulation if x is 1. Default is 0. + Note that Four-Score emulation will only be active + if "gamepad" is mapped to one or both virtual input + ports. + -gg Enable Game Genie emulation. + -pal Emulate a PAL NES. + -no8lim x Disables the 8 sprites per scanline limitation. + 0 = Limitation enabled. + 1 = Limitation disabled. + -subase x Save extra game data files(such as battery-backed RAM) + under the base directory if enabled. + 0 = Disabled. + 1 = Enabled. + -snapname x Selects what type of file name screen snapshots will + have. + 0 = Numeric(0.png) + 1 = File base and numeric(mario-0.png) + -clipsides x Clip leftmost and rightmost 8 columns of pixels of + video output. + 0 = Disabled. + 1 = Enabled. + -slstart x Set the first drawn emulated scanline for NTSC emulation + mode. Valid values for x are 0 through 239. + The default value is 8. + -slend x Set the last drawn emulated scanline for NTSC emulation + mode. Valid values for x are 0 through 239. + The default value is 239. + -slstartp x Set the first drawn emulated scanline for PAL emulation + mode. Valid values for x are 0 through 239. + The default value is 0. + -slendp x Set the last drawn emulated scanline for PAL emulation + mode. Valid values for x are 0 through 239. + The default value is 239. + -connect s Connect to server 's' for TCP/IP network play. + -server Be a host/server for TCP/IP network play. + -netport x Use TCP/IP port x for network play. The default + port is 4046. diff --git a/Documentation/rel/2.0.win b/Documentation/rel/2.0.win new file mode 100644 index 0000000..15bc082 --- /dev/null +++ b/Documentation/rel/2.0.win @@ -0,0 +1,8 @@ +/******************************************************************************/ +/* 2.0) Starting FCE Ultra */ +/******************************************************************************/ + + FCE Ultra can be started by running the executable "fceu.exe". + I do not recommend running it from a DOS "box". + + diff --git a/Documentation/rel/2.1.dos b/Documentation/rel/2.1.dos new file mode 100644 index 0000000..31f5995 --- /dev/null +++ b/Documentation/rel/2.1.dos @@ -0,0 +1,59 @@ +/******************************************************************************/ +/* 2.1) General Keyoard Key Mapping: */ +/******************************************************************************/ + + Default Key Mapping: + + For emulated Family BASIC Keyboard: + Enable/Disable Keyboard Input Scroll Lock + (enabling emulated keyboard input will disable + commands keys) + All emulated keys are mapped to the closest open key on the PC + keyboard, with a few exceptions. The emulated "@" key is + mapped to the "`"(grave) key, and the emulated "kana" key + is mapped to the "Insert" key(in the 3x2 key block above the + cursor keys). + + For emulated game pads: + Left Control B + Left Alt A + Enter/Return Start + Tab Select + Cursor Down Down + Cursor Up Up + Cursor Left Left + Cursor Right Right + + For emulated power pads(keys correspond to button locations on + side "B"): + O P [ ] + K L ; ' + M , . / + + For FDS games: + I Insert disk. + E Eject disk. + S Select disk/disk side. + + For VS Unisystem games: + C Insert coin. + V Show/Hide dip switches. + 1-8 Toggle dip switches(when dip switches + are shown). + + T Select tint to adjust. + H Select hue to adjust. + +/- Increase/decrease tint or hue. + + 0-9 Select save state. + Caps Lock Select virtual joystick. + + F2 Activate cheat interface. + + F5/F7 Save/Load state. + F9 Save screen snapshot. + F10 Reset. + F11 Power off/on. + ESC/F12 Exit. + + diff --git a/Documentation/rel/2.1.linux b/Documentation/rel/2.1.linux new file mode 100644 index 0000000..85915f3 --- /dev/null +++ b/Documentation/rel/2.1.linux @@ -0,0 +1,62 @@ +/******************************************************************************/ +/* 2.1) General Keyoard Key Mapping: */ +/******************************************************************************/ + + Default Key Mapping: + + For emulated Family BASIC Keyboard: + Enable/Disable Keyboard Input Scroll Lock + (enabling emulated keyboard input will disable + commands keys) + All emulated keys are mapped to the closest open key on the PC + keyboard, with a few exceptions. The emulated "@" key is + mapped to the "`"(grave) key, and the emulated "kana" key + is mapped to the "Insert" key(in the 3x2 key block above the + cursor keys). + + For emulated game pads: + Left Control B + Left Alt A + Enter/Return Start + Tab Select + Cursor Down Down + Cursor Up Up + Cursor Left Left + Cursor Right Right + + For emulated power pads(keys correspond to button locations on + side "B"): + O P [ ] + K L ; ' + M , . / + + For FDS games: + I Insert disk. + E Eject disk. + S Select disk/disk side. + + For VS Unisystem games: + C Insert coin. + V Show/Hide dip switches. + 1-8 Toggle dip switches(when dip switches + are shown). + + T Select tint to adjust. + H Select hue to adjust. + +/- Increase/decrease tint or hue. + + 0-9 Select save state. + Caps Lock Select virtual joystick. + + F2 Activate cheat interface. + + F3 Lock virtual console. + F4 Unlock virtual console. + + F5/F7 Save/Load state. + F9 Save screen snapshot. + F10 Reset. + F11 Power off/on. + ESC/F12 Exit. + + diff --git a/Documentation/rel/2.1.win b/Documentation/rel/2.1.win new file mode 100644 index 0000000..4676385 --- /dev/null +++ b/Documentation/rel/2.1.win @@ -0,0 +1,125 @@ +/******************************************************************************/ +/* 2.1) Using FCE Ultra: */ +/******************************************************************************/ + + After starting FCE Ultra, you'll probably want to load a game. Do + this by going to File/Open. + + Menu descriptions: + + File + Open - Loads a new game. + Save State - Saves the current NES state. + Load State - Loads a saved NES state. + Log Sound As - Logs sound to a file. It will not work if sound + is disabled. + Exit - Exit the emulator. + + NES + Reset - Resets the virtual NES. + Power - Power cycles the virtual NES. + Cheats - Activates the cheat interface. See "cheat.txt" for + more details. + + Config + Hide Menu - Hides the menu. + Game Genie - If checked, enable Game Genie emulation. + Game Genie emulation will only begin or end when a new + game is loaded. + PAL Emulation - If checked, enable PAL emulation. Changes take effect + immediately, though I recommend resetting the virtual + NES afterward PAL emulation is enabled or disabled. + Directories - Configure the directories that FCE Ultra will store + its files in. + Input - Enter input configuration dialog. + Note that not all virtual devices are configurable. + Miscellaneous - Enter miscellaneous configuration dialog. + Network Play - Enter network play configuration dialog. + Palette - Enter palette configuration dialog. + Sound - Enter sound configuration dialog. + Sound enabled: + Sound emulation and output are enabled when this is checked. + Force 8-bit sound: + Forces 8-bit sound output. Use only when absolutely + necessary(very rare). + Sample rate: + Specifies how many sound samples will be played back per + second. Unless you know what you are doing, you probably + don't need to change this setting. + Use secondary sound buffer: + Uses a secondary sound buffer. This option may be required + for sound to work with certain sound cards/devices. + Selecting "with global focus" will cause sound to be played + while FCE Ultra has lost window focus, but you will probably + also want to select "Active While Focus Lost" in the Config + menu as well, otherwise you will just get repeating sound + when FCE Ultra loses focus. + Length of sound buffer: + Specifies what length of sound(in milliseconds) should be + buffered by FCE Ultra. DirectSound and the Windows kernel + may or may not cause a little more latency than what you + might expect(usually not any more than a few milliseconds), + depending on your setup. + Use larger values if you have sound problems such as popping + or gaps, though. Larger values will increase the latency of + the sound, however. Finally, larger values are ideal for + background music listening. + Volume: + Specifies the volume of FCE Ultra's sound output. Setting + the volume too high MIGHT cause noticeable clipping on some + sounds(loud drums, for example), but don't let that possibility + stop you from experimenting. + + Video - Enter video configuration dialog. + + Default Key Mapping: + + For emulated Family BASIC Keyboard: + Enable/Disable Keyboard Input Scroll Lock + (enabling emulated keyboard input will disable + commands keys) + All emulated keys are mapped to the closest open key on the PC + keyboard, with a few exceptions. The emulated "@" key is + mapped to the "`"(grave) key, and the emulated "kana" key + is mapped to the "Insert" key(in the 3x2 key block above the + cursor keys). + + For emulated game pads: + Left Control B + Left Alt A + Enter/Return Start + Tab Select + Cursor Down Down + Cursor Up Up + Cursor Left Left + Cursor Right Right + + For emulated power pads(keys correspond to button locations on + side "B"): + O P [ ] + K L ; ' + M , . / + + For FDS games: + I Insert disk. + E Eject disk. + S Select disk/disk side. + + For VS Unisystem games: + C Insert coin. + V Show/Hide dip switches. + 1-8 Toggle dip switches(when dip switches + are shown). + + 0-9 Select save state. + + F5/F7 Save/Load state. + F9 Save screen snapshot. + + F3 Hide/Show menu. + F4 Toggle between windowed/full screen modes. + F10 Reset. + F11 Power off/on. + F12 Exit. + + diff --git a/Documentation/rel/3.0.dos b/Documentation/rel/3.0.dos new file mode 100644 index 0000000..73aa6ad --- /dev/null +++ b/Documentation/rel/3.0.dos @@ -0,0 +1,19 @@ +/******************************************************************************/ +/* 3.0) Platform Specific Notes */ +/******************************************************************************/ + + FCE Ultra's base directory is the directory in which the executable + is located. + + Sound Blaster sound output requires that the 'BLASTER' environment + variable is set. To set it(permanently), add the following line + to your autoexec.bat file: + + set BLASTER=A240 I5 D1 H7 + + Where 240(hexadecimal) is the Sound Blaster's base I/O address, 5 + is the IRQ number, 1 is the 8-bit DMA channel, and 7 is the 16-bit + DMA channel(if applicable). + *DO NOT GUESS THE SETTINGS* + Invalid settings can result in very bad things happening. + diff --git a/Documentation/rel/3.0.linux b/Documentation/rel/3.0.linux new file mode 100644 index 0000000..faa5407 --- /dev/null +++ b/Documentation/rel/3.0.linux @@ -0,0 +1,37 @@ +/******************************************************************************/ +/* 3.0) Platform Specific Notes */ +/******************************************************************************/ + + 1. FCE Ultra uses SVGAlib, so you'll obviously need to have it + installed. + + 2. Be careful when using the non-VGA video modes(such as 640x480). + SVGAlib doesn't always handle virtual terminal switching well when + one of these modes is being used. Also, since FCE Ultra enables a + a linear frame buffer for these modes, heed this warning from + the SVGAlib documentation: + + Furthermore, some cards (Cirrus) just enable this buffer + at a fixed hardware address. For Cirrus it is mapped at + 14MB so you should never used it if you have more than + 14MB of memory (But how does an application know?). The + Mach32 support for this is smarter. It makes this feature + only available when it is safe to be used. + + 3. FCE Ultra must be run as root in order to get access to the + VGA hardware. SVGAlib will drop root privileges if you are running + the program as a normal user and the suid root bit is set and the + executable is owned by root. Example: + chown root fceu ; chmod 4755 fceu + Make sure you have the latest stable release of SVGAlib, though. + + 4. FCE Ultra will save all data(state saves/screen snapshots) in + ~/.fceultra, so make sure your "HOME" environment variable is set + correctly. + + If you believe FCE Ultra is too slow, you can try the following: + * Kill some of the other processes. + * Disable sound emulation. + * Raise the priority of FCE Ultra with the 'nice' command. + Ex: nice -n -20 ./fceu + diff --git a/Documentation/rel/3.0.win b/Documentation/rel/3.0.win new file mode 100644 index 0000000..1fb9dd2 --- /dev/null +++ b/Documentation/rel/3.0.win @@ -0,0 +1,11 @@ +/******************************************************************************/ +/* 3.0) Platform Specific Notes */ +/******************************************************************************/ + + Your desktop color depth must be 16bpp, 24bpp, or 32bpp for FCE Ultra + to run properly in windowed mode. + + FCE Ultra's base directory is the directory in which the executable + is located. + + diff --git a/Documentation/rel/3.1 b/Documentation/rel/3.1 new file mode 100644 index 0000000..68b3afc --- /dev/null +++ b/Documentation/rel/3.1 @@ -0,0 +1,20 @@ +/******************************************************************************/ +/* 3.1) Network Play Notes */ +/******************************************************************************/ + + In TCP/IP network play, the server will be player one, and the + client will be player 2. + + Zapper emulation and power pad emulation currently do not work with + network play. + + Having Game Genie or PAL emulation enabled on only one side + will cause problems. + + Both players MUST use the same ROM/disk image and SRAM + file(if applicable). + + When using FDS or VS Unisystem games with network play, only player + 1 will be able to insert disk, eject disk, insert coins, toggle dip + switches, etc. + diff --git a/Documentation/rel/3.2 b/Documentation/rel/3.2 new file mode 100644 index 0000000..725374f --- /dev/null +++ b/Documentation/rel/3.2 @@ -0,0 +1,28 @@ +/******************************************************************************/ +/* 3.2) VS Unisystem Notes */ +/******************************************************************************/ + + FCE Ultra currently only supports VS Unisystem ROM images in the + iNES format. + + ROM Images: + + * VS Unisystem games that are about 49,000 bytes in size most likely + use mapper 99. + * Other VS Unisystem games will use other mappers. Here is a short + list of games and the mappers they use: + + CastleVania - 2 + Dr. Mario - 1 + Goonies - 151 + Gradius - 151 + Ice Climber - 99 + Platoon - 68 + + Palette(s): + + * The colors in many VS Unisystem games may be incorrect. This + is due to each game having its own PPU, and thus using a + different palette than games that use a different PPU. + + diff --git a/Documentation/rel/3.3 b/Documentation/rel/3.3 new file mode 100644 index 0000000..5cf33f1 --- /dev/null +++ b/Documentation/rel/3.3 @@ -0,0 +1,18 @@ +/******************************************************************************/ +/* 3.3) Famicom Disk System Notes */ +/******************************************************************************/ + + You will need the FDS BIOS ROM image in the base FCE Ultra directory. + It must be named "disksys.rom". I will not give this file to you, so + don't ask. + + Two types of FDS disk images are supported: disk images with the + FWNES-style header, and disk images with no header. + + You should make backups of all of your FDS games you use with FCE + Ultra. This is because FCE Ultra will write the disk image back to + the storage medium, and the disk image in RAM might have been corrupted + because of inaccurate emulation(this case is not likely to occur, but + it could occur). + + diff --git a/Documentation/rel/3.4 b/Documentation/rel/3.4 new file mode 100644 index 0000000..4d014dd --- /dev/null +++ b/Documentation/rel/3.4 @@ -0,0 +1,23 @@ +/******************************************************************************/ +/* 3.4) Light Gun Notes */ +/******************************************************************************/ + + Currently, the NES Zapper and the light gun used with the VS + Unisystem(I will call both the same name, Zapper) are supported. + Most(all?) NES games expect the Zapper to be plugged into port 2. + and most(all?) VS Unisystem games expect the Zapper to be plugged + into port(?) 1. + + The LEFT mouse button is the emulated trigger button for the + Zapper. The RIGHT mouse button is also emulated as the trigger, + but as long as you have the RIGHT mouse button held down, no color + detection will take place, which is effectively like pulling the + trigger while the Zapper is pointed away from the television screen. + Note that you must hold the RIGHT button down for a short + time(greater than just a fast click, shorter than a second). + + Zapper emulation currently does NOT work with network play, so + don't even try it. I may add support in the future if enough + people want it or if I want it. + + diff --git a/Documentation/rel/3.5 b/Documentation/rel/3.5 new file mode 100644 index 0000000..474c4c9 --- /dev/null +++ b/Documentation/rel/3.5 @@ -0,0 +1,35 @@ +/******************************************************************************/ +/* 3.5) Palette Notes */ +/******************************************************************************/ + + Palettes files are expected to contain 64 8-bit RGB triplets(each in + that order; red comes first in the triplet in the file, then green, + then blue). Each 8-bit value represents brightness for that particular + color. 0 is minimum, 255 is maximum. + + Palettes can be set on a per-game basis. To do this, put a palette + file in the "gameinfo" directory with the same base filename + as the game you wish to associate with and the extension "pal". + Examples: + + File name: Palette file name: + BigBad.nes BigBad.pal + BigBad.zip BigBad.pal + BigBad.Better.nes BigBad.Better.pal + + + With so many ways to choose a palette, figuring out which one will + be active may be difficult. Here's a list of what palettes will + be used, in order from highest priority to least priority(if a condition + doesn't exist for a higher priority palette, the emulator will + continue down its list of palettes). + + NSF Palette(for NSFs only) + Palette loaded from the "gameinfo" directory. + NTSC Color Emulation(only for NTSC NES games). + VS Unisystem palette(if the game is a VS Unisystem game and a palette + is available). + Custom global palette. + Default NES palette. + + diff --git a/Documentation/rel/3.6 b/Documentation/rel/3.6 new file mode 100644 index 0000000..0c0ff57 --- /dev/null +++ b/Documentation/rel/3.6 @@ -0,0 +1,19 @@ +/******************************************************************************/ +/* 3.6) Compressed File Notes */ +/******************************************************************************/ + + FCE Ultra can load data from both PKZIP-format files and + gzip-format files. Only one type of (de)compression algorithm is + supported: "deflate"; this seems to be the most popular compression + algorithm, though. + + A compressed FDS disk image will only be saved back to disk if it + uses the gzip format. + + All files in a PKZIP format file will be scanned for the + followings extensions: .nes, .fds, .nsf, .unf, .nez, .unif + The first compressed file to have one of these extensions will be + loaded. If no compressed file has one of these extensions, the + first compressed file will be loaded. + + diff --git a/Documentation/rel/3.7 b/Documentation/rel/3.7 new file mode 100644 index 0000000..483a030 --- /dev/null +++ b/Documentation/rel/3.7 @@ -0,0 +1,15 @@ +/******************************************************************************/ +/* 3.7) Game Genie Notes */ +/******************************************************************************/ + + The Game Genie ROM image is loaded from the file "gg.rom" in the + base directory the first time Game Genie emulation is enabled and + a ROM image is loaded since the time FCE Ultra has run. + + The ROM image may either be the 24592 byte iNES-format image, or + the 4352 raw ROM image. + + Remember that enabling/disabling Game Genie emulation will not take + effect until a new game is loaded(this statement shouldn't concern + any of the "run once" command-line driven ports). + diff --git a/Documentation/rel/4.0 b/Documentation/rel/4.0 new file mode 100644 index 0000000..d22245c --- /dev/null +++ b/Documentation/rel/4.0 @@ -0,0 +1,12 @@ +/******************************************************************************/ +/* 4.0) Contacting the author */ +/******************************************************************************/ + + I can be reached via email at xodnizel@users.sourceforge.net. + Bero can be reached via email at 9bero9@geocities.co.jp + (Note that Bero can not and will not answer any questions + regarding the operation of FCE Ultra, so please don't ask him. + If you understand this, remove the 9's from the email address + provided to get his real email address.) + + diff --git a/Documentation/rel/4.1 b/Documentation/rel/4.1 new file mode 100644 index 0000000..e177ee2 --- /dev/null +++ b/Documentation/rel/4.1 @@ -0,0 +1,36 @@ +/******************************************************************************/ +/* 4.1) Credits */ +/******************************************************************************/ + +\Firebug\ - High-level mapper information. +Bero - Original FCE source code. +Brad Taylor - NES sound channel guide. +Chris Hickman - Archaic Ruins. +Donald Moore - DC PasoFami NES packs. +Fredrik Olson - NES four-player adapter information. +Gilles Vollant - PKZIP file loading functions. +goroh - Various documents. +Jeff Tamer - Insaniacal fun. +Jeremy Chadwick - General NES information. +Justin Smith - Giving me obscure ROM images in the "dark ages of + NES emulation". +Kevin Horton - Low level NES information and sound information. +Ki - Various technical information. +Mark Knibbs - Various NES information. +Marat Fayzullin - General NES information. +Matthew Conte - Sound information. +N. Andou - Awesome NES/SNES emulators, at the time... +nori - FDS sound information. +Quietust - VRC7 sound translation code by The Quietust + (quietust@ircN.org). + Ideas and corrections. +R. Hoelscher - Famicom four-player adapter information. +Rob Mocca - DC PasoFami NES packs, testing. +Sean Whalen - Node99. +Tatsuyuki Satoh - OPL2 emulator +TheRedEye - ROM images, testing. + + +Info-ZIP - ZLIB + +...and everyone else who has helped me. diff --git a/Documentation/rel/d b/Documentation/rel/d new file mode 100644 index 0000000..028eaef --- /dev/null +++ b/Documentation/rel/d @@ -0,0 +1,5 @@ +#!/bin/sh + +./da.sh linux +./da.sh win +./da.sh dos diff --git a/Documentation/rel/da.sh b/Documentation/rel/da.sh new file mode 100644 index 0000000..a0e5e3a --- /dev/null +++ b/Documentation/rel/da.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +cat top.$1 new new.$1 toc 1.0 1.1.$1 2.0.$1 2.1.$1 3.0.$1 3.1 3.2 3.3 3.4 3.5 3.6 3.7 4.0 4.1 > readme-$1.txt diff --git a/Documentation/rel/new b/Documentation/rel/new new file mode 100644 index 0000000..c18f25f --- /dev/null +++ b/Documentation/rel/new @@ -0,0 +1,56 @@ + * Screen snapshots can now be taken while playing an NSF. + * Saving screen snapshots will no longer corrupt the frame buffer. + * Added many more games to the list of games that usually are found + with bad iNES headers. + * Fixed more network play bugs. It should now work correctly(how + many times have I said or implied that...). + * The NSF player will now disable the FDS sound channel on song + initialization(fixes a problem with the Zelda no Densetsu + rip). + * Reads from $4090 and $4092 while emulating the FDS will now return + the current envelope settings. Affects "Ai Senshi Nicole" + and "Bio Miracle Bokutte Upa", at least. + * Merged a lot of pirate MMC3 multicart emulation code with the main + MMC3 emulation code. + * Added support for the MMC5's split-screen mode. This fixes the + introduction in "Uchuu Keibitai SDF". + * Writes to $8000-$FFFF with D7 set during MMC1 emulation will + cause the MMC1 mode register to be OR'd with $C. This fixes + "Robocop 3". + * Replaced an MMC1 hack that I used to get "Bill and Ted's Excellent + Video Game Adventure" to work with something more accurate. + * Fixed the MMC5 read handler to return the data last on the data + bus instead of $FF when a read occured to an unmapped address. + This fixes the lockup problem in "Bandit Kings of Ancient China" + and possibly other games. + * Added support for the game "Ishin no Arashi" in the iNES format + (I added an entry with its CRC32 value and the number of 8KB + WRAM banks it needs into the MMC5 WRAM size table). + * Added support for MMC1 games in the iNES format with 16KB of RAM + via CRC32 comparisons(currently only Genghis Khan(USA), Romance + of the 3 Kingdoms(USA), and Nobunaga's Ambition(USA and Japan) are + recognized). + * iNES mapper 1 now supports pageable CHR RAM if CHR ROM is not + present. Fixes "Family School". + * Added support for iNES mappers 51 and 52. Thanks to Kevin Horton + for the information. + * Modified MMC3(iNES mapper 4/118/119) IRQ counter emulation. Fixes + problems in "MegaMan 3", "Gun Nac", and the Japanese version of + "Klax", but it *might* cause problems with other games. + * Fixed an iNES mapper 32 emulation bug. "Ai Sensei no Oshiete" + works now. + * Fixed iNES mapper 33/48 IRQ emulation. + * Fixed iNES mapper 16 IRQ emulation. + * Added support for "Famicom Jump 2" as iNES mapper 153. + If a good(as far as I can tell) dump is loaded, FCE Ultra will + automatically fix the mapper number. + * The VS Unisystem bit in iNES headers is no longer recognized. + Too many games have this bit set when it shouldn't be set. + Now VS Unisystem games are recognized by CRC32 value. + * Reads from $4015 no longer reset DMC IRQ. This fixes the + title screen of "Romancia". + * PPU NMI now occurs a few cycles later. Fixes the "BattleToads" + lockup problem. + * BRK emulation now sets the I flag. + * Changed a few zero-page address mode functions to read directly + from emulated RAM. diff --git a/Documentation/rel/new.dos b/Documentation/rel/new.dos new file mode 100644 index 0000000..9a69443 --- /dev/null +++ b/Documentation/rel/new.dos @@ -0,0 +1,5 @@ + * Added a new video mode(256x224 at a refresh rate of 103 Hz). + * Increased the refresh rate of video mode 2 to 65 Hz. + * Increased the refresh rate of video mode 3 to 120 Hz. + * Added speed throttling used when sound is disabled, and a + command-line switch to control it. diff --git a/Documentation/rel/new.linux b/Documentation/rel/new.linux new file mode 100644 index 0000000..abaee9f --- /dev/null +++ b/Documentation/rel/new.linux @@ -0,0 +1,6 @@ + * Added a new video mode(256x224 at a refresh rate of 103 Hz). + * Increased the refresh rate of video mode 2 to 65 Hz. + * Increased the refresh rate of video mode 3 to 120 Hz. + * Added speed throttling used when sound is disabled, and a + command-line switch to control it. + diff --git a/Documentation/rel/new.win b/Documentation/rel/new.win new file mode 100644 index 0000000..e69de29 diff --git a/Documentation/rel/readme-dos.txt b/Documentation/rel/readme-dos.txt new file mode 100644 index 0000000..150c566 --- /dev/null +++ b/Documentation/rel/readme-dos.txt @@ -0,0 +1,646 @@ + FCE Ultra + 0.81 + + http://fceultra.sourceforge.net/ + + +What is new: + + * Screen snapshots can now be taken while playing an NSF. + * Saving screen snapshots will no longer corrupt the frame buffer. + * Added many more games to the list of games that usually are found + with bad iNES headers. + * The NSF player will now disable the FDS sound channel on song + initialization(fixes a problem with the Zelda no Densetsu + rip). + * Reads from $4090 and $4092 while emulating the FDS will now return + the current envelope settings. Affects "Ai Senshi Nicole" + and "Bio Miracle Bokutte Upa", at least. + * Merged a lot of pirate MMC3 multicart emulation code with the main + MMC3 emulation code. + * Added support for the MMC5's split-screen mode. This fixes the + introduction in "Uchuu Keibitai SDF". + * Writes to $8000-$FFFF with D7 set during MMC1 emulation will + cause the MMC1 mode register to be OR'd with $C. This fixes + "Robocop 3". + * Replaced an MMC1 hack that I used to get "Bill and Ted's Excellent + Video Game Adventure" to work with something more accurate. + * Fixed the MMC5 read handler to return the data last on the data + bus instead of $FF when a read occured to an unmapped address. + This fixes the lockup problem in "Bandit Kings of Ancient China" + and possibly other games. + * Added support for the game "Ishin no Arashi" in the iNES format + (I added an entry with its CRC32 value and the number of 8KB + WRAM banks it needs into the MMC5 WRAM size table). + * Added support for MMC1 games in the iNES format with 16KB of RAM + via CRC32 comparisons(currently only Genghis Khan(USA), Romance + of the 3 Kingdoms(USA), and Nobunaga's Ambition(USA and Japan) are + recognized). + * iNES mapper 1 now supports pageable CHR RAM if CHR ROM is not + present. Fixes "Family School". + * Added support for iNES mappers 51 and 52. Thanks to Kevin Horton + for the information. + * Modified MMC3(iNES mapper 4/118/119) IRQ counter emulation. Fixes + problems in "MegaMan 3", "Gun Nac", and the Japanese version of + "Klax", but it *might* cause problems with other games. + * Fixed an iNES mapper 32 emulation bug. "Ai Sensei no Oshiete" + works now. + * Fixed iNES mapper 33/48 IRQ emulation. + * Fixed iNES mapper 16 IRQ emulation. + * Added support for "Famicom Jump 2" as iNES mapper 153. + If a good(as far as I can tell) dump is loaded, FCE Ultra will + automatically fix the mapper number. + * The VS Unisystem bit in iNES headers is no longer recognized. + Too many games have this bit set when it shouldn't be set. + Now VS Unisystem games are recognized by CRC32 value. + * Reads from $4015 no longer reset DMC IRQ. This fixes the + title screen of "Romancia". + * PPU NMI now occurs a few cycles later. Fixes the "BattleToads" + lockup problem. + * BRK emulation now sets the I flag. + * Changed a few zero-page address mode functions to read directly + from emulated RAM. + * Added a new video mode(256x224 at a refresh rate of 103 Hz). + * Increased the refresh rate of video mode 2 to 65 Hz. + * Increased the refresh rate of video mode 3 to 120 Hz. + * Added speed throttling used when sound is disabled, and a + command-line switch to control it. + + +Contents: + + 1. Basic information + 1.0 What FCE Ultra is. + 1.1 System requirements. + 2. How to use + 2.0 Starting FCE Ultra + 2.1 Using FCE Ultra + 3. Notes + 3.0 Platform Specific Notes + 3.2 VS Unisystem Notes + 3.3 Famicom Disk System Notes + 3.4 Light Gun Notes + 3.5 Palette Notes + 3.6 Compressed File Notes + 3.7 Game Genie Notes + 4. Extra + 4.0 Contacting the author + 4.1 Credits + +/******************************************************************************/ +/* 1.0) What FCE Ultra is: */ +/******************************************************************************/ + + FCE Ultra is an NTSC and PAL Famicom/NES emulator for various + platforms. It is based upon Bero's original FCE source code. Current + features include good PPU, CPU, pAPU, expansion chip, and joystick + emulation. Also a feature unique to this emulator(at the current + time) is authentic Game Genie emulation. Save states and snapshot + features also have been implemented. The VS Unisystem is emulated + as well. FCE Ultra supports iNES format ROM images, UNIF format ROM + images, headerless and FWNES style FDS disk images, and NSF files. + + FCE Ultra currently supports the following iNES mappers(many partially): + +Number: Description: Game Examples: +-------------------------------------------------------------------------------- + 0 No Mapper Donkey Kong, Mario Bros + 1 Nintendo MMC1 MegaMan 2, Final Fantasy + 2 Simple 16KB PRG Switch MegaMan 1, Archon, 1944 + 3 Simple 8KB CHR Switch Spy Hunter, Gradius + 4 Nintendo MMC3 Recca, TMNT 2, Final Fantasy 3 + 5 Nintendo MMC5 Castlevania 3, Just Breed, Uchuu Keibitai SDF + 6 FFE F4 Series(hacked) Saint Seiya, Ganbare Goemon + 7 AOROM Battle Toads, Lion King + 8 FFE F3 Series(hacked) Doraemon Kaitakuhen + 9 Nintendo MMC2 Punchout! + 10 Nintendo MMC4 Fire Emblem, Fire Emblem Gaiden + 11 Color Dreams Crystal Mines, Bible Adventures + 13 CPROM Videomation + 15 Multi-cart(pirate) 100-in-1: Contra Function 16 + 16 Bandai Dragon Ball Z, Gundam Knight + 17 FFE F8 Series(hacked) Parodius, Last Armageddon + 18 Jaleco SS806 Pizza Pop, Plazma Ball + 19 Namco 106 Splatter House, Mappy Kids + 21 Konami VRC4 2A WaiWai World 2, Ganbare Goemon Gaiden 2 + 22 Konami VRC4 1B Twinbee 3 + 23 Konami VRC2B WaiWai World, Getsufuu Maden + 24 Konami VRC6 Akumajo Densetsu(Dracula 3) + 25 Konami VRC4 Gradius 2, Bio Miracle: Boku tte Upa + 26 Konami VRC6 A0-A1 Inverse Esper Dream 2, Madara + 32 Irem G-101 Image Fight 2, Perman + 33 Taito TC0190/TC0350 Don Doko Don 1&2 + 34 NINA-001 and BNROM Impossible Mission 2, Deadly Towers, Bug Honey + 40 (pirate) Super Mario Bros. 2 + 41 Caltron 6-in-1 Caltron 6-in-1 + 42 (pirate) "Mario Baby" + 43 Multi-cart(pirate) Golden Game 150 in 1 + 44 Multi-cart(pirate) Super HiK 7 in 1 + 45 Multi-cart(pirate) Super 1000000 in 1 + 46 Game Station Rumble Station + 47 NES-QJ Nintendo World Cup/Super Spike V.B. + 48 Taito TC190V Flintstones + 49 Multi-cart(pirate) Super HiK 4 in 1 + 51 Multi-cart(pirate) 11 in 1 Ball Games + 52 Multi-cart(pirate) Mario Party 7 in 1 + 64 Tengen RAMBO-1 Shinobi, Klax + 65 Irem H-3001 Daiku no Gensan 2 + 66 GNROM SMB + Duck Hunt + 67 Sunsoft Mapper "3" Fantasy Zone 2 + 68 Sunsoft Mapper "4" After Burner 2, Nantetta Baseball + 69 Sunsoft FME-7 Batman: ROTJ, Gimmick! + 70 ?? Kamen Rider Club + 71 Camerica Fire Hawk, Linus Spacehead + 72 Jaleco ?? Pinball Quest + 73 Konami VRC3 Salamander + 75 Jaleco SS8805/Konami VRC1 Tetsuwan Atom, King Kong 2 + 76 Namco 109 Megami Tenshi 1 + 77 Irem ?? Napoleon Senki + 78 Irem 74HC161/32 Holy Diver + 79 NINA-06 F15 City War, Krazy Kreatures + 80 Taito X-005 Minelvation Saga + 82 Taito ?? Kyuukyoku Harikiri Stadium - Heisei Gannen Ban + 83 Multi-cart(pirate) Dragon Ball Party + 85 Konami VRC7 Lagrange Point + 86 Jaleco ?? More Pro Baseball + 87 ?? Argus + 89 Sunsoft ?? Mito Koumon + 90 Pirate Super Mario World, Mortal Kombat + 92 Jaleco ?? MOERO Pro Soccer, MOERO Pro Yakyuu '88 + 93 ?? Fantasy Zone + 94 ?? Senjou no Ookami + 95 Namco ?? Dragon Buster + 97 ?? Kaiketsu Yanchamaru + 99 VS System 8KB CHR Switch VS SMB, VS Excite Bike +105 NES-EVENT Nintendo World Championships +112 Asder Sango Fighter, Hwang Di +113 MB-91 Deathbots +118 MMC3-TLSROM/TKSROM Board Ys 3, Goal! 2, NES Play Action Football +119 MMC3-TQROM Board High Speed, Pin*Bot +140 Jaleco ?? Bio Senshi Dan +151 Konami VS System Expansion VS The Goonies, VS Gradius +152 ?? Arkanoid 2, Saint Seiya Ougon Densetsu +153 Bandai ?? Famicom Jump 2 +180 ?? Crazy Climber +182 ?? Super Donkey Kong +184 ?? Wing of Madoola, The +189 Micro Genius TXC ?? Thunder Warrior +225 Multi-cart(pirate) 58-in-1/110-in-1/52 Games +226 Multi-cart(pirate) 76-in-1 +227 Multi-cart(pirate) 1200-in-1 +228 Action 52 Action 52, Cheetahmen 2 +229 Multi-cart(pirate) 31-in-1 +232 BIC-48 Quattro Arcade, Quattro Sports +234 Multi-cart ?? Maxi-15 +240 ?? Gen Ke Le Zhuan, Shen Huo Le Zhuan +242 ?? Wai Xing Zhan Shi +246 ?? Fong Shen Ban +248 ?? Bao Qing Tian +250 ?? Time Diver Avenger + + FCE Ultra currently supports the following UNIF boards(minus the + prefixes HVC-, NES-, BTL-, and BMC-, as they are currently ignored): + +Group: Name: Game Examples: +-------------------------------------------------------------------------------- +Bootleg: + MARIO1-MALEE2 Super Mario Bros. Malee 2 + NovelDiamond9999999in1 Novel Diamond 999999 in 1 + Super24in1SC03 Super 24 in 1 + Supervision16in1 Supervision 16-in-1 + +Unlicensed: + Sachen-8259A Super Cartridge Version 1 + Sachen-8259B Silver Eagle + Sachen-74LS374N Auto Upturn + SA-016-1M Master Chu and the Drunkard Hu + SA-72007 Sidewinder + SA-72008 Jovial Race + SA-0036 Mahjong 16 + SA-0037 Mahjong Trap + TC-U01-1.5M Challenge of the Dragon + +MMC1: + SAROM Dragon Warrior + SBROM Dance Aerobics + SCROM Orb 3D + SEROM Boulderdash + SGROM Defender of the Crown + SKROM Dungeon Magic + SLROM Castlevania 2 + SL1ROM Sky Shark + SNROM Shingen the Ruler + SOROM Nobunaga's Ambition + +MMC3: + TFROM Legacy of the Wizard + TGROM Megaman 4 + TKROM Kirby's Adventure + TKSROM Ys 3 + TLROM Super Spike V'Ball + TLSROM Goal! 2 + TR1ROM Gauntlet + TQROM Pinbot + TSROM Super Mario Bros. 3 + TVROM Rad Racer 2 + +MMC5: + EKROM Gemfire + ELROM Castlevania 3 + ETROM Nobunaga's Ambition 2 + EWROM Romance of the Three Kingdoms 2 + +MMC6: + HKROM Star Tropics + +Nintendo +discrete +logic: + CNROM Gotcha + CPROM Videomation + MHROM + NROM-128 Mario Bros. + NROM-256 Super Mario Bros. + RROM-128 + UNROM Megaman + + +/******************************************************************************/ +/* 1.1) System requirements: */ +/******************************************************************************/ + + Minimum system requirements: + + Pentium 60 MHz + 8 MB RAM + 400 KB free disk space + Linux 2.0.36 + VGA adapter + + Recommended system specifications(at least): + + Pentium 233 MHz + 16 MB RAM + 5 MB free disk space + Windows 9x/Me/2000/XP(long filename support) + VGA adapter + Sound Blaster Pro or newer sound card(or compatible) + + +/******************************************************************************/ +/* 2.0) Starting FCE Ultra */ +/******************************************************************************/ + + Start FCE Ultra using the following format: + + fceu romimage.nes + + can be one or more of the following: + + -vmode x Select video mode(all are 8 bpp). + 1 = 256x240 @ 72 hz + 2 = 256x256 @ 65 hz + 3 = 256x256(with scanlines) @ 120 hz + 6 = 256x224(with scanlines) @ 120 hz + 8 = 256x224 @ 103 hz + -vsync x Wait for the screen's vertical retrace before updating + the screen. This *may* cause sound distortion. + 0 = Disabled. + 1 = Enabled. + -cpalette x Load a custom global palette from file x. + The filename x is saved in the configuration file, + not the data from the file. Note that you should + probably use an absolute("C:\nes\test.pal") filename + rather than a relative("test.pal") filename. If x is 0, + the default (built in) global palette will be used. + -ntsccol Emulate an NTSC's TV's colors based on user-supplied + hue and tint values. + 0 = Disabled. + 1 = Enabled. + -sound x Sound. + 0 = Disabled. + Otherwise, x = playback rate in samples per second. + -soundvol x Sound volume. x is an integral percentage value. + The default value is, obviously, 100. + Setting it higher will increase the likelihood that + sound clipping will occur. This shouldn't be noticeable + if the volume is reasonable(200% or lower; perhaps even + higher for many games). + -f8bit x Force 8-bit sound. Enabling this will decrease + sound quality noticeably without increasing + performance noticeably. Only use when 16-bit sound + is problematic. + 0 = Disabled. + 1 = Enabled. + -nothrottle x Disables speed throttling used when sound is disabled + if x is non-zero. The default value of x is 0. + -joy x Joystick mapped to virtual joystick x. + 0 = Disabled, reset configuration. + -inputx str Select device mapped to virtual NES-style input port + x(1-2). + The default for both virtual input ports is "gamepad". + str may be: none, gamepad, zapper, powerpada, + powerpadb, arkanoid + -fcexp str Select Famicom expansion port device. If you select + a device other than "none", you should enable the + "gamepad" on both NES-style virtual input ports. + The default is "none". + str may be: none, shadow, arkanoid, 4player, fkb + -nofs x Disables Four-Score emulation if x is 1. Default is 0. + Note that Four-Score emulation will only be active + if "gamepad" is mapped to one or both virtual input + ports. + -gg Enable Game Genie emulation. + -pal Emulate a PAL NES. + -no8lim x Disables the 8 sprites per scanline limitation. + 0 = Limitation enabled. + 1 = Limitation disabled. + -subase x Save extra game data files(such as battery-backed RAM) + under the base directory if enabled. + 0 = Disabled. + 1 = Enabled. + -snapname x Selects what type of file name screen snapshots will + have. + 0 = Numeric(0.png) + 1 = File base and numeric(mario-0.png) + -clipsides x Clip leftmost and rightmost 8 columns of pixels of + video output. + 0 = Disabled. + 1 = Enabled. + -slstart x Set the first drawn emulated scanline for NTSC emulation + mode. Valid values for x are 0 through 239. + The default value is 8. + -slend x Set the last drawn emulated scanline for NTSC emulation + mode. Valid values for x are 0 through 239. + The default value is 239. + -slstartp x Set the first drawn emulated scanline for PAL emulation + mode. Valid values for x are 0 through 239. + The default value is 0. + -slendp x Set the last drawn emulated scanline for PAL emulation + mode. Valid values for x are 0 through 239. + The default value is 239. + +/******************************************************************************/ +/* 2.1) General Keyoard Key Mapping: */ +/******************************************************************************/ + + Default Key Mapping: + + For emulated Family BASIC Keyboard: + Enable/Disable Keyboard Input Scroll Lock + (enabling emulated keyboard input will disable + commands keys) + All emulated keys are mapped to the closest open key on the PC + keyboard, with a few exceptions. The emulated "@" key is + mapped to the "`"(grave) key, and the emulated "kana" key + is mapped to the "Insert" key(in the 3x2 key block above the + cursor keys). + + For emulated game pads: + Left Control B + Left Alt A + Enter/Return Start + Tab Select + Cursor Down Down + Cursor Up Up + Cursor Left Left + Cursor Right Right + + For emulated power pads(keys correspond to button locations on + side "B"): + O P [ ] + K L ; ' + M , . / + + For FDS games: + I Insert disk. + E Eject disk. + S Select disk/disk side. + + For VS Unisystem games: + C Insert coin. + V Show/Hide dip switches. + 1-8 Toggle dip switches(when dip switches + are shown). + + T Select tint to adjust. + H Select hue to adjust. + +/- Increase/decrease tint or hue. + + 0-9 Select save state. + Caps Lock Select virtual joystick. + + F2 Activate cheat interface. + + F5/F7 Save/Load state. + F9 Save screen snapshot. + F10 Reset. + F11 Power off/on. + ESC/F12 Exit. + + +/******************************************************************************/ +/* 3.0) Platform Specific Notes */ +/******************************************************************************/ + + FCE Ultra's base directory is the directory in which the executable + is located. + + Sound Blaster sound output requires that the 'BLASTER' environment + variable is set. To set it(permanently), add the following line + to your autoexec.bat file: + + set BLASTER=A240 I5 D1 H7 + + Where 240(hexadecimal) is the Sound Blaster's base I/O address, 5 + is the IRQ number, 1 is the 8-bit DMA channel, and 7 is the 16-bit + DMA channel(if applicable). + *DO NOT GUESS THE SETTINGS* + Invalid settings can result in very bad things happening. + +/******************************************************************************/ +/* 3.2) VS Unisystem Notes */ +/******************************************************************************/ + + FCE Ultra currently only supports VS Unisystem ROM images in the + iNES format. + + ROM Images: + + * VS Unisystem games that are about 49,000 bytes in size most likely + use mapper 99. + * Other VS Unisystem games will use other mappers. Here is a short + list of games and the mappers they use: + + CastleVania - 2 + Dr. Mario - 1 + Goonies - 151 + Gradius - 151 + Ice Climber - 99 + Platoon - 68 + + Palette(s): + + * The colors in many VS Unisystem games may be incorrect. This + is due to each game having its own PPU, and thus using a + different palette than games that use a different PPU. + + +/******************************************************************************/ +/* 3.3) Famicom Disk System Notes */ +/******************************************************************************/ + + You will need the FDS BIOS ROM image in the base FCE Ultra directory. + It must be named "disksys.rom". I will not give this file to you, so + don't ask. + + Two types of FDS disk images are supported: disk images with the + FWNES-style header, and disk images with no header. + + You should make backups of all of your FDS games you use with FCE + Ultra. This is because FCE Ultra will write the disk image back to + the storage medium, and the disk image in RAM might have been corrupted + because of inaccurate emulation(this case is not likely to occur, but + it could occur). + + +/******************************************************************************/ +/* 3.4) Light Gun Notes */ +/******************************************************************************/ + + Currently, the NES Zapper and the light gun used with the VS + Unisystem(I will call both the same name, Zapper) are supported. + Most(all?) NES games expect the Zapper to be plugged into port 2. + and most(all?) VS Unisystem games expect the Zapper to be plugged + into port(?) 1. + + The LEFT mouse button is the emulated trigger button for the + Zapper. The RIGHT mouse button is also emulated as the trigger, + but as long as you have the RIGHT mouse button held down, no color + detection will take place, which is effectively like pulling the + trigger while the Zapper is pointed away from the television screen. + Note that you must hold the RIGHT button down for a short + time(greater than just a fast click, shorter than a second). + + Zapper emulation currently does NOT work with network play, so + don't even try it. I may add support in the future if enough + people want it or if I want it. + + +/******************************************************************************/ +/* 3.5) Palette Notes */ +/******************************************************************************/ + + Palettes files are expected to contain 64 8-bit RGB triplets(each in + that order; red comes first in the triplet in the file, then green, + then blue). Each 8-bit value represents brightness for that particular + color. 0 is minimum, 255 is maximum. + + Palettes can be set on a per-game basis. To do this, put a palette + file in the "gameinfo" directory with the same base filename + as the game you wish to associate with and the extension "pal". + Examples: + + File name: Palette file name: + BigBad.nes BigBad.pal + BigBad.zip BigBad.pal + BigBad.Better.nes BigBad.Better.pal + + + With so many ways to choose a palette, figuring out which one will + be active may be difficult. Here's a list of what palettes will + be used, in order from highest priority to least priority(if a condition + doesn't exist for a higher priority palette, the emulator will + continue down its list of palettes). + + NSF Palette(for NSFs only) + Palette loaded from the "gameinfo" directory. + NTSC Color Emulation(only for NTSC NES games). + VS Unisystem palette(if the game is a VS Unisystem game and a palette + is available). + Custom global palette. + Default NES palette. + + +/******************************************************************************/ +/* 3.6) Compressed File Notes */ +/******************************************************************************/ + + FCE Ultra can load data from both PKZIP-format files and + gzip-format files. Only one type of (de)compression algorithm is + supported: "deflate"; this seems to be the most popular compression + algorithm, though. + + A compressed FDS disk image will only be saved back to disk if it + uses the gzip format. + + All files in a PKZIP format file will be scanned for the + followings extensions: .nes, .fds, .nsf, .unf, .nez, .unif + The first compressed file to have one of these extensions will be + loaded. If no compressed file has one of these extensions, the + first compressed file will be loaded. + + +/******************************************************************************/ +/* 3.7) Game Genie Notes */ +/******************************************************************************/ + + The Game Genie ROM image is loaded from the file "gg.rom" in the + base directory the first time Game Genie emulation is enabled and + a ROM image is loaded since the time FCE Ultra has run. + + The ROM image may either be the 24592 byte iNES-format image, or + the 4352 raw ROM image. + + Remember that enabling/disabling Game Genie emulation will not take + effect until a new game is loaded(this statement shouldn't concern + any of the "run once" command-line driven ports). + +/******************************************************************************/ +/* 4.0) Contacting the author */ +/******************************************************************************/ + + I can be reached via email at xodnizel@users.sourceforge.net. + Bero can be reached via email at 9bero9@geocities.co.jp + (Note that Bero can not and will not answer any questions + regarding the operation of FCE Ultra, so please don't ask him. + If you understand this, remove the 9's from the email address + provided to get his real email address.) + + +/******************************************************************************/ +/* 4.1) Credits */ +/******************************************************************************/ + +\Firebug\ - High-level mapper information. +Bero - Original FCE source code. +Brad Taylor - NES sound channel guide. +Chris Hickman - Archaic Ruins. +Donald Moore - DC PasoFami NES packs. +Fredrik Olson - NES four-player adapter information. +Gilles Vollant - PKZIP file loading functions. +goroh - Various documents. +Jeff Tamer - Insaniacal fun. +Jeremy Chadwick - General NES information. +Justin Smith - Giving me obscure ROM images in the "dark ages of + NES emulation". +Kevin Horton - Low level NES information and sound information. +Ki - Various technical information. +Mark Knibbs - Various NES information. +Marat Fayzullin - General NES information. +Matthew Conte - Sound information. +N. Andou - Awesome NES/SNES emulators, at the time... +nori - FDS sound information. +Quietust - VRC7 sound translation code by The Quietust + (quietust@ircN.org). + Ideas and corrections. +R. Hoelscher - Famicom four-player adapter information. +Rob Mocca - DC PasoFami NES packs, testing. +Sean Whalen - Node99. +Tatsuyuki Satoh - OPL2 emulator +TheRedEye - ROM images, testing. + + +Info-ZIP - ZLIB + +...and everyone else who has helped me. diff --git a/Documentation/rel/readme-linux.txt b/Documentation/rel/readme-linux.txt new file mode 100644 index 0000000..880e919 --- /dev/null +++ b/Documentation/rel/readme-linux.txt @@ -0,0 +1,707 @@ + FCE Ultra + 0.81 + + http://fceultra.sourceforge.net/ + + +What is new: + + * Screen snapshots can now be taken while playing an NSF. + * Saving screen snapshots will no longer corrupt the frame buffer. + * Added many more games to the list of games that usually are found + with bad iNES headers. + * Fixed more network play bugs. It should now work correctly(how + many times have I said or implied that...). + * The NSF player will now disable the FDS sound channel on song + initialization(fixes a problem with the Zelda no Densetsu + rip). + * Reads from $4090 and $4092 while emulating the FDS will now return + the current envelope settings. Affects "Ai Senshi Nicole" + and "Bio Miracle Bokutte Upa", at least. + * Merged a lot of pirate MMC3 multicart emulation code with the main + MMC3 emulation code. + * Added support for the MMC5's split-screen mode. This fixes the + introduction in "Uchuu Keibitai SDF". + * Writes to $8000-$FFFF with D7 set during MMC1 emulation will + cause the MMC1 mode register to be OR'd with $C. This fixes + "Robocop 3". + * Replaced an MMC1 hack that I used to get "Bill and Ted's Excellent + Video Game Adventure" to work with something more accurate. + * Fixed the MMC5 read handler to return the data last on the data + bus instead of $FF when a read occured to an unmapped address. + This fixes the lockup problem in "Bandit Kings of Ancient China" + and possibly other games. + * Added support for the game "Ishin no Arashi" in the iNES format + (I added an entry with its CRC32 value and the number of 8KB + WRAM banks it needs into the MMC5 WRAM size table). + * Added support for MMC1 games in the iNES format with 16KB of RAM + via CRC32 comparisons(currently only Genghis Khan(USA), Romance + of the 3 Kingdoms(USA), and Nobunaga's Ambition(USA and Japan) are + recognized). + * iNES mapper 1 now supports pageable CHR RAM if CHR ROM is not + present. Fixes "Family School". + * Added support for iNES mappers 51 and 52. Thanks to Kevin Horton + for the information. + * Modified MMC3(iNES mapper 4/118/119) IRQ counter emulation. Fixes + problems in "MegaMan 3", "Gun Nac", and the Japanese version of + "Klax", but it *might* cause problems with other games. + * Fixed an iNES mapper 32 emulation bug. "Ai Sensei no Oshiete" + works now. + * Fixed iNES mapper 33/48 IRQ emulation. + * Fixed iNES mapper 16 IRQ emulation. + * Added support for "Famicom Jump 2" as iNES mapper 153. + If a good(as far as I can tell) dump is loaded, FCE Ultra will + automatically fix the mapper number. + * The VS Unisystem bit in iNES headers is no longer recognized. + Too many games have this bit set when it shouldn't be set. + Now VS Unisystem games are recognized by CRC32 value. + * Reads from $4015 no longer reset DMC IRQ. This fixes the + title screen of "Romancia". + * PPU NMI now occurs a few cycles later. Fixes the "BattleToads" + lockup problem. + * BRK emulation now sets the I flag. + * Changed a few zero-page address mode functions to read directly + from emulated RAM. + * Added a new video mode(256x224 at a refresh rate of 103 Hz). + * Increased the refresh rate of video mode 2 to 65 Hz. + * Increased the refresh rate of video mode 3 to 120 Hz. + * Added speed throttling used when sound is disabled, and a + command-line switch to control it. + + + +Contents: + + 1. Basic information + 1.0 What FCE Ultra is. + 1.1 System requirements. + 2. How to use + 2.0 Starting FCE Ultra + 2.1 Using FCE Ultra + 3. Notes + 3.0 Platform Specific Notes + 3.1 Network Play Notes + 3.2 VS Unisystem Notes + 3.3 Famicom Disk System Notes + 3.4 Light Gun Notes + 3.5 Palette Notes + 3.6 Compressed File Notes + 3.7 Game Genie Notes + 4. Extra + 4.0 Contacting the author + 4.1 Credits + +/******************************************************************************/ +/* 1.0) What FCE Ultra is: */ +/******************************************************************************/ + + FCE Ultra is an NTSC and PAL Famicom/NES emulator for various + platforms. It is based upon Bero's original FCE source code. Current + features include good PPU, CPU, pAPU, expansion chip, and joystick + emulation. Also a feature unique to this emulator(at the current + time) is authentic Game Genie emulation. Save states and snapshot + features also have been implemented. The VS Unisystem is emulated + as well. FCE Ultra supports iNES format ROM images, UNIF format ROM + images, headerless and FWNES style FDS disk images, and NSF files. + + FCE Ultra currently supports the following iNES mappers(many partially): + +Number: Description: Game Examples: +-------------------------------------------------------------------------------- + 0 No Mapper Donkey Kong, Mario Bros + 1 Nintendo MMC1 MegaMan 2, Final Fantasy + 2 Simple 16KB PRG Switch MegaMan 1, Archon, 1944 + 3 Simple 8KB CHR Switch Spy Hunter, Gradius + 4 Nintendo MMC3 Recca, TMNT 2, Final Fantasy 3 + 5 Nintendo MMC5 Castlevania 3, Just Breed, Uchuu Keibitai SDF + 6 FFE F4 Series(hacked) Saint Seiya, Ganbare Goemon + 7 AOROM Battle Toads, Lion King + 8 FFE F3 Series(hacked) Doraemon Kaitakuhen + 9 Nintendo MMC2 Punchout! + 10 Nintendo MMC4 Fire Emblem, Fire Emblem Gaiden + 11 Color Dreams Crystal Mines, Bible Adventures + 13 CPROM Videomation + 15 Multi-cart(pirate) 100-in-1: Contra Function 16 + 16 Bandai Dragon Ball Z, Gundam Knight + 17 FFE F8 Series(hacked) Parodius, Last Armageddon + 18 Jaleco SS806 Pizza Pop, Plazma Ball + 19 Namco 106 Splatter House, Mappy Kids + 21 Konami VRC4 2A WaiWai World 2, Ganbare Goemon Gaiden 2 + 22 Konami VRC4 1B Twinbee 3 + 23 Konami VRC2B WaiWai World, Getsufuu Maden + 24 Konami VRC6 Akumajo Densetsu(Dracula 3) + 25 Konami VRC4 Gradius 2, Bio Miracle: Boku tte Upa + 26 Konami VRC6 A0-A1 Inverse Esper Dream 2, Madara + 32 Irem G-101 Image Fight 2, Perman + 33 Taito TC0190/TC0350 Don Doko Don 1&2 + 34 NINA-001 and BNROM Impossible Mission 2, Deadly Towers, Bug Honey + 40 (pirate) Super Mario Bros. 2 + 41 Caltron 6-in-1 Caltron 6-in-1 + 42 (pirate) "Mario Baby" + 43 Multi-cart(pirate) Golden Game 150 in 1 + 44 Multi-cart(pirate) Super HiK 7 in 1 + 45 Multi-cart(pirate) Super 1000000 in 1 + 46 Game Station Rumble Station + 47 NES-QJ Nintendo World Cup/Super Spike V.B. + 48 Taito TC190V Flintstones + 49 Multi-cart(pirate) Super HiK 4 in 1 + 51 Multi-cart(pirate) 11 in 1 Ball Games + 52 Multi-cart(pirate) Mario Party 7 in 1 + 64 Tengen RAMBO-1 Shinobi, Klax + 65 Irem H-3001 Daiku no Gensan 2 + 66 GNROM SMB + Duck Hunt + 67 Sunsoft Mapper "3" Fantasy Zone 2 + 68 Sunsoft Mapper "4" After Burner 2, Nantetta Baseball + 69 Sunsoft FME-7 Batman: ROTJ, Gimmick! + 70 ?? Kamen Rider Club + 71 Camerica Fire Hawk, Linus Spacehead + 72 Jaleco ?? Pinball Quest + 73 Konami VRC3 Salamander + 75 Jaleco SS8805/Konami VRC1 Tetsuwan Atom, King Kong 2 + 76 Namco 109 Megami Tenshi 1 + 77 Irem ?? Napoleon Senki + 78 Irem 74HC161/32 Holy Diver + 79 NINA-06 F15 City War, Krazy Kreatures + 80 Taito X-005 Minelvation Saga + 82 Taito ?? Kyuukyoku Harikiri Stadium - Heisei Gannen Ban + 83 Multi-cart(pirate) Dragon Ball Party + 85 Konami VRC7 Lagrange Point + 86 Jaleco ?? More Pro Baseball + 87 ?? Argus + 89 Sunsoft ?? Mito Koumon + 90 Pirate Super Mario World, Mortal Kombat + 92 Jaleco ?? MOERO Pro Soccer, MOERO Pro Yakyuu '88 + 93 ?? Fantasy Zone + 94 ?? Senjou no Ookami + 95 Namco ?? Dragon Buster + 97 ?? Kaiketsu Yanchamaru + 99 VS System 8KB CHR Switch VS SMB, VS Excite Bike +105 NES-EVENT Nintendo World Championships +112 Asder Sango Fighter, Hwang Di +113 MB-91 Deathbots +118 MMC3-TLSROM/TKSROM Board Ys 3, Goal! 2, NES Play Action Football +119 MMC3-TQROM Board High Speed, Pin*Bot +140 Jaleco ?? Bio Senshi Dan +151 Konami VS System Expansion VS The Goonies, VS Gradius +152 ?? Arkanoid 2, Saint Seiya Ougon Densetsu +153 Bandai ?? Famicom Jump 2 +180 ?? Crazy Climber +182 ?? Super Donkey Kong +184 ?? Wing of Madoola, The +189 Micro Genius TXC ?? Thunder Warrior +225 Multi-cart(pirate) 58-in-1/110-in-1/52 Games +226 Multi-cart(pirate) 76-in-1 +227 Multi-cart(pirate) 1200-in-1 +228 Action 52 Action 52, Cheetahmen 2 +229 Multi-cart(pirate) 31-in-1 +232 BIC-48 Quattro Arcade, Quattro Sports +234 Multi-cart ?? Maxi-15 +240 ?? Gen Ke Le Zhuan, Shen Huo Le Zhuan +242 ?? Wai Xing Zhan Shi +246 ?? Fong Shen Ban +248 ?? Bao Qing Tian +250 ?? Time Diver Avenger + + FCE Ultra currently supports the following UNIF boards(minus the + prefixes HVC-, NES-, BTL-, and BMC-, as they are currently ignored): + +Group: Name: Game Examples: +-------------------------------------------------------------------------------- +Bootleg: + MARIO1-MALEE2 Super Mario Bros. Malee 2 + NovelDiamond9999999in1 Novel Diamond 999999 in 1 + Super24in1SC03 Super 24 in 1 + Supervision16in1 Supervision 16-in-1 + +Unlicensed: + Sachen-8259A Super Cartridge Version 1 + Sachen-8259B Silver Eagle + Sachen-74LS374N Auto Upturn + SA-016-1M Master Chu and the Drunkard Hu + SA-72007 Sidewinder + SA-72008 Jovial Race + SA-0036 Mahjong 16 + SA-0037 Mahjong Trap + TC-U01-1.5M Challenge of the Dragon + +MMC1: + SAROM Dragon Warrior + SBROM Dance Aerobics + SCROM Orb 3D + SEROM Boulderdash + SGROM Defender of the Crown + SKROM Dungeon Magic + SLROM Castlevania 2 + SL1ROM Sky Shark + SNROM Shingen the Ruler + SOROM Nobunaga's Ambition + +MMC3: + TFROM Legacy of the Wizard + TGROM Megaman 4 + TKROM Kirby's Adventure + TKSROM Ys 3 + TLROM Super Spike V'Ball + TLSROM Goal! 2 + TR1ROM Gauntlet + TQROM Pinbot + TSROM Super Mario Bros. 3 + TVROM Rad Racer 2 + +MMC5: + EKROM Gemfire + ELROM Castlevania 3 + ETROM Nobunaga's Ambition 2 + EWROM Romance of the Three Kingdoms 2 + +MMC6: + HKROM Star Tropics + +Nintendo +discrete +logic: + CNROM Gotcha + CPROM Videomation + MHROM + NROM-128 Mario Bros. + NROM-256 Super Mario Bros. + RROM-128 + UNROM Megaman + + +/******************************************************************************/ +/* 1.1) System requirements: */ +/******************************************************************************/ + + Minimum system requirements: + + Pentium 60 MHz + 8 MB RAM + 400 KB free disk space + Linux 2.0.36 + VGA adapter + + Recommended system specifications(at least): + + Pentium 233 MHz + 16 MB RAM + 5 MB free disk space + Linux 2.2.x + SVGA adapter with 512 KB of RAM + Sound device capable of handling a sample rate of about 44100 hz. + + +/******************************************************************************/ +/* 2.0) Starting FCE Ultra */ +/******************************************************************************/ + + Start FCE Ultra using the following format: + + ./fceu romimage.nes + + can be one or more of the following: + + -vmode x Select video mode(all are 8 bpp). + 1 = 256x240 @ 72 hz + 2 = 256x256 @ 65 hz + 3 = 256x256(with scanlines) @ 120 hz + 4 = 640x480(with scanlines) + 5 = 640x480("1 per 4") + 6 = 256x224(with scanlines) @ 120 hz + 7 = 320x240 + 8 = 256x224 @ 103 hz + -vsync x Wait for the screen's vertical retrace before updating + the screen. If you have sound enabled and enable this, + you may have to increase the number of sound fragments + from the default of 8, to reduce buffer underruns. + Buffer underruns will not be preventable if the + current video refresh rate is < ~60 hz (or < ~50 hz + if PAL emulation is enabled). + 0 = Disabled. + 1 = Enabled. + -cpalette x Load a custom global palette from file x. + The filename x is saved in the configuration file, + not the data from the file. Note that you should + probably use an absolute("/home/jp/test.pal") filename + rather than a relative("test.pal") filename. If x is 0, + the default (built in) global palette will be used. + -ntsccol Emulate an NTSC's TV's colors based on user-supplied + hue and tint values. + 0 = Disabled. + 1 = Enabled. + -sound x Sound. + 0 = Disabled. + Otherwise, x = playback rate in samples per second. + -soundvol x Sound volume. x is an integral percentage value. + The default value is, obviously, 100. + Setting it higher will increase the likelihood that + sound clipping will occur. This shouldn't be noticeable + if the volume is reasonable(200% or lower; perhaps even + higher for many games). + -sfragsize x Set sound fragment size to 2^x(2 to the power of x) + SAMPLES. The default value is 7. + -snfrags x Set number of sound fragments to x. The default value + is 8. + -f8bit x Force 8-bit sound. Enabling this will decrease + sound quality noticeably without increasing + performance noticeably. Only use when 16-bit sound + is problematic. + 0 = Disabled. + 1 = Enabled. + -nothrottle x Disables speed throttling used when sound is disabled + if x is non-zero. The default value of x is 0. + -joyx y Joystick mapped to virtual joystick x(1-4). + 0 = Disabled, reset configuration. + Otherwise, y(1-inf) = joystick number. + -inputx str Select device mapped to virtual NES-style input port + x(1-2). + The default for both virtual input ports is "gamepad". + str may be: none, gamepad, zapper, powerpada, + powerpadb, arkanoid + -fcexp str Select Famicom expansion port device. If you select + a device other than "none", you should enable the + "gamepad" on both NES-style virtual input ports. + The default is "none". + str may be: none, shadow, arkanoid, 4player, fkb + -nofs x Disables Four-Score emulation if x is 1. Default is 0. + Note that Four-Score emulation will only be active + if "gamepad" is mapped to one or both virtual input + ports. + -gg Enable Game Genie emulation. + -pal Emulate a PAL NES. + -no8lim x Disables the 8 sprites per scanline limitation. + 0 = Limitation enabled. + 1 = Limitation disabled. + -subase x Save extra game data files(such as battery-backed RAM) + under the base directory if enabled. + 0 = Disabled. + 1 = Enabled. + -snapname x Selects what type of file name screen snapshots will + have. + 0 = Numeric(0.png) + 1 = File base and numeric(mario-0.png) + -clipsides x Clip leftmost and rightmost 8 columns of pixels of + video output. + 0 = Disabled. + 1 = Enabled. + -slstart x Set the first drawn emulated scanline for NTSC emulation + mode. Valid values for x are 0 through 239. + The default value is 8. + -slend x Set the last drawn emulated scanline for NTSC emulation + mode. Valid values for x are 0 through 239. + The default value is 239. + -slstartp x Set the first drawn emulated scanline for PAL emulation + mode. Valid values for x are 0 through 239. + The default value is 0. + -slendp x Set the last drawn emulated scanline for PAL emulation + mode. Valid values for x are 0 through 239. + The default value is 239. + -connect s Connect to server 's' for TCP/IP network play. + -server Be a host/server for TCP/IP network play. + -netport x Use TCP/IP port x for network play. The default + port is 4046. +/******************************************************************************/ +/* 2.1) General Keyoard Key Mapping: */ +/******************************************************************************/ + + Default Key Mapping: + + For emulated Family BASIC Keyboard: + Enable/Disable Keyboard Input Scroll Lock + (enabling emulated keyboard input will disable + commands keys) + All emulated keys are mapped to the closest open key on the PC + keyboard, with a few exceptions. The emulated "@" key is + mapped to the "`"(grave) key, and the emulated "kana" key + is mapped to the "Insert" key(in the 3x2 key block above the + cursor keys). + + For emulated game pads: + Left Control B + Left Alt A + Enter/Return Start + Tab Select + Cursor Down Down + Cursor Up Up + Cursor Left Left + Cursor Right Right + + For emulated power pads(keys correspond to button locations on + side "B"): + O P [ ] + K L ; ' + M , . / + + For FDS games: + I Insert disk. + E Eject disk. + S Select disk/disk side. + + For VS Unisystem games: + C Insert coin. + V Show/Hide dip switches. + 1-8 Toggle dip switches(when dip switches + are shown). + + T Select tint to adjust. + H Select hue to adjust. + +/- Increase/decrease tint or hue. + + 0-9 Select save state. + Caps Lock Select virtual joystick. + + F2 Activate cheat interface. + + F3 Lock virtual console. + F4 Unlock virtual console. + + F5/F7 Save/Load state. + F9 Save screen snapshot. + F10 Reset. + F11 Power off/on. + ESC/F12 Exit. + + +/******************************************************************************/ +/* 3.0) Platform Specific Notes */ +/******************************************************************************/ + + 1. FCE Ultra uses SVGAlib, so you'll obviously need to have it + installed. + + 2. Be careful when using the non-VGA video modes(such as 640x480). + SVGAlib doesn't always handle virtual terminal switching well when + one of these modes is being used. Also, since FCE Ultra enables a + a linear frame buffer for these modes, heed this warning from + the SVGAlib documentation: + + Furthermore, some cards (Cirrus) just enable this buffer + at a fixed hardware address. For Cirrus it is mapped at + 14MB so you should never used it if you have more than + 14MB of memory (But how does an application know?). The + Mach32 support for this is smarter. It makes this feature + only available when it is safe to be used. + + 3. FCE Ultra must be run as root in order to get access to the + VGA hardware. SVGAlib will drop root privileges if you are running + the program as a normal user and the suid root bit is set and the + executable is owned by root. Example: + chown root fceu ; chmod 4755 fceu + Make sure you have the latest stable release of SVGAlib, though. + + 4. FCE Ultra will save all data(state saves/screen snapshots) in + ~/.fceultra, so make sure your "HOME" environment variable is set + correctly. + + If you believe FCE Ultra is too slow, you can try the following: + * Kill some of the other processes. + * Disable sound emulation. + * Raise the priority of FCE Ultra with the 'nice' command. + Ex: nice -n -20 ./fceu + +/******************************************************************************/ +/* 3.1) Network Play Notes */ +/******************************************************************************/ + + In TCP/IP network play, the server will be player one, and the + client will be player 2. + + Zapper emulation and power pad emulation currently do not work with + network play. + + Having Game Genie or PAL emulation enabled on only one side + will cause problems. + + Both players MUST use the same ROM/disk image and SRAM + file(if applicable). + + When using FDS or VS Unisystem games with network play, only player + 1 will be able to insert disk, eject disk, insert coins, toggle dip + switches, etc. + +/******************************************************************************/ +/* 3.2) VS Unisystem Notes */ +/******************************************************************************/ + + FCE Ultra currently only supports VS Unisystem ROM images in the + iNES format. + + ROM Images: + + * VS Unisystem games that are about 49,000 bytes in size most likely + use mapper 99. + * Other VS Unisystem games will use other mappers. Here is a short + list of games and the mappers they use: + + CastleVania - 2 + Dr. Mario - 1 + Goonies - 151 + Gradius - 151 + Ice Climber - 99 + Platoon - 68 + + Palette(s): + + * The colors in many VS Unisystem games may be incorrect. This + is due to each game having its own PPU, and thus using a + different palette than games that use a different PPU. + + +/******************************************************************************/ +/* 3.3) Famicom Disk System Notes */ +/******************************************************************************/ + + You will need the FDS BIOS ROM image in the base FCE Ultra directory. + It must be named "disksys.rom". I will not give this file to you, so + don't ask. + + Two types of FDS disk images are supported: disk images with the + FWNES-style header, and disk images with no header. + + You should make backups of all of your FDS games you use with FCE + Ultra. This is because FCE Ultra will write the disk image back to + the storage medium, and the disk image in RAM might have been corrupted + because of inaccurate emulation(this case is not likely to occur, but + it could occur). + + +/******************************************************************************/ +/* 3.4) Light Gun Notes */ +/******************************************************************************/ + + Currently, the NES Zapper and the light gun used with the VS + Unisystem(I will call both the same name, Zapper) are supported. + Most(all?) NES games expect the Zapper to be plugged into port 2. + and most(all?) VS Unisystem games expect the Zapper to be plugged + into port(?) 1. + + The LEFT mouse button is the emulated trigger button for the + Zapper. The RIGHT mouse button is also emulated as the trigger, + but as long as you have the RIGHT mouse button held down, no color + detection will take place, which is effectively like pulling the + trigger while the Zapper is pointed away from the television screen. + Note that you must hold the RIGHT button down for a short + time(greater than just a fast click, shorter than a second). + + Zapper emulation currently does NOT work with network play, so + don't even try it. I may add support in the future if enough + people want it or if I want it. + + +/******************************************************************************/ +/* 3.5) Palette Notes */ +/******************************************************************************/ + + Palettes files are expected to contain 64 8-bit RGB triplets(each in + that order; red comes first in the triplet in the file, then green, + then blue). Each 8-bit value represents brightness for that particular + color. 0 is minimum, 255 is maximum. + + Palettes can be set on a per-game basis. To do this, put a palette + file in the "gameinfo" directory with the same base filename + as the game you wish to associate with and the extension "pal". + Examples: + + File name: Palette file name: + BigBad.nes BigBad.pal + BigBad.zip BigBad.pal + BigBad.Better.nes BigBad.Better.pal + + + With so many ways to choose a palette, figuring out which one will + be active may be difficult. Here's a list of what palettes will + be used, in order from highest priority to least priority(if a condition + doesn't exist for a higher priority palette, the emulator will + continue down its list of palettes). + + NSF Palette(for NSFs only) + Palette loaded from the "gameinfo" directory. + NTSC Color Emulation(only for NTSC NES games). + VS Unisystem palette(if the game is a VS Unisystem game and a palette + is available). + Custom global palette. + Default NES palette. + + +/******************************************************************************/ +/* 3.6) Compressed File Notes */ +/******************************************************************************/ + + FCE Ultra can load data from both PKZIP-format files and + gzip-format files. Only one type of (de)compression algorithm is + supported: "deflate"; this seems to be the most popular compression + algorithm, though. + + A compressed FDS disk image will only be saved back to disk if it + uses the gzip format. + + All files in a PKZIP format file will be scanned for the + followings extensions: .nes, .fds, .nsf, .unf, .nez, .unif + The first compressed file to have one of these extensions will be + loaded. If no compressed file has one of these extensions, the + first compressed file will be loaded. + + +/******************************************************************************/ +/* 3.7) Game Genie Notes */ +/******************************************************************************/ + + The Game Genie ROM image is loaded from the file "gg.rom" in the + base directory the first time Game Genie emulation is enabled and + a ROM image is loaded since the time FCE Ultra has run. + + The ROM image may either be the 24592 byte iNES-format image, or + the 4352 raw ROM image. + + Remember that enabling/disabling Game Genie emulation will not take + effect until a new game is loaded(this statement shouldn't concern + any of the "run once" command-line driven ports). + +/******************************************************************************/ +/* 4.0) Contacting the author */ +/******************************************************************************/ + + I can be reached via email at xodnizel@users.sourceforge.net. + Bero can be reached via email at 9bero9@geocities.co.jp + (Note that Bero can not and will not answer any questions + regarding the operation of FCE Ultra, so please don't ask him. + If you understand this, remove the 9's from the email address + provided to get his real email address.) + + +/******************************************************************************/ +/* 4.1) Credits */ +/******************************************************************************/ + +\Firebug\ - High-level mapper information. +Bero - Original FCE source code. +Brad Taylor - NES sound channel guide. +Chris Hickman - Archaic Ruins. +Donald Moore - DC PasoFami NES packs. +Fredrik Olson - NES four-player adapter information. +Gilles Vollant - PKZIP file loading functions. +goroh - Various documents. +Jeff Tamer - Insaniacal fun. +Jeremy Chadwick - General NES information. +Justin Smith - Giving me obscure ROM images in the "dark ages of + NES emulation". +Kevin Horton - Low level NES information and sound information. +Ki - Various technical information. +Mark Knibbs - Various NES information. +Marat Fayzullin - General NES information. +Matthew Conte - Sound information. +N. Andou - Awesome NES/SNES emulators, at the time... +nori - FDS sound information. +Quietust - VRC7 sound translation code by The Quietust + (quietust@ircN.org). + Ideas and corrections. +R. Hoelscher - Famicom four-player adapter information. +Rob Mocca - DC PasoFami NES packs, testing. +Sean Whalen - Node99. +Tatsuyuki Satoh - OPL2 emulator +TheRedEye - ROM images, testing. + + +Info-ZIP - ZLIB + +...and everyone else who has helped me. diff --git a/Documentation/rel/readme-win.txt b/Documentation/rel/readme-win.txt new file mode 100644 index 0000000..8c4425d --- /dev/null +++ b/Documentation/rel/readme-win.txt @@ -0,0 +1,641 @@ + FCE Ultra + 0.81 + + http://fceultra.sourceforge.net/ + + +What is new: + + * Screen snapshots can now be taken while playing an NSF. + * Saving screen snapshots will no longer corrupt the frame buffer. + * Added many more games to the list of games that usually are found + with bad iNES headers. + * Fixed more network play bugs. It should now work correctly(how + many times have I said or implied that...). + * The NSF player will now disable the FDS sound channel on song + initialization(fixes a problem with the Zelda no Densetsu + rip). + * Reads from $4090 and $4092 while emulating the FDS will now return + the current envelope settings. Affects "Ai Senshi Nicole" + and "Bio Miracle Bokutte Upa", at least. + * Merged a lot of pirate MMC3 multicart emulation code with the main + MMC3 emulation code. + * Added support for the MMC5's split-screen mode. This fixes the + introduction in "Uchuu Keibitai SDF". + * Writes to $8000-$FFFF with D7 set during MMC1 emulation will + cause the MMC1 mode register to be OR'd with $C. This fixes + "Robocop 3". + * Replaced an MMC1 hack that I used to get "Bill and Ted's Excellent + Video Game Adventure" to work with something more accurate. + * Fixed the MMC5 read handler to return the data last on the data + bus instead of $FF when a read occured to an unmapped address. + This fixes the lockup problem in "Bandit Kings of Ancient China" + and possibly other games. + * Added support for the game "Ishin no Arashi" in the iNES format + (I added an entry with its CRC32 value and the number of 8KB + WRAM banks it needs into the MMC5 WRAM size table). + * Added support for MMC1 games in the iNES format with 16KB of RAM + via CRC32 comparisons(currently only Genghis Khan(USA), Romance + of the 3 Kingdoms(USA), and Nobunaga's Ambition(USA and Japan) are + recognized). + * iNES mapper 1 now supports pageable CHR RAM if CHR ROM is not + present. Fixes "Family School". + * Added support for iNES mappers 51 and 52. Thanks to Kevin Horton + for the information. + * Modified MMC3(iNES mapper 4/118/119) IRQ counter emulation. Fixes + problems in "MegaMan 3", "Gun Nac", and the Japanese version of + "Klax", but it *might* cause problems with other games. + * Fixed an iNES mapper 32 emulation bug. "Ai Sensei no Oshiete" + works now. + * Fixed iNES mapper 33/48 IRQ emulation. + * Fixed iNES mapper 16 IRQ emulation. + * Added support for "Famicom Jump 2" as iNES mapper 153. + If a good(as far as I can tell) dump is loaded, FCE Ultra will + automatically fix the mapper number. + * The VS Unisystem bit in iNES headers is no longer recognized. + Too many games have this bit set when it shouldn't be set. + Now VS Unisystem games are recognized by CRC32 value. + * Reads from $4015 no longer reset DMC IRQ. This fixes the + title screen of "Romancia". + * PPU NMI now occurs a few cycles later. Fixes the "BattleToads" + lockup problem. + * BRK emulation now sets the I flag. + * Changed a few zero-page address mode functions to read directly + from emulated RAM. + + +Contents: + + 1. Basic information + 1.0 What FCE Ultra is. + 1.1 System requirements. + 2. How to use + 2.0 Starting FCE Ultra + 2.1 Using FCE Ultra + 3. Notes + 3.0 Platform Specific Notes + 3.1 Network Play Notes + 3.2 VS Unisystem Notes + 3.3 Famicom Disk System Notes + 3.4 Light Gun Notes + 3.5 Palette Notes + 3.6 Compressed File Notes + 3.7 Game Genie Notes + 4. Extra + 4.0 Contacting the author + 4.1 Credits + +/******************************************************************************/ +/* 1.0) What FCE Ultra is: */ +/******************************************************************************/ + + FCE Ultra is an NTSC and PAL Famicom/NES emulator for various + platforms. It is based upon Bero's original FCE source code. Current + features include good PPU, CPU, pAPU, expansion chip, and joystick + emulation. Also a feature unique to this emulator(at the current + time) is authentic Game Genie emulation. Save states and snapshot + features also have been implemented. The VS Unisystem is emulated + as well. FCE Ultra supports iNES format ROM images, UNIF format ROM + images, headerless and FWNES style FDS disk images, and NSF files. + + FCE Ultra currently supports the following iNES mappers(many partially): + +Number: Description: Game Examples: +-------------------------------------------------------------------------------- + 0 No Mapper Donkey Kong, Mario Bros + 1 Nintendo MMC1 MegaMan 2, Final Fantasy + 2 Simple 16KB PRG Switch MegaMan 1, Archon, 1944 + 3 Simple 8KB CHR Switch Spy Hunter, Gradius + 4 Nintendo MMC3 Recca, TMNT 2, Final Fantasy 3 + 5 Nintendo MMC5 Castlevania 3, Just Breed, Uchuu Keibitai SDF + 6 FFE F4 Series(hacked) Saint Seiya, Ganbare Goemon + 7 AOROM Battle Toads, Lion King + 8 FFE F3 Series(hacked) Doraemon Kaitakuhen + 9 Nintendo MMC2 Punchout! + 10 Nintendo MMC4 Fire Emblem, Fire Emblem Gaiden + 11 Color Dreams Crystal Mines, Bible Adventures + 13 CPROM Videomation + 15 Multi-cart(pirate) 100-in-1: Contra Function 16 + 16 Bandai Dragon Ball Z, Gundam Knight + 17 FFE F8 Series(hacked) Parodius, Last Armageddon + 18 Jaleco SS806 Pizza Pop, Plazma Ball + 19 Namco 106 Splatter House, Mappy Kids + 21 Konami VRC4 2A WaiWai World 2, Ganbare Goemon Gaiden 2 + 22 Konami VRC4 1B Twinbee 3 + 23 Konami VRC2B WaiWai World, Getsufuu Maden + 24 Konami VRC6 Akumajo Densetsu(Dracula 3) + 25 Konami VRC4 Gradius 2, Bio Miracle: Boku tte Upa + 26 Konami VRC6 A0-A1 Inverse Esper Dream 2, Madara + 32 Irem G-101 Image Fight 2, Perman + 33 Taito TC0190/TC0350 Don Doko Don 1&2 + 34 NINA-001 and BNROM Impossible Mission 2, Deadly Towers, Bug Honey + 40 (pirate) Super Mario Bros. 2 + 41 Caltron 6-in-1 Caltron 6-in-1 + 42 (pirate) "Mario Baby" + 43 Multi-cart(pirate) Golden Game 150 in 1 + 44 Multi-cart(pirate) Super HiK 7 in 1 + 45 Multi-cart(pirate) Super 1000000 in 1 + 46 Game Station Rumble Station + 47 NES-QJ Nintendo World Cup/Super Spike V.B. + 48 Taito TC190V Flintstones + 49 Multi-cart(pirate) Super HiK 4 in 1 + 51 Multi-cart(pirate) 11 in 1 Ball Games + 52 Multi-cart(pirate) Mario Party 7 in 1 + 64 Tengen RAMBO-1 Shinobi, Klax + 65 Irem H-3001 Daiku no Gensan 2 + 66 GNROM SMB + Duck Hunt + 67 Sunsoft Mapper "3" Fantasy Zone 2 + 68 Sunsoft Mapper "4" After Burner 2, Nantetta Baseball + 69 Sunsoft FME-7 Batman: ROTJ, Gimmick! + 70 ?? Kamen Rider Club + 71 Camerica Fire Hawk, Linus Spacehead + 72 Jaleco ?? Pinball Quest + 73 Konami VRC3 Salamander + 75 Jaleco SS8805/Konami VRC1 Tetsuwan Atom, King Kong 2 + 76 Namco 109 Megami Tenshi 1 + 77 Irem ?? Napoleon Senki + 78 Irem 74HC161/32 Holy Diver + 79 NINA-06 F15 City War, Krazy Kreatures + 80 Taito X-005 Minelvation Saga + 82 Taito ?? Kyuukyoku Harikiri Stadium - Heisei Gannen Ban + 83 Multi-cart(pirate) Dragon Ball Party + 85 Konami VRC7 Lagrange Point + 86 Jaleco ?? More Pro Baseball + 87 ?? Argus + 89 Sunsoft ?? Mito Koumon + 90 Pirate Super Mario World, Mortal Kombat + 92 Jaleco ?? MOERO Pro Soccer, MOERO Pro Yakyuu '88 + 93 ?? Fantasy Zone + 94 ?? Senjou no Ookami + 95 Namco ?? Dragon Buster + 97 ?? Kaiketsu Yanchamaru + 99 VS System 8KB CHR Switch VS SMB, VS Excite Bike +105 NES-EVENT Nintendo World Championships +112 Asder Sango Fighter, Hwang Di +113 MB-91 Deathbots +118 MMC3-TLSROM/TKSROM Board Ys 3, Goal! 2, NES Play Action Football +119 MMC3-TQROM Board High Speed, Pin*Bot +140 Jaleco ?? Bio Senshi Dan +151 Konami VS System Expansion VS The Goonies, VS Gradius +152 ?? Arkanoid 2, Saint Seiya Ougon Densetsu +153 Bandai ?? Famicom Jump 2 +180 ?? Crazy Climber +182 ?? Super Donkey Kong +184 ?? Wing of Madoola, The +189 Micro Genius TXC ?? Thunder Warrior +225 Multi-cart(pirate) 58-in-1/110-in-1/52 Games +226 Multi-cart(pirate) 76-in-1 +227 Multi-cart(pirate) 1200-in-1 +228 Action 52 Action 52, Cheetahmen 2 +229 Multi-cart(pirate) 31-in-1 +232 BIC-48 Quattro Arcade, Quattro Sports +234 Multi-cart ?? Maxi-15 +240 ?? Gen Ke Le Zhuan, Shen Huo Le Zhuan +242 ?? Wai Xing Zhan Shi +246 ?? Fong Shen Ban +248 ?? Bao Qing Tian +250 ?? Time Diver Avenger + + FCE Ultra currently supports the following UNIF boards(minus the + prefixes HVC-, NES-, BTL-, and BMC-, as they are currently ignored): + +Group: Name: Game Examples: +-------------------------------------------------------------------------------- +Bootleg: + MARIO1-MALEE2 Super Mario Bros. Malee 2 + NovelDiamond9999999in1 Novel Diamond 999999 in 1 + Super24in1SC03 Super 24 in 1 + Supervision16in1 Supervision 16-in-1 + +Unlicensed: + Sachen-8259A Super Cartridge Version 1 + Sachen-8259B Silver Eagle + Sachen-74LS374N Auto Upturn + SA-016-1M Master Chu and the Drunkard Hu + SA-72007 Sidewinder + SA-72008 Jovial Race + SA-0036 Mahjong 16 + SA-0037 Mahjong Trap + TC-U01-1.5M Challenge of the Dragon + +MMC1: + SAROM Dragon Warrior + SBROM Dance Aerobics + SCROM Orb 3D + SEROM Boulderdash + SGROM Defender of the Crown + SKROM Dungeon Magic + SLROM Castlevania 2 + SL1ROM Sky Shark + SNROM Shingen the Ruler + SOROM Nobunaga's Ambition + +MMC3: + TFROM Legacy of the Wizard + TGROM Megaman 4 + TKROM Kirby's Adventure + TKSROM Ys 3 + TLROM Super Spike V'Ball + TLSROM Goal! 2 + TR1ROM Gauntlet + TQROM Pinbot + TSROM Super Mario Bros. 3 + TVROM Rad Racer 2 + +MMC5: + EKROM Gemfire + ELROM Castlevania 3 + ETROM Nobunaga's Ambition 2 + EWROM Romance of the Three Kingdoms 2 + +MMC6: + HKROM Star Tropics + +Nintendo +discrete +logic: + CNROM Gotcha + CPROM Videomation + MHROM + NROM-128 Mario Bros. + NROM-256 Super Mario Bros. + RROM-128 + UNROM Megaman + + +/******************************************************************************/ +/* 1.1) System requirements: */ +/******************************************************************************/ + + Minimum system specifications: + + Pentium 60 MHz + 8 MB RAM + 400 KB free disk space + Windows 95 + DirectX 7.0 + Video adapter + + Recommended minimum system specifications: + + Pentium 233 MHz + 16 MB RAM + 5 MB free disk space + Windows 98SE + Video adapter with 2D acceleration abilities + DirectX 8.0 + Joystick + Mouse + Sound device capable of handling a sample rate of 44100 hz. + + +/******************************************************************************/ +/* 2.0) Starting FCE Ultra */ +/******************************************************************************/ + + FCE Ultra can be started by running the executable "fceu.exe". + I do not recommend running it from a DOS "box". + + +/******************************************************************************/ +/* 2.1) Using FCE Ultra: */ +/******************************************************************************/ + + After starting FCE Ultra, you'll probably want to load a game. Do + this by going to File/Open. + + Menu descriptions: + + File + Open - Loads a new game. + Save State - Saves the current NES state. + Load State - Loads a saved NES state. + Log Sound As - Logs sound to a file. It will not work if sound + is disabled. + Exit - Exit the emulator. + + NES + Reset - Resets the virtual NES. + Power - Power cycles the virtual NES. + Cheats - Activates the cheat interface. See "cheat.txt" for + more details. + + Config + Hide Menu - Hides the menu. + Game Genie - If checked, enable Game Genie emulation. + Game Genie emulation will only begin or end when a new + game is loaded. + PAL Emulation - If checked, enable PAL emulation. Changes take effect + immediately, though I recommend resetting the virtual + NES afterward PAL emulation is enabled or disabled. + Directories - Configure the directories that FCE Ultra will store + its files in. + Input - Enter input configuration dialog. + Note that not all virtual devices are configurable. + Miscellaneous - Enter miscellaneous configuration dialog. + Network Play - Enter network play configuration dialog. + Palette - Enter palette configuration dialog. + Sound - Enter sound configuration dialog. + Sound enabled: + Sound emulation and output are enabled when this is checked. + Force 8-bit sound: + Forces 8-bit sound output. Use only when absolutely + necessary(very rare). + Sample rate: + Specifies how many sound samples will be played back per + second. Unless you know what you are doing, you probably + don't need to change this setting. + Use secondary sound buffer: + Uses a secondary sound buffer. This option may be required + for sound to work with certain sound cards/devices. + Selecting "with global focus" will cause sound to be played + while FCE Ultra has lost window focus, but you will probably + also want to select "Active While Focus Lost" in the Config + menu as well, otherwise you will just get repeating sound + when FCE Ultra loses focus. + Length of sound buffer: + Specifies what length of sound(in milliseconds) should be + buffered by FCE Ultra. DirectSound and the Windows kernel + may or may not cause a little more latency than what you + might expect(usually not any more than a few milliseconds), + depending on your setup. + Use larger values if you have sound problems such as popping + or gaps, though. Larger values will increase the latency of + the sound, however. Finally, larger values are ideal for + background music listening. + Volume: + Specifies the volume of FCE Ultra's sound output. Setting + the volume too high MIGHT cause noticeable clipping on some + sounds(loud drums, for example), but don't let that possibility + stop you from experimenting. + + Video - Enter video configuration dialog. + + Default Key Mapping: + + For emulated Family BASIC Keyboard: + Enable/Disable Keyboard Input Scroll Lock + (enabling emulated keyboard input will disable + commands keys) + All emulated keys are mapped to the closest open key on the PC + keyboard, with a few exceptions. The emulated "@" key is + mapped to the "`"(grave) key, and the emulated "kana" key + is mapped to the "Insert" key(in the 3x2 key block above the + cursor keys). + + For emulated game pads: + Left Control B + Left Alt A + Enter/Return Start + Tab Select + Cursor Down Down + Cursor Up Up + Cursor Left Left + Cursor Right Right + + For emulated power pads(keys correspond to button locations on + side "B"): + O P [ ] + K L ; ' + M , . / + + For FDS games: + I Insert disk. + E Eject disk. + S Select disk/disk side. + + For VS Unisystem games: + C Insert coin. + V Show/Hide dip switches. + 1-8 Toggle dip switches(when dip switches + are shown). + + 0-9 Select save state. + + F5/F7 Save/Load state. + F9 Save screen snapshot. + + F3 Hide/Show menu. + F4 Toggle between windowed/full screen modes. + F10 Reset. + F11 Power off/on. + F12 Exit. + + +/******************************************************************************/ +/* 3.0) Platform Specific Notes */ +/******************************************************************************/ + + Your desktop color depth must be 16bpp, 24bpp, or 32bpp for FCE Ultra + to run properly in windowed mode. + + FCE Ultra's base directory is the directory in which the executable + is located. + + +/******************************************************************************/ +/* 3.1) Network Play Notes */ +/******************************************************************************/ + + In TCP/IP network play, the server will be player one, and the + client will be player 2. + + Zapper emulation and power pad emulation currently do not work with + network play. + + Having Game Genie or PAL emulation enabled on only one side + will cause problems. + + Both players MUST use the same ROM/disk image and SRAM + file(if applicable). + + When using FDS or VS Unisystem games with network play, only player + 1 will be able to insert disk, eject disk, insert coins, toggle dip + switches, etc. + +/******************************************************************************/ +/* 3.2) VS Unisystem Notes */ +/******************************************************************************/ + + FCE Ultra currently only supports VS Unisystem ROM images in the + iNES format. + + ROM Images: + + * VS Unisystem games that are about 49,000 bytes in size most likely + use mapper 99. + * Other VS Unisystem games will use other mappers. Here is a short + list of games and the mappers they use: + + CastleVania - 2 + Dr. Mario - 1 + Goonies - 151 + Gradius - 151 + Ice Climber - 99 + Platoon - 68 + + Palette(s): + + * The colors in many VS Unisystem games may be incorrect. This + is due to each game having its own PPU, and thus using a + different palette than games that use a different PPU. + + +/******************************************************************************/ +/* 3.3) Famicom Disk System Notes */ +/******************************************************************************/ + + You will need the FDS BIOS ROM image in the base FCE Ultra directory. + It must be named "disksys.rom". I will not give this file to you, so + don't ask. + + Two types of FDS disk images are supported: disk images with the + FWNES-style header, and disk images with no header. + + You should make backups of all of your FDS games you use with FCE + Ultra. This is because FCE Ultra will write the disk image back to + the storage medium, and the disk image in RAM might have been corrupted + because of inaccurate emulation(this case is not likely to occur, but + it could occur). + + +/******************************************************************************/ +/* 3.4) Light Gun Notes */ +/******************************************************************************/ + + Currently, the NES Zapper and the light gun used with the VS + Unisystem(I will call both the same name, Zapper) are supported. + Most(all?) NES games expect the Zapper to be plugged into port 2. + and most(all?) VS Unisystem games expect the Zapper to be plugged + into port(?) 1. + + The LEFT mouse button is the emulated trigger button for the + Zapper. The RIGHT mouse button is also emulated as the trigger, + but as long as you have the RIGHT mouse button held down, no color + detection will take place, which is effectively like pulling the + trigger while the Zapper is pointed away from the television screen. + Note that you must hold the RIGHT button down for a short + time(greater than just a fast click, shorter than a second). + + Zapper emulation currently does NOT work with network play, so + don't even try it. I may add support in the future if enough + people want it or if I want it. + + +/******************************************************************************/ +/* 3.5) Palette Notes */ +/******************************************************************************/ + + Palettes files are expected to contain 64 8-bit RGB triplets(each in + that order; red comes first in the triplet in the file, then green, + then blue). Each 8-bit value represents brightness for that particular + color. 0 is minimum, 255 is maximum. + + Palettes can be set on a per-game basis. To do this, put a palette + file in the "gameinfo" directory with the same base filename + as the game you wish to associate with and the extension "pal". + Examples: + + File name: Palette file name: + BigBad.nes BigBad.pal + BigBad.zip BigBad.pal + BigBad.Better.nes BigBad.Better.pal + + + With so many ways to choose a palette, figuring out which one will + be active may be difficult. Here's a list of what palettes will + be used, in order from highest priority to least priority(if a condition + doesn't exist for a higher priority palette, the emulator will + continue down its list of palettes). + + NSF Palette(for NSFs only) + Palette loaded from the "gameinfo" directory. + NTSC Color Emulation(only for NTSC NES games). + VS Unisystem palette(if the game is a VS Unisystem game and a palette + is available). + Custom global palette. + Default NES palette. + + +/******************************************************************************/ +/* 3.6) Compressed File Notes */ +/******************************************************************************/ + + FCE Ultra can load data from both PKZIP-format files and + gzip-format files. Only one type of (de)compression algorithm is + supported: "deflate"; this seems to be the most popular compression + algorithm, though. + + A compressed FDS disk image will only be saved back to disk if it + uses the gzip format. + + All files in a PKZIP format file will be scanned for the + followings extensions: .nes, .fds, .nsf, .unf, .nez, .unif + The first compressed file to have one of these extensions will be + loaded. If no compressed file has one of these extensions, the + first compressed file will be loaded. + + +/******************************************************************************/ +/* 3.7) Game Genie Notes */ +/******************************************************************************/ + + The Game Genie ROM image is loaded from the file "gg.rom" in the + base directory the first time Game Genie emulation is enabled and + a ROM image is loaded since the time FCE Ultra has run. + + The ROM image may either be the 24592 byte iNES-format image, or + the 4352 raw ROM image. + + Remember that enabling/disabling Game Genie emulation will not take + effect until a new game is loaded(this statement shouldn't concern + any of the "run once" command-line driven ports). + +/******************************************************************************/ +/* 4.0) Contacting the author */ +/******************************************************************************/ + + I can be reached via email at xodnizel@users.sourceforge.net. + Bero can be reached via email at 9bero9@geocities.co.jp + (Note that Bero can not and will not answer any questions + regarding the operation of FCE Ultra, so please don't ask him. + If you understand this, remove the 9's from the email address + provided to get his real email address.) + + +/******************************************************************************/ +/* 4.1) Credits */ +/******************************************************************************/ + +\Firebug\ - High-level mapper information. +Bero - Original FCE source code. +Brad Taylor - NES sound channel guide. +Chris Hickman - Archaic Ruins. +Donald Moore - DC PasoFami NES packs. +Fredrik Olson - NES four-player adapter information. +Gilles Vollant - PKZIP file loading functions. +goroh - Various documents. +Jeff Tamer - Insaniacal fun. +Jeremy Chadwick - General NES information. +Justin Smith - Giving me obscure ROM images in the "dark ages of + NES emulation". +Kevin Horton - Low level NES information and sound information. +Ki - Various technical information. +Mark Knibbs - Various NES information. +Marat Fayzullin - General NES information. +Matthew Conte - Sound information. +N. Andou - Awesome NES/SNES emulators, at the time... +nori - FDS sound information. +Quietust - VRC7 sound translation code by The Quietust + (quietust@ircN.org). + Ideas and corrections. +R. Hoelscher - Famicom four-player adapter information. +Rob Mocca - DC PasoFami NES packs, testing. +Sean Whalen - Node99. +Tatsuyuki Satoh - OPL2 emulator +TheRedEye - ROM images, testing. + + +Info-ZIP - ZLIB + +...and everyone else who has helped me. diff --git a/Documentation/rel/toc b/Documentation/rel/toc new file mode 100644 index 0000000..5abafb3 --- /dev/null +++ b/Documentation/rel/toc @@ -0,0 +1,23 @@ + + +Contents: + + 1. Basic information + 1.0 What FCE Ultra is. + 1.1 System requirements. + 2. How to use + 2.0 Starting FCE Ultra + 2.1 Using FCE Ultra + 3. Notes + 3.0 Platform Specific Notes + 3.1 Network Play Notes + 3.2 VS Unisystem Notes + 3.3 Famicom Disk System Notes + 3.4 Light Gun Notes + 3.5 Palette Notes + 3.6 Compressed File Notes + 3.7 Game Genie Notes + 4. Extra + 4.0 Contacting the author + 4.1 Credits + diff --git a/Documentation/rel/top.dos b/Documentation/rel/top.dos new file mode 100644 index 0000000..372f204 --- /dev/null +++ b/Documentation/rel/top.dos @@ -0,0 +1,8 @@ + FCE Ultra + 0.81 + + http://fceultra.sourceforge.net/ + + +What is new: + diff --git a/Documentation/rel/top.linux b/Documentation/rel/top.linux new file mode 100644 index 0000000..372f204 --- /dev/null +++ b/Documentation/rel/top.linux @@ -0,0 +1,8 @@ + FCE Ultra + 0.81 + + http://fceultra.sourceforge.net/ + + +What is new: + diff --git a/Documentation/rel/top.win b/Documentation/rel/top.win new file mode 100644 index 0000000..372f204 --- /dev/null +++ b/Documentation/rel/top.win @@ -0,0 +1,8 @@ + FCE Ultra + 0.81 + + http://fceultra.sourceforge.net/ + + +What is new: + diff --git a/Documentation/tech/README.now b/Documentation/tech/README.now new file mode 100644 index 0000000..4575e0c --- /dev/null +++ b/Documentation/tech/README.now @@ -0,0 +1,6 @@ +Many(possibly all) of these documents contain flaws or are incomplete, so +don't pull out your hair if there are inconsistencies between the documents, +what's in FCE Ultra, and what you observe. That's not to say that FCE Ultra +doesn't have its share of (emulation) flaws, though... + +For many more NES-related documents, try http://nesdev.parodius.com diff --git a/Documentation/tech/README.sound b/Documentation/tech/README.sound new file mode 100644 index 0000000..cce9504 --- /dev/null +++ b/Documentation/tech/README.sound @@ -0,0 +1,2 @@ +Sound information is in the "cpu" subdirectory, due to the intimate +relationship between the sound circuitry and the cpu. diff --git a/Documentation/tech/UNIF_current.txt b/Documentation/tech/UNIF_current.txt new file mode 100644 index 0000000..31e17c8 --- /dev/null +++ b/Documentation/tech/UNIF_current.txt @@ -0,0 +1,302 @@ +**************************************************************************** + UNIF file format specifications + (Universal NES Image) file format + + Created by Tennessee Carmel-Veilleux (veilleux@ameth.org) + REV 7b, November 28th, 2000 + + +***********THIS IS AN OPEN STANDARD. IT IS OPEN TO SUGGESTIONS************** + +Overview +-------- +The UNIF format is a portable, flexible REPLACEMENT of the NES standard +(Designed by Marat Fayzullin). It is a chunked file format in the lines of +the Amiga IFF (LBM), Microsoft RIFF (WAV) and Autodesk 3D studio mesh +files (3DS). The goal of having a chunked definition is to provide +flexibility and ease of implementation, as data is described in blocks +with type IDs referring to them and header information to provide a +selective data reading. The format uses symetric data conversion for +numerical compatibility between the different platforms' byte ordering. +The ordering used is the 6502 Byte order (Intel), so that no more +bickering arrises from people who do not appreciate Network Byte Ordering +(Motorola). + +*** +The extension suggested for use with this format is .UNF (.UNIF under Operating +systems which support longer extensions). +*** + +Byte ordering +------------- +Byte ordering used throughout the file for DWORDs and WORDs is 6502 Byte +order. The 6502 byte order is LSB, least significant byte first +(Little-endian): + + 3 2 1 0 <- Byte order for MSB (Network Byte order, m68k, PowerPC) + 0 1 2 3 <- Byte order for LSB (80x86, 6502, Z80) + +Care must be taken to convert the WORDs and DWORDs if you are using a +non-LSB platform (Mac, Amiga, some others). + +File format +----------- +00h-1Fh : Header +20h-EOF : Chunks + +I can not think of an easier format to describe :) + +Header +------ +The 4-byte header contains the string "UNIF" , NON null-terminated, case +as written. This is for identification purposes, a little like .NES' "NES^Z" +It is followed by Revision number and reserved bytes. + +Format: 00h-03h: "UNIF" tag identifier + 04h-07h: DWORD -> Revision number, currently 4 + 08h-1Fh: Reserved for future usage + +Sample structure: + +structure UNIF_header [ + char identification[4]; /* MUST be "UNIF" */ + dword revision; /* Revision number */ + byte expansion[24]; +]; + +Chunks +------ +Each chunks is composed of 3 distinct elements: + 00h-03h: Chunk ID string + 04h-07h: DWORD -> Block Length of Data + 08h-?? : Data + +All the chunks are written sequentially in the file. If you do not understand +a chunk by its ID, simply jump over it using the data length information. +*** ALL THE CHUNKS ARE OPTIONAL *** +That means that there are NO mandatory chunks, and you support only the +ONES YOU WISH, passing over the others while you are interpreting the +file. + +Sample structure: + +structure UNIF_chunk [ + char chunk_ID[4]; /* Chunk identification string. Can also be considered a + number. ASCII format */ + dword length; /* Data length, in little-endian format */ +]; + +The different chunks: +--------------------- +******************************************************************************* +How chunks are described: +ID field: Contains the 4-characters string identifier for the chunk + +Length: Length of the block. If it is "??", then the block may have + variable data size depending on the cartridge. + +Revision: First revision in which the chunk appeared. If your reader supports + a lower revision, you might be unable to read the chunk, simply pass + over it. The Revision used by the cart is written in the header. The + number represents the Revision Number of the most recent chunk + contained in the file. Example : If you have 5 chunks of revision 1 + and 2 chunks of revision 4 in the file, the Revision number in the + header will be 4. + +Description: Complete description of the contents and encoding of the chunk +******************************************************************************* + +ID: [MAPR] +Length: ?? (Suggested max: 32 chars) +Revision: 1 +Description: This is supplemental information about the mapper. DO NOT USE + A MAPPER NUMBER HERE ! Rather use the BOARD NAME. There is + already a list in progress describing each NES cart and the + board it uses. The string is NULL-TERMINATED. + +examples: N,R,O,M,,0 -> This "No mapper" + U,N,R,O,M,0 -> This is (LS161+LS32) + +NOTA: This mapper organization suggests that emulators must be rewritten +to emulate the ACTUAL CARTRIDGE HARDWARE, and not solely a case of another +mapper number. That means you have to make for UNROM: +1- Mapper handler (74LS161+74LS32) +2- CHR-RAM Handler + +Those components can be reused, since many boards only have a slight +difference in them compared to similar ones. + +**IT SHOULD BE NOTED THAT**: A board name tells you EVERYTHING there is to +know about a board. You do not need other chunks to tell you how much RAM +there is, or if it is VRAM. It is all implied by the board name. A list +will soon be distributed containing board name information. + +Address of board table for North American Games and Board Names description: +http://www.parodius.com/~veilleux/boardtable.txt +http://www.parodius.com/~veilleux/boardnames + +ID: [READ] +Length: ?? +Revision: 1 +Description: Commentaries for the user of the ROM image. In the case of a +homebrew game, this can be very useful to store credit and maker +information. *** This could be "Incitation to littering". Please do not +put garbage in there. It is meant for either mapper information or +licensing information for homebrew games.*** + + +ID: [NAME] +Length: ?? +Revision: 1 +Description: NULL-terminated string containing the name of the game. If not + present, use the filename as the name. + +ID: [TVCI] +Length: BYTE +Revision: 6 +Description: Television Standards Compatability Information set to: +0- Originally NTSC cartridge +1- Originally PAL cartridge +2- Does not matter +NOTE: ALL North American carts that are dumps of the North American +Version are NTSC. All licensed famicom games are NTSC. + +ID: [DINF] +Length: 204 +Revision: 2 +Description: Dumper information block: + structure dumper_info [ + + char dumper_name[100]; /* NULL-terminated string containing the name + of the person who dumped the cart. */ + byte day; /* Day of the month when cartridge was dumped */ + byte month; /* Month of the year when cartridge was dumped */ + word year; /* Year during which the cartridge was dumped */ + char dumper_agent[100]; /* NULL-terminated string containing the name of + the ROM-dumping means used */ + ] + +ID: [CTRL] +Length: BYTE +Revision: 7 +Description: Bitfield containing information about the controllers used by the +cartridge. + +Bit 0: Regular Joypad +Bit 1: Zapper +Bit 2: R.O.B +Bit 3: Arkanoid Controller +Bit 4: Power Pad +Bit 5: Four-Score adapter +Bit 6: Expansion (Do not touch) +Bit 7: Expansion (Do not touch) + +ID: [PCK0] through [PCKF] +Length: DWORD +Reivision: 5 +Description: This block contains a 32-bit CRC which can be used to make +sure that the ROM content matches a checksum when burning on EPROM. This +block provides a checksum for [PRG0] through [PRGF] inclusively + +ID: [CCK0] through [CCKF] +Length: DWORD +Reivision: 5 +Description: This block contains a 32-bit CRC which can be used to make +sure that the ROM content matches a checksum when burning on EPROM. This +block provides a checksum for [CHR0] through [CHRF] inclusively + +ID: [PRG0] through [PRGF] +Length: ?? +Revision: 4 +Description: Chunks containing the Binary data of the PRG ROM. If there +are more than 1 PRG chips on the PRG bus, use PRG1, PRG2, PRG4, etc. +The way PRGs are handled depends on the mapper and emulator. Most generaly +(99%), only use PRG0. (Some carts have been witnessed with 8 PRG ROMs). + +ID: [CHR0] through [CHRF] +Length: ?? +Revision: 4 +Description: Chunks containing the binary data of the CHR ROM. If there +are more than 1 CHR chips on the CHR bus, use CHR1, CHR2, CHR4, etc. The +way CHRs are handled depends on the mapper and emulator. Most generaly +(99%), only CHR0 is used. + +ID: [BATR] +Length: BYTE +Revision: 5 +Description: The presence of this block indicates that the board indeed +contains a battery. This is necessary because many boards have the +capability of a battery (the traces and holes are there), but they only +use RAM and don't add the battery at manufacturing time. Examples: + * SAROM: MMC1B, PRG ROM, CHR ROM, optional 8k of RAM (battery) + * SKROM: MMC1B, PRG ROM, CHR ROM, 8k optional RAM (battery) + +Both these boards (SAROM and SKROM) can have a battery, but usually they +don't have it. + +ID: [VROR] +Length: BYTE +Revision: 5 +Description: This is a VRAM Override. If this chunk is present, then the +CHR-ROM area will be considered as RAM even if ROM is present. This +overrides board identification. This is present so that homemade carts +which use NROM or others and replace the CHR-ROM with CHR-RAM can still be +interpreted (since NROM is always CHR-ROM in commercial games). + +ID: [MIRR] +Length: BYTE +Revision: 5 +Description: This chunk tells you how the hardwired mirroring is setup on +the board. The board name CANNOT tell you (in most cases) what the +mirroring is, since the all have solder pads to select the mirroring at +manufacturing time. The following values are legal: + + * $00 - Horizontal Mirroring (Hard Wired) + * $01 - Vertical Mirroring (Hard Wired) + * $02 - Mirror All Pages From $2000 (Hard Wired) + * $03 - Mirror All Pages From $2400 (Hard Wired) + * $04 - Four Screens of VRAM (Hard Wired) + * $05 - Mirroring Controlled By Mapper Hardware + +Conclusion +---------- +This ends the specification for Revision 6 of UNIF. If you have ANY +suggestions to make regarding the UNIF file format, such as chunk ideas or +modifications, or would like to collaborate to the elaboration and design +process, e-mail me at veilleux@ameth.org. + +A multi-platform C Code Library for UNIF support was made by Evan Teran. It +is available at http://www.pretendo.org/~proxy/. + +[References] +{.NES file format specifications} by Marat Fayzullin (fms@cs.umd.edu) +{NESDEV mailing list} by Various authors +{NES technical documentation} by Jeremy Chadwick (yoshi@parodius.com) + +[Credits] +Neal Tew for his neat emulator and great contribution to the NESdev +community. + +Jeremy Chadwick (yoshi@parodius.com) for his contribution to the NESdev +community and great advice over the time. + +Mark Knibbs (mark_k@iname.com) for his excellent web site as well as his +more than honorable contribution to the NES world + +Matthew Conte (itsbroke@classicgaming.com) for his CajoNES and Nofrendo +programs + +Michael Iwaniec (mrbananmos@yahoo.com) for his interest in UNIF and +constructive criticism. + +Kevin Horton (khorton@iquest.net) for his proposals and support of +UNIF. He's also been a fantastic help to me in my learning curve of the +NES's hardware aspects. + +/Firebug/ (firebug@cfl.rr.com) for the ideas brought with NIFF + +T. Alex Reed {W1k} (rizen@netzero.net) for suggestions of additions +to UNIF. He also pointed out some mistakes in the original specifications. + +Evan Teran {PrOxY} (emt3734@rit.edu) for making suggestions as well as +writing a .NES->UNIF converter and the LIB_UNIF library. diff --git a/Documentation/tech/cpu/4017.txt b/Documentation/tech/cpu/4017.txt new file mode 100644 index 0000000..25cb839 --- /dev/null +++ b/Documentation/tech/cpu/4017.txt @@ -0,0 +1,97 @@ +This is an email posted to nesdev by Ki a while back. I have removed one +line at the end regarding the B flag of the cpu(the information was +incorrect, which Ki noted in a later email). + +-------------------------------------------------------------------------------- + + By reading Brad's NESSOUND document, we know that there is a +"frame counter" in the NES/FC APU. I would like to post +some more on this. + + The frame counter is reset upon any write to $4017. It is +reset at system power-on as well, but is NOT reset upon +system reset. + + Thanks to Samus Aran, we now know the exact period of the +PPU's single frame. In another words, we are now sure that +the NMI occurs on every 29780 2/3 CPU cycles. + + However, the APU's single frame is NOT 29780 2/3 CPU cycles. +What I mean by "APU's single frame" here is that it is the +number of CPU cycles taken between the frame IRQs. + + The APU's single frame seems to be: + + 1789772.727... / 60 = 29829 6/11 [CPU CYCLE] + + Below is a simple diagram which shows the difference +in periods of the PPU's single frame and the APU's. + + + RESET 29780 2/3 CPU CYCLES NMI +PPU |------------------------------------------| + | 29829 6/11 CPU CYCLES IRQ +APU |----------|----------|----------|----------| + + + Note that if you write $00 to $4017 on every NMI, the frame +IRQ would NEVER go off even if it is enabled. This is because +the the period of NMI is slightly shorter than the period of +the frame IRQ. This causes the frame counter to be reset +before the frame IRQ goes off. + +When you write zero to bit 7 of $4017, the frame counter will +be reset, and the first sound update will be done after 7457 CPU +cycles (i.e. 29829/4). 2nd update will be done 7457 after that, +same goes for 3rd update and 4th update, but the frame IRQ occurs +on 4th update, resetting the frame counter as well. + +When you write 1 to bit 7 of $4017, the frame counter will be +reset, but the first sound update will occur at the same time. +2nd, 3rd, and 4th update will be done after 7457, 14914, 22371 +CPU cycles after the first update respectively, but the 5th +update will be 14914 cycles after the 4th update. This causes +sound output to last 1.25 times longer than that of bit 7 = 0. + + +$4017W: + +o when the MSB of $4017 is 0: + +bit7=0 + |---------|---------|---------|---------|---------|---------|---- + 1st 2nd 3rd 4th 5th(1st) 6th(2nd) + + +o when the MSB of $4017 is 1: + +bit7=1 + |---------|---------|---------|-------------------|---------|---- + 1st 2nd 3rd 4th 5th(1st) 6th(2nd) + + +On 1st, 3rd, 5th, ... updates, the envelope decay and the +linear counter are updated. + +On 2nd, 4th, 6th, ... updates, the envelope decay, the +linear counter, the length counter, and the frequency sweep +are updated. +---- + + The original info was provided by goroh, and verified by me. +However, it could still be wrong. Please tell me if you +find anything wrong. +---- + +(Correction from my last posting) + + I have checked once again and it turned out that the frame IRQ +was NOT disabled upon system reset. What actually prevented the +frame IRQ to occur after system reset was, in fact, the I flag. +I checked this flag shortly after system reset (right after stack +pointer was initialized), and the flag was 1, although I never +executed "sei" after reset. Therefore the I flag of the PR2A03G +is 1 on system reset. + + Thanks Matthew Conte and Samus Aran for pointing out the +inaccuracy. diff --git a/Documentation/tech/cpu/6502_cpu.txt b/Documentation/tech/cpu/6502_cpu.txt new file mode 100644 index 0000000..938556c --- /dev/null +++ b/Documentation/tech/cpu/6502_cpu.txt @@ -0,0 +1,1537 @@ +# +# $Id: 6502_cpu.txt,v 1.1 2002/05/21 00:42:27 xodnizel Exp $ +# +# This file is part of Commodore 64 emulator +# and Program Development System. +# +# See README for copyright notice +# +# This file contains documentation for 6502/6510/8500/8502 instruction set. +# +# +# Written by +# John West (john@ucc.gu.uwa.edu.au) +# Marko MŠkelŠ (msmakela@kruuna.helsinki.fi) +# +# +# $Log: 6502_cpu.txt,v $ +# Revision 1.1 2002/05/21 00:42:27 xodnizel +# updates +# +# Revision 1.8 1994/06/03 19:50:04 jopi +# Patchlevel 2 +# +# Revision 1.7 1994/04/15 13:07:04 jopi +# 65xx Register descriptions added +# +# Revision 1.6 1994/02/18 16:09:36 jopi +# +# Revision 1.5 1994/01/26 16:08:37 jopi +# X64 version 0.2 PL 1 +# +# Revision 1.4 1993/11/10 01:55:34 jopi +# +# Revision 1.3 93/06/21 13:37:18 jopi +# X64 version 0.2 PL 0 +# +# Revision 1.2 93/06/21 13:07:15 jopi +# *** empty log message *** +# +# + + Note: To extract the uuencoded ML programs in this article most + easily you may use e.g. "uud" by Edwin Kremer , + which extracts them all at once. + + +Documentation for the NMOS 65xx/85xx Instruction Set + + 6510 Instructions by Addressing Modes + 6502 Registers + 6510/8502 Undocumented Commands + Register selection for load and store + Decimal mode in NMOS 6500 series + 6510 features + Different CPU types + 6510 Instruction Timing + How Real Programmers Acknowledge Interrupts + Memory Management + Autostart Code + Notes + References + + +6510 Instructions by Addressing Modes + +off- ++++++++++ Positive ++++++++++ ---------- Negative ---------- +set 00 20 40 60 80 a0 c0 e0 mode + ++00 BRK JSR RTI RTS NOP* LDY CPY CPX Impl/immed ++01 ORA AND EOR ADC STA LDA CMP SBC (indir,x) ++02 t t t t NOP*t LDX NOP*t NOP*t ? /immed ++03 SLO* RLA* SRE* RRA* SAX* LAX* DCP* ISB* (indir,x) ++04 NOP* BIT NOP* NOP* STY LDY CPY CPX Zeropage ++05 ORA AND EOR ADC STA LDA CMP SBC Zeropage ++06 ASL ROL LSR ROR STX LDX DEC INC Zeropage ++07 SLO* RLA* SRE* RRA* SAX* LAX* DCP* ISB* Zeropage + ++08 PHP PLP PHA PLA DEY TAY INY INX Implied ++09 ORA AND EOR ADC NOP* LDA CMP SBC Immediate ++0a ASL ROL LSR ROR TXA TAX DEX NOP Accu/impl ++0b ANC** ANC** ASR** ARR** ANE** LXA** SBX** SBC* Immediate ++0c NOP* BIT JMP JMP () STY LDY CPY CPX Absolute ++0d ORA AND EOR ADC STA LDA CMP SBC Absolute ++0e ASL ROL LSR ROR STX LDX DEC INC Absolute ++0f SLO* RLA* SRE* RRA* SAX* LAX* DCP* ISB* Absolute + ++10 BPL BMI BVC BVS BCC BCS BNE BEQ Relative ++11 ORA AND EOR ADC STA LDA CMP SBC (indir),y ++12 t t t t t t t t ? ++13 SLO* RLA* SRE* RRA* SHA** LAX* DCP* ISB* (indir),y ++14 NOP* NOP* NOP* NOP* STY LDY NOP* NOP* Zeropage,x ++15 ORA AND EOR ADC STA LDA CMP SBC Zeropage,x ++16 ASL ROL LSR ROR STX y) LDX y) DEC INC Zeropage,x ++17 SLO* RLA* SRE* RRA* SAX* y) LAX* y) DCP* ISB* Zeropage,x + ++18 CLC SEC CLI SEI TYA CLV CLD SED Implied ++19 ORA AND EOR ADC STA LDA CMP SBC Absolute,y ++1a NOP* NOP* NOP* NOP* TXS TSX NOP* NOP* Implied ++1b SLO* RLA* SRE* RRA* SHS** LAS** DCP* ISB* Absolute,y ++1c NOP* NOP* NOP* NOP* SHY** LDY NOP* NOP* Absolute,x ++1d ORA AND EOR ADC STA LDA CMP SBC Absolute,x ++1e ASL ROL LSR ROR SHX**y) LDX y) DEC INC Absolute,x ++1f SLO* RLA* SRE* RRA* SHA**y) LAX* y) DCP* ISB* Absolute,x + + ROR intruction is available on MC650x microprocessors after + June, 1976. + + Legend: + + t Jams the machine + *t Jams very rarely + * Undocumented command + ** Unusual operation + y) indexed using Y instead of X + () indirect instead of absolute + +Note that the NOP instructions do have other addressing modes than the +implied addressing. The NOP instruction is just like any other load +instruction, except it does not store the result anywhere nor affects the +flags. + +6502 Registers + +The NMOS 65xx processors are not ruined with too many registers. In addition +to that, the registers are mostly 8-bit. Here is a brief description of each +register: + + PC Program Counter + This register points the address from which the next instruction + byte (opcode or parameter) will be fetched. Unlike other + registers, this one is 16 bits in length. The low and high 8-bit + halves of the register are called PCL and PCH, respectively. The + Program Counter may be read by pushing its value on the stack. + This can be done either by jumping to a subroutine or by causing + an interrupt. + S Stack pointer + The NMOS 65xx processors have 256 bytes of stack memory, ranging + from $0100 to $01FF. The S register is a 8-bit offset to the stack + page. In other words, whenever anything is being pushed on the + stack, it will be stored to the address $0100+S. + + The Stack pointer can be read and written by transfering its value + to or from the index register X (see below) with the TSX and TXS + instructions. + P Processor status + This 8-bit register stores the state of the processor. The bits in + this register are called flags. Most of the flags have something + to do with arithmetic operations. + + The P register can be read by pushing it on the stack (with PHP or + by causing an interrupt). If you only need to read one flag, you + can use the branch instructions. Setting the flags is possible by + pulling the P register from stack or by using the flag set or + clear instructions. + + Following is a list of the flags, starting from the 8th bit of the + P register (bit 7, value $80): + N Negative flag + This flag will be set after any arithmetic operations + (when any of the registers A, X or Y is being loaded + with a value). Generally, the N flag will be copied from + the topmost bit of the register being loaded. + + Note that TXS (Transfer X to S) is not an arithmetic + operation. Also note that the BIT instruction affects + the Negative flag just like arithmetic operations. + Finally, the Negative flag behaves differently in + Decimal operations (see description below). + V oVerflow flag + Like the Negative flag, this flag is intended to be used + with 8-bit signed integer numbers. The flag will be + affected by addition and subtraction, the instructions + PLP, CLV and BIT, and the hardware signal -SO. Note that + there is no SEV instruction, even though the MOS + engineers loved to use East European abbreviations, like + DDR (Deutsche Demokratische Republik vs. Data Direction + Register). (The Russian abbreviation for their former + trade association COMECON is SEV.) The -SO (Set + Overflow) signal is available on some processors, at + least the 6502, to set the V flag. This enables response + to an I/O activity in equal or less than three clock + cycles when using a BVC instruction branching to itself + ($50 $FE). + + The CLV instruction clears the V flag, and the PLP and + BIT instructions copy the flag value from the bit 6 of + the topmost stack entry or from memory. + + After a binary addition or subtraction, the V flag will + be set on a sign overflow, cleared otherwise. What is a + sign overflow? For instance, if you are trying to add + 123 and 45 together, the result (168) does not fit in a + 8-bit signed integer (upper limit 127 and lower limit + -128). Similarly, adding -123 to -45 causes the + overflow, just like subtracting -45 from 123 or 123 from + -45 would do. + + Like the N flag, the V flag will not be set as expected + in the Decimal mode. Later in this document is a precise + operation description. + + A common misbelief is that the V flag could only be set + by arithmetic operations, not cleared. + 1 unused flag + To the current knowledge, this flag is always 1. + B Break flag + This flag is used to distinguish software (BRK) + interrupts from hardware interrupts (IRQ or NMI). The B + flag is always set except when the P register is being + pushed on stack when jumping to an interrupt routine to + process only a hardware interrupt. + + The official NMOS 65xx documentation claims that the BRK + instruction could only cause a jump to the IRQ vector + ($FFFE). However, if an NMI interrupt occurs while + executing a BRK instruction, the processor will jump to + the NMI vector ($FFFA), and the P register will be + pushed on the stack with the B flag set. + D Decimal mode flag + This flag is used to select the (Binary Coded) Decimal + mode for addition and subtraction. In most applications, + the flag is zero. + + The Decimal mode has many oddities, and it operates + differently on CMOS processors. See the description of + the ADC, SBC and ARR instructions below. + I Interrupt disable flag + This flag can be used to prevent the processor from + jumping to the IRQ handler vector ($FFFE) whenever the + hardware line -IRQ is active. The flag will be + automatically set after taking an interrupt, so that the + processor would not keep jumping to the interrupt + routine if the -IRQ signal remains low for several clock + cycles. + Z Zero flag + The Zero flag will be affected in the same cases than + the Negative flag. Generally, it will be set if an + arithmetic register is being loaded with the value zero, + and cleared otherwise. The flag will behave differently + in Decimal operations. + C Carry flag + This flag is used in additions, subtractions, + comparisons and bit rotations. In additions and + subtractions, it acts as a 9th bit and lets you to chain + operations to calculate with bigger than 8-bit numbers. + When subtracting, the Carry flag is the negative of + Borrow: if an overflow occurs, the flag will be clear, + otherwise set. Comparisons are a special case of + subtraction: they assume Carry flag set and Decimal flag + clear, and do not store the result of the subtraction + anywhere. + + There are four kinds of bit rotations. All of them store + the bit that is being rotated off to the Carry flag. The + left shifting instructions are ROL and ASL. ROL copies + the initial Carry flag to the lowmost bit of the byte; + ASL always clears it. Similarly, the ROR and LSR + instructions shift to the right. + A Accumulator + The accumulator is the main register for arithmetic and logic + operations. Unlike the index registers X and Y, it has a direct + connection to the Arithmetic and Logic Unit (ALU). This is why + many operations are only available for the accumulator, not the + index registers. + X Index register X + This is the main register for addressing data with indices. It has + a special addressing mode, indexed indirect, which lets you to + have a vector table on the zero page. + Y Index register Y + The Y register has the least operations available. On the other + hand, only it has the indirect indexed addressing mode that + enables access to any memory place without having to use + self-modifying code. + +6510/8502 Undocumented Commands + +-- A brief explanation about what may happen while using don't care states. + + ANE $8B A = (A | #$EE) & X & #byte + same as + A = ((A & #$11 & X) | ( #$EE & X)) & #byte + + In real 6510/8502 the internal parameter #$11 + may occasionally be #$10, #$01 or even #$00. + This occurs when the video chip starts DMA + between the opcode fetch and the parameter fetch + of the instruction. The value probably depends + on the data that was left on the bus by the VIC-II. + + LXA $AB C=Lehti: A = X = ANE + Alternate: A = X = (A & #byte) + + TXA and TAX have to be responsible for these. + + SHA $93,$9F Store (A & X & (ADDR_HI + 1)) + SHX $9E Store (X & (ADDR_HI + 1)) + SHY $9C Store (Y & (ADDR_HI + 1)) + SHS $9B SHA and TXS, where X is replaced by (A & X). + + Note: The value to be stored is copied also + to ADDR_HI if page boundary is crossed. + + SBX $CB Carry and Decimal flags are ignored but the + Carry flag will be set in substraction. This + is due to the CMP command, which is executed + instead of the real SBC. + + ARR $6B This instruction first performs an AND + between the accumulator and the immediate + parameter, then it shifts the accumulator to + the right. However, this is not the whole + truth. See the description below. + +Many undocumented commands do not use AND between registers, the CPU +just throws the bytes to a bus simultaneously and lets the +open-collector drivers perform the AND. I.e. the command called 'SAX', +which is in the STORE section (opcodes $A0...$BF), stores the result +of (A & X) by this way. + +More fortunate is its opposite, 'LAX' which just loads a byte +simultaneously into both A and X. + + $6B ARR + +This instruction seems to be a harmless combination of AND and ROR at +first sight, but it turns out that it affects the V flag and also has +a special kind of decimal mode. This is because the instruction has +inherited some properties of the ADC instruction ($69) in addition to +the ROR ($6A). + +In Binary mode (D flag clear), the instruction effectively does an AND +between the accumulator and the immediate parameter, and then shifts +the accumulator to the right, copying the C flag to the 8th bit. It +sets the Negative and Zero flags just like the ROR would. The ADC code +shows up in the Carry and oVerflow flags. The C flag will be copied +from the bit 6 of the result (which doesn't seem too logical), and the +V flag is the result of an Exclusive OR operation between the bit 6 +and the bit 5 of the result. This makes sense, since the V flag will +be normally set by an Exclusive OR, too. + +In Decimal mode (D flag set), the ARR instruction first performs the +AND and ROR, just like in Binary mode. The N flag will be copied from +the initial C flag, and the Z flag will be set according to the ROR +result, as expected. The V flag will be set if the bit 6 of the +accumulator changed its state between the AND and the ROR, cleared +otherwise. + +Now comes the funny part. If the low nybble of the AND result, +incremented by its lowmost bit, is greater than 5, the low nybble in +the ROR result will be incremented by 6. The low nybble may overflow +as a consequence of this BCD fixup, but the high nybble won't be +adjusted. The high nybble will be BCD fixed in a similar way. If the +high nybble of the AND result, incremented by its lowmost bit, is +greater than 5, the high nybble in the ROR result will be incremented +by 6, and the Carry flag will be set. Otherwise the C flag will be +cleared. + +To help you understand this description, here is a C routine that +illustrates the ARR operation in Decimal mode: + + unsigned + A, /* Accumulator */ + AL, /* low nybble of accumulator */ + AH, /* high nybble of accumulator */ + + C, /* Carry flag */ + Z, /* Zero flag */ + V, /* oVerflow flag */ + N, /* Negative flag */ + + t, /* temporary value */ + s; /* value to be ARRed with Accumulator */ + + t = A & s; /* Perform the AND. */ + + AH = t >> 4; /* Separate the high */ + AL = t & 15; /* and low nybbles. */ + + N = C; /* Set the N and */ + Z = !(A = (t >> 1) | (C << 7)); /* Z flags traditionally */ + V = (t ^ A) & 64; /* and V flag in a weird way. */ + + if (AL + (AL & 1) > 5) /* BCD "fixup" for low nybble. */ + A = (A & 0xF0) | ((A + 6) & 0xF); + + if (C = AH + (AH & 1) > 5) /* Set the Carry flag. */ + A = (A + 0x60) & 0xFF; /* BCD "fixup" for high nybble. */ + + $CB SBX X <- (A & X) - Immediate + +The 'SBX' ($CB) may seem to be very complex operation, even though it +is a combination of the subtraction of accumulator and parameter, as +in the 'CMP' instruction, and the command 'DEX'. As a result, both A +and X are connected to ALU but only the subtraction takes place. Since +the comparison logic was used, the result of subtraction should be +normally ignored, but the 'DEX' now happily stores to X the value of +(A & X) - Immediate. That is why this instruction does not have any +decimal mode, and it does not affect the V flag. Also Carry flag will +be ignored in the subtraction but set according to the result. + + Proof: + +begin 644 vsbx +M`0@9$,D'GL(H-#,IJC(U-JS"*#0T*:HR-@```*D`H#V1*Z`_D2N@09$KJ0>% +M^QBE^VEZJ+$KH#F1*ZD`2"BI`*(`RP`(:-B@.5$K*4#P`E@`H#VQ*SAI`)$K +JD-Z@/[$K:0"1*Y#4J2X@TO\XH$&Q*VD`D2N0Q,;[$+188/_^]_:_OK>V +` +end + + and + +begin 644 sbx +M`0@9$,D'GL(H-#,IJC(U-JS"*#0T*:HR-@```'BI`*!-D2N@3Y$KH%&1*ZD# +MA?L8I?M*2)`#J1@LJ3B@29$K:$J0`ZGX+*G8R)$K&/BXJ?2B8\L)AOP(:(7] +MV#B@3;$KH$\Q*Z!1\2L(1?SP`0!H1?TIM]#XH$VQ*SAI`)$KD,N@3[$K:0"1 +9*Y#!J2X@TO\XH%&Q*VD`D2N0L<;[$))88-#X +` +end + +These test programs show if your machine is compatible with ours +regarding the opcode $CB. The first test, vsbx, proves that SBX does +not affect the V flag. The latter one, sbx, proves the rest of our +theory. The vsbx test tests 33554432 SBX combinations (16777216 +different A, X and Immediate combinations, and two different V flag +states), and the sbx test doubles that amount (16777216*4 D and C flag +combinations). Both tests have run successfully on a C64 and a Vic20. +They ought to run on C16, +4 and the PET series as well. The tests +stop with BRK, if the opcode $CB does not work as expected. Successful +operation ends in RTS. As the tests are very slow, they print dots on +the screen while running so that you know that the machine has not +jammed. On computers running at 1 MHz, the first test prints +approximately one dot every four seconds and a total of 2048 dots, +whereas the second one prints half that amount, one dot every seven +seconds. + +If the tests fail on your machine, please let us know your processor's +part number and revision. If possible, save the executable (after it +has stopped with BRK) under another name and send it to us so that we +know at which stage the program stopped. + +The following program is a Commodore 64 executable that Marko M"akel"a +developed when trying to find out how the V flag is affected by SBX. +(It was believed that the SBX affects the flag in a weird way, and +this program shows how SBX sets the flag differently from SBC.) You +may find the subroutine at $C150 useful when researching other +undocumented instructions' flags. Run the program in a machine +language monitor, as it makes use of the BRK instruction. The result +tables will be written on pages $C2 and $C3. + +begin 644 sbx-c100 +M`,%XH`",#L&,$,&,$L&XJ8*B@LL7AOL(:(7\N#BM#L$M$,'M$L$(Q?OP`B@` +M:$7\\`,@4,'N#L'0U.X0P=#/SB#0[A+!T,<``````````````)BJ\!>M#L$M +L$,'=_\'0":T2P=W_PM`!8,K0Z:T.P2T0P9D`PID`!*T2P9D`PYD`! + +Other undocumented instructions usually cause two preceding opcodes +being executed. However 'NOP' seems to completely disappear from 'SBC' +code $EB. + +The most difficult to comprehend are the rest of the instructions +located on the '$0B' line. + +All the instructions located at the positive (left) side of this line +should rotate either memory or the accumulator, but the addressing +mode turns out to be immediate! No problem. Just read the operand, let +it be ANDed with the accumulator and finally use accumulator +addressing mode for the instructions above them. + +RELIGION_MODE_ON +/* This part of the document is not accurate. You can + read it as a fairy tale, but do not count on it when + performing your own measurements. */ + +The rest two instructions on the same line, called 'ANE' and 'LXA' +($8B and $AB respectively) often give quite unpredictable results. +However, the most usual operation is to store ((A | #$ee) & X & #$nn) +to accumulator. Note that this does not work reliably in a real 64! +In the Commodore 128 the opcode $8B uses values 8C, CC, EE, and +occasionally 0C and 8E for the OR instead of EE,EF,FE and FF used in +the C64. With a C128 running at 2 MHz #$EE is always used. Opcode $AB +does not cause this OR taking place on 8502 while 6510 always performs +it. Note that this behaviour depends on processor and/or video chip +revision. + +Let's take a closer look at $8B (6510). + + A <- X & D & (A | VAL) + + where VAL comes from this table: + + X high D high D low VAL + even even --- $EE (1) + even odd --- $EE + odd even --- $EE + odd odd 0 $EE + odd odd not 0 $FE (2) + +(1) If the bottom 2 bits of A are both 1, then the LSB of the result may + be 0. The values of X and D are different every time I run the test. + This appears to be very rare. +(2) VAL is $FE most of the time. Sometimes it is $EE - it seems to be random, + not related to any of the data. This is much more common than (1). + + In decimal mode, VAL is usually $FE. + +Two different functions have been discovered for LAX, opcode $AB. One +is A = X = ANE (see above) and the other, encountered with 6510 and +8502, is less complicated A = X = (A & #byte). However, according to +what is reported, the version altering only the lowest bits of each +nybble seems to be more common. + +What happens, is that $AB loads a value into both A and X, ANDing the +low bit of each nybble with the corresponding bit of the old +A. However, there are exceptions. Sometimes the low bit is cleared +even when A contains a '1', and sometimes other bits are cleared. The +exceptions seem random (they change every time I run the test). Oops - +that was in decimal mode. Much the same with D=0. + +What causes the randomness? Probably it is that it is marginal logic +levels - when too much wired-anding goes on, some of the signals get +very close to the threshold. Perhaps we're seeing some of them step +over it. The low bit of each nybble is special, since it has to cope +with carry differently (remember decimal mode). We never see a '0' +turn into a '1'. + +Since these instructions are unpredictable, they should not be used. + +There is still very strange instruction left, the one named SHA/X/Y, +which is the only one with only indexed addressing modes. Actually, +the commands 'SHA', 'SHX' and 'SHY' are generated by the indexing +algorithm. + +While using indexed addressing, effective address for page boundary +crossing is calculated as soon as possible so it does not slow down +operation. As a result, in the case of SHA/X/Y, the address and data +are processed at the same time making AND between them to take place. +Thus, the value to be stored by SAX, for example, is in fact (A & X & +(ADDR_HI + 1)). On page boundary crossing the same value is copied +also to high byte of the effective address. + +RELIGION_MODE_OFF + + +Register selection for load and store + + bit1 bit0 A X Y + 0 0 x + 0 1 x + 1 0 x + 1 1 x x + +So, A and X are selected by bits 1 and 0 respectively, while + ~(bit1|bit0) enables Y. + +Indexing is determined by bit4, even in relative addressing mode, +which is one kind of indexing. + +Lines containing opcodes xxx000x1 (01 and 03) are treated as absolute +after the effective address has been loaded into CPU. + +Zeropage,y and Absolute,y (codes 10x1 x11x) are distinquished by bit5. + + +Decimal mode in NMOS 6500 series + + Most sources claim that the NMOS 6500 series sets the N, V and Z +flags unpredictably when performing addition or subtraction in decimal +mode. Of course, this is not true. While testing how the flags are +set, I also wanted to see what happens if you use illegal BCD values. + + ADC works in Decimal mode in a quite complicated way. It is amazing +how it can do that all in a single cycle. Here's a C code version of +the instruction: + + unsigned + A, /* Accumulator */ + AL, /* low nybble of accumulator */ + AH, /* high nybble of accumulator */ + + C, /* Carry flag */ + Z, /* Zero flag */ + V, /* oVerflow flag */ + N, /* Negative flag */ + + s; /* value to be added to Accumulator */ + + AL = (A & 15) + (s & 15) + C; /* Calculate the lower nybble. */ + + AH = (A >> 4) + (s >> 4) + (AL > 15); /* Calculate the upper nybble. */ + + if (AL > 9) AL += 6; /* BCD fixup for lower nybble. */ + + Z = ((A + s + C) & 255 != 0); /* Zero flag is set just + like in Binary mode. */ + + /* Negative and Overflow flags are set with the same logic than in + Binary mode, but after fixing the lower nybble. */ + + N = (AH & 8 != 0); + V = ((AH << 4) ^ A) & 128 && !((A ^ s) & 128); + + if (AH > 9) AH += 6; /* BCD fixup for upper nybble. */ + + /* Carry is the only flag set after fixing the result. */ + + C = (AH > 15); + A = ((AH << 4) | (AL & 15)) & 255; + + The C flag is set as the quiche eaters expect, but the N and V flags +are set after fixing the lower nybble but before fixing the upper one. +They use the same logic than binary mode ADC. The Z flag is set before +any BCD fixup, so the D flag does not have any influence on it. + +Proof: The following test program tests all 131072 ADC combinations in + Decimal mode, and aborts with BRK if anything breaks this theory. + If everything goes well, it ends in RTS. + +begin 600 dadc +M 0@9",D'GL(H-#,IJC(U-JS"*#0T*:HR-@ 'BI&* A/N$_$B@+)$KH(V1 +M*Q@(I?PI#X7]I?LI#V7]R0J0 FD%J"D/A?VE^RGP9?PI\ C $) ":0^JL @H +ML ?)H) &""@X:5\X!?V%_0AH*3W@ ! ""8"HBD7[$ JE^T7\, 28"4"H**7[ +M9?S0!)@) J@8N/BE^V7\V A%_= G:(3]1?W0(.;[T(?F_-"#:$D8\ )88*D= +0&&4KA?NI &4LA?RI.&S[ A% + +end + + All programs in this chapter have been successfully tested on a Vic20 +and a Commodore 64 and a Commodore 128D in C64 mode. They should run on +C16, +4 and on the PET series as well. If not, please report the problem +to Marko M"akel"a. Each test in this chapter should run in less than a +minute at 1 MHz. + +SBC is much easier. Just like CMP, its flags are not affected by +the D flag. + +Proof: + +begin 600 dsbc-cmp-flags +M 0@9",D'GL(H-#,IJC(U-JS"*#0T*:HR-@ 'B@ (3[A/RB XH8:66HL2N@ +M09$KH$R1*XII::BQ*Z!%D2N@4)$K^#BXI?OE_-@(:(7].+BE^^7\"&A%_? ! +5 .;[T./F_-#?RA"_8!@X&#CEY<7% + +end + + The only difference in SBC's operation in decimal mode from binary mode +is the result-fixup: + + unsigned + A, /* Accumulator */ + AL, /* low nybble of accumulator */ + AH, /* high nybble of accumulator */ + + C, /* Carry flag */ + Z, /* Zero flag */ + V, /* oVerflow flag */ + N, /* Negative flag */ + + s; /* value to be added to Accumulator */ + + AL = (A & 15) - (s & 15) - !C; /* Calculate the lower nybble. */ + + if (AL & 16) AL -= 6; /* BCD fixup for lower nybble. */ + + AH = (A >> 4) - (s >> 4) - (AL & 16); /* Calculate the upper nybble. */ + + if (AH & 16) AH -= 6; /* BCD fixup for upper nybble. */ + + /* The flags are set just like in Binary mode. */ + + C = (A - s - !C) & 256 != 0; + Z = (A - s - !C) & 255 != 0; + V = ((A - s - !C) ^ s) & 128 && (A ^ s) & 128; + N = (A - s - !C) & 128 != 0; + + A = ((AH << 4) | (AL & 15)) & 255; + + Again Z flag is set before any BCD fixup. The N and V flags are set +at any time before fixing the high nybble. The C flag may be set in any +phase. + + Decimal subtraction is easier than decimal addition, as you have to +make the BCD fixup only when a nybble overflows. In decimal addition, +you had to verify if the nybble was greater than 9. The processor has +an internal "half carry" flag for the lower nybble, used to trigger +the BCD fixup. When calculating with legal BCD values, the lower nybble +cannot overflow again when fixing it. +So, the processor does not handle overflows while performing the fixup. +Similarly, the BCD fixup occurs in the high nybble only if the value +overflows, i.e. when the C flag will be cleared. + + Because SBC's flags are not affected by the Decimal mode flag, you +could guess that CMP uses the SBC logic, only setting the C flag +first. But the SBX instruction shows that CMP also temporarily clears +the D flag, although it is totally unnecessary. + + The following program, which tests SBC's result and flags, +contains the 6502 version of the pseudo code example above. + +begin 600 dsbc +M 0@9",D'GL(H-#,IJC(U-JS"*#0T*:HR-@ 'BI&* A/N$_$B@+)$KH':1 +M*S@(I?PI#X7]I?LI#^7]L /I!1@I#ZBE_"GPA?VE^RGP"#CE_2GPL KI7RBP +M#ND/.+ )*+ &Z0^P NE?A/T%_87]*+BE^^7\"&BH.+CXI?OE_-@(1?W0FVB$ +8_47]T)3F^]">YOS0FFA)&- $J3C0B%A@ + +end + + Obviously the undocumented instructions RRA (ROR+ADC) and ISB +(INC+SBC) have inherited also the decimal operation from the official +instructions ADC and SBC. The program droradc proves this statement +for ROR, and the dincsbc test proves this for ISB. Finally, +dincsbc-deccmp proves that ISB's and DCP's (DEC+CMP) flags are not +affected by the D flag. + +begin 644 droradc +M`0@9",D'GL(H-#,IJC(U-JS"*#0T*:HR-@```'BI&*``A/N$_$B@+)$KH(V1 +M*S@(I?PI#X7]I?LI#V7]R0J0`FD%J"D/A?VE^RGP9?PI\`C`$)`":0^JL`@H +ML`?)H)`&""@X:5\X!?V%_0AH*3W@`!`""8"HBD7[$`JE^T7\,`28"4"H**7[ +M9?S0!)@)`J@XN/BE^R;\9_S8"$7]T"=HA/U%_=`@YOO0A>;\T(%H21CP`EA@ +2J1T892N%^ZD`92R%_*DX;/L` +` +end + +begin 644 dincsbc +M`0@9",D'GL(H-#,IJC(U-JS"*#0T*:HR-@```'BI&*``A/N$_$B@+)$KH':1 +M*S@(I?PI#X7]I?LI#^7]L`/I!1@I#ZBE_"GPA?VE^RGP"#CE_2GPL`KI7RBP +M#ND/.+`)*+`&Z0^P`NE?A/T%_87]*+BE^^7\"&BH.+CXI?O&_.?\V`A%_="9 +::(3]1?W0DN;[T)SF_-"8:$D8T`2I.-"&6&#\ +` +end + +begin 644 dincsbc-deccmp +M`0@9",D'GL(H-#,IJC(U-JS"*#0T*:HR-@```'B@`(3[A/RB`XH8:7>HL2N@ +M3Y$KH%R1*XII>ZBQ*Z!3D2N@8)$KBFE_J+$KH%61*Z!BD2OX.+BE^^;\Q_S8 +L"&B%_3BXI?OF_,?\"&A%_?`!`.;[T-_F_-#;RA"M8!@X&#CFYL;&Q\?GYP#8 +` +end + + +6510 features + + o PHP always pushes the Break (B) flag as a `1' to the stack. + Jukka Tapanim"aki claimed in C=lehti issue 3/89, on page 27 that the + processor makes a logical OR between the status register's bit 4 + and the bit 8 of the stack pointer register (which is always 1). + He did not give any reasons for this argument, and has refused to clarify + it afterwards. Well, this was not the only error in his article... + + o Indirect addressing modes do not handle page boundary crossing at all. + When the parameter's low byte is $FF, the effective address wraps + around and the CPU fetches high byte from $xx00 instead of $xx00+$0100. + E.g. JMP ($01FF) fetches PCL from $01FF and PCH from $0100, + and LDA ($FF),Y fetches the base address from $FF and $00. + + o Indexed zero page addressing modes never fix the page address on + crossing the zero page boundary. + E.g. LDX #$01 : LDA ($FF,X) loads the effective address from $00 and $01. + + o The processor always fetches the byte following a relative branch + instruction. If the branch is taken, the processor reads then the + opcode from the destination address. If page boundary is crossed, it + first reads a byte from the old page from a location that is bigger + or smaller than the correct address by one page. + + o If you cross a page boundary in any other indexed mode, + the processor reads an incorrect location first, a location that is + smaller by one page. + + o Read-Modify-Write instructions write unmodified data, then modified + (so INC effectively does LDX loc;STX loc;INX;STX loc) + + o -RDY is ignored during writes + (This is why you must wait 3 cycles before doing any DMA -- + the maximum number of consecutive writes is 3, which occurs + during interrupts except -RESET.) + + o Some undefined opcodes may give really unpredictable results. + + o All registers except the Program Counter remain unmodified after -RESET. + (This is why you must preset D and I flags in the RESET handler.) + + +Different CPU types + +The Rockwell data booklet 29651N52 (technical information about R65C00 +microprocessors, dated October 1984), lists the following differences between +NMOS R6502 microprocessor and CMOS R65C00 family: + + + 1. Indexed addressing across page boundary. + NMOS: Extra read of invalid address. + CMOS: Extra read of last instruction byte. + + + 2. Execution of invalid op codes. + NMOS: Some terminate only by reset. Results are undefined. + CMOS: All are NOPs (reserved for future use). + + + 3. Jump indirect, operand = XXFF. + NMOS: Page address does not increment. + CMOS: Page address increments and adds one additional cycle. + + + 4. Read/modify/write instructions at effective address. + NMOS: One read and two write cycles. + CMOS: Two read and one write cycle. + + + 5. Decimal flag. + NMOS: Indeterminate after reset. + CMOS: Initialized to binary mode (D=0) after reset and interrupts. + + + 6. Flags after decimal operation. + NMOS: Invalid N, V and Z flags. + CMOS: Valid flag adds one additional cycle. + + + 7. Interrupt after fetch of BRK instruction. + NMOS: Interrupt vector is loaded, BRK vector is ignored. + CMOS: BRK is executed, then interrupt is executed. + + +6510 Instruction Timing + + The NMOS 6500 series processors always perform at least two reads +for each instruction. In addition to the operation code (opcode), they +fetch the next byte. This is quite efficient, as most instructions are +two or three bytes long. + + The processors also use a sort of pipelining. If an instruction does +not store data in memory on its last cycle, the processor can fetch +the opcode of the next instruction while executing the last cycle. For +instance, the instruction EOR #$FF truly takes three cycles. On the +first cycle, the opcode $49 will be fetched. During the second cycle +the processor decodes the opcode and fetches the parameter #$FF. On +the third cycle, the processor will perform the operation and store +the result to accumulator, but simultaneously it fetches the opcode +for the next instruction. This is why the instruction effectively +takes only two cycles. + + The following tables show what happens on the bus while executing +different kinds of instructions. + + Interrupts + + NMI and IRQ both take 7 cycles. Their timing diagram is much like + BRK's (see below). IRQ will be executed only when the I flag is + clear. IRQ and BRK both set the I flag, whereas the NMI does not + affect its state. + + The processor will usually wait for the current instruction to + complete before executing the interrupt sequence. To process the + interrupt before the next instruction, the interrupt must occur + before the last cycle of the current instruction. + + There is one exception to this rule: the BRK instruction. If a + hardware interrupt (NMI or IRQ) occurs before the fourth (flags + saving) cycle of BRK, the BRK instruction will be skipped, and + the processor will jump to the hardware interrupt vector. This + sequence will always take 7 cycles. + + You do not completely lose the BRK interrupt, the B flag will be + set in the pushed status register if a BRK instruction gets + interrupted. When BRK and IRQ occur at the same time, this does + not cause any problems, as your program will consider it as a + BRK, and the IRQ would occur again after the processor returned + from your BRK routine, unless you cleared the interrupt source in + your BRK handler. But the simultaneous occurrence of NMI and BRK + is far more fatal. If you do not check the B flag in the NMI + routine and subtract two from the return address when needed, the + BRK instruction will be skipped. + + If the NMI and IRQ interrupts overlap each other (one interrupt + occurs before fetching the interrupt vector for the other + interrupt), the processor will most probably jump to the NMI + vector in every case, and then jump to the IRQ vector after + processing the first instruction of the NMI handler. This has not + been measured yet, but the IRQ is very similar to BRK, and many + sources state that the NMI has higher priority than IRQ. However, + it might be that the processor takes the interrupt that comes + later, i.e. you could lose an NMI interrupt if an IRQ occurred in + four cycles after it. + + After finishing the interrupt sequence, the processor will start + to execute the first instruction of the interrupt routine. This + proves that the processor uses a sort of pipelining: it finishes + the current instruction (or interrupt sequence) while reading the + opcode of the next instruction. + + RESET does not push program counter on stack, and it lasts + probably 6 cycles after deactivating the signal. Like NMI, RESET + preserves all registers except PC. + + Instructions accessing the stack + + BRK + + # address R/W description + --- ------- --- ----------------------------------------------- + 1 PC R fetch opcode, increment PC + 2 PC R read next instruction byte (and throw it away), + increment PC + 3 $0100,S W push PCH on stack (with B flag set), decrement S + 4 $0100,S W push PCL on stack, decrement S + 5 $0100,S W push P on stack, decrement S + 6 $FFFE R fetch PCL + 7 $FFFF R fetch PCH + + RTI + + # address R/W description + --- ------- --- ----------------------------------------------- + 1 PC R fetch opcode, increment PC + 2 PC R read next instruction byte (and throw it away) + 3 $0100,S R increment S + 4 $0100,S R pull P from stack, increment S + 5 $0100,S R pull PCL from stack, increment S + 6 $0100,S R pull PCH from stack + + RTS + + # address R/W description + --- ------- --- ----------------------------------------------- + 1 PC R fetch opcode, increment PC + 2 PC R read next instruction byte (and throw it away) + 3 $0100,S R increment S + 4 $0100,S R pull PCL from stack, increment S + 5 $0100,S R pull PCH from stack + 6 PC R increment PC + + PHA, PHP + + # address R/W description + --- ------- --- ----------------------------------------------- + 1 PC R fetch opcode, increment PC + 2 PC R read next instruction byte (and throw it away) + 3 $0100,S W push register on stack, decrement S + + PLA, PLP + + # address R/W description + --- ------- --- ----------------------------------------------- + 1 PC R fetch opcode, increment PC + 2 PC R read next instruction byte (and throw it away) + 3 $0100,S R increment S + 4 $0100,S R pull register from stack + + JSR + + # address R/W description + --- ------- --- ------------------------------------------------- + 1 PC R fetch opcode, increment PC + 2 PC R fetch low address byte, increment PC + 3 $0100,S R internal operation (predecrement S?) + 4 $0100,S W push PCH on stack, decrement S + 5 $0100,S W push PCL on stack, decrement S + 6 PC R copy low address byte to PCL, fetch high address + byte to PCH + + Accumulator or implied addressing + + # address R/W description + --- ------- --- ----------------------------------------------- + 1 PC R fetch opcode, increment PC + 2 PC R read next instruction byte (and throw it away) + + Immediate addressing + + # address R/W description + --- ------- --- ------------------------------------------ + 1 PC R fetch opcode, increment PC + 2 PC R fetch value, increment PC + + Absolute addressing + + JMP + + # address R/W description + --- ------- --- ------------------------------------------------- + 1 PC R fetch opcode, increment PC + 2 PC R fetch low address byte, increment PC + 3 PC R copy low address byte to PCL, fetch high address + byte to PCH + + Read instructions (LDA, LDX, LDY, EOR, AND, ORA, ADC, SBC, CMP, BIT, + LAX, NOP) + + # address R/W description + --- ------- --- ------------------------------------------ + 1 PC R fetch opcode, increment PC + 2 PC R fetch low byte of address, increment PC + 3 PC R fetch high byte of address, increment PC + 4 address R read from effective address + + Read-Modify-Write instructions (ASL, LSR, ROL, ROR, INC, DEC, + SLO, SRE, RLA, RRA, ISB, DCP) + + # address R/W description + --- ------- --- ------------------------------------------ + 1 PC R fetch opcode, increment PC + 2 PC R fetch low byte of address, increment PC + 3 PC R fetch high byte of address, increment PC + 4 address R read from effective address + 5 address W write the value back to effective address, + and do the operation on it + 6 address W write the new value to effective address + + Write instructions (STA, STX, STY, SAX) + + # address R/W description + --- ------- --- ------------------------------------------ + 1 PC R fetch opcode, increment PC + 2 PC R fetch low byte of address, increment PC + 3 PC R fetch high byte of address, increment PC + 4 address W write register to effective address + + Zero page addressing + + Read instructions (LDA, LDX, LDY, EOR, AND, ORA, ADC, SBC, CMP, BIT, + LAX, NOP) + + # address R/W description + --- ------- --- ------------------------------------------ + 1 PC R fetch opcode, increment PC + 2 PC R fetch address, increment PC + 3 address R read from effective address + + Read-Modify-Write instructions (ASL, LSR, ROL, ROR, INC, DEC, + SLO, SRE, RLA, RRA, ISB, DCP) + + # address R/W description + --- ------- --- ------------------------------------------ + 1 PC R fetch opcode, increment PC + 2 PC R fetch address, increment PC + 3 address R read from effective address + 4 address W write the value back to effective address, + and do the operation on it + 5 address W write the new value to effective address + + Write instructions (STA, STX, STY, SAX) + + # address R/W description + --- ------- --- ------------------------------------------ + 1 PC R fetch opcode, increment PC + 2 PC R fetch address, increment PC + 3 address W write register to effective address + + Zero page indexed addressing + + Read instructions (LDA, LDX, LDY, EOR, AND, ORA, ADC, SBC, CMP, BIT, + LAX, NOP) + + # address R/W description + --- --------- --- ------------------------------------------ + 1 PC R fetch opcode, increment PC + 2 PC R fetch address, increment PC + 3 address R read from address, add index register to it + 4 address+I* R read from effective address + + Notes: I denotes either index register (X or Y). + + * The high byte of the effective address is always zero, + i.e. page boundary crossings are not handled. + + Read-Modify-Write instructions (ASL, LSR, ROL, ROR, INC, DEC, + SLO, SRE, RLA, RRA, ISB, DCP) + + # address R/W description + --- --------- --- --------------------------------------------- + 1 PC R fetch opcode, increment PC + 2 PC R fetch address, increment PC + 3 address R read from address, add index register X to it + 4 address+X* R read from effective address + 5 address+X* W write the value back to effective address, + and do the operation on it + 6 address+X* W write the new value to effective address + + Note: * The high byte of the effective address is always zero, + i.e. page boundary crossings are not handled. + + Write instructions (STA, STX, STY, SAX) + + # address R/W description + --- --------- --- ------------------------------------------- + 1 PC R fetch opcode, increment PC + 2 PC R fetch address, increment PC + 3 address R read from address, add index register to it + 4 address+I* W write to effective address + + Notes: I denotes either index register (X or Y). + + * The high byte of the effective address is always zero, + i.e. page boundary crossings are not handled. + + Absolute indexed addressing + + Read instructions (LDA, LDX, LDY, EOR, AND, ORA, ADC, SBC, CMP, BIT, + LAX, LAE, SHS, NOP) + + # address R/W description + --- --------- --- ------------------------------------------ + 1 PC R fetch opcode, increment PC + 2 PC R fetch low byte of address, increment PC + 3 PC R fetch high byte of address, + add index register to low address byte, + increment PC + 4 address+I* R read from effective address, + fix the high byte of effective address + 5+ address+I R re-read from effective address + + Notes: I denotes either index register (X or Y). + + * The high byte of the effective address may be invalid + at this time, i.e. it may be smaller by $100. + + + This cycle will be executed only if the effective address + was invalid during cycle #4, i.e. page boundary was crossed. + + Read-Modify-Write instructions (ASL, LSR, ROL, ROR, INC, DEC, + SLO, SRE, RLA, RRA, ISB, DCP) + + # address R/W description + --- --------- --- ------------------------------------------ + 1 PC R fetch opcode, increment PC + 2 PC R fetch low byte of address, increment PC + 3 PC R fetch high byte of address, + add index register X to low address byte, + increment PC + 4 address+X* R read from effective address, + fix the high byte of effective address + 5 address+X R re-read from effective address + 6 address+X W write the value back to effective address, + and do the operation on it + 7 address+X W write the new value to effective address + + Notes: * The high byte of the effective address may be invalid + at this time, i.e. it may be smaller by $100. + + Write instructions (STA, STX, STY, SHA, SHX, SHY) + + # address R/W description + --- --------- --- ------------------------------------------ + 1 PC R fetch opcode, increment PC + 2 PC R fetch low byte of address, increment PC + 3 PC R fetch high byte of address, + add index register to low address byte, + increment PC + 4 address+I* R read from effective address, + fix the high byte of effective address + 5 address+I W write to effective address + + Notes: I denotes either index register (X or Y). + + * The high byte of the effective address may be invalid + at this time, i.e. it may be smaller by $100. Because + the processor cannot undo a write to an invalid + address, it always reads from the address first. + + Relative addressing (BCC, BCS, BNE, BEQ, BPL, BMI, BVC, BVS) + + # address R/W description + --- --------- --- --------------------------------------------- + 1 PC R fetch opcode, increment PC + 2 PC R fetch operand, increment PC + 3 PC R Fetch opcode of next instruction, + If branch is taken, add operand to PCL. + Otherwise increment PC. + 4+ PC* R Fetch opcode of next instruction. + Fix PCH. If it did not change, increment PC. + 5! PC R Fetch opcode of next instruction, + increment PC. + + Notes: The opcode fetch of the next instruction is included to + this diagram for illustration purposes. When determining + real execution times, remember to subtract the last + cycle. + + * The high byte of Program Counter (PCH) may be invalid + at this time, i.e. it may be smaller or bigger by $100. + + + If branch is taken, this cycle will be executed. + + ! If branch occurs to different page, this cycle will be + executed. + + Indexed indirect addressing + + Read instructions (LDA, ORA, EOR, AND, ADC, CMP, SBC, LAX) + + # address R/W description + --- ----------- --- ------------------------------------------ + 1 PC R fetch opcode, increment PC + 2 PC R fetch pointer address, increment PC + 3 pointer R read from the address, add X to it + 4 pointer+X R fetch effective address low + 5 pointer+X+1 R fetch effective address high + 6 address R read from effective address + + Note: The effective address is always fetched from zero page, + i.e. the zero page boundary crossing is not handled. + + Read-Modify-Write instructions (SLO, SRE, RLA, RRA, ISB, DCP) + + # address R/W description + --- ----------- --- ------------------------------------------ + 1 PC R fetch opcode, increment PC + 2 PC R fetch pointer address, increment PC + 3 pointer R read from the address, add X to it + 4 pointer+X R fetch effective address low + 5 pointer+X+1 R fetch effective address high + 6 address R read from effective address + 7 address W write the value back to effective address, + and do the operation on it + 8 address W write the new value to effective address + + Note: The effective address is always fetched from zero page, + i.e. the zero page boundary crossing is not handled. + + Write instructions (STA, SAX) + + # address R/W description + --- ----------- --- ------------------------------------------ + 1 PC R fetch opcode, increment PC + 2 PC R fetch pointer address, increment PC + 3 pointer R read from the address, add X to it + 4 pointer+X R fetch effective address low + 5 pointer+X+1 R fetch effective address high + 6 address W write to effective address + + Note: The effective address is always fetched from zero page, + i.e. the zero page boundary crossing is not handled. + + Indirect indexed addressing + + Read instructions (LDA, EOR, AND, ORA, ADC, SBC, CMP) + + # address R/W description + --- ----------- --- ------------------------------------------ + 1 PC R fetch opcode, increment PC + 2 PC R fetch pointer address, increment PC + 3 pointer R fetch effective address low + 4 pointer+1 R fetch effective address high, + add Y to low byte of effective address + 5 address+Y* R read from effective address, + fix high byte of effective address + 6+ address+Y R read from effective address + + Notes: The effective address is always fetched from zero page, + i.e. the zero page boundary crossing is not handled. + + * The high byte of the effective address may be invalid + at this time, i.e. it may be smaller by $100. + + + This cycle will be executed only if the effective address + was invalid during cycle #5, i.e. page boundary was crossed. + + Read-Modify-Write instructions (SLO, SRE, RLA, RRA, ISB, DCP) + + # address R/W description + --- ----------- --- ------------------------------------------ + 1 PC R fetch opcode, increment PC + 2 PC R fetch pointer address, increment PC + 3 pointer R fetch effective address low + 4 pointer+1 R fetch effective address high, + add Y to low byte of effective address + 5 address+Y* R read from effective address, + fix high byte of effective address + 6 address+Y R read from effective address + 7 address+Y W write the value back to effective address, + and do the operation on it + 8 address+Y W write the new value to effective address + + Notes: The effective address is always fetched from zero page, + i.e. the zero page boundary crossing is not handled. + + * The high byte of the effective address may be invalid + at this time, i.e. it may be smaller by $100. + + Write instructions (STA, SHA) + + # address R/W description + --- ----------- --- ------------------------------------------ + 1 PC R fetch opcode, increment PC + 2 PC R fetch pointer address, increment PC + 3 pointer R fetch effective address low + 4 pointer+1 R fetch effective address high, + add Y to low byte of effective address + 5 address+Y* R read from effective address, + fix high byte of effective address + 6 address+Y W write to effective address + + Notes: The effective address is always fetched from zero page, + i.e. the zero page boundary crossing is not handled. + + * The high byte of the effective address may be invalid + at this time, i.e. it may be smaller by $100. + + Absolute indirect addressing (JMP) + + # address R/W description + --- --------- --- ------------------------------------------ + 1 PC R fetch opcode, increment PC + 2 PC R fetch pointer address low, increment PC + 3 PC R fetch pointer address high, increment PC + 4 pointer R fetch low address to latch + 5 pointer+1* R fetch PCH, copy latch to PCL + + Note: * The PCH will always be fetched from the same page + than PCL, i.e. page boundary crossing is not handled. + + How Real Programmers Acknowledge Interrupts + + With RMW instructions: + + ; beginning of combined raster/timer interrupt routine + LSR $D019 ; clear VIC interrupts, read raster interrupt flag to C + BCS raster ; jump if VIC caused an interrupt + ... ; timer interrupt routine + + Operational diagram of LSR $D019: + + # data address R/W + --- ---- ------- --- --------------------------------- + 1 4E PC R fetch opcode + 2 19 PC+1 R fetch address low + 3 D0 PC+2 R fetch address high + 4 xx $D019 R read memory + 5 xx $D019 W write the value back, rotate right + 6 xx/2 $D019 W write the new value back + + The 5th cycle acknowledges the interrupt by writing the same + value back. If only raster interrupts are used, the 6th cycle + has no effect on the VIC. (It might acknowledge also some + other interrupts.) + + With indexed addressing: + + ; acknowledge interrupts to both CIAs + LDX #$10 + LDA $DCFD,X + + Operational diagram of LDA $DCFD,X: + + # data address R/W description + --- ---- ------- --- --------------------------------- + 1 BD PC R fetch opcode + 2 FD PC+1 R fetch address low + 3 DC PC+2 R fetch address high, add X to address low + 4 xx $DC0D R read from address, fix high byte of address + 5 yy $DD0D R read from right address + + ; acknowledge interrupts to CIA 2 + LDX #$10 + STA $DDFD,X + + Operational diagram of STA $DDFD,X: + + # data address R/W description + --- ---- ------- --- --------------------------------- + 1 9D PC R fetch opcode + 2 FD PC+1 R fetch address low + 3 DC PC+2 R fetch address high, add X to address low + 4 xx $DD0D R read from address, fix high byte of address + 5 ac $DE0D W write to right address + + With branch instructions: + + ; acknowledge interrupts to CIA 2 + LDA #$00 ; clear N flag + JMP $DD0A + DD0A BPL $DC9D ; branch + DC9D BRK ; return + + You need the following preparations to initialize the CIA registers: + + LDA #$91 ; argument of BPL + STA $DD0B + LDA #$10 ; BPL + STA $DD0A + STA $DD08 ; load the ToD values from the latches + LDA $DD0B ; freeze the ToD display + LDA #$7F + STA $DC0D ; assure that $DC0D is $00 + + Operational diagram of BPL $DC9D: + + # data address R/W description + --- ---- ------- --- --------------------------------- + 1 10 $DD0A R fetch opcode + 2 91 $DD0B R fetch argument + 3 xx $DD0C R fetch opcode, add argument to PCL + 4 yy $DD9D R fetch opcode, fix PCH + ( 5 00 $DC9D R fetch opcode ) + + ; acknowledge interrupts to CIA 1 + LSR ; clear N flag + JMP $DCFA + DCFA BPL $DD0D + DD0D BRK + + ; Again you need to set the ToD registers of CIA 1 and the + ; Interrupt Control Register of CIA 2 first. + + Operational diagram of BPL $DD0D: + + # data address R/W description + --- ---- ------- --- --------------------------------- + 1 10 $DCFA R fetch opcode + 2 11 $DCFB R fetch argument + 3 xx $DCFC R fetch opcode, add argument to PCL + 4 yy $DC0D R fetch opcode, fix PCH + ( 5 00 $DD0D R fetch opcode ) + + ; acknowledge interrupts to CIA 2 automagically + ; preparations + LDA #$7F + STA $DD0D ; disable all interrupt sources of CIA2 + LDA $DD0E + AND #$BE ; ensure that $DD0C remains constant + STA $DD0E ; and stop the timer + LDA #$FD + STA $DD0C ; parameter of BPL + LDA #$10 + STA $DD0B ; BPL + LDA #$40 + STA $DD0A ; RTI/parameter of LSR + LDA #$46 + STA $DD09 ; LSR + STA $DD08 ; load the ToD values from the latches + LDA $DD0B ; freeze the ToD display + LDA #$09 + STA $0318 + LDA #$DD + STA $0319 ; change NMI vector to $DD09 + LDA #$FF ; Try changing this instruction's operand + STA $DD05 ; (see comment below). + LDA #$FF + STA $DD04 ; set interrupt frequency to 1/65536 cycles + LDA $DD0E + AND #$80 + ORA #$11 + LDX #$81 + STX $DD0D ; enable timer interrupt + STA $DD0E ; start timer + + LDA #$00 ; To see that the interrupts really occur, + STA $D011 ; use something like this and see how + LOOP DEC $D020 ; changing the byte loaded to $DD05 from + BNE LOOP ; #$FF to #$0F changes the image. + + When an NMI occurs, the processor jumps to Kernal code, which jumps to + ($0318), which points to the following routine: + + DD09 LSR $40 ; clear N flag + BPL $DD0A ; Note: $DD0A contains RTI. + + Operational diagram of BPL $DD0A: + + # data address R/W description + --- ---- ------- --- --------------------------------- + 1 10 $DD0B R fetch opcode + 2 11 $DD0C R fetch argument + 3 xx $DD0D R fetch opcode, add argument to PCL + 4 40 $DD0A R fetch opcode, (fix PCH) + + With RTI: + + ; the fastest possible interrupt handler in the 6500 family + ; preparations + SEI + LDA $01 ; disable ROM and enable I/O + AND #$FD + ORA #$05 + STA $01 + LDA #$7F + STA $DD0D ; disable CIA 2's all interrupt sources + LDA $DD0E + AND #$BE ; ensure that $DD0C remains constant + STA $DD0E ; and stop the timer + LDA #$40 + STA $DD0C ; store RTI to $DD0C + LDA #$0C + STA $FFFA + LDA #$DD + STA $FFFB ; change NMI vector to $DD0C + LDA #$FF ; Try changing this instruction's operand + STA $DD05 ; (see comment below). + LDA #$FF + STA $DD04 ; set interrupt frequency to 1/65536 cycles + LDA $DD0E + AND #$80 + ORA #$11 + LDX #$81 + STX $DD0D ; enable timer interrupt + STA $DD0E ; start timer + + LDA #$00 ; To see that the interrupts really occur, + STA $D011 ; use something like this and see how + LOOP DEC $D020 ; changing the byte loaded to $DD05 from + BNE LOOP ; #$FF to #$0F changes the image. + + When an NMI occurs, the processor jumps to Kernal code, which + jumps to ($0318), which points to the following routine: + + DD0C RTI + + How on earth can this clear the interrupts? Remember, the + processor always fetches two successive bytes for each + instruction. + + A little more practical version of this is redirecting the NMI + (or IRQ) to your own routine, whose last instruction is JMP + $DD0C or JMP $DC0C. If you want to confuse more, change the 0 + in the address to a hexadecimal digit different from the one + you used when writing the RTI. + + Or you can combine the latter two methods: + + DD09 LSR $xx ; xx is any appropriate BCD value 00-59. + BPL $DCFC + DCFC RTI + + This example acknowledges interrupts to both CIAs. + + If you want to confuse the examiners of your code, you can use any +of these techniques. Although these examples use no undefined opcodes, +they do not necessarily run correctly on CMOS processors. However, the +RTI example should run on 65C02 and 65C816, and the latter branch +instruction example might work as well. + + The RMW instruction method has been used in some demos, others were +developed by Marko M"akel"a. His favourite is the automagical RTI +method, although it does not have any practical applications, except +for some time dependent data decryption routines for very complicated +copy protections. + + diff --git a/Documentation/tech/cpu/NESSOUND.txt b/Documentation/tech/cpu/NESSOUND.txt new file mode 100644 index 0000000..bb6d059 --- /dev/null +++ b/Documentation/tech/cpu/NESSOUND.txt @@ -0,0 +1,697 @@ +The NES sound channel guide 1.8 +Written by Brad Taylor. +btmine@hotmail.com + +Last updated: July 27th, 2000. + +All results were obtained by studying prior information available (from +nestech 1.00, and postings on NESDev from miscellanious people), and through +a series of experiments conducted by me. Results acquired by individuals +prior to my reverse-engineering have been double checked, and final results +have been confirmed. Credit is due to those individual(s) who contributed +any information in regards to the the miscellanious sound channels wihtin +the NES. + +A special thanks goes out to Matthew Conte, for his expertise on +pseudo-random number generation (amoung other things), which allowed for the +full reverse engineering of the NES's noise channel to take place. Without +his help, I would still be trying to find a needle in a haystack, as far as +the noise's method of pseudo-random number generation goes. Additionally, +his previous findings / reverse engineering work on the NES's sound hardware +really got the ball of NES sound emulation rolling. If it weren't for Matt's +original work, this document wouldn't exist. + +Thanks to Kentaro Ishihara, for his excellent work on finding the difference +in upward frequency sweep between the 2 square wave channels. + +**************** +* Introduction * +**************** + +The 2A03 (NES's integrated CPU) has 4 internal channels to it that have the +ability to generate semi-analog sound, for musical playback purposes. These +channels are 2 square wave channels, one triangle wave channel, and a noise +generation channel. This document will go into full detail on every aspect +of the operation and timing of the mentioned sound channels. + + +******************* +* Channel details * +******************* + +Each channel has different characteristics to it that make up it's +operation. + +The square channel(s) have the ability to generate a square wave frequency +in the range of 54.6 Hz to 12.4 KHz. It's key features are frequency sweep +abilities, and output duty cycle adjustment. + +The triangle wave channel has the ability to generate an output triangle +wave with a resolution of 4-bits (16 steps), in the range of 27.3 Hz to 55.9 +KHz. The key features this channel has is it's analog triangle wave output, +and it's linear counter, which can be set to automatically disable the +channel's sound after a certain period of time has gone by. + +The noise channel is used for producing random frequencys, which results in +a "noisey" sounding output. Output frequencys can range anywhere from 29.3 +Hz to 447 KHz. It's key feature is it's pseudo- random number generator, +which generates the random output frequencys heard by the channel. + + +***************** +* Frame counter * +***************** + +The 2A03 has an internal frame counter. It has the ability to generate 60 Hz +(1/1 framerate), 120 Hz (1/2 framerate), and 240 Hz (1/4 framerate) signals, +used by some of the sound hardware. The 1/4 framerate is calculated by +taking twice the CPU clock speed (3579545.454545 Hz), and dividing it by +14915 (i.e., the divide-by-14915 counter is decremented on the rising AND +falling edge of the CPU's clock signal). + + +************************ +* Sound hardware delay * +************************ + +After resetting the 2A03, the first time any sound channel(s) length counter +contains a non-zero value (channel is enabled), there will be a 2048 CPU +clock cycle delay before any of the sound hardware is clocked. After the 2K +clock cycles go by, the NES sound hardware will be clocked normally. This +phenomenon only occurs prior to a system reset, and only occurs during the +first 2048 CPU clocks for any sound channel prior to a sound channel being +enabled. + +The information in regards to this delay is only provided to keep this +entire document persistently accurate on the 2A03's sound hardware, but may +not be 100% accurate in itself. I haven't done much tests on the behaviour +of this delay (mainly because I don't care, as I view it as a inconvenience +anyway), so that's why I believe there could be some inaccuracies. + + +************************ +* Register Assignments * +************************ + +The sound hardware internal to the 2A03 has been designated these special +memory addresses in the CPU's memory map. + +$4000-$4003 Square wave 1 +$4004-$4007 Square wave 2 (identical to the first, except for upward +frequency sweeps (see "sweep unit" section)) +$4008-$400B Triangle +$400C-$400F Noise +$4015 Channel enable / length counter status + +Note that $4015 is the only R/W register. All others are write only (attempt +to read them will most likely result in a returned 040H, due to heavy +capacitance on the NES's data bus). Reading a "write only" register, will +have no effect on the specific register, or channel. + +Every sound channel has 4 registers affiliated with it. The description of +the register sets are as follows: + ++----------------+ +| Register set 1 | ++----------------+ + +$4000(sq1)/$4004(sq2)/$400C(noise) bits +--------------------------------------- +0-3 volume / envelope decay rate +4 envelope decay disable +5 length counter clock disable / envelope decay looping enable +6-7 duty cycle type (unused on noise channel) + +$4008(tri) bits +--------------- +0-6 linear counter load register +7 length counter clock disable / linear counter start + + ++----------------+ +| Register set 2 | ++----------------+ + +$4001(sq1)/$4005(sq2) bits +-------------------------- +0-2 right shift amount +3 decrease / increase (1/0) wavelength +4-6 sweep update rate +7 sweep enable + +$4009(tri)/$400D(noise) bits +---------------------------- +0-7 unused + + ++----------------+ +| Register set 3 | ++----------------+ + +$4002(sq1)/$4006(sq2)/$400A(Tri) bits +------------------------------------- +0-7 8 LSB of wavelength + +$400E(noise) bits +----------------- +0-3 playback sample rate +4-6 unused +7 random number type generation + + ++----------------+ +| Register set 4 | ++----------------+ + +$4003(sq1)/$4007(sq2)/$400B(tri)/$400F(noise) bits +-------------------------------------------------- +0-2 3 MS bits of wavelength (unused on noise channel) +3-7 length counter load register + + ++--------------------------------+ +| length counter status register | ++--------------------------------+ + +$4015(read) +----------- +0 square wave channel 1 +1 square wave channel 2 +2 triangle wave channel +3 noise channel +4 DMC (see "DMC.TXT" for details) +5-6 unused +7 IRQ status of DMC (see "DMC.TXT" for details) + + ++-------------------------+ +| channel enable register | ++-------------------------+ + +$4015(write) +------------ +0 square wave channel 1 +1 square wave channel 2 +2 triangle wave channel +3 noise channel +4 DMC channel (see "DMC.TXT" for details) +5-7 unused + + +************************ +* Channel architecture * +************************ + +This section will describe the internal components making up each individual +channel. Each component will then be described in full detail. + +Device Triangle Noise Square +------ -------- ------ ------ +triangle step generator X +linear counter X +programmable timer X X X +length counter X X X +4-bit DAC X X X +volume/envelope decay unit X X +sweep unit X +duty cycle generator X +wavelength converter X +random number generator X + + ++-------------------------+ +| Triangle step generator | ++-------------------------+ + +This is a 5-bit, single direction counter, and it is only used in the +triangle channel. Each of the 4 LSB outputs of the counter lead to one input +on a corresponding mutually exclusive XNOR gate. The 4 XNOR gates have been +strobed together, which results in the inverted representation of the 4 LSB +of the counter appearing on the outputs of the gates when the strobe is 0, +and a non-inverting action taking place when the strobe is 1. The strobe is +naturally connected to the MSB of the counter, which effectively produces on +the output of the XNOR gates a count sequence which reflects the scenario of +a near- ideal triangle step generator (D,E,F,F,E,D,...,2,1,0,0,1,2,...). At +this point, the outputs of the XNOR gates will be fed into the input of a +4-bit DAC. + +This 5-bit counter will be halted whenever the Triangle channel's length or +linear counter contains a count of 0. This results in a "latching" +behaviour; the counter will NOT be reset to any definite state. + +On system reset, this counter is loaded with 0. + +The counter's clock input is connected directly to the terminal count output +pin of the 11-bit programmable timer in the triangle channel. As a result of +the 5-bit triangle step generator, the output triangle wave frequency will +be 32 times less than the frequency of the triangle channel's programmable +timer is set to generate. + + ++----------------+ +| Linear counter | ++----------------+ + +The linear counter is only found in the triangle channel. It is a 7-bit +presettable down counter, with a decoded output condition of 0 available +(not exactly the same as terminal count). Here's the bit assignments: + +$4008 bits +---------- +0-6 bits 0-6 of the linear counter load register (NOT the linear counter +itself) +7 linear counter start + +The counter is clocked at 240 Hz (1/4 framerate), and the calculated length +in frames is 0.25*N, where N is the 7-bit loaded value. The counter is +always being clocked, except when 0 appears on the output of the counter. At +this point, the linear counter & triangle step counter clocks signals are +disabled, which results in both counters latching their current state (the +linear counter will stay at 0, and the triangle step counter will stop, and +the channel will be silenced due to this). + +The linear counter has 2 modes: load, and count. When the linear counter is +in load mode, it essentially becomes transparent (i.e. whatever value is +currently in, or being written to $4008, will appear on the output of the +counter). Because of this, no count action can occur in load mode. When the +mode changes from load to count, the counter will now latch the value +currently in it, and start counting down from there. In the count mode, the +current value of $4008 is ignored by the counter (but still retained in +$4008). Described below is how the mode of the linear counter is set: + +Writes to $400B +--------------- +cur mode +--- ---- +1 load +0 load (during the write cycle), count + +Cur is the current state of the MSB of $4008. + +Writes to $4008 +--------------- +old new mode +--- --- ---- +0 X count +1 0 no change (during the write cycle), count +1 1 no change + +Old and new represent the state(s) of the MSB of $4008. Old is the value +being replaced in the MSB of $4008 on the write, and new is the value +replacing the old one. + +"no change" indicates that the mode of the linear counter will not change +from the last. + + ++--------------------+ +| Programmable timer | ++--------------------+ + +The programmable timer is a 11-bit presettable down counter, and is found in +the square, triangle, and noise channel(s). The bit assignments are as +follows: + +$4002(sq1)/$4006(sq2)/$400A(Tri) bits +------------------------------------- +0-7 represent bits 0-7 of the 11-bit wavelength + +$4003(sq1)/$4007(sq2)/$400B(Tri) bits +------------------------------------- +0-2 represent bits 8-A of the 11-bit wavelength + +Note that on the noise channel, the 11 bits are not available directly. See +the wavelength converter section, for more details. + +The counter has automatic syncronous reloading upon terminal count +(count=0), therefore the counter will count for N+1 (N is the 11-bit loaded +value) clock cycles before arriving at terminal count, and reloading. This +counter will typically be clocked at the 2A03's internal 6502 speed (1.79 +MHz), and produces an output frequency of 1.79 MHz/(N+1). The terminal +count's output spike length is typically no longer than half a CPU clock. +The TC signal will then be fed to the appropriate device for the particular +sound channel (for square, this terminal count spike will lead to the duty +cycle generator. For the triangle, the spike will be fed to the triangle +step generator. For noise, this signal will go to the random number +generator unit). + + ++----------------+ +| Length counter | ++----------------+ + +The length counter is found in all sound channels. It is essentially a 7-bit +down counter, and is conditionally clocked at a frequency of 60 Hz. + +When the length counter arrives at a count of 0, the counter will be stopped +(stay on 0), and the appropriate channel will be silenced. + +The length counter clock disable bit, found in all the channels, can also be +used to halt the count sequence of the length counter for the appropriate +channel, by writing a 1 out to it. A 0 condition will permit counting +(unless of course, the counter's current count = 0). Location(s) of the +length counter clock disable bit: + +$4000(sq1)/$4004(sq2)/$400C(noise) bits +--------------------------------------- +5 length counter clock disable + +$4008(tri) bits +--------------- +7 length counter clock disable + +To load the length counter with a specified count, a write must be made out +to the length register. Location(s) of the length register: + +$4003(sq1)/$4007(sq2)/$400B(tri)/$400F(noise) bits +-------------------------------------------------- +3-7 length + +The 5-bit length value written, determines what 7-bit value the length +counter will start counting from. A conversion table here will show how the +values are translated. + + +-----------------------+ + | bit3=0 | + +-------+---------------+ + | |frames | + |bits +-------+-------+ + |4-6 |bit7=0 |bit7=1 | + +-------+-------+-------+ + |0 |05 |06 | + |1 |0A |0C | + |2 |14 |18 | + |3 |28 |30 | + |4 |50 |60 | + |5 |1E |24 | + |6 |07 |08 | + |7 |0E |10 | + +-------+-------+-------+ + + +---------------+ + | bit3=1 | + +-------+-------+ + |bits | | + |4-7 |frames | + +-------+-------+ + |0 |7F | + |1 |01 | + |2 |02 | + |3 |03 | + |4 |04 | + |5 |05 | + |6 |06 | + |7 |07 | + |8 |08 | + |9 |09 | + |A |0A | + |B |0B | + |C |0C | + |D |0D | + |E |0E | + |F |0F | + +-------+-------+ + +The length counter's real-time status for each channel can be attained. A 0 +is returned for a zero count status in the length counter (channel's sound +is disabled), and 1 for a non-zero status. Here's the bit description of the +length counter status register: + +$4015(read) +----------- +0 length counter status of square wave channel 1 +1 length counter status of square wave channel 2 +2 length counter status of triangle wave channel +3 length counter status of noise channel +4 length counter status of DMC (see "DMC.TXT" for details) +5-6 unused +7 IRQ status of DMC (see "DMC.TXT" for details) + +Writing a 0 to the channel enable register will force the length counters to +always contain a count equal to 0, which renders that specific channel +disabled (as if it doesn't exist). Writing a 1 to the channel enable +register disables the forced length counter value of 0, but will not change +the count itself (it will still be whatever it was prior to the writing of +1). + +Bit description of the channel enable register: + +$4015(write) +------------ +0 enable square wave channel 1 +1 enable square wave channel 2 +2 enable triangle wave channel +3 enable noise channel +4 enable DMC channel (see "DMC.TXT" for details) +5-7 unused + +Note that all 5 used bits in this register will be set to 0 upon system +reset. + + ++-----------+ +| 4-bit DAC | ++-----------+ + +This is just a standard 4-bit DAC with 16 steps of output voltage +resolution, and is used by all 4 sound channels. + +On the 2A03, square wave 1 & 2 are mixed together, and are available via pin +1. Triangle & noise are available on pin 2. These analog outputs require a +negative current source, to attain linear symmetry on the various output +voltage levels generated by the channel(s) (moreover, to get the sound to be +audible). Since the NES just uses external 100 ohm pull-down resistors, this +results in the output waveforms being of very small amplitude, but with +minimal linearity asymmetry. + + ++------------------------------+ +| Volume / envelope decay unit | ++------------------------------+ + +The volume / envelope decay hardware is found only in the square wave and +noise channels. + +$4000(sq1)/$4004(sq2)/$400C(noise) +---------------------------------- +0-3 volume / envelope decay rate +4 envelope decay disable +5 envelope decay looping enable + +When the envelope decay disable bit (bit 4) is set (1), the current volume +value (bits 0-3) is sent directly to the channel's DAC. However, depending +on certain conditions, this 4-bit volume value will be ignored, and a value +of 0 will be sent to the DAC instead. This means that while the channel is +enabled (producing sound), the output of the channel (what you'll hear from +the DAC) will either be the 4-bit volume value, or 0. This also means that a +4-bit volume value of 0 will result in no audible sound. These conditions +are as follows: + +- When hardware in the channel wants to disable it's sound output (like the +length counter, or sweep unit (square channels only)). + +- On the negative portion of the output frequency signal coming from the +duty cycle / random number generator hardware (square wave channel / noise +channel). + +When the envelope decay disable bit is cleared, bits 0-3 now control the +envelope decay rate, and an internal 4-bit down counter (hereon the envelope +decay counter) now controls the channel's volume level. "Envelope decay" is +used to describe the action of the channel's audio output volume starting +from a certain value, and decreasing by 1 at a fixed (linear) rate (which +produces a "fade-out" sounding effect). This fixed decrement rate is +controlled by the envelope decay rate (bits 0-3). The calculated decrement +rate is 240Hz/(N+1), where N is any value between $0-$F. + +When the channel's envelope decay counter reaches a value of 0, depending on +the status of the envelope decay looping enable bit (bit 5, which is shared +with the length counter's clock disable bit), 2 different things will +happen: + +bit 5 action +----- ------ +0 The envelope decay count will stay at 0 (channel silenced). +1 The envelope decay count will wrap-around to $F (upon the next clock +cycle). The envelope decay counter will then continue to count down +normally. + +Only a write out to $4003/$4007/$400F will reset the current envelope decay +counter to a known state (to $F, the maximum volume level) for the +appropriate channel's envelope decay hardware. Otherwise, the envelope decay +counter is always counting down (by 1) at the frequency currently contained +in the volume / envelope decay rate bits (even when envelope decays are +disabled (setting bit 4)), except when the envelope decay counter contains a +value of 0, and envelope decay looping (bit 5) is disabled (0). + + ++------------+ +| Sweep unit | ++------------+ + +The sweep unit is only found in the square wave channels. The controls for +the sweep unit have been mapped in at $4001 for square 1, and $4005 for +square 2. + +The controls +------------ +Bit 7 when this bit is set (1), sweeping is active. This results in +real-time increasing or decreasing of the the current wavelength value (the +audible frequency will decrease or increase, respectively). The wavelength +value in $4002/3 ($4006/7) is constantly read & updated by the sweep. +Modifying the contents of $4002/3 will be immediately audible, and will +result in the sweep now starting from this new wavelength value. + +Bits 6-4 These 3 bits represent the sweep refresh rate, or the frequency at +which $4002/3 is updated with the new calculated wavelength. The refresh +rate frequency is 120Hz/(N+1), where N is the value written, between 0 and +7. + +Bit 3 This bit controls the sweep mode. When this bit is set (1), sweeps +will decrease the current wavelength value, as a 0 will increase the current +wavelength. + +Bits 2-0 These bits control the right shift amount of the new calculated +sweep update wavelength. Code that shows how the sweep unit calculates a new +sweep wavelength is as follows: + +bit 3 +----- +0 New = Wavelength + (Wavelength >> N) +1 New = Wavelength - (Wavelength >> N) (minus an additional 1, if using +square wave channel 1) + +where N is the the shift right value, between 0-7. + +Note that in decrease mode, for subtracting the 2 values: +1's compliment (NOT) is being used for square wave channel 1 +2's compliment (NEG) is being used for square wave channel 2 + +This information is currently the only known difference between the 2 square +wave channels. + +On each sweep refresh clock, the Wavelength register will be updated with +the New value, but only if all 3 of these conditions are met: + +- bit 7 is set (sweeping enabled) +- the shift value (which is N in the formula) does not equal to 0 +- the channel's length counter contains a non-zero value + +Notes +----- +There are certain conditions that will cause the sweep unit to silence the +channel, and halt the sweep refresh clock (which effectively stops sweep +action, if any). Note that these conditions pertain regardless of any sweep +refresh rate values, or if sweeping is enabled/disabled (via bit 7). + +- an 11-bit wavelength value less than $008 will cause this condition +- if the sweep unit is currently set to increase mode, the New calculated +wavelength value will always be tested to see if a carry (bit $B) was +generated or not (if sweeping is enabled, this carry will be examined before +the Wavelength register is updated) from the shift addition calculation. If +carry equals 1, the channel is silenced, and sweep action is halted. + + ++----------------------+ +| Duty cycle generator | ++----------------------+ + +The duty cycle generator takes the fequency produced from the 11-bit +programmable timer, and uses a 4 bit counter to produce 4 types of duty +cycles. The output frequency is then 1/16 that of the programmable timer. +The duty cycle hardware is only found in the square wave channels. The bit +assignments are as follows: + +$4000(sq1)/$4004(sq2) +--------------------- +6-7 Duty cycle type + + duty (positive/negative) +val in clock cycles +--- --------------- +00 2/14 +01 4/12 +10 8/ 8 +11 12/ 4 + +Where val represents bits 6-7 of $4000/$4004. + +The output frequency at this point will now be fed to the volume/envelope +decay hardware. + + ++----------------------+ +| Wavelength converter | ++----------------------+ + +The wavelength converter is only used in the noise channel. It is used to +convert a given 4-bit value to an 11-bit wavelength, which then is sent to +the noise's own programmable timer. Here is the bit descriptions: + +$400E bits +---------- +0-3 The 4-bit value to be converted + +Below is a conversion chart that shows what 4-bit value will represent the +11-bit wavelength to be fed to the channel's programmable timer: + +value octave scale CPU clock cycles (11-bit wavelength+1) +----- ------ ----- -------------------------------------- +0 15 A 002 +1 14 A 004 +2 13 A 008 +3 12 A 010 +4 11 A 020 +5 11 D 030 +6 10 A 040 +7 10 F 050 +8 10 C 065 +9 9 A 07F +A 9 D 0BE +B 8 A 0FE +C 8 D 17D +D 7 A 1FC +E 6 A 3F9 +F 5 A 7F2 + +Octave and scale information is provided for the music enthusiast programmer +who is more familiar with notes than clock cycles. + + ++-------------------------+ +| Random number generator | ++-------------------------+ + +The noise channel has a 1-bit pseudo-random number generator. It's based on +a 15-bit shift register, and an exclusive or gate. The generator can produce +two types of random number sequences: long, and short. The long sequence +generates 32,767-bit long number patterns. The short sequence generates +93-bit long number patterns. The 93-bit mode will generally produce higher +sounding playback frequencys on the channel. Here is the bit that controls +the mode: + +$400E bits +---------- +7 mode + +If mode=0, then 32,767-bit long number sequences will be produced (32K +mode), otherwise 93-bit long number sequences will be produced (93-bit +mode). + +The following diagram shows where the XOR taps are taken off the shift +register to produce the 1-bit pseudo-random number sequences for each mode. + +mode <----- +---- EDCBA9876543210 +32K ** +93-bit * * + +The current result of the XOR will be transferred into bit position 0 of the +SR, upon the next shift cycle. The 1-bit random number output is taken from +pin E, is inverted, then is sent to the volume/envelope decay hardware for +the noise channel. The shift register is shifted upon recieving 2 clock +pulses from the programmable timer (the shift frequency will be half that of +the frequency from the programmable timer (one octave lower)). + +On system reset, this shift register is loaded with a value of 1. + + diff --git a/Documentation/tech/cpu/dmc.txt b/Documentation/tech/cpu/dmc.txt new file mode 100644 index 0000000..c33f4de --- /dev/null +++ b/Documentation/tech/cpu/dmc.txt @@ -0,0 +1,235 @@ +Delta modulation channel tutorial 1.0 +Written by Brad Taylor + +Last updated: August 20th, 2000. + +All results were obtained by studying prior information available (from +nestech 1.00, and postings on NESDev from miscellanious people), and through +a series of experiments conducted by me. Results aquired by individuals +prior to my reverse-engineering have been double checked, and final results +have been confirmed. Credit is due to those individual(s) who contributed +any information in regards to the DMC. + +Description +----------- + +The delta modulation channel (DMC) is a complex digital network of counters +and registers used to produce analog sound. It's primary function is to play +"samples" from memory, and have an internal counter connected to a digital +to analog converter (DAC) updated accordingly. The channel is able to be +assigned a pointer to a chunk of memory to be played. At timed intervals, +the DMC will halt the 2A03 (NES's CPU) for 1 clock cycle to retrieve the +sample to pe played. This method of playback will be refered to here on as +direct memory access (DMA). Another method of playback known as pulse code +modulation (PCM) is available by the channel, which requires the constant +updating of one of the DMC's memory-mapped registers. + +Registers +--------- + +The DMC has 5 registers assigned to it. They are as follows: + +$4010: play mode and DMA frequency +$4011: delta counter +$4012: play code's starting address +$4013: length of play code +$4015: DMC/IRQ status + +Note that $4015 is the only R/W register. All others are write only (attempt +to read them will most likely result in a returned 040H, due to heavy +capacitance on the NES's data bus). + +$4010 - Play mode and DMA frequency +----------------------------------- +This register is used to control the frequency of the DMA fetches, and to +control the playback mode. + +Bits +---- +6-7 this is the playback mode. + + 00 - play DMC sample until length counter reaches 0 (see $4013) + x1 - loop the DMC sample (x = immaterial) + 10 - play DMC sample until length counter reaches 0, then generate a CPU +IRQ + +Looping (playback mode "x1") will have the chunk of memory played over and +over, until the channel is disabled (via $4015). In this case, after the +length counter reaches 0, it will be reloaded with the calculated length +value of $4013. + +If playback mode "10" is chosen, an interrupt will be dispached when the +length counter reaches 0 (after the sample is done playing). There are 2 +ways to acknowledge the DMC's interrupt request upon recieving it. The first +is a write to this register ($4010), with the MSB (bit 7) cleared (0). The +second is any write to $4015 (see the $4015 register description for more +details). + +If playback mode "00" is chosen, the sample plays until the length counter +reaches 0. No interrupt is generated. + +5-4 appear to be unused + +3-0 this is the DMC frequency control. Valid values are from 0 - F. The +value of this register determines how many CPU clocks to wait before the DMA +will fetch another byte from memory. The # of clocks to wait -1 is initially +loaded into an internal 12-bit down counter. The down counter is then +decremented at the frequency of the CPU (1.79MHz). The channel fetches the +next DMC sample byte when the count reaches 0, and then reloads the count. +This process repeats until the channel is disabled by $4015, or when the +length counter has reached 0 (if not in the looping playback mode). The +exact number of CPU clock cycles is as follows: + +value CPU +written clocks octave scale +------- ------ ------ ----- +F 1B0 8 C +E 240 7 G +D 2A0 7 E +C 350 7 C +B 400 6 A +A 470 6 G +9 500 6 F +8 5F0 6 D +7 6B0 6 C +6 710 5 B +5 7F0 5 A +4 8F0 5 G +3 A00 5 F +2 AA0 5 E +1 BE0 5 D +0 D60 5 C + +The octave and scale values shown represent the DMC DMA clock cycle rate +equivelant. These values are merely shown for the music enthusiast +programmer, who is more familiar with notes than clock cycles. + +Every fetched byte is loaded into a internal 8-bit shift register. The shift +register is then clocked at 8x the DMA frequency (which means that the CPU +clock count would be 1/8th that of the DMA clock count), or shifted at +3 +the octave of the DMA (same scale). The data shifted out of the register is +in serial form, and the least significant bit (LSB, or bit 0) of the fetched +byte is the first one to be shifted out (then bit 1, bit 2, etc.). + +The bits shifted out are then fed to the UP/DOWN control pin of the internal +delta counter, which will effectively have the counter increment it's +retained value by one on "1" bit samples, and decrement it's value by one on +"0" bit samples. This counter is clocked at the same frequency of the shift +register's. + +The counter is only 6 bits in size, and has it's 6 outputs tied to the 6 MSB +inputs of a 7 bit DAC. The analog output of the DAC is then what you hear +being played by the DMC. + +Wrap around counting is not allowed on this counter. Instead, a "clipping" +behaviour is exhibited. If the internal value of the counter has reached 0, +and the next bit sample is a 0 (instructing a decrement), the counter will +take no action. Likewise, if the counter's value is currently at -1 +(111111B, or 03FH), and the bit sample to be played is a 1, the counter will +not increment. + + +$4011 - Delta counter load register +----------------------------------- + +bits +---- +7 appears to be unused +1-6 the load inputs of the internal delta counter +0 LSB of the DAC + +A write to this register effectively loads the internal delta counter with a +6 bit value, but can be used for 7 bit PCM playback. Bit 0 is connected +directly to the LSB (bit 0) of the DAC, and has no effect on the internal +delta counter. Bit 7 appears to be unused. + +This register can be used to output direct 7-bit digital PCM data to the +DMC's audio output. To use this register for PCM playback, the programmer +would be responsible for making sure that this register is updated at a +constant rate. The rate is completely user-definable. For the regular CD +quality 44100 Hz playback sample rate, this register would have to be +written to approximately every 40 CPU cycles (assuming the 2A03 is running @ +1.79 MHz). + + +$4012 - DMA address load register +---------------------------- + +This register contains the initial address where the DMC is to fetch samples +from memory for playback. The effective address value is $4012 shl 6 or +0C000H. This register is connected to the load pins of the internal DMA +address pointer register (counter). The counter is incremented after every +DMA byte fetch. The counter is 15 bits in size, and has addresses wrap +around from $FFFF to $8000 (not $C000, as you might have guessed). The DMA +address pointer register is reloaded with the initial calculated address, +when the DMC is activated from an inactive state, or when the length counter +has arrived at terminal count (count=0), if in the looping playback mode. + + +$4013 - DMA length register +--------------------------- + +This register contains the length of the chunk of memory to be played by the +DMC, and it's size is measured in bytes. The value of $4013 shl 4 is loaded +into a 12 bit internal down counter, dubbed the length counter. The length +counter is decremented after every DMA fetch, and when it arrives at 0, the +DMC will take action(s) based on the 2 MSB of $4010. This counter will be +loaded with the current calculated address value of $4013 when the DMC is +activated from an inactive state. Because the value that is loaded by the +length counter is $4013 shl 4, this effectively produces a calculated byte +sample length of $4013 shl 4 + 1 (i.e. if $4013=0, sample length is 1 byte +long; if $4013=FF, sample length is $FF1 bytes long). + + +$4015 - DMC status +------------------ + +This contains the current status of the DMC channel. There are 2 read bits, +and 1 write bit. + +bits +---- +7(R) DMC's IRQ status (1=CPU IRQ being caused by DMC) +4(R) DMC is currently enabled (playing a stream of samples) +4(W) enable/disable DMC (1=start/continue playing a sample;0=stop playing) + +When an IRQ goes off inside the 2A03, Bit 7 of $4015 can tell the interrupt +handler if it was caused by the DMC hardware or not. This bit will be set +(1) if the DMC is responsible for the IRQ. Of course, if your program has no +other IRQ-generating hardware going while it's using the DMC, then reading +this register is not neccessary upon IRQ generation. Note that reading this +register will NOT clear bit 7 (meaning that the DMC's IRQ will still NOT be +acknowledged). Also note that if the 2 MSB of $4010 were set to 10, no IRQ +will be generated, and bit 7 will always be 0. + +Upon generation of a IRQ, to let the DMC know that the software has +acknowledged the /IRQ (and to reset the DMC's internal IRQ flag), any write +out to $4015 will reset the flag, or a write out to $4010 with the MSB set +to 0 will do. These practices should be performed inside the IRQ handler +routine. To replay the same sample that just finished, all you need to do is +just write a 1 out to bit 4 of $4015. + +Bit 4 of $4015 reports the real-time status of the DMC. A returned value of +1 denotes that the channel is currently playing a stream of samples. A +returned value of 0 indicates that the channel is inactive. If the +programmer needed to know when a stream of samples was finished playing, but +didn't want to use the IRQ generation feature of the DMC, then polling this +bit would be a valid option. + +Writing a value to $4015's 4th bit has the effect of enabling the channel +(start, or continue playing a stream of samples), or disabling the channel +(stop all DMC activity). Note that writing a 1 to this bit while the channel +is currently enabled, will have no effect on counters or registers internal +to the DMC. + +The conditions that control the time the DMC will stay enabled are +determined by the 2 MSB of $4010, and register $4013 (if applicable). + + +System Reset +------------ + +On system reset, all 7 used bits of $4011 are reset to 0, the IRQ flag is +cleared (disabled), and the channel is disabled. All other registers will +remain unmodified. + diff --git a/Documentation/tech/exp/mmc5-e.txt b/Documentation/tech/exp/mmc5-e.txt new file mode 100644 index 0000000..eab191a --- /dev/null +++ b/Documentation/tech/exp/mmc5-e.txt @@ -0,0 +1,250 @@ +========= mmc5 infomation ========== +date 1998/05/31 +by goroh +translated May 31, 1998 by Sgt. Bowhack +mail goroh_kun@geocities.co.jp + +5000,5004 ch1,ch2 Pulse Control + bit CCwevvvv + CC Duty Cycle (Positive vs. Negative) + #0:87.5% #1:75.0% #2:50.0% #3:25.0% + w Waveform Hold (e.g. Looping) + 0: Off 1: On + e Envelope Select + 0: Varied 1: Fixed + < e=0 > + vvvv Playback Rate + #0<-fast<--->-slow--> #15 + < e=1 > + vvvv Output Volume + +5002,5006 ch1,ch2 frequency L + bit ffffffff +5003,5007 ch1,ch2 frequency H + bit tttttfff + ttttt sound occurence time + +Objective is to remove the continuous changing of frequency for +square wave setup and do the same to the main part of the square wave +of studying the main part of the famicom. (?- Sgt. Bowhack) + +5010 ch3 synthetic voice business channel + bit -------O + O wave output 0:Off 1:On + +5011 ch4 synthetic voice business channel 2 + bit vvvvvvvv + vvvvvvvv wave size + +5015 sound output channel + bit ------BA + A: ch1 output 1:enable 0:disable + B: ch2 output 1:enable 0:disable + +5100 PRG-page size Setting + bit ------SS + SS PRG-page size + 0: 32k 1:16k 2,3:8k +* Reset is misled the first times for about 8k (?- SB) + +5101 CHR-page size Setting + bit ------SS + SS CHR-page size + 0:8k 1:4k 2:2k 3:1k + +5102 W BBR-RAM Write Protect 1 + bit ------AA +5103 W BBR-RAM Write Protect 2 + bit ------BB + (AA,BB) = (2,1) permitted to write to BBR-RAM only when crowded +*Reset write around becomes prohibited when crowded + +5104 Grafix Mode Setting + $5c00-$5fff decides how it should be used + bit ------MM + #00:Enable only Split Mode + #01:Enable Split Mode & ExGrafix Mode + #02:ExRAM Mode + #03:ExRAM Mode & Write Protect + +Consideration + MMC5 has 2 graphic mode extensions that allow more than 256 characters +on one standard game screen. It uses Split Mode so it can display the +specified CHR-page and scroll position seperate from ExGrafix Mode to +be able to choose a palette, and the other divides it vertically. + +5105 W NameTable Setting + bit ddccbbaa + aa: Select VRAM at 0x2000-0x23ff + bb: Select VRAM at 0x2400-0x27ff + cc: Select VRAM at 0x2800-0x2bff + dd: Select VRAM at 0x2c00-0x2fff + #0:use VRAM 0x000-0x3ff + #1:use VRAM 0x400-0x7ff + #2:use ExVRAM 0x000-0x3ff + #3:use ExNameTable(Fill Mode) + +Consideration + The name table can designate 4 kinds of this resister and be a useful +special quality for this because painting and smashing it with a +character that there is 1 sheet for the remaining sheets can generally +be used. (?-SB) + +5106 W Fill Mode Setting 1 + bit vvvvvvvv + Fill chr-table + For whether it paints or smashes it at any non-designated character + +5107 W Fill Mode Setting 2 + bit ------pp + Whether or not it uses any non-designated palettes + +5113 RAM-page for $6000-$7FFF + bit -----p-- + +5114-5117 Program Bank switch + < page_size=32k > + $5117 [8]-[F] bit pppppp-- + + < page_size=16k > + $5115 [8]-[B] bit ppppppp- + $5117 [C]-[F] bit ppppppp- + + < page_size=8k > + $5114 [8][9] bit pppppppp + $5115 [A][B] bit pppppppp + $5116 [C][D] bit pppppppp + $5117* [E][F] bit pppppppp + +*Reset is around early, Last Page misled + +5120-512b Charactor Bank switch + < page_size=8k > + $5120-$5127 switch to mode A + $5128-$512b switch to mode B + $5127 [0]-[7] modeA + $512b [0]-[7] modeB + + < page_size=4k > + $5120-$5127 switch to mode A + $5128-$512b switch to mode B + $5123 [0]-[3] modeA + $5127 [4]-[7] modeA + $512b [0]-[3],[4]-[7] modeB + + < page_size=2k > + $5120-$5127 switch to mode A + $5128-$512b switch to mode B + $5121 [0]-[1] modeA + $5123 [2]-[3] modeA + $5125 [4]-[5] modeA + $5127 [6]-[7] modeA + $5129 [0]-[1],[4]-[5] modeB + $512b [2]-[3],[6]-[7] modeB + + < page_size=1k > + $5120-$5127 switch to mode A + $5128-$512b switch to mode B + $5120 [0] modeA + $5121 [1] modeA + $5122 [2] modeA + $5123 [3] modeA + $5124 [4] modeA + $5125 [5] modeA + $5126 [6] modeA + $5127 [7] modeA + $5128 [0],[4] modeB + $5129 [1],[5] modeB + $512a [2],[6] modeB + $512b [3],[7] modeB + +Consideration + MMC5 has mode A ,mode B and 2 kinds of CHR-page memory resistors. +They can be used for refreshing it. (?-SB) + +5130 ??? +analyzing it... + +5200 W Split Mode Control 1 + bit Ec-vvvvv + For the E function 0:don't use 1:use + c boundary's side is for using Split Mode extension of graphics + 0: left side 1: right side + vvvvv left boundary is designated with the char. # to count places + +Sample. + 5200 <- #00 + (not?) used yet + 5200 <- #82 + Used for SplitMode GFX extension from left 1-2 character + 5200 <- #c2 + Used for SplitMode GFX extension from the right side 3 chars. + 5200 <- #c0 + Used for SplitMode GFX extension on the whole screen + 5200 <- #d0 + Used for SplitMode GFX extension on the right side of the screen + 5200 <- #90 + Used for SplitMode GFX extension on the left side of the screen + +5201 W SplitMode setup for SplitMode Ext. GFX use 1 + $2005 determines the vertical movement; it can also delay ext. gfx's + vert. movement if necessary. It's written 2 times in bulk in the same + way as it would slip off a grade in $2005 (??-SB) + +5202 W SplitMode setup for SplitMode Ext. GFX use 2 + bit --pppppp + uses vertical division of ext. gfx CHR-page designation + index_size=4k(0x1000byte) +In case it uses a character 0x4000-0x4fff for the ext. gfx in question + $5202 <- 4 + +5203 W scanline break point + For scanline # that it splits and wants to make it designate it in bulk + +5204 WR IRQ enable/disable + W bit I------- + I 1:IRQ Enable 0:IRQ Disable + R bit I------- + I 1:Scanline Hit 0:Scanline not Hit + $5203 is designated as scanline when arrived. + +5205 WR mult input/output +5206 WR mult input/output +($5205in)*($5206in) = $5205,$5206out + +5c00-5fbf ext. gfx business VRAM + shows an attribute of every position character + + + bit PPpppppp + PP: use character palette number + pppppp: use background CHR-PAGE number index=4k + #0-#3F are designations, $0000-$3FFF is CHR-data's range + Use for extension gfx + + + SplitMode uses a Name Table for extension gfx use. + bit pppppppp + pppppppp: use for background char. number designation + + + Used for Extension RAM + +5fc0-5fff + + (not?) used yet + + + SplitMode uses gfx's Attribute Table extension. + PPU uses $23c0-$23ff in the same way as the Attribute Table + + + Used for Extension RAM + +Consideration + 5c00-5fff has 3 uses. + Split Mode and ExGrafix Mode's VBlank is written so as to become + crowded, it writes a 0 and becomes crowded. + Every mode tries to go around ExRAM mode including reading but it + writes it, is effective in bulk and #5c-#5f is the output at times + where it is effective. \ No newline at end of file diff --git a/Documentation/tech/exp/mmc5_bank_switch.txt b/Documentation/tech/exp/mmc5_bank_switch.txt new file mode 100644 index 0000000..b82f38c --- /dev/null +++ b/Documentation/tech/exp/mmc5_bank_switch.txt @@ -0,0 +1,128 @@ +MMC5 Bankswitching +by Kevin Horton +-------------------- + +5100: Controls paging of RAM and ROM + +Bits: ???? ??xx + +For xx: + +00: 32K bankswitching. Only 5117 can be used to control banks. 5114 thru + 5116 have no effect. + + +01: 16K bankswitching. Only 5115 and 5117 can be used to control banks. + 5114 and 5116 have no effect. + + +10: 8K/16K bankswitching. 5115-5117 are used. 5114 has no effect. + +11: 8K bankswitching. 5114-5117 are used. + +(See below for detailed description) + +-- + +5113: RAM page 6000-7FFF bank. Lower 3 bits are used, for a possible + 64K of WRAM. (Note more bits *may* be possible for more RAM. + This has not been confirmed yet). + +Bits: ???? ?xxx + +WRAM follows a certain convention, based on the style of MMC5 board used. +8K and 32K carts are usually implemented with a single chip, while 16K +carts use two 8K'ers. This is important since enabling changes, and hence +valid banks. + +for xxx: + + 8K 16K 32K 40K 64K + +0: bank 0 bank 0 bank 0 bank 0 bank 0 +1: bank 0 bank 0 bank 1 bank 1 bank 1 +2: bank 0 bank 0 bank 2 bank 2 bank 2 +3: bank 0 bank 0 bank 3 bank 3 bank 3 +4: open bus bank 1 open bus bank 4 bank 4 +5: open bus bank 1 open bus bank 4 bank 5 +6: open bus bank 1 open bus bank 4 bank 6 +7: open bus bank 1 open bus bank 4 bank 7 + +Note that the 40K and 64K examples are hypothetical. The first three, +however *are* real and is what you find inside a real MMC5 cart. + +Also note, that 5114-5116 follow this identical convention, if set up +to switch in RAM banks. + +-- + +Bankswitching is a bit complicated. This table should make things clearer. +The numbers at the top are what you write to 5100 to select mode. + +Legend: + +- = this has no effect +--- = this register is not used, and writes to it are ignored +R = PRG ROM/WRAM select. 0=WRAM, 1=PRG ROM +b = bank bits + + + +5100: 00 01 10 11 + +5114 --- --- --- Rbbb bbbb +5115 --- Rbbb bbb- Rbbb bbb- Rbbb bbbb +5116 --- --- Rbbb bbbb Rbbb bbbb +5117 -bbb bb-- -bbb bbb- -bbb bbbb -bbb bbbb + + +Mode 00 +------- + +Only one 32K page can be selected. The lower 2 bits of the desired bank +are ANDed out. writing 084h, 085h, 086h, and 087h to 5117 in this mode +all result in selection of the same 32K. No RAM is available in this mode. + +Mode 01 +------- + +There are two selectable 16K pages. Similar to above, the lowest bit written +is not used to select banks. In this mode, writing to 5115 selects 16K +at 8000-BFFF, and 5117 selects 16K at C000-FFFF. RAM can be enabled in this +mode for 8000-BFFF. If RAM is enabled for 8000-BFFF, remember that the +lowest bank select bit is not used. + +Mode 10 +------- + +This is the oddest one. There is one 16K selectable page, and two 8K +selectable pages. 5115 selects the 16K page at 8000-BFFF, 5116 selects +an 8K page at C000-DFFF, and 5117 selects an 8K page at E000-FFFF. +RAM can be enabled for 8000-DFFF. (16K of RAM at 8000-BFFF via bit 7 of +D115, and 8K of RAM at C000-DFFF via bit 7 of d116). Note that RAM banking +works the same as mode 01, above for the 16K bank. + + +Mode 11 +------- + +There are 4 8K selectable pages. 5114 controls 8000-9FFF, etc. all the way +up to 5117 that controls E000-FFFF. The first 3 pages can use RAM, while +the last page cannot. + + +-- + +WRAM write enable. + +5102, 5103 + +To enable writing to RAM, 5102 must have 02h written to it, and 5103 +must have 01h written to it. If this is not the case, you can still +*read* the RAM, but writes to it have no effect. Supposidly only the +lower two bits of 5102 and 5103 are checked, but I didn't verify this. +I *did* however verify that setting the two registers to the above +values allows writing. If voltage goes out of tolerance (Read: you +turn the power on/off) the RAM writing is disabled. (To prevent +corruption of saved-games during power cycling) + diff --git a/Documentation/tech/nsfspec.txt b/Documentation/tech/nsfspec.txt new file mode 100644 index 0000000..2ef526d --- /dev/null +++ b/Documentation/tech/nsfspec.txt @@ -0,0 +1,336 @@ + NES Music Format Spec + --------------------- + + +By: Kevin Horton khorton@iquest.net + + +NOTE: +----- + + +Remember that I am very willing to add stuff and update this spec. If +you find a new sound chip or other change let me know and I will get back +with you. E-mail to the above address. + + +V1.61 - 06/27/2000 Updated spec a bit +V1.60 - 06/01/2000 Updated Sunsoft, MMC5, and Namco chip information +V1.50 - 05/28/2000 Updated FDS, added Sunsoft and Namco chips +V1.32 - 11/27/1999 Added MMC5 register locations +V1.30 - 11/14/1999 Added MMC5 audio bit, added some register info +V1.20 - 09/12/1999 VRC and FDS prelim sound info added +V1.00 - 05/11/1999 First official NSF specification file + + + +This file encompasses a way to transfer NES music data in a small, easy to +use format. + +The basic idea is one rips the music/sound code from an NES game and prepends +a small header to the data. + +A program of some form (6502/sound emulator) then takes the data and loads +it into the proper place into the 6502's address space, then inits and plays +the tune. + +Here's an overview of the header: + +offset # of bytes Function +---------------------------- + +0000 5 STRING "NESM",01Ah ; denotes an NES sound format file +0005 1 BYTE Version number (currently 01h) +0006 1 BYTE Total songs (1=1 song, 2=2 songs, etc) +0007 1 BYTE Starting song (1= 1st song, 2=2nd song, etc) +0008 2 WORD (lo/hi) load address of data (8000-FFFF) +000a 2 WORD (lo/hi) init address of data (8000-FFFF) +000c 2 WORD (lo/hi) play address of data (8000-FFFF) +000e 32 STRING The name of the song, null terminated +002e 32 STRING The artist, if known, null terminated +004e 32 STRING The Copyright holder, null terminated +006e 2 WORD (lo/hi) speed, in 1/1000000th sec ticks, NTSC (see text) +0070 8 BYTE Bankswitch Init Values (see text, and FDS section) +0078 2 WORD (lo/hi) speed, in 1/1000000th sec ticks, PAL (see text) +007a 1 BYTE PAL/NTSC bits: + bit 0: if clear, this is an NTSC tune + bit 0: if set, this is a PAL tune + bit 1: if set, this is a dual PAL/NTSC tune + bits 2-7: not used. they *must* be 0 +007b 1 BYTE Extra Sound Chip Support + bit 0: if set, this song uses VRCVI + bit 1: if set, this song uses VRCVII + bit 2: if set, this song uses FDS Sound + bit 3: if set, this song uses MMC5 audio + bit 4: if set, this song uses Namco 106 + bit 5: if set, this song uses Sunsoft FME-07 + bits 6,7: future expansion: they *must* be 0 +007c 4 ---- 4 extra bytes for expansion (must be 00h) +0080 nnn ---- The music program/data follows + +This may look somewhat familiar; if so that's because this is somewhat +sorta of based on the PSID file format for C64 music/sound. + + +Loading a tune into RAM +----------------------- + +If offsets 0070h to 0077h have 00h in them, then bankswitching is *not* +used. If one or more bytes are something other than 00h then bankswitching +is used. If bankswitching is used then the load address is still used, +but you now use (ADDRESS AND 0FFFh) to determine where on the first bank +to load the data. + + +Each bank is 4K in size, and that means there are 8 of them for the +entire 08000h-0ffffh range in the 6502's address space. You determine where +in memory the data goes by setting bytes 070h thru 077h in the file. +These determine the inital bank values that will be used, and hence where +the data will be loaded into the address space. + +Here's an example: + +METROID.NSF will be used for the following explaination. + +The file is set up like so: (starting at 070h in the file) + + +0070: 05 05 05 05 05 05 05 05 - 00 00 00 00 00 00 00 00 +0080: ... music data goes here... + +Since 0070h-0077h are something other than 00h, then we know that this +tune uses bankswitching. The load address for the data is specified as +08000h. We take this AND 0fffh and get 0000h, so we will load data in +at byte 0 of bank 0, since data is loaded into the banks sequentially +starting from bank 0 up until the music data is fully loaded. + +Metroid has 6 4K banks in it, numbered 0 through 5. The 6502's address +space has 8 4K bankswitchable blocks on it, starting at 08000h-08fffh, +09000h-09fffh, 0a000h-0afffh ... 0f000h-0ffffh. Each one of these is 4K in +size, and the current bank is controlled by writes to 05ff8h thru 05fffh, +one byte per bank. So, 05ff8h controls the 08000h-08fffh range, 05ff9h +controls the 09000h-09fffh range, etc. up to 05fffh which controls the +0f000h-0ffffh range. When the song is loaded into RAM, it is loaded into +the banks and not the 6502's address space. Once this is done, then the +bank control registers are written to set up the inital bank values. +To do this, the value at 0070h in the file is written to 05ff8h, 0071h +is written to 05ff9h, etc. all the way to 0077h is written to 05fffh. +This is should be done before every call to the init routine. + +If the tune was not bankswitched, then it is simply loaded in at the +specified load address, until EOF + + +Initalizing a tune +------------------ + +This is pretty simple. Load the desired song # into the accumulator, +minus 1 and set the X register to specify PAL (X=1) or NTSC (X=0). +If this is a single standard tune (i.e. PAL *or* NTSC but not both) +then the X register contents should not matter. Once the song # and +optional PAL/NTSC standard are loaded, simply call the INIT address. +Once init is done, it should perform an RTS. + + +Playing a tune +-------------- + +Once the tune has been initalized, it can now be played. To do this, +simply call the play address several times a second. How many times +per second is determined by offsets 006eh and 006fh in the file. +These bytes denote the speed of playback in 1/1000000ths of a second. +For the "usual" 60Hz playback rate, set this to 411ah. + +To generate a differing playback rate, use this formula: + + + 1000000 +PBRATE= --------- + speed + +Where PBRATE is the value you stick into 006e/006fh in the file, and +speed is the desired speed in hertz. + + +"Proper" way to load the tune +----------------------------- + +1) If the tune is bankswitched, go to #3. + +2) Load the data into the 6502's address space starting at the specified + load address. Go to #4. + +3) Load the data into a RAM area, starting at (start_address AND 0fffh). + +4) Tune load is done. + + +"Proper" way to init a tune +--------------------------- + +1) Clear all RAM at 0000h-07ffh. + +2) Clear all RAM at 6000h-7fffh. + +3) Init the sound registers by writing 00h to 04000-0400Fh, 10h to 4010h, + and 00h to 4011h-4013h. + +4) Set volume register 04015h to 00fh. + +5) If this is a banked tune, load the bank values from the header into + 5ff8-5fffh. + +6) Set the accumulator and X registers for the desired song. + +7) Call the music init routine. + + +"Proper" way to play a tune +--------------------------- + +1) Call the play address of the music at periodic intervals determined + by the speed words. Which word to use is determined by which mode + you are in- PAL or NTSC. + + +Sound Chip Support +------------------ + +Byte 007bh of the file stores the sound chip flags. If a particular flag +is set, those sound registers should be enabled. If the flag is clear, +then those registers should be disabled. + +* VRCVI Uses registers 9000-9002, A000-A002, and B000-B002, write only. + +Caveats: 1) The above registers are *write only* and must not disrupt music + code that happens to be stored there. + + 2) Major caveat: The A0 and A1 lines are flipped on a few games!! + If you rip the music and it sounds all funny, flip around + the xxx1 and xxx2 register pairs. (i.e. 9001 and 9002) 9000 + and 9003 can be left untouched. I decided to do this since it + would make things easier all around, and this means you only + will have to change the music code in a very few places (6). + Esper2 and Madara will need this change, while Castlevania 3j + will not for instance. + + 3) See my VRCVI.TXT doc for a complete register description. + +* VRCVII Uses registers 9010 and 9030, write only. + +Caveats: 1) Same caveat as #1, above. + + 2) See my VRCVII.TXT doc for a complete register description. + +* FDS Sound uses registers from 4040 through 4092. + +Caveats: 1) 6000-DFFF is assumed to be RAM, since 6000-DFFF is RAM on the + FDS. E000-FFFF is usually not included in FDS games because + it is the BIOS ROM. However, it can be used on FDS rips to help + the ripper (for modified play/init addresses). + + 2) Bankswitching operates slightly different on FDS tunes. + 5FF6 and 5FF7 control the banks 6000-6FFF and 7000-7FFF + respectively. NSF header offsets 76h and 77h correspond to + *both* 6000-7FFF *AND* E000-FFFF. Keep this in mind! + +* MMC5 Sound Uses registers 5000-5015, write only as well as 5205 and 5206, + and 5C00-5FF5 + +Caveats: 1) Generating a proper doc file. Be patient. + + 2) 5205 and 5206 are a hardware 8*8 multiplier. The idea being + you write your two bytes to be multiplied into 5205 and 5206 + and after doing so, you read the result back out. Still working + on what exactly triggers it (I think a write to either 5205 + or 5206 triggers the multiply). + + 3) 5C00-5FF5 should be RAM to emulate EXRAM while in MMC5 mode. + +Note: Thanks to Mamiya for the EXRAM info. + + +* Namco 106 Sound Uses registers 4800 and F800. + + This works similar to VRC7. 4800 is the "data" port which is + readable and writable, while F800 is the "address" port and is + writable only. + + The address is 7 bits plus a "mode" bit. Bit 7 controls + address auto-incrementing. If bit 7 is set, the address will + auto-increment after a byte of data is read or written from/to + 4800. + + $40 ffffffff f:frequency L + $42 ffffffff f:frequency M + $44 ---sssff f:frequency H s:tone length (8-s)*4 in 4bit-samples + $46 tttttttt t:tone address(4bit-address,$41 means high-4bits of $20) + $47 -cccvvvv v:linear volume 1+c:number of channels in use($7F only) + $40-47:ch1 $48-4F:ch2 ... $78-7F:ch8 + ch2-ch8 same to ch1 + + $00-3F(8ch)...77(1ch) hhhhllll tone data + h:odd address data(signed 4bit) + l:even address data(signed 4bit) + + real frequency = (f * NES_BASECYCLES) / (40000h * (c+1) * (8-s)*4 * 45) + NES_BASECYCLES 21477270(Hz) + +Note: Very Special thanks to Mamiya for this information! + + +* Sunsoft FME-07 Sound uses registers C000 and E000 + + This is similar to the common AY 3-8910 sound chip that is + used on tons of arcade machines, and in the Intellivision. + + C000 is the address port + E000 is the data port + + Both are write-only, and behave like the AY 3-8910. + +Note: Special thanks to Mamiya for this information as well + + +Caveats +------- + +1) The starting song number and maximum song numbers start counting at + 1, while the init address of the tune starts counting at 0. To + "fix", simply pass the desired song number minus 1 to the init + routine. + +2) The NTSC speed word is used *only* for NTSC tunes, or dual PAL/NTSC tunes. + The PAL speed word is used *only* for PAL tunes, or dual PAL/NTSC tunes. + +3) The length of the text in the name, artist, and copyright fields must + be 31 characters or less! There has to be at least a single NULL byte + (00h) after the text, between fields. + +4) If a field is not known (name, artist, copyright) then the field must + contain the string "" (without quotes). + +5) There should be 8K of RAM present at 6000-7FFFh. MMC5 tunes need RAM at + 5C00-5FF7 to emulate its EXRAM. 8000-FFFF Should be read-only (not + writable) after a tune has loaded. The only time this area should be + writable is if an FDS tune is being played. + +6) Do not assume the state of *anything* on entry to the init routine + except A and X. Y can be anything, as can the flags. + +7) Do not assume the state of *anything* on entry to the play routine either. + Flags, X, A, and Y could be at any state. I've fixed about 10 tunes + because of this problem and the problem, above. + +8) The stack sits at 1FFh and grows down. Make sure the tune does not + attempt to use 1F0h-1FFh for variables. (Armed Dragon Villigust did and + I had to relocate its RAM usage to 2xx) + +9) Variables should sit in the 0000h-07FFh area *only*. If the tune writes + outside this range, say 1400h this is bad and should be relocated. + (Terminator 3 did this and I relocated it to 04xx). + +That's it! + + + diff --git a/Documentation/tech/ppu/loopy1.txt b/Documentation/tech/ppu/loopy1.txt new file mode 100644 index 0000000..bda6d85 --- /dev/null +++ b/Documentation/tech/ppu/loopy1.txt @@ -0,0 +1,63 @@ +Subject: [nesdev] the skinny on nes scrolling +Date: Tue, 13 Apr 1999 16:42:00 -0600 +From: loopy +Reply-To: nesdev@onelist.com +To: nesdev@onelist.com + +From: loopy + +--------- +the current information on background scrolling is sufficient for most games; +however, there are a few that require a more complete understanding. + +here are the related registers: + (v) vram address, a.k.a. 2006 which we all know and love. (16 bits) + (t) another temp vram address (16 bits) + (you can really call them 15 bits, the last isn't used) + (x) tile X offset (3 bits) + +the ppu uses the vram address for both reading/writing to vram thru 2007, +and for fetching nametable data to draw the background. as it's drawing the +background, it updates the address to point to the nametable data currently +being drawn. bits 0-11 hold the nametable address (-$2000). bits 12-14 are +the tile Y offset. + +--------- +stuff that affects register contents: +(sorry for the shorthand logic but i think it's easier to see this way) + +2000 write: + t:0000110000000000=d:00000011 +2005 first write: + t:0000000000011111=d:11111000 + x=d:00000111 +2005 second write: + t:0000001111100000=d:11111000 + t:0111000000000000=d:00000111 +2006 first write: + t:0011111100000000=d:00111111 + t:1100000000000000=0 +2006 second write: + t:0000000011111111=d:11111111 + v=t +scanline start (if background and sprites are enabled): + v:0000010000011111=t:0000010000011111 +frame start (line 0) (if background and sprites are enabled): + v=t + +note! 2005 and 2006 share the toggle that selects between first/second +writes. reading 2002 will clear it. + +note! all of this info agrees with the tests i've run on a real nes. BUT +if there's something you don't agree with, please let me know so i can verify +it. + +________________________________________________________ +NetZero - We believe in a FREE Internet. Shouldn't you? +Get your FREE Internet Access and Email at +http://www.netzero.net/download.html + +------------------------------------------------------------------------ +New hobbies? New curiosities? New enthusiasms? +http://www.ONElist.com +Sign up for a new e-mail list today! diff --git a/Documentation/tech/ppu/loopy2.txt b/Documentation/tech/ppu/loopy2.txt new file mode 100644 index 0000000..7a4585e --- /dev/null +++ b/Documentation/tech/ppu/loopy2.txt @@ -0,0 +1,33 @@ +Subject: [nesdev] Re: the skinny on nes scrolling +Date: Tue, 13 Apr 1999 17:48:54 -0600 +From: loopy +Reply-To: nesdev@onelist.com +To: nesdev@onelist.com + +From: loopy + +(more notes on ppu logic) + +you can think of bits 0,1,2,3,4 of the vram address as the "x scroll"(*8) +that the ppu increments as it draws. as it wraps from 31 to 0, bit 10 is +switched. you should see how this causes horizontal wrapping between name +tables (0,1) and (2,3). + +you can think of bits 5,6,7,8,9 as the "y scroll"(*8). this functions +slightly different from the X. it wraps to 0 and bit 11 is switched when +it's incremented from _29_ instead of 31. there are some odd side effects +from this.. if you manually set the value above 29 (from either 2005 or +2006), the wrapping from 29 obviously won't happen, and attrib data will be +used as name table data. the "y scroll" still wraps to 0 from 31, but +without switching bit 11. this explains why writing 240+ to 'Y' in 2005 +appeared as a negative scroll value. + +________________________________________________________ +NetZero - We believe in a FREE Internet. Shouldn't you? +Get your FREE Internet Access and Email at +http://www.netzero.net/download.html + +------------------------------------------------------------------------ +Looking for a new hobby? Want to make a new friend? +http://www.ONElist.com +Come join one of the 115,000 e-mail communities at ONElist! diff --git a/Documentation/tech/ppu/timing.txt b/Documentation/tech/ppu/timing.txt new file mode 100644 index 0000000..6a669b4 --- /dev/null +++ b/Documentation/tech/ppu/timing.txt @@ -0,0 +1,266 @@ +NTSC PPU timing +by Samus Aran (livingmonolith@hotmail.com) +date: Sept. 25th, Y2K + +This weekend, I setup an experiment with my NTSC NES MB & my PC so's I could +RE the PPU's timing. What I did was (using a PC interface) analyse the +changes that occur on the PPU's address and data pins on every rising & +falling edge of the PPU's clock. I was not planning on removing the PPU from +the motherboard (yet), so basically I just kept everything intact (minus the +stuff I added onto the MB so I could monitor the PPU's signals), and popped +in a game, so that it would initialize the PPU for me (I used DK classics, +since it was only taking somthing like 4 frames before it was turning on the +background/sprites). + +The only change I made was taking out the 21 MHz clock generator circuitry. +To replace the clock signal, I connected a port controlled latch to the +NES's main clock line instead. Now, by writing a 0 or a 1 out to an PC ISA +port of my choice (I was using $104), I was able to control the 21 MHz +clockline of the NES. After I would create a rise or a fall on the NES's +clock line, I would then read in the data that appeared on the PPU's address +and data pins, which included monitoring what PPU registers the game +read/wrote to (& the data that was read/written). + +My findings: + +- The PPU makes NO external access to name or character tables, unless the +background or sprites are enabled. This means that the PPU's address and +data busses are dead while in this state. + +- Because the PPU's palette RAM is internal to it, the PPU has multiport +access to it, and therefore, instant access to it at all times (this is why +reading palette RAM via $2007 does not require a throw-away read). This is +why when a scanline is being rendered, never does the PPU put the palette +address on it's bus; it's simply unneccessary. Additionally, when the +programmer accesses palette RAM via $2006/7, the palette address accessed +actually does show up on the PPU's external address bus, but the PPU's /R & +/W flags are not activated. This is required; to prevent writing over name +table data falling under the approprite mirrored area. I don't know why +Nintendo didn't just devote an exclusive area for palette RAM, like it did +for sprite RAM. + +- Sprite DMA is 6144 clock cycles long (or in CPU clock cycles, 6144/12). +256 individual transfers are made from CPU memory to a temp register inside +the CPU, then from the CPU's temp reg, to $2004. + +- One scanline is EXACTLY 1364 cycles long. In comparison to the CPU's +speed, one scanline is 1364/12 CPU cycles long. + +- One frame is EXACTLY 357368 cycles long, or EXACTLY 262 scanlines long. + + +Sequence of pixel rendering +--------------------------- + +External PPU memory is accessed every 8 clock cycles by the PPU when it's +drawing the background. Therefore, the PPU will typically access external +memory 170 times per scanline. After the 170th fetch, the PPU does nothing +for 4 clock cycles (except in the case of a 1360 clock cycle scanline (more +on this later)), and thus making the scanline up of 1364 cycles. + + accesses + -------- + + 1 thru 128: + + 1. Fetch 1 name table byte + 2. Fetch 1 attribute table byte + 3. Fetch 2 pattern table bitmap bytes + + This process is repeated 32 times (32 tiles in a scanline). + + This is when the PPU retrieves the appropriate data from PPU memory for +rendering the background. The first background tile fetched here is actually +the 3rd to be drawn on the screen (the background data for the first 2 tiles +to be rendered on the next scanline are fetched at the end of the scanline +prior to this one). + +In one complete cycle of fetches (4 fetches, or 32 cycles), the PPU renders +or draws 8 pixels on the screen. However, this does not suggest that the PPU +is always drawing on-screen results while background data is being fetched. +There is a delay inside the PPU from when the first background tile is +fetched, and when the first pixel to be displayed on the screen is rendered. +It is important to be aware of this delay, since it specifically relates to +the "sprite 0 hit" flag's timing. I currently do not know what the delay +time is (as far as clock cycles go). + + Note that the PPU fetches a nametable byte for every 8 horizontal pixels +it draws. It should be understood that with some custom cartridge hardware, +the PPU's color area could be increased (more about this at the end of this +document). + + It is also during this time that the PPU evaluates the "Y coordinate" +entries of all 64 sprites (starting with sprite 0) in sprite RAM, to see if +the sprites are within range (to be drawn on the screen) FOR THE NEXT +SCANLINE. For sprite entries that have been found to be in range, they (that +is, the sprite's nametable, and x coordinate bytes, attribute (5 bits) and +fine y scroll (3 or 4 bits, depending on bit 5 of $2000 ("sprite size")) +bits) accumulate into a part of PPU memory called the "sprite temporary +memory", which is big enough to hold the data for up to 8 sprites. If 8 +sprites have accumulated into the temporary memory and the PPU is still +finding more sprites in range for drawing on the next scanline, then the +sprite data is ignored (not loaded into the sprite temporary memory), and +the PPU raises a flag (bit 5 of $2002) indicating that it is going to be +dropping sprites for the next scanline. + + 129 thru 160: + + 1. Fetch 2 garbage name table bytes + 2. Fetch 2 pattern table bitmap bytes for applicable sprites ON THE NEXT +SCANLINE + + This process is repeated 8 times. + + This is the period of time when the PPU retrieves the appropriate pattern +table data for the sprites to be drawn on the next scanline. Where the PPU +fetches pattern table data for an individual sprite depends on the nametable +byte, and fine y scroll bits of a single sprite entry in the sprite +temporary memory, and bits 3 and 5 of $2000 ("sprite pattern table select" +and "sprite size" bits, respectively). The fetched pattern table data (which +is 2 bytes), plus the associated 5 attribute bytes, and the x coordinate +byte in sprite temporary memory are then loaded into a part of the PPU +called the "sprite buffer memory". This memory area again, is large enough +to hold the contents for 8 sprites. The makeup of one sprite memory cell +here is composed of 2 8-bit shift registers (the fetched pattern table data +is loaded in here, where it will be serialized at the appropriate time), a +5-bit latch (which holds the attribute data for a sprite), and a 8-bit down +counter (this is where the x coordinate is loaded). The counter is +decremented every time the PPU draws a pixel on screen, and when the counter +reaches 0, the pattern table data in the shift registers will start to +serialize, and be drawn on the screen. + + Even if no sprites exist on the next scanline, a pattern table fetch takes +place. + + Although the fetched name table data is thrown away, I still can't make +much sense out of the name table address accesses the PPU makes during this +time. However, the address does seem to relate to the first name table tile +to be rendered on the screen. + + It should also be noted that because this fetch is required for sprites on +the next line, it is neccessary for a garbage scanline to exist prior to the +very first scanline to be actually rendered, so that sprite RAM entries can +be evaluated, and the appropriate bitmap data retrieved. + + Finally, it would appear to me that the PPU's 8 sprite/scanline +bottleneck exists clearly because the PPU could only find the time in one +scanline to fetch the pattern bitmaps for 8 sprites. However, why the PPU +doesn't attempt to access pattern table data in the time when it fetches 2 +garbage name table bytes is a good question. + + 161 thru 168: + + 1. Fetch 1 name table byte + 2. Fetch 1 attribute table byte + 3. Fetch 2 pattern table bitmap bytes + + This process is repeated 2 times. + + It is during this time that the PPU fetches the appliciable background +data for the first and second tiles to be rendered on the screen for the +next scanline. The rest of tiles (3..128) are fetched at the beginning of +the following scanline. + + 169 thru 170: + + 1. Fetch 1 name table byte + + This process is repeated 2 times. + + I'm unclear of the reason why this particular access to memory is made. +The nametable address that is accessed 2 times in a row here, is also the +same nametable address that points to the 3rd tile to be rendered on the +screen (or basically, the first nametable address that will be accessed when +the PPU is fetching background data on the next scanline). + + + After memory access 170, the PPU simply rests for 4 cycles (or the +equivelant of half a memory access cycle) before repeating the whole +pixel/scanline rendering process. If the scanline being rendered is the very +first one on every second frame, then this delay simply doesn't exist. + + +Sequence of line rendering +-------------------------- + + 1. Starting at the instant the VINT flag is pulled down (when a NMI is +generated), 20 scanlines make up the period of time on the PPU which I like +to call the VINT period. During this time, the PPU makes NO access to it's +external memory (i.e. name / pattern tables, etc.). + + 2. After 20 scanlines worth of time go by (since the VINT flag was set), +the PPU starts to render scanlines. Now, the first scanline it renders is a +dummy one; although it will access it's external memory in the same sequence +it would for drawing a valid scanline, the fetched background data is thrown +away, and the places that the PPU accesses name table data is unexplainable +(for now). + +IMPORTANT! this is the only scanline that has variable length. On every +second rendered frame, this scanline is only 1360 cycles. Otherwise it's +1364. + + 3. after rendering 1 dummy scanline, the PPU starts to render the actual +data to be displayed on the screen. This is done for 240 scanlines, of +course. + + 4. after the very last rendered scanline finishes, the PPU does nothing for +1 scanline (i.e. makes no external memory accesses). When this scanline +finishes, the VINT flag is set, and the process of drawing lines starts all +over again. + +This makes a total of 262 scanlines. Although one scanline is slightly +shorter on every second rendered frame (4 cycles), I don't know if this +feature is neccessary to implement in emulators, since it only makes 1/3 a +CPU cycle difference per frame (and there's NO way that a game could take +into account 1/3 of a CPU cycle). + + +Food for thought +---------------- + +What's important to remember about the NES's 2C02 or picture proecssing unit +(hereon PPU) is that all screen data is fetched & drawn on a real-time +basis. For example, let's consider how the PPU draws background tiles. + +We know that one name table byte is associated with an 8x8 cluster of pixels +(and therefore, 16 bytes worth of pattern bitmap data, plus 2 attribute +bits). Therefore, it would make sense for the PPU to only have to fetch a +name table byte once for each 8x8 pixel array it draws (one tile), and 1 +attribute byte fetch for every 4x4 tile matrix that it draws. However, since +the PPU always draws one complete scanline before drawing the next, The PPU +will actually fetch the same name table byte 8 times, once each scanline at +the appropriate x coordinate. Since these name table address access reads +are redundant, with some custom cartridge hardware, it would be possible to +make the PPU appear as if it had background tiles as small as 8x1 pixels! + +Additionally, an attribute table byte is fetched from name table RAM once +per 2 fetched pattern bitmap bytes (or, every 8 pixels worth of pattern +bitmap data). This is useful information to keep in mind, for with some +custom cartridge hardware, this would allow the NES's PPU to appear to have +an effective color area as small as of 8*1 pixels (!), where only the 8 +pixels are limited to having 4 exclusive colors, which, is *alot* better +than the PPU's default color area of 16x16 pixels. + +So basically, what I'm getting at here, is that the PPU has absolutely NO +memory whatsoever of what it rendered last scanline, and therefore all data +must be processed/evaluated again, whether it's name table accesses, +attribute table accesses, or even it's internal sprite RAM accesses. + +What's good, and what's bad about the way the PPU draws it's pictures: + +What's good about it is that it makes the PPU a hell of alot more versatile, +provided you have the appropriate hardware to assist in the improvement of +the PPU's background drawing techniques (MMC5 comes to mind). Also, by doing +background rendering in the real time, the PPU complexity is less, and less +internal temporary registers are required. + +What's bad about it is that it eats up memory bandwidth like it's going out +of style. When the PPU is rendering scanlines, the PPU is accessing the VRAM +every chance it gets, which takes away from the time that the programmer +gets to access the VRAM. In contrast, if redundantly loaded data (like +attribute bytes) were kept in internal PPU RAM, this would allow some time +for the PPU to allow access to it's VRAM. + +All in all though, Nintendo engineered quite a cost effective, versatile +graphic processor. Now, if only they brought the 4 expansion pins on the PPU +out of the deck! diff --git a/Makefile.base b/Makefile.base new file mode 100644 index 0000000..3e3f273 --- /dev/null +++ b/Makefile.base @@ -0,0 +1,42 @@ +CFLAGS = -Wall -Winline ${TFLAGS} +OBJECTS = fce.o x6502.o video.o general.o endian.o svga.o sound.o nsf.o fds.o netplay.o ines.o state.o unif.o input.o file.o cart.o crc32.o memory.o cheat.o debug.o + +fceu: fceu2 +include mappers/Makefile +include boards/Makefile +include mbshare/Makefile +include input/Makefile + +fceu2: ${OBJECTS} ${MOBJS} ${MUOBJS} ${MUSOBJS} ${INPOBJS} ${OBJDRIVER} + ${CC} -s -o fceu ${OBJECTS} ${MOBJS} ${MUOBJS} ${MUSOBJS} ${INPOBJS} ${OBJDRIVER} ${LDRIVER} + +clean: + ${RM} fceu fceu.exe ${OBJECTS} ${INPOBJS} ${MOBJS} ${MUOBJS} ${MUSOBJS} ${OBJDRIVER} + +nsf.o: nsf.c nsf.h fce.h x6502.h svga.h video.h sound.h nsfbgnew.h general.h file.h +x6502.o: x6502.c x6502.h ops.h fce.h sound.h +video.o: video.c types.h video.h svga.h version.h general.h +sound.o: sound.c sound.h types.h fce.h svga.h x6502.h +svga.o: svga.c svga.h types.h palette.h state.h netplay.h fds.h fce.h nsf.h video.h sound.h palettes/*.h driver.h drawing.h +netplay.o: netplay.c netplay.h types.h svga.h + +state.o: state.c state.h + +unif.o: unif.c unif.h file.h cart.h + +memory.o: memory.c memory.h + +cart.o: cart.c cart.h types.h version.h fce.h +fce.o: fce.c *.h +fds.o: fds.h x6502.h types.h version.h fce.h svga.h sound.h general.h state.h file.h memory.h +ines.o: ines.c ines.h x6502.h types.h fce.h ines.h version.h svga.h general.h state.h file.h memory.h cart.h crc32.h banksw.h +input.o: input.c input.h x6502.h types.h fce.h sound.h netplay.h driver.h svga.h + +crc32.o: crc32.c crc32.h types.h +endian.o: endian.c endian.h types.h +file.o: file.c file.h types.h endian.h memory.h driver.h +general.o: general.c general.h types.h state.h version.h + +cheat.o: cheat.c driver.h + +debug.o: debug.c debug.h fce.h diff --git a/Makefile.beos b/Makefile.beos new file mode 100644 index 0000000..0537114 --- /dev/null +++ b/Makefile.beos @@ -0,0 +1,23 @@ +CC = gcc +TFLAGS = -no-fpic -DC80x86 -DNETWORK -DFPS `sdl-config --cflags` -mcpu=i686 -O2 -Izlib -fomit-frame-pointer -DLSB_FIRST -DSDL -DUNIX -DPSS_STYLE=1 -DZLIB +RM = rm -f +B = drivers/cli/ + +all: fceu + +include zlib/Makefile + +OBJDRIVER = ${B}sdl.o ${B}main.o ${B}throttle.o ${B}sdl-netplay.o ${B}sdl-sound.o ${B}sdl-video.o ${B}sdl-joystick.o drivers/common/cheat.o drivers/common/config.o drivers/common/args.o drivers/common/vidblit.o ${UNZIPOBJS} +LDRIVER = -static `sdl-config --libs` -lz -lSDL_net + +include Makefile.base + +${B}sdl-joystick.o: ${B}sdl-joystick.c +${B}main.o: ${B}main.c ${B}main.h ${B}usage.h ${B}input.c ${B}keyscan.h +${B}sdl.o: ${B}sdl.c ${B}sdl.h +${B}sdl-video.o: ${B}sdl-video.c +${B}sdl-sound.o: ${B}sdl-sound.c +${B}sdl-netplay.o: ${B}sdl-netplay.c +${B}throttle.o: ${B}throttle.c ${B}main.h ${B}throttle.h + +include Makefile.common diff --git a/Makefile.common b/Makefile.common new file mode 100644 index 0000000..891b77a --- /dev/null +++ b/Makefile.common @@ -0,0 +1,6 @@ +drivers/common/cheat.o: drivers/common/cheat.c drivers/common/cheat.h +drivers/common/config.o: drivers/common/config.c drivers/common/config.h +drivers/common/args.o: drivers/common/args.c drivers/common/args.h +drivers/common/unixdsp.o: drivers/common/unixdsp.c drivers/common/unixdsp.h +drivers/common/videblit.o: drivers/common/vidblit.c drivers/common/vidblit.h + diff --git a/Makefile.dos b/Makefile.dos new file mode 100644 index 0000000..bef31aa --- /dev/null +++ b/Makefile.dos @@ -0,0 +1,23 @@ +CC = gcc +TFLAGS = -O2 -mcpu=i686 -Izlib -fomit-frame-pointer -DC80x86 -DLSB_FIRST -DDOS -DPSS_STYLE=2 -DZLIB -D_USE_LIBM_MATH_H +RM = del +B = drivers/cli/ + +all: fceu + +include zlib/Makefile + +OBJDRIVER = ${B}main.o ${B}dos.o ${B}throttle.o ${B}dos-joystick.o ${B}dos-keyboard.o ${B}dos-mouse.o ${B}dos-sound.o ${B}dos-video.o drivers/common/cheat.o drivers/common/config.o drivers/common/args.o ${ZLIBOBJS} ${UNZIPOBJS} +LDRIVER = -lm + +include Makefile.base + +${B}main.o: ${B}main.c ${B}main.h ${B}usage.h ${B}input.c +${B}dos.o: ${B}dos.c ${B}dos.h +${B}throttle.o: ${B}throttle.c ${B}main.h ${B}throttle.h +${B}dos-joystick.o: ${B}dos-joystick.c ${B}dos.h ${B}dos-joystick.h +${B}dos-keyboard.o: ${B}dos-keyboard.c ${B}keyscan.h +${B}dos-mouse.o: ${B}dos-mouse.c +${B}dos-sound.o: ${B}dos-sound.c ${B}dos.h ${B}dos-sound.h ${B}dos-joystick.h +${B}dos-video.o: ${B}dos-video.c ${B}dos.h ${B}dos-video.h +include Makefile.common diff --git a/Makefile.linuxvga b/Makefile.linuxvga new file mode 100644 index 0000000..a123168 --- /dev/null +++ b/Makefile.linuxvga @@ -0,0 +1,22 @@ +CC = gcc +TFLAGS = -DFPS -mcpu=i686 -O2 -Izlib -fomit-frame-pointer -DC80x86 -DLSB_FIRST -DSVGALIB -DUNIX -DLINUX -DNETWORK -DPSS_STYLE=1 -DZLIB +RM = rm -f +B = drivers/cli/ + +all: fceu + +include zlib/Makefile + +OBJDRIVER = ${B}svgalib.o ${B}main.o ${B}throttle.o ${B}svga-video.o ${B}unix-netplay.o ${B}lnx-joystick.o drivers/common/unixdsp.o drivers/common/cheat.o drivers/common/config.o drivers/common/args.o ${ZLIBOBJS} ${UNZIPOBJS} +LDRIVER = -lm -lvga + +include Makefile.base + +${B}lnx-joystick.o: ${B}lnx-joystick.c +${B}main.o: ${B}main.c ${B}main.h ${B}usage.h ${B}input.c +${B}throttle.o: ${B}throttle.c ${B}main.h ${B}throttle.h +${B}svgalib.o: ${B}svgalib.c ${B}svgalib.h +${B}svga-video.o: ${B}svga-video.c ${B}svga-video.h ${B}vgatweak.c +${B}unix-netplay.o: ${B}unix-netplay.c + +include Makefile.common diff --git a/Makefile.unixsdl b/Makefile.unixsdl new file mode 100644 index 0000000..d8122d9 --- /dev/null +++ b/Makefile.unixsdl @@ -0,0 +1,25 @@ +CC = gcc +TFLAGS = -DNETWORK -DFPS `sdl-config --cflags` -mcpu=i686 -O2 -Izlib -fomit-frame-pointer -DC80x86 -DLSB_FIRST -DSDL -DUNIX -DPSS_STYLE=1 -DZLIB + +RM = rm -f +B = drivers/cli/ + +all: fceu + +include zlib/Makefile + +OBJDRIVER = ${B}sdl.o ${B}main.o ${B}throttle.o ${B}unix-netplay.o ${B}sdl-sound.o ${B}sdl-video.o ${B}sdl-joystick.o drivers/common/cheat.o drivers/common/config.o drivers/common/args.o drivers/common/vidblit.o ${UNZIPOBJS} +LDRIVER = -lm `sdl-config --libs` -lz + +include Makefile.base + +${B}sdl-joystick.o: ${B}sdl-joystick.c +${B}main.o: ${B}main.c ${B}main.h ${B}usage.h ${B}input.c ${B}keyscan.h +${B}sdl.o: ${B}sdl.c ${B}sdl.h +${B}sdl-video.o: ${B}sdl-video.c +${B}sdl-sound.o: ${B}sdl-sound.c +#${B}sdl-netplay.o: ${B}sdl-netplay.c +${B}unix-netplay.o: ${B}unix-netplay.c +${B}throttle.o: ${B}throttle.c ${B}main.h ${B}throttle.h + +include Makefile.common diff --git a/Makefile.win b/Makefile.win new file mode 100644 index 0000000..1767c71 --- /dev/null +++ b/Makefile.win @@ -0,0 +1,22 @@ +CC = gcc +TFLAGS = -mcpu=i686 -O2 -Izlib -fomit-frame-pointer -DNOSTDOUT -DC80x86 -DLSB_FIRST -DWINDOWS -DNETWORK -DPSS_STYLE=2 -DZLIB +RM = del +B = drivers/win/ +all: fceu + +include zlib/Makefile + +LDRIVER = -mwindows -lddraw -ldinput -ldsound -lgdi32 -ldxguid -lwinmm -lshell32 -lwsock32 -lcomdlg32 -lole32 +OBJDRIVER = ${B}main.o ${B}input.o ${B}joystick.o ${B}keyboard.o ${B}cheat.o ${B}res.o ${ZLIBOBJS} ${UNZIPOBJS} drivers/common/config.o + +include Makefile.base + +${B}main.o: ${B}main.c drivers/win/netplay.c ${B}config.c ${B}throttle.c ${B}video.c drivers/win/window.c drivers/win/sound.c drivers/win/wave.c +${B}cheat.o: ${B}common.h ${B}cheat.h +${B}input.o: ${B}common.h ${B}input.h ${B}joystick.h ${B}keyboard.h +${B}joystick.o: ${B}common.h ${B}joystick.h ${B}input.h +${B}keyboard.o: ${B}common.h ${B}keyboard.h ${B}input.h +drivers/win/res.o: drivers/win/res.res + windres -o drivers/win/res.o drivers/win/res.res + +include Makefile.common diff --git a/banksw.h b/banksw.h new file mode 100644 index 0000000..f45c489 --- /dev/null +++ b/banksw.h @@ -0,0 +1,101 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 1998 Bero + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +void FASTAPASS(2) VRAM_BANK1(uint32 A, uint8 V) +{ + V&=7; + PPUCHRRAM|=(1<<(A>>10)); + CHRBankList[(A)>>10]=V; + VPage[(A)>>10]=&CHRRAM[V<<10]-(A); +} + +void FASTAPASS(2) VRAM_BANK4(uint32 A, uint32 V) +{ + V&=1; + PPUCHRRAM|=(0xF<<(A>>10)); + CHRBankList[(A)>>10]=(V<<2); + CHRBankList[((A)>>10)+1]=(V<<2)+1; + CHRBankList[((A)>>10)+2]=(V<<2)+2; + CHRBankList[((A)>>10)+3]=(V<<2)+3; + VPage[(A)>>10]=&CHRRAM[V<<10]-(A); +} + +void FASTAPASS(2) VROM_BANK1(uint32 A,uint32 V) +{ + setchr1(A,V); + CHRBankList[(A)>>10]=V; +} + +void FASTAPASS(2) VROM_BANK2(uint32 A,uint32 V) +{ + setchr2(A,V); + CHRBankList[(A)>>10]=(V<<1); + CHRBankList[((A)>>10)+1]=(V<<1)+1; +} + +void FASTAPASS(2) VROM_BANK4(uint32 A, uint32 V) +{ + setchr4(A,V); + CHRBankList[(A)>>10]=(V<<2); + CHRBankList[((A)>>10)+1]=(V<<2)+1; + CHRBankList[((A)>>10)+2]=(V<<2)+2; + CHRBankList[((A)>>10)+3]=(V<<2)+3; +} + +void FASTAPASS(1) VROM_BANK8(uint32 V) +{ + setchr8(V); + CHRBankList[0]=(V<<3); + CHRBankList[1]=(V<<3)+1; + CHRBankList[2]=(V<<3)+2; + CHRBankList[3]=(V<<3)+3; + CHRBankList[4]=(V<<3)+4; + CHRBankList[5]=(V<<3)+5; + CHRBankList[6]=(V<<3)+6; + CHRBankList[7]=(V<<3)+7; +} + +void FASTAPASS(2) ROM_BANK8(uint32 A, uint32 V) +{ + setprg8(A,V); + if(A>=0x8000) + PRGBankList[((A-0x8000)>>13)]=V; +} + +void FASTAPASS(2) ROM_BANK16(uint32 A, uint32 V) +{ + setprg16(A,V); + if(A>=0x8000) + { + PRGBankList[((A-0x8000)>>13)]=V<<1; + PRGBankList[((A-0x8000)>>13)+1]=(V<<1)+1; + } +} + +void FASTAPASS(1) ROM_BANK32(uint32 V) +{ + setprg32(0x8000,V); + PRGBankList[0]=V<<2; + PRGBankList[1]=(V<<2)+1; + PRGBankList[2]=(V<<2)+2; + PRGBankList[3]=(V<<2)+3; +} + diff --git a/boards/Makefile b/boards/Makefile new file mode 100644 index 0000000..58dd4ef --- /dev/null +++ b/boards/Makefile @@ -0,0 +1,9 @@ +MUOBJS = boards/simple.o boards/malee.o boards/supervision.o boards/novel.o boards/super24.o boards/h2288.o boards/sachen.o + +boards/simple.o: boards/simple.c +boards/malee.o: boards/malee.c +boards/supervision.o: boards/supervision.c +boards/novel.o: boards/novel.c +boards/super24.o: boards/super24.c +boards/h2288.o: boards/h2288.c +boards/sachen.o: boards/sachen.c diff --git a/boards/h2288.c b/boards/h2288.c new file mode 100644 index 0000000..c8ad1e0 --- /dev/null +++ b/boards/h2288.c @@ -0,0 +1,99 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Not finished. Darn evil game... *Mumble*... */ + +#include "mapinc.h" + +static uint8 cmd; +static uint8 regs[8]; + +static void DoPRG(void) +{ + if(cmd&0x40) + { + setprg8(0xC000,regs[4]); + setprg8(0xA000,regs[5]); + setprg8(0x8000,~1); + setprg8(0xE000,~0); + } + else + { + setprg8(0x8000,regs[4]); + setprg8(0xA000,regs[5]); + setprg8(0xC000,~1); + setprg8(0xE000,~0); + } +} + +static void DoCHR(void) +{ + uint32 base=(cmd&0x80)<<5; + + setchr2(0x0000^base,regs[0]); + setchr2(0x0800^base,regs[2]); + + setchr1(0x1000^base,regs[6]); + setchr1(0x1400^base,regs[1]); + setchr1(0x1800^base,regs[7]); + setchr1(0x1c00^base,regs[3]); +} + +static DECLFW(H2288Write) +{ + //printf("$%04x:$%02x, $%04x\n",A,V,X.PC.W); + //RAM[0x7FB]=0x60; + switch(A&0xE001) + { + case 0xa000:setmirror((V&1)^1);break; + case 0x8000:// DumpMem("out",0x0000,0xFFFF); + cmd=V;DoPRG();DoCHR();break; + case 0x8001:regs[cmd&7]=V; + if((cmd&7)==4 || (cmd&7)==5) + DoPRG(); + else + DoCHR(); + break; + } +} + +static DECLFR(H2288Read) +{ + //printf("Rd: $%04x, $%04x\n",A,X.PC.W); + return((X.DB&0xFE)|(A&(A>>8))); +} + +static void H2288Reset(void) +{ + int x; + + SetReadHandler(0x5800,0x5FFF,H2288Read); + SetReadHandler(0x8000,0xFFFF,CartBR); + SetWriteHandler(0x8000,0xFFFF,H2288Write); + for(x=0;x<8;x++) regs[x]=0; + cmd=0; + DoPRG(); + DoCHR(); +} + +void H2288_Init(void) +{ + BoardPower=H2288Reset; +} diff --git a/boards/malee.c b/boards/malee.c new file mode 100644 index 0000000..f575159 --- /dev/null +++ b/boards/malee.c @@ -0,0 +1,45 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + +DECLFW(MWrite) +{ + (GameMemBlock-0x7000)[A]=V; +} + +static void MALEEReset(void) +{ + setprg2r(0x10,0x7000,0); + SetReadHandler(0x8000,0xFFFF,CartBR); + SetReadHandler(0x6000,0x67ff,CartBR); + SetReadHandler(0x7000,0x77FF,CartBR); + SetWriteHandler(0x7000,0x77FF,MWrite); + setprg2r(1,0x6000,0); + setprg32(0x8000,0); + setchr8(0); +} + +void MALEE_Init(void) +{ + AddExState(GameMemBlock, 2048, 0,"RAM"); + SetupCartPRGMapping(0x10,GameMemBlock,2048,1); + BoardPower=MALEEReset; +} diff --git a/boards/mapinc.h b/boards/mapinc.h new file mode 100644 index 0000000..e6d4c41 --- /dev/null +++ b/boards/mapinc.h @@ -0,0 +1,11 @@ +#include "../types.h" +#include "../x6502.h" +#include "../fce.h" +#include "../version.h" +#include "../memory.h" +#include "../sound.h" +#include "../svga.h" +#include "../state.h" +#define UNIFPRIV +#include "../unif.h" +#include "../cart.h" diff --git a/boards/novel.c b/boards/novel.c new file mode 100644 index 0000000..7d620db --- /dev/null +++ b/boards/novel.c @@ -0,0 +1,53 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + +static void DoNovel(void) +{ + setprg32(0x8000,GameMemBlock[0]&3); + setchr8(GameMemBlock[0]&7); +} + +static DECLFW(NovelWrite) +{ + GameMemBlock[0]=A&0xFF; + DoNovel(); +} + +static void NovelReset(void) +{ + SetWriteHandler(0x8000,0xFFFF,NovelWrite); + SetReadHandler(0x8000,0xFFFF,CartBR); + setprg32(0x8000,0); + setchr8(0); +} + +static void NovelRestore(int version) +{ + DoNovel(); +} + +void Novel_Init(void) +{ + AddExState(&GameMemBlock[0], 1, 0,"L1"); + BoardPower=NovelReset; + GameStateRestore=NovelRestore; +} diff --git a/boards/sachen.c b/boards/sachen.c new file mode 100644 index 0000000..d4d20c2 --- /dev/null +++ b/boards/sachen.c @@ -0,0 +1,289 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + +static uint8 cmd; +static uint8 latch[8]; +#define CHRRAM (GameMemBlock) + +static void S74LS374NSynco(void) +{ + setprg32(0x8000,latch[0]); + setchr8(latch[1]); + setmirror(latch[2]&1); +// setchr8(6); +} + +static DECLFW(S74LS374NWrite) +{ + //printf("$%04x:$%02x\n",A,V); + A&=0x4101; + if(A==0x4100) + cmd=V&7; + else + { + switch(cmd) + { + case 0:latch[0]=0;latch[1]=3;break; + case 4:latch[1]&=3;latch[1]|=(V<<2);break; + case 5:latch[0]=V&0x7;break; + case 6:latch[1]&=0x1C;latch[1]|=V&3;break; + case 7:latch[2]=V&1;break; + } + S74LS374NSynco(); + } +} + +static void S74LS374NReset(void) +{ + latch[0]=latch[2]=0; + latch[1]=3; + S74LS374NSynco(); + SetReadHandler(0x8000,0xFFFF,CartBR); + SetWriteHandler(0x4100,0x7FFF,S74LS374NWrite); +} + +static void S74LS374NRestore(int version) +{ + S74LS374NSynco(); +} + +void S74LS374N_Init(void) +{ + BoardPower=S74LS374NReset; + GameStateRestore=S74LS374NRestore; + AddExState(latch, 3, 0, "LATC"); + AddExState(&cmd, 1, 0, "CMD"); +} + +static int type; +static void S8259Synco(void) +{ + int x; + + setprg32(0x8000,latch[5]&7); + + if(!UNIFchrrama) // No CHR RAM? Then BS'ing is ok. + { + if(!type) + { + for(x=0;x<4;x++) + setchr2(0x800*x,(x&1)|((latch[x]&7)<<1)|((latch[4]&7)<<4)); + } + else + { + for(x=0;x<4;x++) + setchr2(0x800*x,(latch[x]&0x7)|((latch[4]&7)<<3)); + } + } + switch((latch[7]>>1)&3) + { + case 0:setmirrorw(0,0,0,1);break; + case 1:setmirror(MI_H);break; + case 2:setmirror(MI_V);break; + case 3:setmirror(MI_0);break; + } +} + +static DECLFW(S8259Write) +{ + A&=0x4101; + if(A==0x4100) cmd=V; + else + { + latch[cmd&7]=V; + S8259Synco(); + } +} + +static void S8259Reset(void) +{ + int x; + cmd=0; + + for(x=0;x<8;x++) latch[x]=0; + if(UNIFchrrama) setchr8(0); + + S8259Synco(); + SetReadHandler(0x8000,0xFFFF,CartBR); + SetWriteHandler(0x4100,0x7FFF,S8259Write); +} + +static void S8259Restore(int version) +{ + S8259Synco(); +} + +void S8259A_Init(void) +{ + BoardPower=S8259Reset; + GameStateRestore=S8259Restore; + AddExState(latch, 8, 0, "LATC"); + AddExState(&cmd, 1, 0, "CMD"); + type=0; + + //if(!CHRsize[0]) + //{ + // SetupCartCHRMapping(0,CHRRAM,8192,1); + // AddExState(CHRRAM, 8192, 0, "CHRR"); + //} +} + +void S8259B_Init(void) +{ + BoardPower=S8259Reset; + GameStateRestore=S8259Restore; + AddExState(latch, 8, 0, "LATC"); + AddExState(&cmd, 1, 0, "CMD"); + type=1; +} + +static void(*WSync)(void); + +static void SA0161MSynco() +{ + setprg32(0x8000,(latch[0]>>3)&1); + setchr8(latch[0]&7); +} + +static DECLFW(SAWrite) +{ + if(A&0x100) + { + latch[0]=V; + WSync(); + } +} + +static void SAReset(void) +{ + latch[0]=0; + WSync(); + SetReadHandler(0x8000,0xFFFF,CartBR); + SetWriteHandler(0x4100,0x5FFF,SAWrite); +} + +void SA0161M_Init(void) +{ + WSync=SA0161MSynco; + GameStateRestore=SA0161MSynco; + BoardPower=SAReset; + AddExState(&latch[0], 1, 0, "LATC"); +} + +static void SA72007Synco() +{ + setprg32(0x8000,0); + setchr8(latch[0]>>7); +} + +void SA72007_Init(void) +{ + WSync=SA72007Synco; + GameStateRestore=SA72007Synco; + BoardPower=SAReset; + AddExState(&latch[0], 1, 0, "LATC"); +} + +static void SA72008Synco() +{ + setprg32(0x8000,(latch[0]>>2)&1); + setchr8(latch[0]&3); +} + +void SA72008_Init(void) +{ + WSync=SA72008Synco; + GameStateRestore=SA72008Synco; + BoardPower=SAReset; + AddExState(&latch[0], 1, 0, "LATC"); +} + +static DECLFW(SADWrite) +{ + latch[0]=V; + WSync(); +} + +static void SADReset(void) +{ + latch[0]=0; + WSync(); + SetReadHandler(0x8000,0xFFFF,CartBR); + SetWriteHandler(0x8000,0xFFFF,SADWrite); +} + +static void SA0036Synco() +{ + setprg32(0x8000,0); + setchr8(latch[0]>>7); +} + +static void SA0037Synco() +{ + setprg32(0x8000,(latch[0]>>3)&1); + setchr8(latch[0]&7); +} + +void SA0036_Init(void) +{ + WSync=SA0036Synco; + GameStateRestore=SA0036Synco; + BoardPower=SADReset; + AddExState(&latch[0], 1, 0, "LATC"); +} + +void SA0037_Init(void) +{ + WSync=SA0037Synco; + GameStateRestore=SA0037Synco; + BoardPower=SADReset; + AddExState(&latch[0], 1, 0, "LATC"); +} + +static void TCU01Synco() +{ + setprg32(0x8000,(latch[0]>>2)&1); + setchr8((latch[0]>>3)&0xF); +} + +static DECLFW(TCWrite) +{ + if((A&0x103)==0x102) + latch[0]=V; + TCU01Synco(); +} + +static void TCU01Reset(void) +{ + latch[0]=0; + SetReadHandler(0x8000,0xFFFF,CartBR); + SetWriteHandler(0x4100,0xFFFF,TCWrite); + TCU01Synco(); +} + +void TCU01_Init(void) +{ + GameStateRestore=TCU01Synco; + BoardPower=TCU01Reset; + AddExState(&latch[0], 1, 0, "LATC"); +} + diff --git a/boards/simple.c b/boards/simple.c new file mode 100644 index 0000000..ca4d1db --- /dev/null +++ b/boards/simple.c @@ -0,0 +1,162 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + +#define CHRRAM (GameMemBlock) +static uint8 latche; + +DECLFW(CPROMWrite) +{ + latche=V&3; + setvram4(0x1000,CHRRAM+((V&3)<<12)); +} + +static void CPROMReset(void) +{ + setprg32(0x8000,0); + setvram8(0); + SetReadHandler(0x8000,0xFFFF,CartBR); + SetWriteHandler(0x8000,0xffff,CPROMWrite); +} + +static void CPROMRestore(int version) +{ + setvram4(0x1000,CHRRAM+((latche)<<12)); +} + +void CPROM_Init(void) +{ + BoardPower=CPROMReset; + GameStateRestore=CPROMRestore; + AddExState(&latche, 1, 0, "LATC"); +} + +DECLFW(CNROMWrite) +{ + latche=V&3; + setchr8(V&3); +} + +static void CNROMReset(void) +{ + setprg16(0x8000,0); + setprg16(0xC000,1); + SetReadHandler(0x8000,0xFFFF,CartBR); + SetWriteHandler(0x8000,0xffff,CNROMWrite); +} + +static void CNROMRestore(int version) +{ + setchr8(latche); +} + +void CNROM_Init(void) +{ + BoardPower=CNROMReset; + GameStateRestore=CNROMRestore; + AddExState(&latche, 1, 0, "LATC"); +} + +static void NROM128Reset(void) +{ + setprg16(0x8000,0); + setprg16(0xC000,0); + setchr8(0); + SetReadHandler(0x8000,0xFFFF,CartBR); +} + +static void NROM256Reset(void) +{ + setprg16(0x8000,0); + setprg16(0xC000,1); + setchr8(0); + SetReadHandler(0x8000,0xFFFF,CartBR); +} +void NROM128_Init(void) +{ + BoardPower=NROM128Reset; +} + +void NROM256_Init(void) +{ + BoardPower=NROM256Reset; +} + +static DECLFW(MHROMWrite) +{ + setprg32(0x8000,V>>4); + setchr8(V); + latche=V; +} + +static void MHROMReset(void) +{ + setprg32(0x8000,0); + setchr8(0); + latche=0; + SetReadHandler(0x8000,0xFFFF,CartBR); +} + +static void MHROMRestore(int version) +{ + setprg32(0x8000,latche); + setchr8(latche); + SetWriteHandler(0x8000,0xffff,MHROMWrite); +} + +void MHROM_Init(void) +{ + BoardPower=MHROMReset; + AddExState(&latche, 1, 0,"LATC"); + PRGmask32[0]&=1; + CHRmask8[0]&=1; + GameStateRestore=MHROMRestore; +} + +static void UNROMRestore(int version) +{ + setprg16(0x8000,latche); +} + +static DECLFW(UNROMWrite) +{ + setprg16(0x8000,V); + latche=V; +} + +static void UNROMReset(void) +{ + setprg16(0x8000,0); + setprg16(0xc000,~0); + setvram8(CHRRAM); + SetWriteHandler(0x8000,0xffff,UNROMWrite); + SetReadHandler(0x8000,0xFFFF,CartBR); + latche=0; +} + +void UNROM_Init(void) +{ + BoardPower=UNROMReset; + PRGmask16[0]&=7; + AddExState(&latche, 1, 0, "LATC"); + AddExState(CHRRAM, 8192, 0, "CHRR"); + GameStateRestore=UNROMRestore; +} diff --git a/boards/super24.c b/boards/super24.c new file mode 100644 index 0000000..64af337 --- /dev/null +++ b/boards/super24.c @@ -0,0 +1,236 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" +//undef printf + +static int32 IRQCount,IRQLatch; +static uint8 IRQa,resetmode,mbia; +static uint8 sizer,bigbank,bigbank2; + +static uint8 DRegBuf[8],MMC3_cmd; + +static int masko8[8]={63,31,15,1,3,0,0,0}; +//static int masko1[8]={511,255,127,7,7,0,0,0}; + +static void swsetprg8(uint32 A, uint32 V) +{ + V&=masko8[sizer&7]; + V|=(bigbank*2); + setprg8r((V/64)&15,A,V); +} + +static void swsetchr1(uint32 A, uint32 V) +{ + if(sizer&0x20) + setchr1r(0x10,A,V); + else + { +// V&=masko1[sizer&7]; + V|=bigbank2*8; + setchr1r((V/512)&15,A,V); + } +} + +static void swsetchr2(uint32 A, uint32 V) +{ + if(sizer&0x20) + setchr2r(0x10,A,V); + else + { + //V&=masko1[sizer&7]>>1; + V|=bigbank2*4; + setchr2r((V/256)&15,A,V); + } +} + +static void Sup24_hb(void) +{ + if(ScreenON || SpriteON) + { + resetmode=0; + if(IRQCount>=0) + { + IRQCount--; + if(IRQCount<0) + { + if(IRQa) + { + resetmode = 1; + X6502_IRQBegin(FCEU_IQEXT); + } + } + } + } +} + +static DECLFW(Sup24IRQWrite) +{ + switch(A&0xE001) + { + case 0xc000:IRQLatch=V; + if(resetmode==1) + IRQCount=IRQLatch; + break; + case 0xc001:resetmode=1; + IRQCount=IRQLatch; + break; + case 0xE000:IRQa=0;X6502_IRQEnd(FCEU_IQEXT); + if(resetmode==1) + {IRQCount=IRQLatch;} + break; + case 0xE001:IRQa=1; + if(resetmode==1) + {IRQCount=IRQLatch;} + break; + } +} + +static INLINE void FixMMC3PRG(int V) +{ + swsetprg8(0xA000,DRegBuf[7]); + swsetprg8(0xE000,~0); + if(V&0x40) + { + swsetprg8(0xC000,DRegBuf[6]); + swsetprg8(0x8000,~1); + } + else + { + swsetprg8(0x8000,DRegBuf[6]); + swsetprg8(0xC000,~1); + } +} + +static INLINE void FixMMC3CHR(int V) +{ + int cbase=(V&0x80)<<5; + swsetchr2((cbase^0x000),DRegBuf[0]>>1); + swsetchr2((cbase^0x800),DRegBuf[1]>>1); + swsetchr1(cbase^0x1000,DRegBuf[2]); + swsetchr1(cbase^0x1400,DRegBuf[3]); + swsetchr1(cbase^0x1800,DRegBuf[4]); + swsetchr1(cbase^0x1c00,DRegBuf[5]); +} + +static DECLFW(Super24hiwrite) +{ + //printf("$%04x:$%02x, %d\n",A,V,scanline); + switch(A&0xE001) + { + case 0x8000: + if((V&0x40) != (MMC3_cmd&0x40)) + FixMMC3PRG(V); + if((V&0x80) != (MMC3_cmd&0x80)) + FixMMC3CHR(V); + MMC3_cmd = V; + break; + + case 0x8001: + { + int cbase=(MMC3_cmd&0x80)<<5; + DRegBuf[MMC3_cmd&0x7]=V; + switch(MMC3_cmd&0x07) + { + case 0: V>>=1;swsetchr2((cbase^0x000),V);break; + case 1: V>>=1;swsetchr2((cbase^0x800),V);break; + case 2: swsetchr1(cbase^0x1000,V); break; + case 3: swsetchr1(cbase^0x1400,V); break; + case 4: swsetchr1(cbase^0x1800,V); break; + case 5: swsetchr1(cbase^0x1C00,V); break; + case 6: if (MMC3_cmd&0x40) swsetprg8(0xC000,V); + else swsetprg8(0x8000,V); + break; + case 7: swsetprg8(0xA000,V); + break; + } + } + break; + + case 0xA000: + mbia=V; + setmirror((V&1)^1); + break; + } +} + + +DECLFW(Super24Write) +{ + //printf("$%04x:$%02x\n",A,V); + switch(A) + { + case 0x5ff0:sizer=V; + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); + break; + case 0x5FF1: + bigbank=V; + FixMMC3PRG(MMC3_cmd); + break; + case 0x5FF2: + bigbank2=V; + FixMMC3CHR(MMC3_cmd); + break; + } +} + +static void Super24Reset(void) +{ + SetWriteHandler(0x8000,0xBFFF,Super24hiwrite); + SetWriteHandler(0x5000,0x7FFF,Super24Write); + SetWriteHandler(0xC000,0xFFFF,Sup24IRQWrite); + SetReadHandler(0x8000,0xFFFF,CartBR); + GameHBIRQHook=Sup24_hb; + IRQCount=IRQLatch=IRQa=resetmode=0; + sizer=0x24; + bigbank=159; + bigbank2=0; + + MMC3_cmd=0; + DRegBuf[6]=0; + DRegBuf[7]=1; + + FixMMC3PRG(0); + FixMMC3CHR(0); +} + +static void MrRestore(int version) +{ + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); + setmirror((mbia&1)^1); +} + +void Super24_Init(void) +{ + BoardPower=Super24Reset; + SetupCartCHRMapping(0x10, GameMemBlock, 8192, 1); + GameStateRestore=MrRestore; + + AddExState(GameMemBlock, 8192, 0, "CHRR"); + AddExState(DRegBuf, 8, 0, "DREG"); + AddExState(&IRQCount, 4, 1, "IRQC"); + AddExState(&IRQLatch, 4, 1, "IQL1"); + AddExState(&IRQa, 1, 0, "IRQA"); + AddExState(&sizer, 1, 0, "SIZA"); + AddExState(&bigbank, 1, 0, "BIG1"); + AddExState(&bigbank2, 1, 0, "BIG2"); +} diff --git a/boards/supervision.c b/boards/supervision.c new file mode 100644 index 0000000..fc22599 --- /dev/null +++ b/boards/supervision.c @@ -0,0 +1,76 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + +#define CHRRAM (GameMemBlock+16) + +static void DoSuper(void) +{ + setprg8r((GameMemBlock[0]&0xC)>>2,0x6000,((GameMemBlock[0]&0x3)<<4)|0xF); + if(GameMemBlock[0]&0x10) + { + setprg16r((GameMemBlock[0]&0xC)>>2,0x8000,((GameMemBlock[0]&0x3)<<3)|(GameMemBlock[1]&7)); + setprg16r((GameMemBlock[0]&0xC)>>2,0xc000,((GameMemBlock[0]&0x3)<<3)|7); + } + else + setprg32r(4,0x8000,0); + + setmirror(((GameMemBlock[0]&0x20)>>5)^1); +} + +static DECLFW(SuperWrite) +{ + if(!(GameMemBlock[0]&0x10)) + { + GameMemBlock[0]=V; + DoSuper(); + } +} + +static DECLFW(SuperHi) +{ + GameMemBlock[1]=V; + DoSuper(); +} + +static void SuperReset(void) +{ + SetWriteHandler(0x6000,0x7FFF,SuperWrite); + SetWriteHandler(0x8000,0xFFFF,SuperHi); + SetReadHandler(0x6000,0xFFFF,CartBR); + GameMemBlock[0]=GameMemBlock[1]=0; + setprg32r(4,0x8000,0); + setvram8(CHRRAM); +} + +static void SuperRestore(int version) +{ + DoSuper(); +} + +void Supervision16_Init(void) +{ + AddExState(&GameMemBlock[0], 1, 0,"L1"); + AddExState(&GameMemBlock[1], 1, 0,"L2"); + AddExState(CHRRAM, 8192, 0, "CHRR"); + BoardPower=SuperReset; + GameStateRestore=SuperRestore; +} diff --git a/cart.c b/cart.c new file mode 100644 index 0000000..030e764 --- /dev/null +++ b/cart.c @@ -0,0 +1,590 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include /* For GG loading code. */ + +#include "types.h" +#include "version.h" +#include "fce.h" +#include "cart.h" +#include "memory.h" + +#include "general.h" +#include "svga.h" + +/* + This file contains all code for coordinating the mapping in of the + address space external to the NES. + It's also (ab)used by the NSF code. +*/ + +uint8 *Page[32],*VPage[8]; +uint8 **VPageR=VPage; +uint8 *VPageG[8]; +uint8 *MMC5SPRVPage[8]; +uint8 *MMC5BGVPage[8]; + +/* 16 are (sort of) reserved for UNIF/iNES and 16 to map other stuff. */ + +static int CHRram[32]; +static int PRGram[32]; + +uint8 *PRGptr[32]; +uint8 *CHRptr[32]; + +uint32 PRGsize[32]; +uint32 CHRsize[32]; + +uint32 PRGmask2[32]; +uint32 PRGmask4[32]; +uint32 PRGmask8[32]; +uint32 PRGmask16[32]; +uint32 PRGmask32[32]; + +uint32 CHRmask1[32]; +uint32 CHRmask2[32]; +uint32 CHRmask4[32]; +uint32 CHRmask8[32]; + +int geniestage=0; + +int modcon; + +uint8 genieval[3]; +uint8 geniech[3]; + +uint32 genieaddr[3]; + +static INLINE void setpageptr(int s, uint32 A, uint8 *p) +{ + uint32 AB=A>>11; + int x; + + for(x=(s>>1)-1;x>=0;x--) + Page[AB+x]=p-A; +} + +static char nothing[8192]; +void ResetCartMapping(void) +{ + int x; + + for(x=0;x<32;x++) + { + Page[x]=nothing-x*2048; + PRGptr[x]=CHRptr[x]=0; + PRGsize[x]=CHRsize[x]=0; + } + for(x=0;x<8;x++) + { + MMC5SPRVPage[x]=MMC5BGVPage[x]=VPageR[x]=nothing-0x400*x; + } + +} + +void SetupCartPRGMapping(int chip, uint8 *p, uint32 size, int ram) +{ + PRGptr[chip]=p; + PRGsize[chip]=size; + + PRGmask2[chip]=(size>>11)-1; + PRGmask4[chip]=(size>>12)-1; + PRGmask8[chip]=(size>>13)-1; + PRGmask16[chip]=(size>>14)-1; + PRGmask32[chip]=(size>>15)-1; + + PRGram[chip]=ram?1:0; +} + +void SetupCartCHRMapping(int chip, uint8 *p, uint32 size, int ram) +{ + CHRptr[chip]=p; + CHRsize[chip]=size; + + CHRmask1[chip]=(size>>10)-1; + CHRmask2[chip]=(size>>11)-1; + CHRmask4[chip]=(size>>12)-1; + CHRmask8[chip]=(size>>13)-1; + + CHRram[chip]=ram; +} + +DECLFR(CartBR) +{ + return Page[A>>11][A]; +} + +void FASTAPASS(3) GINLINE setprg2r(int r, unsigned int A, unsigned int V) +{ + if(!PRGptr[r]) return; + V&=PRGmask2[r]; + + setpageptr(2,A,(&PRGptr[r][V<<11])); +} + +void FASTAPASS(2) setprg2(uint32 A, uint32 V) +{ + setprg2r(0,A,V); +} + +void FASTAPASS(3) GINLINE setprg4r(int r, unsigned int A, unsigned int V) +{ + if(!PRGptr[r]) return; + V&=PRGmask4[r]; + setpageptr(4,A,(&PRGptr[r][V<<12])); +} + +void FASTAPASS(2) setprg4(uint32 A, uint32 V) +{ + setprg4r(0,A,V); +} + +void FASTAPASS(3) GINLINE setprg8r(int r, unsigned int A, unsigned int V) +{ + if(!PRGptr[r]) return; + + if(PRGsize[r]>=8192) + { + V&=PRGmask8[r]; + setpageptr(8,A,(&PRGptr[r][V<<13])); + } + else + { + uint32 VA=V<<2; + int x; + for(x=0;x<4;x++) + setpageptr(2,A+(x<<11),(&PRGptr[r][((VA+x)&PRGmask2[r])<<11])); + } +} + +void FASTAPASS(2) setprg8(uint32 A, uint32 V) +{ + setprg8r(0,A,V); +} + +void FASTAPASS(3) GINLINE setprg16r(int r, unsigned int A, unsigned int V) +{ + if(!PRGptr[r]) return; + + if(PRGsize[r]>=16384) + { + V&=PRGmask16[r]; + setpageptr(16,A,(&PRGptr[r][V<<14])); + } + else + { + uint32 VA=V<<3; + int x; + + for(x=0;x<8;x++) + setpageptr(2,A+(x<<11),(&PRGptr[r][((VA+x)&PRGmask2[r])<<11])); + } +} + +void FASTAPASS(2) setprg16(uint32 A, uint32 V) +{ + setprg16r(0,A,V); +} + +void FASTAPASS(3) GINLINE setprg32r(int r,unsigned int A, unsigned int V) +{ + if(!PRGptr[r]) return; + if(PRGsize[r]>=32768) + { + V&=PRGmask32[r]; + setpageptr(32,A,(&PRGptr[r][V<<15])); + } + else + { + uint32 VA=V<<4; + int x; + + for(x=0;x<16;x++) + setpageptr(2,A+(x<<11),(&PRGptr[r][((VA+x)&PRGmask2[r])<<11])); + } +} + +void FASTAPASS(2) setprg32(uint32 A, uint32 V) +{ + setprg32r(0,A,V); +} + +void GINLINE FASTAPASS(3) setchr1r(int r, unsigned int A, unsigned int V) +{ + if(!CHRptr[r]) return; + V&=CHRmask1[r]; + if(CHRram[r]) + PPUCHRRAM|=(1<<(A>>10)); + else + PPUCHRRAM&=~(1<<(A>>10)); + VPageR[(A)>>10]=&CHRptr[r][(V)<<10]-(A); +} + +void GINLINE FASTAPASS(3) setchr2r(int r, unsigned int A, unsigned int V) +{ + if(!CHRptr[r]) return; + V&=CHRmask2[r]; + VPageR[(A)>>10]=VPageR[((A)>>10)+1]=&CHRptr[r][(V)<<11]-(A); + if(CHRram[r]) + PPUCHRRAM|=(3<<(A>>10)); + else + PPUCHRRAM&=~(3<<(A>>10)); +} + +void GINLINE FASTAPASS(3) setchr4r(int r, unsigned int A, unsigned int V) +{ + if(!CHRptr[r]) return; + V&=CHRmask4[r]; + VPageR[(A)>>10]=VPageR[((A)>>10)+1]= + VPageR[((A)>>10)+2]=VPageR[((A)>>10)+3]=&CHRptr[r][(V)<<12]-(A); + if(CHRram[r]) + PPUCHRRAM|=(15<<(A>>10)); + else + PPUCHRRAM&=~(15<<(A>>10)); +} + +void GINLINE FASTAPASS(2) setchr8r(int r, unsigned int V) +{ + int x; + + if(!CHRptr[r]) return; + V&=CHRmask8[r]; + for(x=7;x>=0;x--) + VPageR[x]=&CHRptr[r][V<<13]; + if(CHRram[r]) + PPUCHRRAM|=(255); + else + PPUCHRRAM&=~(255); +} + +void FASTAPASS(2) setchr1(unsigned int A, unsigned int V) +{ + setchr1r(0,A,V); +} + +void FASTAPASS(2) setchr2(unsigned int A, unsigned int V) +{ + setchr2r(0,A,V); +} + +void FASTAPASS(2) setchr4(unsigned int A, unsigned int V) +{ + setchr4r(0,A,V); +} + +void FASTAPASS(1) setchr8(unsigned int V) +{ + setchr8r(0,V); +} + +void FASTAPASS(1) setvram8(uint8 *p) +{ + int x; + for(x=7;x>=0;x--) + VPageR[x]=p; + PPUCHRRAM|=255; +} + +void FASTAPASS(2) setvram4(uint32 A, uint8 *p) +{ + int x; + for(x=3;x>=0;x--) + VPageR[(A>>10)+x]=p-A; + PPUCHRRAM|=(15<<(A>>10)); +} + +void FASTAPASS(3) setvramb1(uint8 *p, uint32 A, uint32 b) +{ + VPageR[A>>10]=p-A+(b<<10); + PPUCHRRAM|=(1<<(A>>10)); +} + +void FASTAPASS(3) setvramb2(uint8 *p, uint32 A, uint32 b) +{ + VPageR[(A>>10)]=VPageR[(A>>10)+1]=p-A+(b<<11); + PPUCHRRAM|=(3<<(A>>10)); +} + +void FASTAPASS(3) setvramb4(uint8 *p, uint32 A, uint32 b) +{ + int x; + + for(x=3;x>=0;x--) + VPageR[(A>>10)+x]=p-A+(b<<12); + PPUCHRRAM|=(15<<(A>>10)); +} + +void FASTAPASS(2) setvramb8(uint8 *p, uint32 b) +{ + int x; + + for(x=7;x>=0;x--) + VPageR[x]=p+(b<<13); + PPUCHRRAM|=255; +} + +/* This function can be called without calling SetupCartMirroring(). */ + +void FASTAPASS(3) setntamem(uint8 *p, int ram, uint32 b) +{ + vnapage[b]=p; + PPUNTARAM&=~(1<>2]=V;break; + + case 0x800b: + case 0x8007: + case 0x8003:geniech[((A-3)&0xF)>>2]=V;break; + + case 0x800a: + case 0x8006: + case 0x8002:genieaddr[((A-2)&0xF)>>2]&=0xFF00;genieaddr[((A-2)&0xF)>>2]|=V;break; + + case 0x8009: + case 0x8005: + case 0x8001:genieaddr[((A-1)&0xF)>>2]&=0xFF;genieaddr[((A-1)&0xF)>>2]|=(V|0x80)<<8;break; + + case 0x8000:if(!V) + FixGenieMap(); + else + { + modcon=V^0xFF; + if(V==0x71) + modcon=0; + } + break; + } +} + +static readfunc GenieBackup[3]; + +static DECLFR(GenieFix1) +{ + uint8 r=GenieBackup[0](A); + + if((modcon>>1)&1) // No check + return genieval[0]; + else if(r==geniech[0]) + return genieval[0]; + + return r; +} + +static DECLFR(GenieFix2) +{ + uint8 r=GenieBackup[1](A); + + if((modcon>>2)&1) // No check + return genieval[1]; + else if(r==geniech[1]) + return genieval[1]; + + return r; +} + +static DECLFR(GenieFix3) +{ + uint8 r=GenieBackup[2](A); + + if((modcon>>3)&1) // No check + return genieval[2]; + else if(r==geniech[2]) + return genieval[2]; + + return r; +} + + +void FixGenieMap(void) +{ + int x; + + geniestage=2; + + for(x=0;x<8;x++) + VPage[x]=VPageG[x]; + + VPageR=VPage; + FlushGenieRW(); + + for(x=0;x<3;x++) + if((modcon>>(4+x))&1) + { + readfunc tmp[3]={GenieFix1,GenieFix2,GenieFix3}; + GenieBackup[x]=GetReadHandler(genieaddr[x]); + SetReadHandler(genieaddr[x],genieaddr[x],tmp[x]); + } +} + +void GeniePower(void) +{ + uint32 x; + + if(!geniestage) + return; + + geniestage=1; + for(x=0;x<3;x++) + { + genieval[x]=0xFF; + geniech[x]=0xFF; + genieaddr[x]=0xFFFF; + } + modcon=0; + + SetWriteHandler(0x8000,0xFFFF,GenieWrite); + SetReadHandler(0x8000,0xFFFF,GenieRead); + + for(x=0;x<8;x++) + VPage[x]=GENIEROM+4096-0x400*x; + + if(AllocGenieRW()) + VPageR=VPageG; + else + geniestage=2; +} + + diff --git a/cart.h b/cart.h new file mode 100644 index 0000000..b066771 --- /dev/null +++ b/cart.h @@ -0,0 +1,70 @@ +extern uint8 *Page[32],*VPage[8],*MMC5SPRVPage[8],*MMC5BGVPage[8]; + +void ResetCartMapping(void); +void SetupCartPRGMapping(int chip, uint8 *p, uint32 size, int ram); +void SetupCartCHRMapping(int chip, uint8 *p, uint32 size, int ram); +void SetupCartMirroring(int m, int hard, uint8 *extra); + +DECLFR(CartBR); +extern uint8 *PRGptr[32]; +extern uint8 *CHRptr[32]; + +extern uint32 PRGsize[32]; +extern uint32 CHRsize[32]; + +extern uint32 PRGmask2[32]; +extern uint32 PRGmask4[32]; +extern uint32 PRGmask8[32]; +extern uint32 PRGmask16[32]; +extern uint32 PRGmask32[32]; + +extern uint32 CHRmask1[32]; +extern uint32 CHRmask2[32]; +extern uint32 CHRmask4[32]; +extern uint32 CHRmask8[32]; + +void FASTAPASS(2) setprg2(uint32 A, uint32 V); +void FASTAPASS(2) setprg4(uint32 A, uint32 V); +void FASTAPASS(2) setprg8(uint32 A, uint32 V); +void FASTAPASS(2) setprg16(uint32 A, uint32 V); +void FASTAPASS(2) setprg32(uint32 A, uint32 V); + +void FASTAPASS(3) setprg2r(int r, unsigned int A, unsigned int V); +void FASTAPASS(3) setprg4r(int r, unsigned int A, unsigned int V); +void FASTAPASS(3) setprg8r(int r, unsigned int A, unsigned int V); +void FASTAPASS(3) setprg16r(int r, unsigned int A, unsigned int V); +void FASTAPASS(3) setprg32r(int r, unsigned int A, unsigned int V); + +void FASTAPASS(3) setchr1r(int r, unsigned int A, unsigned int V); +void FASTAPASS(3) setchr2r(int r, unsigned int A, unsigned int V); +void FASTAPASS(3) setchr4r(int r, unsigned int A, unsigned int V); +void FASTAPASS(2) setchr8r(int r, unsigned int V); + +void FASTAPASS(2) setchr1(unsigned int A, unsigned int V); +void FASTAPASS(2) setchr2(unsigned int A, unsigned int V); +void FASTAPASS(2) setchr4(unsigned int A, unsigned int V); +void FASTAPASS(2) setchr8(unsigned int V); + +void FASTAPASS(2) setvram4(uint32 A, uint8 *p); +void FASTAPASS(1) setvram8(uint8 *p); + +void FASTAPASS(3) setvramb1(uint8 *p, uint32 A, uint32 b); +void FASTAPASS(3) setvramb2(uint8 *p, uint32 A, uint32 b); +void FASTAPASS(3) setvramb4(uint8 *p, uint32 A, uint32 b); +void FASTAPASS(2) setvramb8(uint8 *p, uint32 b); + +void FASTAPASS(1) setmirror(int t); +void setmirrorw(int a, int b, int c, int d); +void FASTAPASS(3) setntamem(uint8 *p, int ram, uint32 b); + +#define MI_H 0 +#define MI_V 1 +#define MI_0 2 +#define MI_1 3 + +extern int geniestage; + +void GeniePower(void); + +void OpenGenie(void); +void CloseGenie(void); diff --git a/cheat.c b/cheat.c new file mode 100644 index 0000000..d789d74 --- /dev/null +++ b/cheat.c @@ -0,0 +1,565 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#include "types.h" +#include "x6502.h" +#include "cheat.h" +#include "fce.h" +#include "svga.h" +#include "general.h" +#include "cart.h" +#include "memory.h" + +static uint8 *CheatRPtrs[64]; + +void FCEU_CheatResetRAM(void) +{ + int x; + + for(x=0;x<64;x++) + CheatRPtrs[x]=0; +} + +void FCEU_CheatAddRAM(int s, uint32 A, uint8 *p) +{ + uint32 AB=A>>10; + int x; + + for(x=s-1;x>=0;x--) + CheatRPtrs[AB+x]=p-A; +} + + +struct CHEATF { + struct CHEATF *next; + char *name; + uint16 addr; + uint8 val; + int status; +}; + +struct CHEATF *cheats=0,*cheatsl=0; + + +#define CHEATC_NONE 0x8000 +#define CHEATC_EXCLUDED 0x4000 +#define CHEATC_NOSHOW 0xC000 + +static uint16 *CheatComp=0; +static int savecheats; + +static int AddCheatEntry(char *name, uint32 addr, uint8 val, int status); + +static void CheatMemErr(void) +{ + FCEUD_PrintError("Error allocating memory for cheat data."); +} + +/* This function doesn't allocate any memory for "name" */ +static int AddCheatEntry(char *name, uint32 addr, uint8 val, int status) +{ + struct CHEATF *temp; + if(!(temp=malloc(sizeof(struct CHEATF)))) + { + CheatMemErr(); + return(0); + } + temp->name=name; + temp->addr=addr; + temp->val=val; + temp->status=status; + + temp->next=0; + + if(cheats) + { + cheatsl->next=temp; + cheatsl=temp; + } + else + cheats=cheatsl=temp; + + return(1); +} + +void LoadGameCheats(void) +{ + FILE *fp; + char *name=0; + unsigned int addr; + unsigned int val; + unsigned int status; + int x; + + char linebuf[256+4+2+2+1+1]; /* 256 for name, 4 for address, 2 for value, 2 for semicolons, 1 for status, 1 for null */ + char namebuf[257]; + int tc=0; + + savecheats=0; + if(!(fp=fopen(FCEU_MakeFName(FCEUMKF_CHEAT,0,0),"rb"))) + return; + + while(fgets(linebuf,256+4+2+2+1+1,fp)>0) + { + addr=val=status=0; + namebuf[0]=0; // If the cheat doesn't have a name... + if(linebuf[0]==':') + { + strncpy(namebuf,&linebuf[1+4+2+2],257); + if(sscanf(&linebuf[1],"%04x%*[:]%02x",&addr,&val)!=2) + continue; + status=0; + } + else + { + strncpy(namebuf,&linebuf[4+2+2],257); + if(sscanf(linebuf,"%04x%*[:]%02x",&addr,&val)!=2) continue; + status=1; + } + for(x=0;x<257;x++) + { + if(namebuf[x]==10 || namebuf[x]==13) + { + namebuf[x]=0; + break; + } + } + namebuf[256]=0; + if(!(name=malloc(strlen(namebuf)+1))) + CheatMemErr(); + else + { + strcpy(name,namebuf); + AddCheatEntry(name,addr,val,status); + tc++; + } + } + fclose(fp); +} + + +void FlushGameCheats(void) +{ + if(CheatComp) + { + free(CheatComp); + CheatComp=0; + } + + if(!savecheats) + { + if(cheats) + { + struct CHEATF *next=cheats; + for(;;) + { + next=next->next; + free(cheats->name); + free(cheats); + if(!next) break; + } + cheats=cheatsl=0; + } + } + else + { + if(cheats) + { + struct CHEATF *next=cheats; + FILE *fp; + if((fp=fopen(FCEU_MakeFName(FCEUMKF_CHEAT,0,0),"wb"))) + { + for(;;) + { + struct CHEATF *t; + if(next->status) + fprintf(fp,"%04x:%02x:%s\n",next->addr,next->val,next->name); + else + fprintf(fp,":%04x:%02x:%s\n",next->addr,next->val,next->name); + free(next->name); + t=next; + next=next->next; + free(t); + if(!next) break; + } + fclose(fp); + } + else + FCEUD_PrintError("Error saving cheats."); + cheats=cheatsl=0; + } + else + remove(FCEU_MakeFName(FCEUMKF_CHEAT,0,0)); + } +} + + +int FCEUI_AddCheat(char *name, uint32 addr, uint8 val) +{ + char *t; + + if(!(t=malloc(strlen(name)+1))) + { + CheatMemErr(); + return(0); + } + strcpy(t,name); + if(!AddCheatEntry(t,addr,val,1)) + { + free(t); + return(0); + } + savecheats=1; + return(1); +} + +int FCEUI_DelCheat(uint32 which) +{ + struct CHEATF *prev; + struct CHEATF *cur; + uint32 x=0; + + for(prev=0,cur=cheats;;) + { + if(x==which) // Remove this cheat. + { + if(prev) // Update pointer to this cheat. + { + if(cur->next) // More cheats. + prev->next=cur->next; + else // No more. + { + prev->next=0; + cheatsl=prev; // Set the previous cheat as the last cheat. + } + } + else // This is the first cheat. + { + if(cur->next) // More cheats + cheats=cur->next; + else + cheats=cheatsl=0; // No (more) cheats. + } + free(cur->name); // Now that all references to this cheat are removed, + free(cur); // free the memory. + break; + } // *END REMOVE THIS CHEAT* + + + if(!cur->next) // No more cheats to go through(this shouldn't ever happen...) + return(0); + prev=cur; + cur=prev->next; + x++; + } + + savecheats=1; + return(1); +} + +void ApplyPeriodicCheats(void) +{ + struct CHEATF *cur=cheats; + if(!cur) return; + + for(;;) + { + if(cur->status) + if(CheatRPtrs[cur->addr>>10]) + CheatRPtrs[cur->addr>>10][cur->addr]=cur->val; + if(cur->next) + cur=cur->next; + else + break; + } +} + + +void FCEUI_ListCheats(int (*callb)(char *name, uint32 a, uint8 v, int s)) +{ + struct CHEATF *next=cheats; + + while(next) + { + if(!callb(next->name,next->addr,next->val,next->status)) break; + next=next->next; + } +} + +int FCEUI_GetCheat(uint32 which, char **name, uint32 *a, uint8 *v, int *s) +{ + struct CHEATF *next=cheats; + uint32 x=0; + + while(next) + { + if(x==which) + { + if(name) + *name=next->name; + if(a) + *a=next->addr; + if(v) + *v=next->val; + if(s) + *s=next->status; + + return(1); + } + next=next->next; + x++; + } + return(0); +} + +/* name can be NULL if the name isn't going to be changed. */ +/* same goes for a, v, and s(except the values of each one must be <0) */ + +int FCEUI_SetCheat(uint32 which, char *name, int32 a, int32 v, int s) +{ + struct CHEATF *next=cheats; + uint32 x=0; + + while(next) + { + if(x==which) + { + if(name) + { + char *t; + + if((t=realloc(next->name,strlen(name+1)))) + { + next->name=t; + strcpy(next->name,name); + } + else + return(0); + } + if(a>=0) + next->addr=a; + if(v>=0) + next->val=v; + if(s>=0) + next->status=s; + savecheats=1; + return(1); + } + next=next->next; + x++; + } + return(0); +} + + +static int InitCheatComp(void) +{ + uint32 x; + + CheatComp=malloc(65536*sizeof(uint16)); + if(!CheatComp) + { + CheatMemErr(); + return(0); + } + for(x=0;x<65536;x++) + CheatComp[x]=CHEATC_NONE; + + return(1); +} + +void FCEUI_CheatSearchSetCurrentAsOriginal(void) +{ + uint32 x; + for(x=0x000;x<0x10000;x++) + if(!(CheatComp[x]&CHEATC_NOSHOW)) + { + if(CheatRPtrs[x>>10]) + CheatComp[x]=CheatRPtrs[x>>10][x]; + else + CheatComp[x]|=CHEATC_NONE; + } +} + +void FCEUI_CheatSearchShowExcluded(void) +{ + uint32 x; + + for(x=0x000;x<0x10000;x++) + CheatComp[x]&=~CHEATC_EXCLUDED; +} + + +int32 FCEUI_CheatSearchGetCount(void) +{ + uint32 x,c=0; + + if(CheatComp) + { + for(x=0x0000;x<0x10000;x++) + if(!(CheatComp[x]&CHEATC_NOSHOW) && CheatRPtrs[x>>10]) + c++; + } + + return c; +} +/* This function will give the initial value of the search and the current value at a location. */ + +void FCEUI_CheatSearchGet(int (*callb)(uint32 a, uint8 last, uint8 current)) +{ + uint32 x; + + if(!CheatComp) + { + if(!InitCheatComp()) + CheatMemErr(); + return; + } + + for(x=0;x<0x10000;x++) + if(!(CheatComp[x]&CHEATC_NOSHOW) && CheatRPtrs[x>>10]) + if(!callb(x,CheatComp[x],CheatRPtrs[x>>10][x])) + break; +} + +void FCEUI_CheatSearchGetRange(uint32 first, uint32 last, int (*callb)(uint32 a, uint8 last, uint8 current)) +{ + uint32 x; + uint32 in=0; + + if(!CheatComp) + { + if(!InitCheatComp()) + CheatMemErr(); + return; + } + + for(x=0;x<0x10000;x++) + if(!(CheatComp[x]&CHEATC_NOSHOW) && CheatRPtrs[x>>10]) + { + if(in>=first) + if(!callb(x,CheatComp[x],CheatRPtrs[x>>10][x])) + break; + in++; + if(in>last) return; + } +} + +void FCEUI_CheatSearchBegin(void) +{ + uint32 x; + + if(!CheatComp) + { + if(!InitCheatComp()) + { + CheatMemErr(); + return; + } + } + for(x=0;x<0x10000;x++) + { + if(CheatRPtrs[x>>10]) + CheatComp[x]=CheatRPtrs[x>>10][x]; + else + CheatComp[x]=CHEATC_NONE; + } +} + + +static int INLINE CAbs(int x) +{ + if(x<0) + return(0-x); + return x; +} + +void FCEUI_CheatSearchEnd(int type, uint8 v1, uint8 v2) +{ + uint32 x; + + if(!CheatComp) + { + if(!InitCheatComp()) + { + CheatMemErr(); + return; + } + } + + + if(!type) // Change to a specific value. + { + for(x=0;x<0x10000;x++) + if(!(CheatComp[x]&CHEATC_NOSHOW)) + { + if(CheatComp[x]==v1 && CheatRPtrs[x>>10][x]==v2) + { + + } + else + CheatComp[x]|=CHEATC_EXCLUDED; + } + } + else if(type==1) // Search for relative change(between values). + { + for(x=0;x<0x10000;x++) + if(!(CheatComp[x]&CHEATC_NOSHOW)) + { + if(CheatComp[x]==v1 && CAbs(CheatComp[x]-CheatRPtrs[x>>10][x])==v2) + { + + } + else + CheatComp[x]|=CHEATC_EXCLUDED; + } + } + else if(type==2) // Purely relative change. + { + for(x=0x000;x<0x10000;x++) + if(!(CheatComp[x]&CHEATC_NOSHOW)) + { + if(CAbs(CheatComp[x]-CheatRPtrs[x>>10][x])==v2) + { + + } + else + CheatComp[x]|=CHEATC_EXCLUDED; + } + } + else if(type==3) // Any change. + { + for(x=0x000;x<0x10000;x++) + if(!(CheatComp[x]&CHEATC_NOSHOW)) + { + if(CheatComp[x]!=CheatRPtrs[x>>10][x]) + { + + } + else + CheatComp[x]|=CHEATC_EXCLUDED; + } + + } +} diff --git a/cheat.h b/cheat.h new file mode 100644 index 0000000..e930f52 --- /dev/null +++ b/cheat.h @@ -0,0 +1,6 @@ +void FCEU_CheatResetRAM(void); +void FCEU_CheatAddRAM(int s, uint32 A, uint8 *p); + +void LoadGameCheats(void); +void FlushGameCheats(void); +void ApplyPeriodicCheats(void); diff --git a/crc32.c b/crc32.c new file mode 100644 index 0000000..c4d272b --- /dev/null +++ b/crc32.c @@ -0,0 +1,100 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef ZLIB +#include "types.h" +#include "crc32.h" + +static uint32 CRC32Table[256] = { + 0x00000000,0x77073096,0xee0e612c,0x990951ba, + 0x076dc419,0x706af48f,0xe963a535,0x9e6495a3, + 0x0edb8832,0x79dcb8a4,0xe0d5e91e,0x97d2d988, + 0x09b64c2b,0x7eb17cbd,0xe7b82d07,0x90bf1d91, + 0x1db71064,0x6ab020f2,0xf3b97148,0x84be41de, + 0x1adad47d,0x6ddde4eb,0xf4d4b551,0x83d385c7, + 0x136c9856,0x646ba8c0,0xfd62f97a,0x8a65c9ec, + 0x14015c4f,0x63066cd9,0xfa0f3d63,0x8d080df5, + 0x3b6e20c8,0x4c69105e,0xd56041e4,0xa2677172, + 0x3c03e4d1,0x4b04d447,0xd20d85fd,0xa50ab56b, + 0x35b5a8fa,0x42b2986c,0xdbbbc9d6,0xacbcf940, + 0x32d86ce3,0x45df5c75,0xdcd60dcf,0xabd13d59, + 0x26d930ac,0x51de003a,0xc8d75180,0xbfd06116, + 0x21b4f4b5,0x56b3c423,0xcfba9599,0xb8bda50f, + 0x2802b89e,0x5f058808,0xc60cd9b2,0xb10be924, + 0x2f6f7c87,0x58684c11,0xc1611dab,0xb6662d3d, + 0x76dc4190,0x01db7106,0x98d220bc,0xefd5102a, + 0x71b18589,0x06b6b51f,0x9fbfe4a5,0xe8b8d433, + 0x7807c9a2,0x0f00f934,0x9609a88e,0xe10e9818, + 0x7f6a0dbb,0x086d3d2d,0x91646c97,0xe6635c01, + 0x6b6b51f4,0x1c6c6162,0x856530d8,0xf262004e, + 0x6c0695ed,0x1b01a57b,0x8208f4c1,0xf50fc457, + 0x65b0d9c6,0x12b7e950,0x8bbeb8ea,0xfcb9887c, + 0x62dd1ddf,0x15da2d49,0x8cd37cf3,0xfbd44c65, + 0x4db26158,0x3ab551ce,0xa3bc0074,0xd4bb30e2, + 0x4adfa541,0x3dd895d7,0xa4d1c46d,0xd3d6f4fb, + 0x4369e96a,0x346ed9fc,0xad678846,0xda60b8d0, + 0x44042d73,0x33031de5,0xaa0a4c5f,0xdd0d7cc9, + 0x5005713c,0x270241aa,0xbe0b1010,0xc90c2086, + 0x5768b525,0x206f85b3,0xb966d409,0xce61e49f, + 0x5edef90e,0x29d9c998,0xb0d09822,0xc7d7a8b4, + 0x59b33d17,0x2eb40d81,0xb7bd5c3b,0xc0ba6cad, + 0xedb88320,0x9abfb3b6,0x03b6e20c,0x74b1d29a, + 0xead54739,0x9dd277af,0x04db2615,0x73dc1683, + 0xe3630b12,0x94643b84,0x0d6d6a3e,0x7a6a5aa8, + 0xe40ecf0b,0x9309ff9d,0x0a00ae27,0x7d079eb1, + 0xf00f9344,0x8708a3d2,0x1e01f268,0x6906c2fe, + 0xf762575d,0x806567cb,0x196c3671,0x6e6b06e7, + 0xfed41b76,0x89d32be0,0x10da7a5a,0x67dd4acc, + 0xf9b9df6f,0x8ebeeff9,0x17b7be43,0x60b08ed5, + 0xd6d6a3e8,0xa1d1937e,0x38d8c2c4,0x4fdff252, + 0xd1bb67f1,0xa6bc5767,0x3fb506dd,0x48b2364b, + 0xd80d2bda,0xaf0a1b4c,0x36034af6,0x41047a60, + 0xdf60efc3,0xa867df55,0x316e8eef,0x4669be79, + 0xcb61b38c,0xbc66831a,0x256fd2a0,0x5268e236, + 0xcc0c7795,0xbb0b4703,0x220216b9,0x5505262f, + 0xc5ba3bbe,0xb2bd0b28,0x2bb45a92,0x5cb36a04, + 0xc2d7ffa7,0xb5d0cf31,0x2cd99e8b,0x5bdeae1d, + 0x9b64c2b0,0xec63f226,0x756aa39c,0x026d930a, + 0x9c0906a9,0xeb0e363f,0x72076785,0x05005713, + 0x95bf4a82,0xe2b87a14,0x7bb12bae,0x0cb61b38, + 0x92d28e9b,0xe5d5be0d,0x7cdcefb7,0x0bdbdf21, + 0x86d3d2d4,0xf1d4e242,0x68ddb3f8,0x1fda836e, + 0x81be16cd,0xf6b9265b,0x6fb077e1,0x18b74777, + 0x88085ae6,0xff0f6a70,0x66063bca,0x11010b5c, + 0x8f659eff,0xf862ae69,0x616bffd3,0x166ccf45, + 0xa00ae278,0xd70dd2ee,0x4e048354,0x3903b3c2, + 0xa7672661,0xd06016f7,0x4969474d,0x3e6e77db, + 0xaed16a4a,0xd9d65adc,0x40df0b66,0x37d83bf0, + 0xa9bcae53,0xdebb9ec5,0x47b2cf7f,0x30b5ffe9, + 0xbdbdf21c,0xcabac28a,0x53b39330,0x24b4a3a6, + 0xbad03605,0xcdd70693,0x54de5729,0x23d967bf, + 0xb3667a2e,0xc4614ab8,0x5d681b02,0x2a6f2b94, + 0xb40bbe37,0xc30c8ea1,0x5a05df1b,0x2d02ef8d +}; + +uint32 CalcCRC32(uint32 crc, uint8 *buf, uint32 len) +{ + crc=~crc; + + while(len--) + crc=(crc>>8)^CRC32Table[(crc&0xFF)^(*buf++)]; + return(~crc); +} +#endif diff --git a/crc32.h b/crc32.h new file mode 100644 index 0000000..fa1291c --- /dev/null +++ b/crc32.h @@ -0,0 +1,6 @@ +#ifdef ZLIB +#include +#define CalcCRC32 crc32 +#else +uint32 CalcCRC32(uint32 crc, uint8 *buf, uint32 len); +#endif diff --git a/debug.c b/debug.c new file mode 100644 index 0000000..206a58d --- /dev/null +++ b/debug.c @@ -0,0 +1,35 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include "types.h" +#include "fce.h" +#include "debug.h" + + + +void DumpMem(char *fname, uint32 start, uint32 end) +{ + FILE *fp=fopen(fname,"wb"); + for(;start<=end;start++) + fputc(ARead[start](start),fp); + fclose(fp); + +} diff --git a/debug.h b/debug.h new file mode 100644 index 0000000..85afa34 --- /dev/null +++ b/debug.h @@ -0,0 +1,2 @@ +void DumpMem(char *fname, uint32 start, uint32 end); + diff --git a/drawing.h b/drawing.h new file mode 100644 index 0000000..4e7bfec --- /dev/null +++ b/drawing.h @@ -0,0 +1,154 @@ +static void DrawDips(void) +{ + uint32 *dest; + int y,x; + + dest=(uint32 *)(XBuf+272*12+164); + for(y=24;y;y--,dest+=(272-72)>>2) + { + for(x=72>>2;x;x--,dest++) + *dest=0x80808080; + } + + dest=(uint32 *)(XBuf+272*(12+4)+164+6 ); + for(y=16;y;y--,dest+=(272>>2)-16) + for(x=8;x;x--) + { + *dest=0x81818181; + dest+=2; + } + + dest=(uint32 *)(XBuf+272*(12+4)+164+6 ); + for(x=0;x<8;x++,dest+=2) + { + uint32 *da=dest+(272>>2); + + if(!((vsdip>>x)&1)) + da+=(272>>2)*10; + + for(y=4;y;y--,da+=272>>2) + *da=0x80808080; + + } +} + +static void DrawMessage(void) +{ + if(howlong) + { + uint8 *t; + howlong--; + t=XBuf+(FSettings.LastSLine-29)*272+32; + if(t>=XBuf) + DrawTextTrans(t,272,errmsg,132); + } +} + +uint8 sstat[2541] = +{ +0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x80,0x80,0x80, +0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x83,0x80,0x80,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x83,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x83,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x83,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x83,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x81,0x81,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x80,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, +0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x83,0x80,0x80,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x83,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x81,0x83,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, +0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x83,0x80,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x81,0x81,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x81,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, +0x80,0x80,0x80,0x80,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x81,0x81,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x80,0x80,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x81,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x80,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x81,0x83,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, +0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x83,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x81,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x80,0x80,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x81,0x83,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x83,0x83, +0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x81,0x81,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x80,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x83,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x83,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x83,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83, +0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x83,0x80,0x80,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x81,0x83,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x83,0x80,0x80,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x81,0x83,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, +0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x80,0x80,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x81,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x83,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x83,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, +0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x83,0x80,0x80,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x81,0x83,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80 +}; + +uint8 fontdata2[2048] = +{ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7e,0x81,0xa5,0x81,0xbd,0x99,0x81,0x7e,0x7e,0xff,0xdb,0xff,0xc3,0xe7,0xff,0x7e,0x36,0x7f,0x7f,0x7f,0x3e,0x1c,0x08,0x00,0x08,0x1c,0x3e,0x7f,0x3e,0x1c,0x08,0x00,0x1c,0x3e,0x1c,0x7f,0x7f,0x3e,0x1c,0x3e,0x08,0x08,0x1c,0x3e,0x7f,0x3e,0x1c,0x3e,0x00,0x00,0x18,0x3c,0x3c,0x18,0x00,0x00,0xff,0xff,0xe7,0xc3,0xc3,0xe7,0xff,0xff,0x00,0x3c,0x66,0x42,0x42,0x66,0x3c,0x00,0xff,0xc3,0x99,0xbd,0xbd,0x99,0xc3,0xff,0xf0,0xe0,0xf0,0xbe,0x33,0x33,0x33,0x1e,0x3c,0x66,0x66,0x66,0x3c,0x18,0x7e,0x18,0xfc,0xcc,0xfc,0x0c,0x0c,0x0e,0x0f,0x07,0xfe,0xc6,0xfe,0xc6,0xc6,0xe6,0x67,0x03,0x99,0x5a,0x3c,0xe7,0xe7,0x3c,0x5a,0x99,0x01,0x07,0x1f,0x7f,0x1f,0x07,0x01,0x00,0x40,0x70,0x7c,0x7f,0x7c,0x70,0x40,0x00,0x18,0x3c,0x7e,0x18,0x18,0x7e,0x3c,0x18,0x66,0x66,0x66,0x66,0x66,0x00,0x66,0x00,0xfe,0xdb,0xdb,0xde,0xd8,0xd8,0xd8,0x00,0x7c,0xc6,0x1c,0x36,0x36,0x1c,0x33,0x1e,0x00,0x00,0x00,0x00,0x7e,0x7e,0x7e,0x00,0x18,0x3c,0x7e,0x18,0x7e,0x3c,0x18,0xff,0x18,0x3c,0x7e,0x18,0x18,0x18,0x18,0x00,0x18,0x18,0x18,0x18,0x7e,0x3c,0x18,0x00,0x00,0x18,0x30,0x7f,0x30,0x18,0x00,0x00,0x00,0x0c,0x06,0x7f,0x06,0x0c,0x00,0x00,0x00,0x00,0x03,0x03,0x03,0x7f,0x00,0x00,0x00,0x24,0x66,0xff,0x66,0x24,0x00,0x00,0x00,0x18,0x3c,0x7e,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0x7e,0x3c,0x18,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x1e,0x1e,0x0c,0x0c,0x00,0x0c,0x00,0x36,0x36,0x36,0x00,0x00,0x00,0x00,0x00,0x36,0x36,0x7f,0x36,0x7f,0x36,0x36,0x00,0x0c,0x3e,0x03,0x1e,0x30,0x1f,0x0c,0x00,0x00,0x63,0x33,0x18,0x0c,0x66,0x63,0x00,0x1c,0x36,0x1c,0x6e,0x3b,0x33,0x6e,0x00,0x06,0x06,0x03,0x00,0x00,0x00,0x00,0x00,0x18,0x0c,0x06,0x06,0x06,0x0c,0x18,0x00,0x06,0x0c,0x18,0x18,0x18,0x0c,0x06,0x00,0x00,0x66,0x3c,0xff,0x3c,0x66,0x00,0x00,0x00,0x0c,0x0c,0x3f,0x0c,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x0c,0x06,0x00,0x00,0x00,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x0c,0x00,0x60,0x30,0x18,0x0c,0x06,0x03,0x01,0x00,0x3e,0x63,0x73,0x7b,0x6f,0x67,0x3e,0x00,0x0c,0x0e,0x0c,0x0c,0x0c,0x0c,0x3f,0x00,0x1e,0x33,0x30,0x1c,0x06,0x33,0x3f,0x00,0x1e,0x33,0x30,0x1c,0x30,0x33,0x1e,0x00,0x38,0x3c,0x36,0x33,0x7f,0x30,0x78,0x00,0x3f,0x03,0x1f,0x30,0x30,0x33,0x1e,0x00,0x1c,0x06,0x03,0x1f,0x33,0x33,0x1e,0x00,0x3f,0x33,0x30,0x18,0x0c,0x0c,0x0c,0x00,0x1e,0x33,0x33,0x1e,0x33,0x33,0x1e,0x00,0x1e,0x33,0x33,0x3e,0x30,0x18,0x0e,0x00,0x00,0x0c,0x0c,0x00,0x00,0x0c,0x0c,0x00,0x00,0x0c,0x0c,0x00,0x00,0x0c,0x0c,0x06,0x18,0x0c,0x06,0x03,0x06,0x0c,0x18,0x00,0x00,0x00,0x3f,0x00,0x00,0x3f,0x00,0x00,0x06,0x0c,0x18,0x30,0x18,0x0c,0x06,0x00,0x1e,0x33,0x30,0x18,0x0c,0x00,0x0c,0x00, +0x3e,0x63,0x7b,0x7b,0x7b,0x03,0x1e,0x00,0x0c,0x1e,0x33,0x33,0x3f,0x33,0x33,0x00,0x3f,0x66,0x66,0x3e,0x66,0x66,0x3f,0x00,0x3c,0x66,0x03,0x03,0x03,0x66,0x3c,0x00,0x1f,0x36,0x66,0x66,0x66,0x36,0x1f,0x00,0x7f,0x46,0x16,0x1e,0x16,0x46,0x7f,0x00,0x7f,0x46,0x16,0x1e,0x16,0x06,0x0f,0x00,0x3c,0x66,0x03,0x03,0x73,0x66,0x7c,0x00,0x33,0x33,0x33,0x3f,0x33,0x33,0x33,0x00,0x1e,0x0c,0x0c,0x0c,0x0c,0x0c,0x1e,0x00,0x78,0x30,0x30,0x30,0x33,0x33,0x1e,0x00,0x67,0x66,0x36,0x1e,0x36,0x66,0x67,0x00,0x0f,0x06,0x06,0x06,0x46,0x66,0x7f,0x00,0x63,0x77,0x7f,0x7f,0x6b,0x63,0x63,0x00,0x63,0x67,0x6f,0x7b,0x73,0x63,0x63,0x00,0x1c,0x36,0x63,0x63,0x63,0x36,0x1c,0x00,0x3f,0x66,0x66,0x3e,0x06,0x06,0x0f,0x00,0x1e,0x33,0x33,0x33,0x3b,0x1e,0x38,0x00,0x3f,0x66,0x66,0x3e,0x36,0x66,0x67,0x00,0x1e,0x33,0x07,0x0e,0x38,0x33,0x1e,0x00,0x3f,0x2d,0x0c,0x0c,0x0c,0x0c,0x1e,0x00,0x33,0x33,0x33,0x33,0x33,0x33,0x3f,0x00,0x33,0x33,0x33,0x33,0x33,0x1e,0x0c,0x00,0x63,0x63,0x63,0x6b,0x7f,0x77,0x63,0x00,0x63,0x63,0x36,0x1c,0x1c,0x36,0x63,0x00,0x33,0x33,0x33,0x1e,0x0c,0x0c,0x1e,0x00,0x7f,0x63,0x31,0x18,0x4c,0x66,0x7f,0x00,0x1e,0x06,0x06,0x06,0x06,0x06,0x1e,0x00,0x03,0x06,0x0c,0x18,0x30,0x60,0x40,0x00,0x1e,0x18,0x18,0x18,0x18,0x18,0x1e,0x00,0x08,0x1c,0x36,0x63,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff, +0x0c,0x0c,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1e,0x30,0x3e,0x33,0x6e,0x00,0x07,0x06,0x06,0x3e,0x66,0x66,0x3b,0x00,0x00,0x00,0x1e,0x33,0x03,0x33,0x1e,0x00,0x38,0x30,0x30,0x3e,0x33,0x33,0x6e,0x00,0x00,0x00,0x1e,0x33,0x3f,0x03,0x1e,0x00,0x1c,0x36,0x06,0x0f,0x06,0x06,0x0f,0x00,0x00,0x00,0x6e,0x33,0x33,0x3e,0x30,0x1f,0x07,0x06,0x36,0x6e,0x66,0x66,0x67,0x00,0x0c,0x00,0x0e,0x0c,0x0c,0x0c,0x1e,0x00,0x30,0x00,0x30,0x30,0x30,0x33,0x33,0x1e,0x07,0x06,0x66,0x36,0x1e,0x36,0x67,0x00,0x0e,0x0c,0x0c,0x0c,0x0c,0x0c,0x1e,0x00,0x00,0x00,0x33,0x7f,0x7f,0x6b,0x63,0x00,0x00,0x00,0x1f,0x33,0x33,0x33,0x33,0x00,0x00,0x00,0x1e,0x33,0x33,0x33,0x1e,0x00,0x00,0x00,0x3b,0x66,0x66,0x3e,0x06,0x0f,0x00,0x00,0x6e,0x33,0x33,0x3e,0x30,0x78,0x00,0x00,0x3b,0x6e,0x66,0x06,0x0f,0x00,0x00,0x00,0x3e,0x03,0x1e,0x30,0x1f,0x00,0x08,0x0c,0x3e,0x0c,0x0c,0x2c,0x18,0x00,0x00,0x00,0x33,0x33,0x33,0x33,0x6e,0x00,0x00,0x00,0x33,0x33,0x33,0x1e,0x0c,0x00,0x00,0x00,0x63,0x6b,0x7f,0x7f,0x36,0x00,0x00,0x00,0x63,0x36,0x1c,0x36,0x63,0x00,0x00,0x00,0x33,0x33,0x33,0x3e,0x30,0x1f,0x00,0x00,0x3f,0x19,0x0c,0x26,0x3f,0x00,0x38,0x0c,0x0c,0x07,0x0c,0x0c,0x38,0x00,0x18,0x18,0x18,0x00,0x18,0x18,0x18,0x00,0x07,0x0c,0x0c,0x38,0x0c,0x0c,0x07,0x00,0x6e,0x3b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x1c,0x36,0x63,0x63,0x7f,0x00, +0x1e,0x33,0x03,0x33,0x1e,0x18,0x30,0x1e,0x00,0x33,0x00,0x33,0x33,0x33,0x7e,0x00,0x38,0x00,0x1e,0x33,0x3f,0x03,0x1e,0x00,0x7e,0xc3,0x3c,0x60,0x7c,0x66,0xfc,0x00,0x33,0x00,0x1e,0x30,0x3e,0x33,0x7e,0x00,0x07,0x00,0x1e,0x30,0x3e,0x33,0x7e,0x00,0x0c,0x0c,0x1e,0x30,0x3e,0x33,0x7e,0x00,0x00,0x00,0x1e,0x03,0x03,0x1e,0x30,0x1c,0x7e,0xc3,0x3c,0x66,0x7e,0x06,0x3c,0x00,0x33,0x00,0x1e,0x33,0x3f,0x03,0x1e,0x00,0x07,0x00,0x1e,0x33,0x3f,0x03,0x1e,0x00,0x33,0x00,0x0e,0x0c,0x0c,0x0c,0x1e,0x00,0x3e,0x63,0x1c,0x18,0x18,0x18,0x3c,0x00,0x07,0x00,0x0e,0x0c,0x0c,0x0c,0x1e,0x00,0x63,0x1c,0x36,0x63,0x7f,0x63,0x63,0x00,0x0c,0x0c,0x00,0x1e,0x33,0x3f,0x33,0x00,0x38,0x00,0x3f,0x06,0x1e,0x06,0x3f,0x00,0x00,0x00,0xfe,0x30,0xfe,0x33,0xfe,0x00,0x7c,0x36,0x33,0x7f,0x33,0x33,0x73,0x00,0x1e,0x33,0x00,0x1e,0x33,0x33,0x1e,0x00,0x00,0x33,0x00,0x1e,0x33,0x33,0x1e,0x00,0x00,0x07,0x00,0x1e,0x33,0x33,0x1e,0x00,0x1e,0x33,0x00,0x33,0x33,0x33,0x7e,0x00,0x00,0x07,0x00,0x33,0x33,0x33,0x7e,0x00,0x00,0x33,0x00,0x33,0x33,0x3e,0x30,0x1f,0xc3,0x18,0x3c,0x66,0x66,0x3c,0x18,0x00,0x33,0x00,0x33,0x33,0x33,0x33,0x1e,0x00,0x18,0x18,0x7e,0x03,0x03,0x7e,0x18,0x18,0x1c,0x36,0x26,0x0f,0x06,0x67,0x3f,0x00,0x33,0x33,0x1e,0x3f,0x0c,0x3f,0x0c,0x0c,0x1f,0x33,0x33,0x5f,0x63,0xf3,0x63,0xe3,0x70,0xd8,0x18,0x3c,0x18,0x18,0x1b,0x0e, +0x38,0x00,0x1e,0x30,0x3e,0x33,0x7e,0x00,0x1c,0x00,0x0e,0x0c,0x0c,0x0c,0x1e,0x00,0x00,0x38,0x00,0x1e,0x33,0x33,0x1e,0x00,0x00,0x38,0x00,0x33,0x33,0x33,0x7e,0x00,0x00,0x1f,0x00,0x1f,0x33,0x33,0x33,0x00,0x3f,0x00,0x33,0x37,0x3f,0x3b,0x33,0x00,0x3c,0x36,0x36,0x7c,0x00,0x7e,0x00,0x00,0x1c,0x36,0x36,0x1c,0x00,0x3e,0x00,0x00,0x0c,0x00,0x0c,0x06,0x03,0x33,0x1e,0x00,0x00,0x00,0x00,0x3f,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x3f,0x30,0x30,0x00,0x00,0xc3,0x63,0x33,0x7b,0xcc,0x66,0x33,0xf0,0xc3,0x63,0x33,0xdb,0xec,0xf6,0xf3,0xc0,0x18,0x18,0x00,0x18,0x18,0x18,0x18,0x00,0x00,0xcc,0x66,0x33,0x66,0xcc,0x00,0x00,0x00,0x33,0x66,0xcc,0x66,0x33,0x00,0x00,0x44,0x11,0x44,0x11,0x44,0x11,0x44,0x11,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xdb,0xee,0xdb,0x77,0xdb,0xee,0xdb,0x77,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1f,0x18,0x18,0x18,0x18,0x18,0x1f,0x18,0x1f,0x18,0x18,0x18,0x6c,0x6c,0x6c,0x6c,0x6f,0x6c,0x6c,0x6c,0x00,0x00,0x00,0x00,0x7f,0x6c,0x6c,0x6c,0x00,0x00,0x1f,0x18,0x1f,0x18,0x18,0x18,0x6c,0x6c,0x6f,0x60,0x6f,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x00,0x00,0x7f,0x60,0x6f,0x6c,0x6c,0x6c,0x6c,0x6c,0x6f,0x60,0x7f,0x00,0x00,0x00,0x6c,0x6c,0x6c,0x6c,0x7f,0x00,0x00,0x00,0x18,0x18,0x1f,0x18,0x1f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1f,0x18,0x18,0x18, +0x18,0x18,0x18,0x18,0xf8,0x00,0x00,0x00,0x18,0x18,0x18,0x18,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xf8,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x18,0x18,0x18,0x18,0xff,0x18,0x18,0x18,0x18,0x18,0xf8,0x18,0xf8,0x18,0x18,0x18,0x6c,0x6c,0x6c,0x6c,0xec,0x6c,0x6c,0x6c,0x6c,0x6c,0xec,0x0c,0xfc,0x00,0x00,0x00,0x00,0x00,0xfc,0x0c,0xec,0x6c,0x6c,0x6c,0x6c,0x6c,0xef,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xef,0x6c,0x6c,0x6c,0x6c,0x6c,0xec,0x0c,0xec,0x6c,0x6c,0x6c,0x00,0x00,0xff,0x00,0xff,0x00,0x00,0x00,0x6c,0x6c,0xef,0x00,0xef,0x6c,0x6c,0x6c,0x18,0x18,0xff,0x00,0xff,0x00,0x00,0x00,0x6c,0x6c,0x6c,0x6c,0xff,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0xff,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0xfc,0x00,0x00,0x00,0x18,0x18,0xf8,0x18,0xf8,0x00,0x00,0x00,0x00,0x00,0xf8,0x18,0xf8,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0xfc,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0xff,0x6c,0x6c,0x6c,0x18,0x18,0xff,0x18,0xff,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf8,0x18,0x18,0x18,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, +0x00,0x00,0x6e,0x3b,0x13,0x3b,0x6e,0x00,0x00,0x1e,0x33,0x1f,0x33,0x1f,0x03,0x03,0x00,0x3f,0x33,0x03,0x03,0x03,0x03,0x00,0x00,0x7f,0x36,0x36,0x36,0x36,0x36,0x00,0x3f,0x33,0x06,0x0c,0x06,0x33,0x3f,0x00,0x00,0x00,0x7e,0x1b,0x1b,0x1b,0x0e,0x00,0x00,0x66,0x66,0x66,0x66,0x3e,0x06,0x03,0x00,0x6e,0x3b,0x18,0x18,0x18,0x18,0x00,0x3f,0x0c,0x1e,0x33,0x33,0x1e,0x0c,0x3f,0x1c,0x36,0x63,0x7f,0x63,0x36,0x1c,0x00,0x1c,0x36,0x63,0x63,0x36,0x36,0x77,0x00,0x38,0x0c,0x18,0x3e,0x33,0x33,0x1e,0x00,0x00,0x00,0x7e,0xdb,0xdb,0x7e,0x00,0x00,0x60,0x30,0x7e,0xdb,0xdb,0x7e,0x06,0x03,0x1c,0x06,0x03,0x1f,0x03,0x06,0x1c,0x00,0x1e,0x33,0x33,0x33,0x33,0x33,0x33,0x00,0x00,0x3f,0x00,0x3f,0x00,0x3f,0x00,0x00,0x0c,0x0c,0x3f,0x0c,0x0c,0x00,0x3f,0x00,0x06,0x0c,0x18,0x0c,0x06,0x00,0x3f,0x00,0x18,0x0c,0x06,0x0c,0x18,0x00,0x3f,0x00,0x70,0xd8,0xd8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1b,0x1b,0x0e,0x0c,0x0c,0x00,0x3f,0x00,0x0c,0x0c,0x00,0x00,0x6e,0x3b,0x00,0x6e,0x3b,0x00,0x00,0x1c,0x36,0x36,0x1c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0xf0,0x30,0x30,0x30,0x37,0x36,0x3c,0x38,0x1e,0x36,0x36,0x36,0x36,0x00,0x00,0x00,0x0e,0x18,0x0c,0x06,0x1e,0x00,0x00,0x00,0x00,0x00,0x3c,0x3c,0x3c,0x3c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +}; + +static void DrawState(void) +{ + uint8 *XBaf; + int x,y,z; + + XBaf=XBuf+4+(FSettings.LastSLine-44)*272; + + if(XBaf>=XBuf) + for(z=1;z<11;z++) + { + if(SaveStateStatus[z%10]) + { + for(y=0;y<13;y++) + for(x=0;x<21;x++) + XBaf[y*272+x+z*21+z]=sstat[y*21+x+(z-1)*21*12]; + } else { + for(y=0;y<13;y++) + for(x=0;x<21;x++) + if(sstat[y*21+x+(z-1)*21*12]!=0x83) + XBaf[y*272+x+z*21+z]=sstat[y*21+x+(z-1)*21*12]; + } + if(CurrentState==z%10) + { + for(x=0;x<21;x++) + XBaf[x+z*21+z*1]=132; + for(x=1;x<12;x++) + { + XBaf[272*x+z*21+z*1]= + XBaf[272*x+z*21+z*1+20]=132; + } + for(x=0;x<21;x++) + XBaf[3264+x+z*21+z*1]=132; + } + } + StateShow--; +} + +void DrawTextTrans(uint8 *dest, uint32 width, uint8 *textmsg, uint8 fgcolor) +{ + uint8 length=strlen(textmsg); + uint8 x; + uint8 y; + uint8 z; + + for(x=0;x>z)&1) dest[y*width+(x<<3)+z]=fgcolor; +} + +void DrawBars(void) +{ + uint8 *XBaf; + short which=0; + int x,x2; + + if(controlselect==1) + { + DrawTextTrans(XBuf+128-12+180*272, 272, "Hue", 0x85); + which=ntschue<<1; + } + else if(controlselect==2) + { + DrawTextTrans(XBuf+128-16+180*272, 272, "Tint", 0x85); + which=ntsctint<<1; + } + + XBaf=XBuf+200*272; + for(x=0;x=-6;x2--) + { + XBaf[x-272*x2]=0x85; + } + } + for(;x<256;x+=2) + { + for(x2=2;x2>=-2;x2--) + XBaf[x-272*x2]=0x85; + } + +} diff --git a/driver.h b/driver.h new file mode 100644 index 0000000..9a298c6 --- /dev/null +++ b/driver.h @@ -0,0 +1,174 @@ +#include "types.h" +#include "git.h" + +/* Prototypes for platform interface functions follow: */ + +void FCEUD_Update(uint8 *XBuf, int32 *Buffer, int Count); + +/* Video interface */ +void FCEUD_SetPalette(uint8 index, uint8 r, uint8 g, uint8 b); +void FCEUD_GetPalette(uint8 i,uint8 *r, unsigned char *g, unsigned char *b); + +/* Sound interface */ +void FCEUD_WriteSoundData(int32 *Buffer, int Count); + +/* Displays an error. Can block or not. */ +void FCEUD_PrintError(char *s); + +#ifdef NETWORK +/* Network interface */ + +int FCEUD_NetworkConnect(void); +int FCEUD_NetworkRecvData(uint8 *data, uint32 len, int block); +int FCEUD_NetworkSendData(uint8 *data, uint32 len); +void FCEUD_NetworkClose(void); + +#endif + +#define DES_NTSCCOL 2 + +#define DES_RESET 0x10 +#define DES_POWER 0x11 + +#define DES_GETNTSCHUE 0x20 +#define DES_GETNTSCTINT 0x21 +#define DES_SETNTSCHUE 0x22 +#define DES_SETNTSCTINT 0x23 + +#define DES_NSFINC 0x50 +#define DES_NSFDEC 0x51 +#define DES_NSFRES 0x52 + +#define DES_VSUNIDIPSET 0x70 +#define DES_VSUNITOGGLEDIPVIEW 0x71 +#define DES_VSUNICOIN 0x72 +#define DES_FDSINSERT 0x80 +#define DES_FDSEJECT 0x81 +#define DES_FDSSELECT 0x82 + +#define DES_NTSCSELHUE 0x90 +#define DES_NTSCSELTINT 0x91 +#define DES_NTSCDEC 0x92 +#define DES_NTSCINC 0x93 + +void DriverInterface(int w, void *d); + +void FCEUI_SetInput(int port, int type, void *ptr, int attrib); +void FCEUI_SetInputFC(int type, void *ptr, int attrib); +void FCEUI_DisableFourScore(int s); + +#include "version.h" + +#define SI_NONE 0 +#define SI_GAMEPAD 1 +#define SI_ZAPPER 2 +#define SI_POWERPAD 3 +#define SI_ARKANOID 4 + +#define SIFC_NONE 0 +#define SIFC_ARKANOID 1 +#define SIFC_SHADOW 2 +#define SIFC_4PLAYER 3 +#define SIFC_FKB 4 +#define SIFC_OEKA 5 + +/* New interface functions */ + +/* 0 to order screen snapshots numerically(0.png), 1 to order them file base-numerically(smb3-0.png). */ +void FCEUI_SetSnapName(int a); + +/* 0 to keep 8-sprites limitation, 1 to remove it */ +void FCEUI_DisableSpriteLimitation(int a); + +/* 0 to save extra game data(*.sav) in same dir as game, 1 to save under base dir */ +void FCEUI_SaveExtraDataUnderBase(int a); + +/* name=path and file to load. returns 0 on failure, 1 on success */ +FCEUGI *FCEUI_LoadGame(char *name); + +/* allocates memory. 0 on failure, 1 on success. */ +int FCEUI_Initialize(void); + +/* begins emulation. Returns after FCEUI_CloseGame() is called */ +void FCEUI_Emulate(void); + +/* Closes currently loaded game, causes FCEUI_Emulate to return */ +void FCEUI_CloseGame(void); + +/* Enable/Disable game genie. a=0 disable, a=1 enable */ +void FCEUI_SetGameGenie(int a); + +/* Set video system a=0 NTSC, a=1 PAL */ +void FCEUI_SetVidSystem(int a); + +/* Convenience function; returns currently emulated video system(0=NTSC, 1=PAL). */ +int FCEUI_GetCurrentVidSystem(int *slstart, int *slend); + +#ifdef FRAMESKIP +/* Should be called from FCEUD_BlitScreen(). Specifies how many frames + to skip until FCEUD_BlitScreen() is called. FCEUD_BlitScreenDummy() + will be called instead of FCEUD_BlitScreen() when when a frame is skipped. +*/ +void FCEUI_FrameSkip(int x); +#endif + +/* First and last scanlines to render, for ntsc and pal emulation. */ +void FCEUI_SetRenderedLines(int ntscf, int ntscl, int palf, int pall); + +/* Sets the base directory(save states, snapshots, etc. are saved in directories + below this directory. */ +void FCEUI_SetBaseDirectory(char *dir); + +/* Tells FCE Ultra to copy the palette data pointed to by pal and use it. + Data pointed to by pal needs to be 64*3 bytes in length. +*/ +void FCEUI_SetPaletteArray(uint8 *pal); + +/* Sets up sound code to render sound at the specified rate, in samples + per second. The sample rate should be as close to 44100 hz as possible. + Sample rates from 8192 through 65535 are ok, though if the sample rate + is too low, some sound channels(noise) won't sound right. + If "Rate" equals 0, sound is disabled. +*/ +void FCEUI_Sound(int Rate); +void FCEUI_SetSoundVolume(uint32 volume); + +#ifdef NETWORK +/* Set network play stuff. type=1 for server, type=2 for client. + skip is only used for server */ +void FCEUI_SetNetworkPlay(int type); +#endif + +void FCEUI_SelectState(int w); +void FCEUI_SaveState(void); +void FCEUI_LoadState(void); +int32 FCEUI_GetDesiredFPS(void); +void FCEUI_SaveSnapshot(void); +void FCEU_DispMessage(char *format, ...); +#define FCEUI_DispMessage FCEU_DispMessage + +int FCEUI_AddCheat(char *name, uint32 addr, uint8 val); +int FCEUI_DelCheat(uint32 which); + +int32 FCEUI_CheatSearchGetCount(void); +void FCEUI_CheatSearchGetRange(uint32 first, uint32 last, int (*callb)(uint32 a, uint8 last, uint8 current)); +void FCEUI_CheatSearchGet(int (*callb)(uint32 a, uint8 last, uint8 current)); +void FCEUI_CheatSearchBegin(void); +void FCEUI_CheatSearchEnd(int type, uint8 v1, uint8 v2); +void FCEUI_ListCheats(int (*callb)(char *name, uint32 a, uint8 v, int s)); + +int FCEUI_GetCheat(uint32 which, char **name, uint32 *a, uint8 *v, int *s); +int FCEUI_SetCheat(uint32 which, char *name, int32 a, int32 v, int s); +void FCEUI_CheatSearchShowExcluded(void); +void FCEUI_CheatSearchSetCurrentAsOriginal(void); + +#define FCEUIOD_STATE 0 +#define FCEUIOD_SNAPS 1 +#define FCEUIOD_NV 2 +#define FCEUIOD_CHEATS 3 +#define FCEUIOD_MISC 4 + +#define FCEUIOD__COUNT 5 + +void FCEUI_SetDirOverride(int which, char *n); + diff --git a/drivers/cli/dface.h b/drivers/cli/dface.h new file mode 100644 index 0000000..92df082 --- /dev/null +++ b/drivers/cli/dface.h @@ -0,0 +1,32 @@ +extern CFGSTRUCT DriverConfig[]; +extern ARGPSTRUCT DriverArgs[]; +extern char *DriverUsage; + +void DoDriverArgs(void); +void GetBaseDirectory(char *BaseDirectory); + +int InitSound(void); +void WriteSound(int32 *Buffer, int Count, int NoWaiting); +void KillSound(void); +void SilenceSound(int s); /* DOS and SDL */ + + +int InitMouse(void); +void KillMouse(void); +void GetMouseData(uint32 *MouseData); + +int InitJoysticks(void); +void KillJoysticks(void); +uint32 *GetJSOr(void); + +int InitKeyboard(void); +int UpdateKeyboard(void); +char *GetKeyboard(void); +void KillKeyboard(void); + +int InitVideo(void); +void KillVideo(void); +void BlitScreen(uint8 *XBuf); +void LockConsole(void); +void UnlockConsole(void); +void ToggleFS(); /* SDL */ diff --git a/drivers/cli/dos-joystick.c b/drivers/cli/dos-joystick.c new file mode 100644 index 0000000..3729187 --- /dev/null +++ b/drivers/cli/dos-joystick.c @@ -0,0 +1,190 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "dos.h" +#include "dos-joystick.h" + +#define JOY_A 1 +#define JOY_B 2 +#define JOY_SELECT 4 +#define JOY_START 8 +#define JOY_UP 0x10 +#define JOY_DOWN 0x20 +#define JOY_LEFT 0x40 +#define JOY_RIGHT 0x80 + +int joy=0; +int joyBMap[4]; + +static int32 joybuttons=0; +static uint32 joyx=0; +static uint32 joyy=0; +static uint32 joyxcenter; +static uint32 joyycenter; + +static void ConfigJoystick(void); +volatile int soundjoyer=0; +volatile int soundjoyeron=0; + +/* Crude method to detect joystick. */ +static int DetectJoystick(void) +{ + uint8 b; + + outportb(0x201,0); + b=inportb(0x201); + sleep(1); + if((inportb(0x201)&3)==(b&3)) + return 0; + else + return 1; +} + +void UpdateJoyData(void) +{ + uint32 xc,yc; + + + joybuttons=((inportb(0x201)&0xF0)^0xF0)>>4; + + xc=yc=0; + + { + outportb(0x201,0); + + for(;;) + { + uint8 b; + + b=inportb(0x201); + if(!(b&3)) + break; + if(b&1) xc++; + if(b&2) yc++; + } + } + + joyx=xc; + joyy=yc; +} + +uint32 GetJSOr(void) +{ + int y; + unsigned long ret; + ret=0; + + if(!soundo) + UpdateJoyData(); + for(y=0;y<4;y++) + if(joybuttons&joyBMap[y]) ret|=(1<=joyxcenter*1.75) ret|=JOY_RIGHT<<((joy-1)<<3); + if(joyy<=joyycenter*.25) ret|=JOY_UP<<((joy-1)<<3); + else if(joyy>=joyycenter*1.75) ret|=JOY_DOWN<<((joy-1)<<3); + + return ret; +} + +int InitJoysticks(void) +{ + if(!joy) return(0); + if(!DetectJoystick()) + { + printf("Joystick not detected!\n"); + joy=0; + return 0; + } + if(soundo) + { + soundjoyeron=1; + while(!soundjoyer); + } + else + UpdateJoyData(); + + joyxcenter=joyx; + joyycenter=joyy; + + if(!(joyBMap[0]|joyBMap[1]|joyBMap[2]|joyBMap[3])) + ConfigJoystick(); + return(1); +} + +static void BConfig(int b) +{ + int c=0; + uint32 st=time(0); + + while(time(0)< (st+4) ) + { + if(!soundo) + UpdateJoyData(); + if(joybuttons) c=joybuttons; + else if(c && !joybuttons) + { + joyBMap[b]=c; + break; + } + + } +} + +void KillJoysticks(void) +{ + +} + +static void ConfigJoystick(void) +{ + static char *genb="** Press button for "; + + printf("\n\n Joystick button configuration:\n\n"); + printf(" Push and release the button to map to the virtual joystick.\n"); + printf(" If you do not wish to assign a button, wait a few seconds\n"); + printf(" and the configuration will continue.\n\n"); + printf(" Press enter to continue...\n"); + getchar(); + + printf("%s\"Select\".\n",genb); + BConfig(2); + + printf("%s\"Start\".\n",genb); + BConfig(3); + + printf("%s\"B\".\n",genb); + BConfig(1); + + printf("%s\"A\".\n",genb); + BConfig(0); +} + diff --git a/drivers/cli/dos-joystick.h b/drivers/cli/dos-joystick.h new file mode 100644 index 0000000..6cfaee5 --- /dev/null +++ b/drivers/cli/dos-joystick.h @@ -0,0 +1,27 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +void UpdateJoyData(void); +uint32 GetJSOr(void); +int InitJoysticks(void); + +/* Variables to save in config file. */ +extern int joy; +extern int joyBMap[4]; diff --git a/drivers/cli/dos-keyboard.c b/drivers/cli/dos-keyboard.c new file mode 100644 index 0000000..22ca9e8 --- /dev/null +++ b/drivers/cli/dos-keyboard.c @@ -0,0 +1,131 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include "keyscan.h" + +static unsigned char lastsc; +static char keybuf[256]; +int newk; + +/* Read scan code from port $60 */ +/* Acknowledge interrupt( output $20 to port $20) */ + +static void ihandler(_go32_dpmi_registers *r) +{ + unsigned char scode=inp(0x60); /* Get scan code. */ + + + if(scode!=0xE0) + { + int offs=0; + + /* I'm only interested in preserving the independent status of the + right ALT and CONTROL keys. + */ + if(lastsc==0xE0) + if((scode&0x7F)==SCAN_LEFTALT || (scode&0x7F)==SCAN_LEFTCONTROL) + offs=0x80; + + + keybuf[(scode&0x7f)|offs]=((scode&0x80)^0x80); + newk++; + } + lastsc=scode; + + outp(0x20,0x20); /* Acknowledge interrupt. */ +} + +static _go32_dpmi_seginfo KBIBack,KBIBackRM; +static _go32_dpmi_seginfo KBI,KBIRM; +static _go32_dpmi_registers KBIRMRegs; +static int initdone=0; + +int InitKeyboard(void) +{ + /* I'll assume that the keyboard is in the correct scancode mode(translated + mode 2, I think). + */ + newk=0; + memset(keybuf,0,sizeof(keybuf)); + KBIRM.pm_offset=KBI.pm_offset=(int)ihandler; + KBIRM.pm_selector=KBI.pm_selector=_my_cs(); + + _go32_dpmi_get_real_mode_interrupt_vector(9,&KBIBackRM); + _go32_dpmi_allocate_real_mode_callback_iret(&KBIRM, &KBIRMRegs); + _go32_dpmi_set_real_mode_interrupt_vector(9,&KBIRM); + + _go32_dpmi_get_protected_mode_interrupt_vector(9,&KBIBack); + _go32_dpmi_allocate_iret_wrapper(&KBI); + _go32_dpmi_set_protected_mode_interrupt_vector(9,&KBI); + lastsc=0; + initdone=1; + return(1); +} + +void KillKeyboard(void) +{ + if(initdone) + { + _go32_dpmi_set_protected_mode_interrupt_vector(9,&KBIBack); + _go32_dpmi_free_iret_wrapper(&KBI); + + _go32_dpmi_set_real_mode_interrupt_vector(9,&KBIBackRM); + _go32_dpmi_free_real_mode_callback(&KBIRM); + initdone=0; + } +} + +/* In FCE Ultra, it doesn't matter if the key states change + in the middle of the keyboard handling code. If you want + to use this code elsewhere, you may want to memcpy() keybuf + to another buffer and return that when GetKeyboard() is + called. +*/ + +char *GetKeyboard(void) +{ + return keybuf; +} + +/* Returns 1 on new scan codes generated, 0 on no new scan codes. */ +int UpdateKeyboard(void) +{ + int t=newk; + + if(t) + { + asm volatile( + "subl %%eax,_newk\n\t" + : + : "a" (t) + ); + + if(keybuf[SCAN_LEFTCONTROL] && keybuf[SCAN_C]) + raise(SIGINT); + return(1); + } + return(0); +} diff --git a/drivers/cli/dos-mouse.c b/drivers/cli/dos-mouse.c new file mode 100644 index 0000000..ae3e341 --- /dev/null +++ b/drivers/cli/dos-mouse.c @@ -0,0 +1,77 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +#include "dos.h" + +int InitMouse(void) +{ + __dpmi_regs regs; + + memset(®s,0,sizeof(regs)); + regs.x.ax=0; + __dpmi_int(0x33,®s); + if(regs.x.ax!=0xFFFF) + return(0); + + memset(®s,0,sizeof(regs)); + regs.x.ax=0x7; + regs.x.cx=0; // Min X + regs.x.dx=260; // Max X + __dpmi_int(0x33,®s); + + memset(®s,0,sizeof(regs)); + regs.x.ax=0x8; + regs.x.cx=0; // Min Y + regs.x.dx=260; // Max Y + __dpmi_int(0x33,®s); + + memset(®s,0,sizeof(regs)); + regs.x.ax=0xF; + regs.x.cx=8; // Mickey X + regs.x.dx=8; // Mickey Y + __dpmi_int(0x33,®s); + + memset(®s,0,sizeof(regs)); + regs.x.ax=0x2; + __dpmi_int(0x33,®s); + + return(1); +} + +uint32 GetMouseData(uint32 *x, uint32 *y) +{ + __dpmi_regs regs; + + memset(®s,0,sizeof(regs)); + regs.x.ax=0x3; + __dpmi_int(0x33,®s); + + *x=regs.x.cx; + *y=regs.x.dx; + return(regs.x.bx&3); +} + +void KillMouse(void) +{ + +} diff --git a/drivers/cli/dos-sound.c b/drivers/cli/dos-sound.c new file mode 100644 index 0000000..19ef271 --- /dev/null +++ b/drivers/cli/dos-sound.c @@ -0,0 +1,567 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dos.h" +#include "dos-sound.h" +#include "dos-joystick.h" + + +static void SBIRQHandler(_go32_dpmi_registers *r); +static uint32 LMBuffer; /* Address of low memory DMA playback buffer. */ +static int LMSelector; + +static uint8 *WaveBuffer; +static unsigned int IVector, SBIRQ, SBDMA, SBDMA16, SBPort; +static int DSPV,hsmode; +static int format; +static int frags, fragsize, fragtotal; +static volatile int WritePtr, ReadPtr; +static volatile int hbusy; +static volatile int whichbuf; + + +static uint8 PICMask; +/* Protected mode interrupt vector info. */ +static _go32_dpmi_seginfo SBIH,SBIHOld; + +/* Real mode interrupt vector info. */ +static _go32_dpmi_seginfo SBIHRM,SBIHRMOld; +static _go32_dpmi_registers SBIHRMRegs; + +static int WriteDSP(uint8 V) +{ + int x; + + for(x=65536;x;x--) + { + if(!(inportb(SBPort+0xC)&0x80)) + { + outportb(SBPort+0xC,V); + return(1); + } + } + return(0); +} + +static int ReadDSP(uint8 *V) +{ + int x; + + for(x=65536;x;x--) /* Should be more than enough time... */ + { + if(inportb(SBPort+0xE)&0x80) + { + *V=inportb(SBPort+0xA); + return(1); + } + } + return(0); +} + + +static int SetVectors(void) +{ + SBIH.pm_offset=SBIHRM.pm_offset=(int)SBIRQHandler; + SBIH.pm_selector=SBIHRM.pm_selector=_my_cs(); + + /* Get and set real mode interrupt vector. */ + _go32_dpmi_get_real_mode_interrupt_vector(IVector,&SBIHRMOld); + _go32_dpmi_allocate_real_mode_callback_iret(&SBIHRM, &SBIHRMRegs); + _go32_dpmi_set_real_mode_interrupt_vector(IVector,&SBIHRM); + + /* Get and set protected mode interrupt vector. */ + _go32_dpmi_get_protected_mode_interrupt_vector(IVector,&SBIHOld); + _go32_dpmi_allocate_iret_wrapper(&SBIH); + _go32_dpmi_set_protected_mode_interrupt_vector(IVector,&SBIH); + + return(1); +} + +static void ResetVectors(void) +{ + _go32_dpmi_set_protected_mode_interrupt_vector(IVector,&SBIHOld); + _go32_dpmi_free_iret_wrapper(&SBIH); + _go32_dpmi_set_real_mode_interrupt_vector(IVector,&SBIHRMOld); + _go32_dpmi_free_real_mode_callback(&SBIHRM); +} + +int GetBLASTER(void) +{ + int check=0; + char *s; + + if(!(s=getenv("BLASTER"))) + { + puts(" Error getting BLASTER environment variable."); + return(0); + } + + while(*s) + { + switch(toupper(*s)) + { + case 'A': check|=(sscanf(s+1,"%x",&SBPort)==1)?1:0;break; + case 'I': check|=(sscanf(s+1,"%d",&SBIRQ)==1)?2:0;break; + case 'D': check|=(sscanf(s+1,"%d",&SBDMA)==1)?4:0;break; + case 'H': check|=(sscanf(s+1,"%d",&SBDMA16)==1)?8:0;break; + } + s++; + } + + if((check^7)&7 || SBDMA>=4 || (SBDMA16<=4 && check&8) || SBIRQ>15) + { + puts(" Invalid or incomplete BLASTER environment variable."); + return(0); + } + if(!(check&8)) + format=0; + return(1); +} + +static int ResetDSP(void) +{ + uint8 b; + + outportb(SBPort+0x6,0x1); + delay(10); + outportb(SBPort+0x6,0x0); + delay(10); + + if(ReadDSP(&b)) + if(b==0xAA) + return(1); + return(0); +} + +static int GetDSPVersion(void) +{ + int ret; + uint8 t; + + if(!WriteDSP(0xE1)) + return(0); + if(!ReadDSP(&t)) + return(0); + ret=t<<8; + if(!ReadDSP(&t)) + return(0); + ret|=t; + + return(ret); +} + +static void KillDMABuffer(void) +{ + __dpmi_free_dos_memory(LMSelector); +} + +static int MakeDMABuffer(void) +{ + uint32 size; + int32 tmp; + + size=fragsize*2; /* Two buffers in the DMA buffer. */ + size<<=format; /* Twice the size for 16-bit than for 8-bit. */ + + size<<=1; /* Double the size in case the first 2 buffers + cross a 64KB or 128KB page boundary. + */ + size=(size+15)>>4; /* Convert to paragraphs */ + + if((tmp=__dpmi_allocate_dos_memory(size,&LMSelector))<0) + return(0); + + LMBuffer=tmp<<=4; + + if(format) /* Check for and fix 128KB page boundary crossing. */ + { + if((LMBuffer&0x20000) != ((LMBuffer+fragsize*2*2-1)&0x20000)) + LMBuffer+=fragsize*2*2; + } + else /* Check for and fix 64KB page boundary crossing. */ + { + if((LMBuffer&0x10000) != ((LMBuffer+fragsize*2-1)&0x10000)) + LMBuffer+=fragsize*2; + } + + DOSMemSet(LMBuffer, format?0:128, (fragsize*2)<>8); + + /* Page of buffer. */ + outportb(PPorts[format?SBDMA16:SBDMA],LMBuffer>>16); + + /* Offset of buffer within page. */ + if(format) + tmp=((SBDMA16&3)<<2)+0xc0; + else + tmp=SBDMA<<1; + + outportb(tmp,(LMBuffer>>format)); + outportb(tmp,(LMBuffer>>(8+format))); +} + +int InitSB(int Rate, int bittage) +{ + hsmode=hbusy=0; + whichbuf=1; + puts("Initializing Sound Blaster..."); + + format=bittage?1:0; + frags=8; + + if(Rate<=11025) + fragsize=1<<5; + else if(Rate<=22050) + fragsize=1<<6; + else + fragsize=1<<7; + + fragtotal=frags*fragsize; + WaveBuffer=malloc(fragtotal<65535)) + { + printf(" Unsupported playback rate: %d samples per second\n",Rate); + return(0); + } + + if(!GetBLASTER()) + return(0); + + /* Disable IRQ line in PIC0 or PIC1 */ + if(SBIRQ>7) + { + PICMask=inportb(0xA1); + outportb(0xA1,PICMask|(1<<(SBIRQ&7))); + } + else + { + PICMask=inportb(0x21); + outportb(0x21,PICMask|(1<>8,DSPV&0xFF); + if(DSPV<0x201) + { + printf(" DSP version number is too low.\n"); + return(0); + } + + if(DSPV<0x400) + format=0; + if(!MakeDMABuffer()) + { + puts(" Error creating low-memory DMA buffer."); + return(0); + } + + if(SBIRQ>7) IVector=SBIRQ+0x68; + else IVector=SBIRQ+0x8; + + if(!SetVectors()) + { + puts(" Error setting interrupt vectors."); + KillDMABuffer(); + return(0); + } + + /* Reenable IRQ line. */ + if(SBIRQ>7) + outportb(0xA1,PICMask&(~(1<<(SBIRQ&7)))); + else + outportb(0x21,PICMask&(~(1<=0x400) + { + WriteDSP(0x41); // Set sampling rate + WriteDSP(Rate>>8); // High byte + WriteDSP(Rate&0xFF); // Low byte + if(!format) + { + WriteDSP(0xC6); // 8-bit output + WriteDSP(0x00); // 8-bit mono unsigned PCM + } + else + { + WriteDSP(0xB6); // 16-bit output + WriteDSP(0x10); // 16-bit mono signed PCM + } + WriteDSP((fragsize-1)&0xFF);// Low byte of size + WriteDSP((fragsize-1)>>8); // High byte of size + } + else + { + int tc,command; + if(Rate>22050) + { + tc=(65536-(256000000/Rate))>>8; + Rate=256000000/(65536-(tc<<8)); + command=0x90; // High-speed auto-initialize DMA mode transfer + hsmode=1; + } + else + { + tc=256-(1000000/Rate); + Rate=1000000/(256-tc); + command=0x1c; // Auto-initialize DMA mode transfer + } + WriteDSP(0x40); // Set DSP time constant + WriteDSP(tc); // time constant + WriteDSP(0x48); // Set DSP block transfer size + WriteDSP((fragsize-1)&0xFF); + WriteDSP((fragsize-1)>>8); + + WriteDSP(command); + } + + /* Enable DMA */ + if(format) + outportb(0xd4,SBDMA16&3); + else + outportb(0xa,SBDMA); + + printf(" %d hz, %d-bit\n",Rate,8<=8) + outportb(0xA0,0x20); + whichbuf^=1; + return; + } + hbusy=1; + + { + /* This code seems to fail on many SB emulators. Bah. + SCREW SB EMULATORS. ^_^ */ + uint32 count; + uint32 block; + uint32 port; + + if(format) + port=((SBDMA16&3)*4)+0xc2; + else + port=(SBDMA*2)+1; + + count=inportb(port); + count|=inportb(port)<<8; + + if(count>=fragsize) + block=1; + else + block=0; + dest=LMBuffer+((block*fragsize)<>2;x;x--,dest+=4) + { + _farnspokel(dest,sby); + } + } + else + { + for(x=(fragsize<>2;x;x--,dest+=4,src++) + { + _farnspokel(dest,*src); + } + ReadPtr=(ReadPtr+fragsize)&(fragtotal-1); + } + + if(soundjoyeron) + { + static int coot=0; + if(!coot) + { + UpdateJoyData(); + soundjoyer=1; + } + coot=(coot+1)&3; + } + hbusy=0; + outportb(0x20,0x20); + if(SBIRQ>=8) + outportb(0xA0,0x20); +} + +void SilenceSound(int s) +{ + ssilence=s; +} + +void WriteSBSound(int32 *Buffer, int Count, int NoBlocking) +{ + int x; + + if(!format) + { + for(x=0;x>8)^128; + WritePtr=(WritePtr+1)&(fragtotal-1); + } + } + else // 16 bit + { + for(x=0;x7)?0xA1:0x21,PICMask|(1<<(SBIRQ&7))); + ResetVectors(); + outportb((SBIRQ>7)?0xA1:0x21,PICMask); + KillDMABuffer(); +} diff --git a/drivers/cli/dos-sound.h b/drivers/cli/dos-sound.h new file mode 100644 index 0000000..7f5185a --- /dev/null +++ b/drivers/cli/dos-sound.h @@ -0,0 +1,26 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +int InitSB(int Rate, int bittage); +void KillSB(void); + +void WriteSBSound(int32 *Buffer, int Count, int NoBlocking); +void SilenceSound(int s); + diff --git a/drivers/cli/dos-video.c b/drivers/cli/dos-video.c new file mode 100644 index 0000000..574c824 --- /dev/null +++ b/drivers/cli/dos-video.c @@ -0,0 +1,246 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 1998 \Firebug\ + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include + +#include "dos.h" +#include "dos-video.h" + +#define TEXT 3 +#define G320x200x256 0x13 + +static void vga_waitretrace(void) +{ + while(inp(0x3da)&0x8); + while(!(inp(0x3da)&0x8)); +} + +static void vga_setmode(int mode) +{ + __dpmi_regs regs; + + memset(®s,0,sizeof(regs)); + regs.x.ax=mode; + + __dpmi_int(0x10,®s); +} + +void vga_setpalette(int i, int r, int g, int b) +{ + outp(0x3c8,i); + outp(0x3c9,r); + outp(0x3c9,g); + outp(0x3c9,b); +} + +int FCEUDvmode=1; + +static int vidready=0; + +/* Part of the VGA low-level mass register setting code derived from + code by \Firebug\. +*/ + +#include "vgatweak.c" + +void SetBorder(void) +{ + inportb(0x3da); + outportb(0x3c0,(0x11|0x20)); + outportb(0x3c0,0x80); +} + +void TweakVGA(int VGAMode) +{ + int I; + + vga_waitretrace(); + + outportb(0x3C8,0x00); + for(I=0;I<768;I++) outportb(0x3C9,0x00); + + outportb(0x3D4,0x11); + I=inportb(0x3D5)&0x7F; + outportb(0x3D4,0x11); + outportb(0x3D5,I); + + switch(VGAMode) + { + case 1: for(I=0;I<25;I++) VGAPortSet(v256x240[I]);break; + case 2: for(I=0;I<25;I++) VGAPortSet(v256x256[I]);break; + case 3: for(I=0;I<25;I++) VGAPortSet(v256x256S[I]);break; + case 6: for(I=0;I<25;I++) VGAPortSet(v256x224S[I]);break; + case 8: for(I=0;I<25;I++) VGAPortSet(v256x224_103[I]);break; + default: break; + } + + outportb(0x3da,0); +} + + +static uint8 palettedbr[256],palettedbg[256],palettedbb[256]; + +static void FlushPalette(void) +{ + int x; + for(x=0;x<256;x++) + { + int z=x; + vga_setpalette(z,palettedbr[x]>>2,palettedbg[x]>>2,palettedbb[x]>>2); + } +} + +void FCEUD_SetPalette(uint8 index, uint8 r, uint8 g, uint8 b) +{ + palettedbr[index]=r; + palettedbg[index]=g; + palettedbb[index]=b; + if(vidready) + { + vga_setpalette(index,r>>2,g>>2,b>>2); + } +} + + +void FCEUD_GetPalette(uint8 i, uint8 *r, uint8 *g, uint8 *b) +{ + *r=palettedbr[i]; + *g=palettedbg[i]; + *b=palettedbb[i]; +} + +static uint32 ScreenLoc; + +int InitVideo(void) +{ + vidready=0; + switch(FCEUDvmode) + { + default: + case 1: + case 2: + case 3: + case 6: + case 8: + vga_setmode(G320x200x256); + vidready|=1; + ScreenLoc=0xa0000; + TweakVGA(FCEUDvmode); + SetBorder(); + DOSMemSet(ScreenLoc, 128, 256*256); + break; + } + vidready|=2; + FlushPalette(); + return 1; +} + +void KillVideo(void) +{ + if(vidready) + { + vga_setmode(TEXT); + vidready=0; + } +} +void LockConsole(void){} +void UnlockConsole(void){} +void BlitScreen(uint8 *XBuf) +{ + uint32 dest; + int tlines; + + if(eoptions&4 && !NoWaiting) + vga_waitretrace(); + + tlines=erendline-srendline+1; + + dest=ScreenLoc; + + switch(FCEUDvmode) + { + case 1:dest+=(((240-tlines)>>1)<<8);break; + case 2: + case 3:dest+=(((256-tlines)>>1)<<8);break; + case 4: + case 5:dest+=(((240-tlines)>>1)*640+((640-512)>>1));break; + case 8: + case 6:if(tlines>224) tlines=224;dest+=(((224-tlines)>>1)<<8);break; + } + + XBuf+=(srendline<<8)+(srendline<<4); + + _farsetsel(_dos_ds); + if(eoptions&DO_CLIPSIDES) + { + asm volatile( + "agoop1:\n\t" + "movl $30,%%eax\n\t" + "agoop2:\n\t" + "movl (%%esi),%%edx\n\t" + "movl 4(%%esi),%%ecx\n\t" + ".byte 0x64 \n\t" + "movl %%edx,(%%edi)\n\t" + ".byte 0x64 \n\t" + "movl %%ecx,4(%%edi)\n\t" + "addl $8,%%esi\n\t" + "addl $8,%%edi\n\t" + "decl %%eax\n\t" + "jne agoop2\n\t" + "addl $32,%%esi\n\t" + "addl $16,%%edi\n\t" + "decb %%bl\n\t" + "jne agoop1\n\t" + : + : "S" (XBuf+8), "D" (dest+8), "b" (tlines) + : "%eax","%cc","%edx","%ecx" ); + } + else + { + asm volatile( + "goop1:\n\t" + "movl $32,%%eax\n\t" + "goop2:\n\t" + "movl (%%esi),%%edx\n\t" + "movl 4(%%esi),%%ecx\n\t" + ".byte 0x64 \n\t" + "movl %%edx,(%%edi)\n\t" + ".byte 0x64 \n\t" + "movl %%ecx,4(%%edi)\n\t" + "addl $8,%%esi\n\t" + "addl $8,%%edi\n\t" + "decl %%eax\n\t" + "jne goop2\n\t" + "addl $16,%%esi\n\t" + "decb %%bl\n\t" + "jne goop1\n\t" + : + : "S" (XBuf), "D" (dest), "b" (tlines) + : "%eax","%cc","%edx","%ecx" ); + } +} + + diff --git a/drivers/cli/dos-video.h b/drivers/cli/dos-video.h new file mode 100644 index 0000000..ee09b51 --- /dev/null +++ b/drivers/cli/dos-video.h @@ -0,0 +1,22 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +extern int FCEUDvmode; + diff --git a/drivers/cli/dos.c b/drivers/cli/dos.c new file mode 100644 index 0000000..8ba1578 --- /dev/null +++ b/drivers/cli/dos.c @@ -0,0 +1,128 @@ +#include +#include +#include +#include +#include +#include + +#include "dos.h" +#include "dos-joystick.h" +#include "dos-video.h" +#include "dos-sound.h" +#include "../common/args.h" +#include "../common/config.h" + +/* _CRT0_FLAG_LOCK_MEMORY might not always result in all memory being locked. + Bummer. I'll add code to explicitly lock the data touched by the sound + interrupt handler(and the handler itself), if necessary(though that might + be tricky...). I'll also to cover the data the keyboard + interrupt handler touches. +*/ + +int _crt0_startup_flags = _CRT0_FLAG_FILL_SBRK_MEMORY | _CRT0_FLAG_LOCK_MEMORY | _CRT0_FLAG_USE_DOS_SLASHES; + +static int f8bit=0; +int soundo=44100; +int doptions=0; + + +CFGSTRUCT DriverConfig[]={ + NAC("sound",soundo), + AC(doptions), + AC(f8bit), + AC(FCEUDvmode), + NACA("joybmap",joyBMap), + AC(joy), + ENDCFGSTRUCT +}; + +char *DriverUsage= +"-vmode x Select video mode(all are 8 bpp).\n\ + 1 = 256x240 6 = 256x224(with scanlines)\n\ + 2 = 256x256 8 = 256x224\n\ + 3 = 256x256(with scanlines)\n\ +-vsync x Wait for the screen's vertical retrace before updating the\n\ + screen. Refer to the documentation for caveats.\n\ + 0 = Disabled.\n\ + 1 = Enabled.\n\ +-sound x Sound.\n\ + 0 = Disabled.\n\ + Otherwise, x = playback rate.\n\ +-f8bit x Force 8-bit sound.\n\ + 0 = Disabled.\n\ + 1 = Enabled.\n\ +-joy x Joystick mapped to virtual joystick x(1-4).\n\ + 0 = Disabled, reset configuration.\n\ + 1 = Enabled."; + +ARGPSTRUCT DriverArgs[]={ + {"-vmode",0,&FCEUDvmode,0}, + {"-sound",0,&soundo,0}, + {"-f8bit",0,&f8bit,0}, + {"-joy",0,&joy,0}, + {"-vsync",0,&eoptions,0x8004}, + {0,0,0,0} +}; + +void DoDriverArgs(void) +{ + if(!joy) memset(joyBMap,0,4); +} + +int InitSound(void) +{ + if(soundo) + { + if(soundo==1) + soundo=44100; + soundo=InitSB(soundo,f8bit?0:1); + FCEUI_Sound(soundo); + } + return(soundo?1:0); +} + +void WriteSound(int32 *Buffer, int Count, int NoWaiting) +{ + WriteSBSound(Buffer,Count,NoWaiting); +} + +void KillSound(void) +{ + if(soundo) + KillSB(); +} + +void DOSMemSet(uint32 A, uint8 V, uint32 count) +{ + uint32 x; + + _farsetsel(_dos_ds); + for(x=0;x=0;x--) + { + if(arg0[x]=='/' || arg0[x]=='\\') + { + strncpy(BaseDirectory,arg0,x); + break; + } + } + + BaseDirectory[x]=0; +} + +int main(int argc, char *argv[]) +{ + puts("\nStarting FCE Ultra "VERSION_STRING"...\n"); + arg0=argv[0]; + return(CLImain(argc,argv)); +} + diff --git a/drivers/cli/dos.h b/drivers/cli/dos.h new file mode 100644 index 0000000..05bc8e5 --- /dev/null +++ b/drivers/cli/dos.h @@ -0,0 +1,28 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "../../driver.h" +#include "main.h" + +extern int eoptions; +extern int soundo; +void DOSMemSet(uint32 A, uint8 V, uint32 count); +#define DO_CLIPSIDES 1 + diff --git a/drivers/cli/input.c b/drivers/cli/input.c new file mode 100644 index 0000000..b946c5b --- /dev/null +++ b/drivers/cli/input.c @@ -0,0 +1,340 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define JOY_A 1 +#define JOY_B 2 +#define JOY_SELECT 4 +#define JOY_START 8 +#define JOY_UP 0x10 +#define JOY_DOWN 0x20 +#define JOY_LEFT 0x40 +#define JOY_RIGHT 0x80 + +static void UpdateFKB(void); + +/* UsrInputType[] is user-specified. InputType[] is current + (game loading can override user settings) +*/ +static int UsrInputType[2]={SI_GAMEPAD,SI_GAMEPAD}; +static int InputType[2]; + +static int UsrInputTypeFC={SI_NONE}; +static int InputTypeFC; + +static uint32 JSreturn; +int NoWaiting=0; + +static void DoCheatSeq(void) +{ + #if defined(DOS) || defined(SDL) + if(inited&1) + SilenceSound(1); + #endif + KillKeyboard(); + KillVideo(); + + DoConsoleCheatConfig(); + InitVideo(); + InitKeyboard(); + #if defined(DOS) || defined(SDL) + if(inited&1) + SilenceSound(0); + #endif +} + +#include "keyscan.h" +static char *keys; +static int DIPS=0; +static uint8 keyonce[MK_COUNT]; +#define KEY(__a) keys[MK(__a)] +#define keyonly(__a,__z) {if(KEY(__a)){if(!keyonce[MK(__a)]) {keyonce[MK(__a)]=1;__z}}else{keyonce[MK(__a)]=0;}} +static int JoySwap=0; +static int cidisabled=0; +static int KeyboardUpdate(void) +{ + + if(!UpdateKeyboard()) + if(keys) + return 0; + + keys=GetKeyboard(); + + if(InputTypeFC==SIFC_FKB) + { + keyonly(SCROLLLOCK,cidisabled^=1; + FCEUI_DispMessage("Family Keyboard %sabled.",cidisabled?"en":"dis");) + #ifdef SDL + SDL_WM_GrabInput(cidisabled?SDL_GRAB_ON:SDL_GRAB_OFF); + #endif + if(cidisabled) return(1); + } + #ifdef SVGALIB + keyonly(F3,LockConsole();) + keyonly(F4,UnlockConsole();) + #elif SDL + keyonly(F4,ToggleFS();) + #endif + NoWaiting&=~1; + if(KEY(GRAVE)) + NoWaiting|=1; + + if(gametype==GIT_FDS) + { + keyonly(S,DriverInterface(DES_FDSSELECT,0);) + keyonly(I,DriverInterface(DES_FDSINSERT,0);) + keyonly(E,DriverInterface(DES_FDSEJECT,0);) + } + + keyonly(F9,FCEUI_SaveSnapshot();) + if(gametype!=GIT_NSF) + { + keyonly(F2,DoCheatSeq();) + keyonly(F5,FCEUI_SaveState();) + keyonly(F7,FCEUI_LoadState();) + } + else + { + keyonly(CURSORLEFT,DriverInterface(DES_NSFDEC,0);) + keyonly(CURSORRIGHT,DriverInterface(DES_NSFINC,0);) + if( KEY(ENTER)) DriverInterface(DES_NSFRES,0); + if( KEY(CURSORUP)) DriverInterface(DES_NSFINC,0); + if( KEY(CURSORDOWN)) DriverInterface(DES_NSFDEC,0); + } + + keyonly(F10,DriverInterface(DES_RESET,0);) + keyonly(F11,DriverInterface(DES_POWER,0);) + if(KEY(F12) || KEY(ESCAPE)) FCEUI_CloseGame(); + + if(gametype==GIT_VSUNI) + { + keyonly(C,DriverInterface(DES_VSUNICOIN,0);) + keyonly(V,DIPS^=1;DriverInterface(DES_VSUNITOGGLEDIPVIEW,0);) + if(!(DIPS&1)) goto DIPSless; + keyonly(1,DriverInterface(DES_VSUNIDIPSET,(void *)1);) + keyonly(2,DriverInterface(DES_VSUNIDIPSET,(void *)2);) + keyonly(3,DriverInterface(DES_VSUNIDIPSET,(void *)3);) + keyonly(4,DriverInterface(DES_VSUNIDIPSET,(void *)4);) + keyonly(5,DriverInterface(DES_VSUNIDIPSET,(void *)5);) + keyonly(6,DriverInterface(DES_VSUNIDIPSET,(void *)6);) + keyonly(7,DriverInterface(DES_VSUNIDIPSET,(void *)7);) + keyonly(8,DriverInterface(DES_VSUNIDIPSET,(void *)8);) + } + else + { + keyonly(H,DriverInterface(DES_NTSCSELHUE,0);) + keyonly(T,DriverInterface(DES_NTSCSELTINT,0);) + if(KEY(KP_MINUS) || KEY(MINUS)) DriverInterface(DES_NTSCDEC,0); + if(KEY(KP_PLUS) || KEY(EQUAL)) DriverInterface(DES_NTSCINC,0); + + DIPSless: + keyonly(0,FCEUI_SelectState(0);) + keyonly(1,FCEUI_SelectState(1);) + keyonly(2,FCEUI_SelectState(2);) + keyonly(3,FCEUI_SelectState(3);) + keyonly(4,FCEUI_SelectState(4);) + keyonly(5,FCEUI_SelectState(5);) + keyonly(6,FCEUI_SelectState(6);) + keyonly(7,FCEUI_SelectState(7);) + keyonly(8,FCEUI_SelectState(8);) + keyonly(9,FCEUI_SelectState(9);) + } + return 1; +} + +static uint32 KeyboardDodo(void) +{ + uint32 JS=0; + + if(gametype!=GIT_NSF) + { + int x,y; + x=y=0; + keyonly(CAPSLOCK, + { + char tmp[64]; + JoySwap=(JoySwap+8)%32; + sprintf(tmp,"Joystick %d selected.",(JoySwap>>3)+1); + FCEUI_DispMessage(tmp); + }) + + if(KEY(LEFTALT) || KEY(X)) JS|=JOY_A<>8)|((JS&0xFF00)<<8); + } + if(t&2) + GetMouseData(MouseData); +} + +static void InitOtherInput(void) +{ + void *InputDPtr; + + int t; + int x; + int attrib; + + for(t=0,x=0;x<2;x++) + { + attrib=0; + InputDPtr=0; + switch(InputType[x]) + { + case SI_POWERPAD:InputDPtr=&powerpadbuf[x];break; + case SI_GAMEPAD:InputDPtr=((uint8 *)&JSreturn)+(x<<1);break; + case SI_ARKANOID:InputDPtr=MouseData;t|=1;break; + case SI_ZAPPER:InputDPtr=MouseData; + t|=1; + attrib=1; + break; + } + FCEUI_SetInput(x,InputType[x],InputDPtr,attrib); + } + + attrib=0; + InputDPtr=0; + switch(InputTypeFC) + { + case SIFC_SHADOW:InputDPtr=MouseData;t|=1;attrib=1;break; + case SIFC_ARKANOID:InputDPtr=MouseData;t|=1;break; + case SIFC_FKB:InputDPtr=fkbkeys;break; + } + + FCEUI_SetInputFC(InputTypeFC,InputDPtr,attrib); + FCEUI_DisableFourScore(eoptions&EO_NOFOURSCORE); + + if(t && !(inited&16)) + { + InitMouse(); + inited|=16; + } +} + +int fkbmap[0x48]= +{ + MK(F1),MK(F2),MK(F3),MK(F4),MK(F5),MK(F6),MK(F7),MK(F8), + MK(1),MK(2),MK(3),MK(4),MK(5),MK(6),MK(7),MK(8),MK(9),MK(0), + MK(MINUS),MK(EQUAL),MK(BACKSLASH),MK(BACKSPACE), + MK(ESCAPE),MK(Q),MK(W),MK(E),MK(R),MK(T),MK(Y),MK(U),MK(I),MK(O), + MK(P),MK(GRAVE),MK(BRACKET_LEFT),MK(ENTER), + MK(LEFTCONTROL),MK(A),MK(S),MK(D),MK(F),MK(G),MK(H),MK(J),MK(K), + MK(L),MK(SEMICOLON),MK(APOSTROPHE),MK(BRACKET_RIGHT),MK(INSERT), + MK(LEFTSHIFT),MK(Z),MK(X),MK(C),MK(V),MK(B),MK(N),MK(M),MK(COMMA), + MK(PERIOD),MK(SLASH),MK(RIGHTALT),MK(RIGHTSHIFT),MK(LEFTALT),MK(SPACE), + MK(DELETE),MK(END),MK(PAGEDOWN), + MK(CURSORUP),MK(CURSORLEFT),MK(CURSORRIGHT),MK(CURSORDOWN) +}; + +static void UpdateFKB(void) +{ + int x; + + for(x=0;x<0x48;x++) + { + fkbkeys[x]=0; + if(keys[fkbmap[x]]) + fkbkeys[x]=1; + } +} diff --git a/drivers/cli/keyscan.h b/drivers/cli/keyscan.h new file mode 100644 index 0000000..af82127 --- /dev/null +++ b/drivers/cli/keyscan.h @@ -0,0 +1,166 @@ +#ifdef SVGALIB + +#include +#define SCANCODE_DELETE SCANCODE_REMOVE +#define SCANCODE_KP_MINUS SCANCODE_KEYPADMINUS +#define SCANCODE_KP_PLUS SCANCODE_KEYPADPLUS +#define MK(k) SCANCODE_##k +#define MK_COUNT 256 +#elif SDL +#include +#define SDLK_A SDLK_a +#define SDLK_B SDLK_b +#define SDLK_C SDLK_c +#define SDLK_D SDLK_d +#define SDLK_E SDLK_e +#define SDLK_F SDLK_f +#define SDLK_G SDLK_g +#define SDLK_H SDLK_h +#define SDLK_I SDLK_i +#define SDLK_J SDLK_j +#define SDLK_K SDLK_k +#define SDLK_L SDLK_l +#define SDLK_M SDLK_m +#define SDLK_N SDLK_n +#define SDLK_O SDLK_o +#define SDLK_P SDLK_p +#define SDLK_Q SDLK_q +#define SDLK_R SDLK_r +#define SDLK_S SDLK_s +#define SDLK_T SDLK_t +#define SDLK_U SDLK_u +#define SDLK_V SDLK_v +#define SDLK_W SDLK_w +#define SDLK_X SDLK_x +#define SDLK_Y SDLK_y +#define SDLK_Z SDLK_z +#define SDLK_LEFTCONTROL SDLK_LCTRL +#define SDLK_RIGHTCONTROL SDLK_RCTRL +#define SDLK_LEFTALT SDLK_LALT +#define SDLK_RIGHTALT SDLK_RALT +#define SDLK_LEFTSHIFT SDLK_LSHIFT +#define SDLK_RIGHTSHIFT SDLK_RSHIFT +#define SDLK_CURSORDOWN SDLK_DOWN +#define SDLK_CURSORUP SDLK_UP +#define SDLK_CURSORLEFT SDLK_LEFT +#define SDLK_CURSORRIGHT SDLK_RIGHT +#define SDLK_ENTER SDLK_RETURN +#define SDLK_EQUAL SDLK_EQUALS +#define SDLK_APOSTROPHE SDLK_QUOTE +#define SDLK_BRACKET_LEFT SDLK_LEFTBRACKET +#define SDLK_BRACKET_RIGHT SDLK_RIGHTBRACKET +#define SDLK_SCROLLLOCK SDLK_SCROLLOCK /* I guess the SDL people don't like lots of Ls... */ +#define SDLK_GRAVE SDLK_BACKQUOTE +#define MK(k) SDLK_##k +#define MK_COUNT (SDLK_LAST+1) +#elif DOS + +#define SCAN_GRAVE 0x29 +#define SCAN_1 0x02 +#define SCAN_2 0x03 +#define SCAN_3 0x04 +#define SCAN_4 0x05 +#define SCAN_5 0x06 +#define SCAN_6 0x07 +#define SCAN_7 0x08 +#define SCAN_8 0x09 +#define SCAN_9 0x0A +#define SCAN_0 0x0B +#define SCAN_MINUS 0x0C +#define SCAN_EQUAL 0x0D +#define SCAN_BACKSLASH 0x2B +#define SCAN_BACKSPACE 0x0E +#define SCAN_TAB 0x0F +#define SCAN_Q 0x10 +#define SCAN_W 0x11 +#define SCAN_E 0x12 +#define SCAN_R 0x13 +#define SCAN_T 0x14 +#define SCAN_Y 0x15 +#define SCAN_U 0x16 +#define SCAN_I 0x17 +#define SCAN_O 0x18 +#define SCAN_P 0x19 +#define SCAN_BRACKET_LEFT 0x1A +#define SCAN_BRACKET_RIGHT 0x1B +#define SCAN_LOWBACKSLASH 0x2B +#define SCAN_CAPSLOCK 0x3A +#define SCAN_A 0x1E +#define SCAN_S 0x1F +#define SCAN_D 0x20 +#define SCAN_F 0x21 +#define SCAN_G 0x22 +#define SCAN_H 0x23 +#define SCAN_J 0x24 +#define SCAN_K 0x25 +#define SCAN_L 0x26 +#define SCAN_SEMICOLON 0x27 +#define SCAN_APOSTROPHE 0x28 +#define SCAN_ENTER 0x1C +#define SCAN_LEFTSHIFT 0x2A +#define SCAN_Z 0x2C +#define SCAN_X 0x2D +#define SCAN_C 0x2E +#define SCAN_V 0x2F +#define SCAN_B 0x30 +#define SCAN_N 0x31 +#define SCAN_M 0x32 +#define SCAN_COMMA 0x33 +#define SCAN_PERIOD 0x34 +#define SCAN_SLASH 0x35 +#define SCAN_RIGHTSHIFT 0x36 +#define SCAN_LEFTCONTROL 0x1D +#define SCAN_LEFTALT 0x38 +#define SCAN_SPACE 0x39 + +/* Extended keys. */ +#define SCAN_RIGHTALT (0x38|0x80) +#define SCAN_RIGHTCONTROL (0x1D|0x80) +#define SCAN_BL_INSERT (0x52|0x80) +#define SCAN_BL_DELETE (0x53|0x80) +#define SCAN_BL_CURSORLEFT (0x4B|0x80) +#define SCAN_BL_HOME (0x47|0x80) +#define SCAN_BL_END (0x4F|0x80) +#define SCAN_BL_CURSORUP (0x48|0x80) +#define SCAN_BL_CURSORDOWN (0x50|0x80) +#define SCAN_BL_PAGEUP (0x49|0x80) +#define SCAN_BL_PAGEDOWN (0x51|0x80) +#define SCAN_BL_CURSORRIGHT (0x4D|0x80) + +#define SCAN_SCROLLLOCK 0x46 +/* Keys in the key pad area. */ +#define SCAN_NUMLOCK 0x45 +#define SCAN_HOME 0x47 +#define SCAN_CURSORLEFT 0x4B +#define SCAN_END 0x4F +#define SCAN_SLASH 0x35 +#define SCAN_CURSORUP 0x48 +#define SCAN_CENTER 0x4C +#define SCAN_CURSORDOWN 0x50 +#define SCAN_INSERT 0x52 +#define SCAN_ASTERISK 0x37 +#define SCAN_PAGEUP 0x49 +#define SCAN_CURSORRIGHT 0x4D +#define SCAN_PAGEDOWN 0x51 +#define SCAN_DELETE 0x53 +#define SCAN_KP_MINUS 0x4A +#define SCAN_KP_PLUS 0x4E +#define SCAN_KP_ENTER 0x1C + +#define SCAN_ESCAPE 0x01 +#define SCAN_F1 0x3B +#define SCAN_F2 0x3C +#define SCAN_F3 0x3D +#define SCAN_F4 0x3E +#define SCAN_F5 0x3F +#define SCAN_F6 0x40 +#define SCAN_F7 0x41 +#define SCAN_F8 0x42 +#define SCAN_F9 0x43 +#define SCAN_F10 0x44 +#define SCAN_F11 0x57 +#define SCAN_F12 0x58 + +#define MK_COUNT 256 +#define MK(k) SCAN_##k +#endif diff --git a/drivers/cli/lnx-joystick.c b/drivers/cli/lnx-joystick.c new file mode 100644 index 0000000..4c2ae6a --- /dev/null +++ b/drivers/cli/lnx-joystick.c @@ -0,0 +1,201 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include "main.h" +#include "lnx-joystick.h" + +int joy[4]={0,0,0,0}; +int joyBMap[4][4]; + +static int32 joybuttons[4]={0,0,0,0}; +static int32 joyx[4]={0,0,0,0}; +static int32 joyy[4]={0,0,0,0}; + +static void ConfigJoystick(int z); +static int fd[4]; + +#define JOY_A 1 +#define JOY_B 2 +#define JOY_SELECT 4 +#define JOY_START 8 +#define JOY_UP 0x10 +#define JOY_DOWN 0x20 +#define JOY_LEFT 0x40 +#define JOY_RIGHT 0x80 + +static void UpdateJoyData(int x) +{ + struct js_event e; + + while(read(fd[x],&e,sizeof(struct js_event))==sizeof(struct js_event)) + { + e.type&=~JS_EVENT_INIT; + if(e.type==JS_EVENT_BUTTON) + { + if(e.value) + joybuttons[x]|=(1<=16383) ret|=JOY_RIGHT<<(x<<3); + if(joyy[x]<=-16383) ret|=JOY_UP<<(x<<3); + else if(joyy[x]>=16383) ret|=JOY_DOWN<<(x<<3); + } + return ret; +} + +void KillJoysticks(void) +{ + int x; + for(x=0;x<4;x++) + if(joy[x]) + close(fd[x]); +} + +int InitJoysticks(void) +{ + char dbuf[64]; + int version; + int z; + + for(z=0;z<4;z++) + { + if(!joy[z]) continue; + sprintf(dbuf,"/dev/js%d",joy[z]-1); + if((fd[z]=open(dbuf,O_RDONLY|O_NONBLOCK))<0) + { + printf("Could not open %s.\n",dbuf); + joy[z]=0; + continue; + } + + if(ioctl(fd[z], JSIOCGVERSION, &version)==-1) + { + printf("Error using ioctl JSIOCGVERSION on %s.\n",dbuf); + joy[z]=0; + close(fd[z]); + continue; + } + + if(!(joyBMap[z][0]|joyBMap[z][1]|joyBMap[z][2]|joyBMap[z][3])) + ConfigJoystick(z); + } + + return(joy[0]|joy[1]|joy[2]|joy[3]); +} + +#define WNOINPUT(); for(;;){uint8 t; if(read(fileno(stdin),&t,1)==-1) \ + {break;}} + +static void BConfig(int z,int b) +{ + WNOINPUT(); + for(;;) + { + uint8 t; + if(read(fileno(stdin),&t,1)==-1) + { + if(errno!=EAGAIN) break; + } + else + break; + + { + struct js_event e; + + while(read(fd[z],&e,sizeof(struct js_event))==sizeof(struct js_event)) + { + if(e.type==JS_EVENT_BUTTON) + { + if(!e.value) + { + joyBMap[z][b]=1< +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "throttle.h" + +#include "../common/config.h" +#include "../common/args.h" +#include "../common/unixdsp.h" +#include "../common/cheat.h" + +#include "dface.h" + +static int ntsccol=0,ntschue=-1,ntsctint=-1; +static int soundvol=100; +static int inited=0; + +int srendlinev[2]={8,0}; +int erendlinev[2]={239,239}; +int srendline,erendline; + + +static char BaseDirectory[2048]; + +int eoptions=0; + +static void DriverKill(void); +static int DriverInitialize(void); + +static int gametype; +#include "input.c" + +static void ParseGI(FCEUGI *gi) +{ + gametype=gi->type; + + InputType[0]=UsrInputType[0]; + InputType[1]=UsrInputType[1]; + InputTypeFC=UsrInputTypeFC; + + if(gi->input[0]>=0) + InputType[0]=gi->input[0]; + if(gi->input[1]>=0) + InputType[1]=gi->input[1]; + if(gi->inputfc>=0) + InputTypeFC=gi->inputfc; + FCEUI_GetCurrentVidSystem(&srendline,&erendline); +} + +void FCEUD_PrintError(char *s) +{ + puts(s); +} + +static char *cpalette=0; +static void LoadCPalette(void) +{ + char tmpp[192]; + FILE *fp; + + if(!(fp=fopen(cpalette,"rb"))) + { + printf(" Error loading custom palette from file: %s\n",cpalette); + return; + } + fread(tmpp,1,192,fp); + FCEUI_SetPaletteArray(tmpp); + fclose(fp); +} + +static CFGSTRUCT fceuconfig[]={ + AC(soundvol), + ACS(cpalette), + AC(ntsctint), + AC(ntschue), + AC(ntsccol), + AC(UsrInputTypeFC), + ACA(UsrInputType), + AC(powerpadside), + AC(powerpadsc), + AC(eoptions), + ACA(srendlinev), + ACA(erendlinev), + ADDCFGSTRUCT(DriverConfig), + ENDCFGSTRUCT +}; + +static void SaveConfig(void) +{ + char tdir[2048]; + sprintf(tdir,"%s"PSS"fceu.cfg",BaseDirectory); + DriverInterface(DES_GETNTSCTINT,&ntsctint); + DriverInterface(DES_GETNTSCHUE,&ntschue); + SaveFCEUConfig(tdir,fceuconfig); +} + +static void LoadConfig(void) +{ + char tdir[2048]; + sprintf(tdir,"%s"PSS"fceu.cfg",BaseDirectory); + LoadFCEUConfig(tdir,fceuconfig); + if(ntsctint>=0) DriverInterface(DES_SETNTSCTINT,&ntsctint); + if(ntschue>=0) DriverInterface(DES_SETNTSCHUE,&ntschue); +} + +static void CreateDirs(void) +{ + char *subs[5]={"fcs","snaps","gameinfo","sav","cheats"}; + char tdir[2048]; + int x; + + mkdir(BaseDirectory,S_IRWXU); + for(x=0;x<5;x++) + { + sprintf(tdir,"%s"PSS"%s",BaseDirectory,subs[x]); + mkdir(tdir,S_IRWXU); + } +} + +static void SetSignals(void (*t)(int)) +{ + int sigs[11]={SIGINT,SIGTERM,SIGHUP,SIGPIPE,SIGSEGV,SIGFPE,SIGKILL,SIGALRM,SIGABRT,SIGUSR1,SIGUSR2}; + int x; + for(x=0;x<11;x++) + signal(sigs[x],t); +} + +static void CloseStuff(int signum) +{ + DriverKill(); + printf("\nSignal %d has been caught and dealt with...\n",signum); + switch(signum) + { + case SIGINT:printf("How DARE you interrupt me!\n");break; + case SIGTERM:printf("MUST TERMINATE ALL HUMANS\n");break; + case SIGHUP:printf("Reach out and hang-up on someone.\n");break; + case SIGPIPE:printf("The pipe has broken! Better watch out for floods...\n");break; + case SIGSEGV:printf("Iyeeeeeeeee!!! A segmentation fault has occurred. Have a fluffy day.\n");break; + /* So much SIGBUS evil. */ + #ifdef SIGBUS + #if(SIGBUS!=SIGSEGV) + case SIGBUS:printf("I told you to be nice to the driver.\n");break; + #endif + #endif + case SIGFPE:printf("Those darn floating points. Ne'er know when they'll bite!\n");break; + case SIGALRM:printf("Don't throw your clock at the meowing cats!\n");break; + case SIGABRT:printf("Abort, Retry, Ignore, Fail?\n");break; + case SIGUSR1: + case SIGUSR2:printf("Killing your processes is not nice.\n");break; + } + exit(1); +} + +static void DoArgs(int argc, char *argv[]) +{ + static char *cortab[5]={"none","gamepad","zapper","powerpad","arkanoid"}; + static int cortabi[5]={SI_NONE,SI_GAMEPAD, + SI_ZAPPER,SI_POWERPAD,SI_ARKANOID}; + static char *fccortab[5]={"none","arkanoid","shadow","4player","fkb"}; + static int fccortabi[5]={SIFC_NONE,SIFC_ARKANOID,SIFC_SHADOW, + SIFC_4PLAYER,SIFC_FKB}; + + int x; + static char *inputa[2]={0,0}; + static char *fcexp=0; + static int docheckie[4]; + + static ARGPSTRUCT FCEUArgs[]={ + {"-soundvol",0,&soundvol,0}, + {"-cpalette",0,&cpalette,0x4001}, + + {"-ntsccol",0,&ntsccol,0}, + {"-pal",&docheckie[0],0,0}, + {"-input1",0,&inputa[0],0x4001},{"-input2",0,&inputa[1],0x4001}, + {"-fcexp",0,&fcexp,0x4001}, + + {"-gg",&docheckie[1],0,0}, + {"-no8lim",0,&eoptions,0x8001}, + {"-subase",0,&eoptions,0x8002}, + {"-snapname",0,&eoptions,0x8000|EO_SNAPNAME}, + {"-nofs",0,&eoptions,0x8000|EO_NOFOURSCORE}, + {"-clipsides",0,&eoptions,0x8000|EO_CLIPSIDES}, + {"-nothrottle",0,&eoptions,0x8000|EO_NOTHROTTLE}, + {"-slstart",0,&srendlinev[0],0},{"-slend",0,&erendlinev[0],0}, + {"-slstartp",0,&srendlinev[1],0},{"-slendp",0,&erendlinev[1],0}, + {0,(void *)DriverArgs,0,0}, + {0,0,0,0} + }; + + memset(docheckie,0,sizeof(docheckie)); + ParseArguments(argc, argv, FCEUArgs); + if(cpalette) + { + if(cpalette[0]=='0') + if(cpalette[1]==0) + { + free(cpalette); + cpalette=0; + } + } + if(docheckie[0]) + FCEUI_SetVidSystem(1); + if(docheckie[1]) + FCEUI_SetGameGenie(1); + + FCEUI_DisableSpriteLimitation(eoptions&1); + FCEUI_SaveExtraDataUnderBase(eoptions&2); + FCEUI_SetSnapName(eoptions&EO_SNAPNAME); + + for(x=0;x<2;x++) + { + if(srendlinev[x]<0 || srendlinev[x]>239) srendlinev[x]=0; + if(erendlinev[x]239) erendlinev[x]=239; + } + + FCEUI_SetRenderedLines(srendlinev[0],erendlinev[0],srendlinev[1],erendlinev[1]); + FCEUI_SetSoundVolume(soundvol); + DriverInterface(DES_NTSCCOL,&ntsccol); + DoDriverArgs(); + + if(fcexp) + { + int y; + for(y=0;y<5;y++) + { + if(!strncmp(fccortab[y],fcexp,8)) + { + UsrInputTypeFC=fccortabi[y]; + break; + } + } + free(fcexp); + } + for(x=0;x<2;x++) + { + int y; + + if(!inputa[x]) + continue; + + for(y=0;y<5;y++) + { + if(!strncmp(cortab[y],inputa[x],8)) + { + UsrInputType[x]=cortabi[y]; + if(y==3) + { + powerpadside&=~(1< +#include +#include +#include + +#include "sdl.h" +static SDL_Joystick *jo[4] = {NULL, NULL, NULL, NULL}; + +static void ConfigJoystick (int z); + +#define JOY_A 0x01 +#define JOY_B 0x02 +#define JOY_SELECT 0x04 +#define JOY_START 0x08 +#define JOY_UP 0x10 +#define JOY_DOWN 0x20 +#define JOY_LEFT 0x40 +#define JOY_RIGHT 0x80 + +/* Gets the current joystick position information. */ +uint32 GetJSOr (void) +{ + int n; /* joystick index */ + int b; /* button index */ + int *joym; /* pointer to a joystick's button map */ + Sint16 pos; /* axis position */ + uint32 ret = 0; /* return value */ + + for (n = 0; n < 4; n++) + { + if (joy[n] == 0) + continue; + joym = joyBMap[n]; + + /* Axis information. */ + pos = SDL_JoystickGetAxis(jo[n], joyAMap[n][0]); + if (pos <= -16383) + ret |= JOY_LEFT << (n << 3); + else if (pos >= 16363) + ret |= JOY_RIGHT << (n << 3); + pos = SDL_JoystickGetAxis(jo[n], joyAMap[n][1]); + if (pos <= -16383) + ret |= JOY_UP << (n << 3); + else if (pos >= 16383) + ret |= JOY_DOWN << (n << 3); + + /* Button information. */ + for (b = 0; b < 4; b++) + { + if (SDL_JoystickGetButton(jo[n], joym[b])) + ret |= (1 << b) << (n << 3); + } + } + + return ret; +} + +/* Cleanup opened joysticks. */ +void KillJoysticks (void) +{ + int n; /* joystick index */ + + for (n = 0; n < 4; n++) + { + if (joy[n] != 0) + SDL_JoystickClose(jo[n]); + } + SDL_QuitSubSystem(SDL_INIT_JOYSTICK); + return; +} + +/* Initialize joysticks. */ +int InitJoysticks (void) +{ + int n; /* joystick index */ + if(!(joy[0]|joy[1]|joy[2]|joy[3])) + return(0); + SDL_InitSubSystem(SDL_INIT_JOYSTICK); + for (n = 0; n < 4; n++) + { + if (joy[n] == 0) + continue; + + /* Open the joystick under SDL. */ + jo[n] = SDL_JoystickOpen(joy[n] - 1); + if (jo[n] == NULL) + { + printf("Could not open joystick %d: %s.\n", + joy[n] - 1, SDL_GetError()); + joy[n] = 0; + continue; + } + + /* Check for a button map. */ + if (!(joyBMap[n][0] | joyBMap[n][1] | joyBMap[n][2] | + joyBMap[n][3])) + { + ConfigJoystick(n); + } + } + + return (1); +} + +#define WNOINPUT(); for(;;){uint8 t; if(read(fileno(stdin),&t,1)==-1) \ + {break;}} + +/* Configure a joystick button. */ +static void BConfig (int n, int b) +{ + SDL_Event event; /* SDL event structure */ + WNOINPUT(); + while (1) + { + uint8 t; + if (read(fileno(stdin), &t, 1) == -1) + { + if (errno != EAGAIN) + break; + } + else + break; + + if (SDL_PollEvent(&event) && event.type == SDL_JOYBUTTONDOWN) + { + joyBMap[n][b] = event.jbutton.button; + goto endsa; + } + } + + endsa: + WNOINPUT(); + + return; +} + +/* Joystick button and axis configuration. */ +void ConfigJoystick (int n) +{ + int sa; /* buffer value */ + char buf[128]; /* input buffer */ + + printf("\n\n Joystick button and axis configuration:\n\n"); + printf(" Select the joystick axes to use for the virtual d-pad.\n"); + printf(" If you do not wish to assign an axis, press Enter to skip\n"); + printf(" that axis.\n"); + printf(" Push the button to map to the virtual joystick.\n"); + printf(" If you do not wish to assign a button, press Enter to skip\n"); + printf(" that button.\n Press enter to continue...\n"); + getchar(); + printf("**** Configuring joystick %d ****\n\n", n + 1); + + printf("** Enter axis to use for the x-axis (default 0).\n"); + fgets(buf, sizeof(buf), stdin); + joyAMap[n][0] = ('0' <= buf[0] && buf[9] <= '9') ? atoi(buf) : 0; + + printf("** Enter axis to use for the y-axis (default 1).\n"); + fgets(buf, sizeof(buf), stdin); + joyAMap[n][1] = ('0' <= buf[0] && buf[9] <= '9') ? atoi(buf) : 1; + + sa = fcntl(fileno(stdin), F_GETFL); + fcntl(fileno(stdin), F_SETFL, O_NONBLOCK); + + printf("** Press button for \"Select\".\n"); + BConfig(n, 2); + + printf("** Press button for \"Start\".\n"); + BConfig(n, 3); + + printf("** Press button for \"B\".\n"); + BConfig(n, 1); + + printf("** Press button for \"A\".\n"); + BConfig(n, 0); + + fcntl(fileno(stdin), F_SETFL, sa); +} diff --git a/drivers/cli/sdl-netplay.c b/drivers/cli/sdl-netplay.c new file mode 100644 index 0000000..38a4c81 --- /dev/null +++ b/drivers/cli/sdl-netplay.c @@ -0,0 +1,163 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2001 LULU + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "sdl.h" +#include +#include "sdl-netplay.h" + +char *netplayhost=0; + +static int tonowait; + +int Port=0xFCE; +int FDnetplay=0; + + +static SDLNet_SocketSet socketset = NULL; +static TCPsocket tcpsock = NULL, servsock = NULL; + +void cleanup(void) +{ + if (tcpsock != NULL) { + SDLNet_TCP_DelSocket(socketset, tcpsock); + SDLNet_TCP_Close(tcpsock); + tcpsock = NULL; + } + if (servsock != NULL) { + SDLNet_TCP_DelSocket(socketset, servsock); + SDLNet_TCP_Close(servsock); + servsock = NULL; + } + if (socketset != NULL) { + SDLNet_FreeSocketSet(socketset); + socketset = NULL; + } +} + +int FCEUD_NetworkConnect(void) +{ + IPaddress serverIP; + + tonowait=0; + + if (netplay == 2) { + /* client */ + printf("connecting to %s\n", netplayhost); + + SDLNet_ResolveHost(&serverIP, netplayhost, Port); + if (serverIP.host == INADDR_NONE) { + fprintf(stderr, "Couldn't connected to %s\n", netplayhost); + return -1; + } else { + tcpsock = SDLNet_TCP_Open(&serverIP); + if (tcpsock == NULL) { + fprintf(stderr, "Couldn't connected to %s\n", netplayhost); + return -1; + } + } + printf("connected to %s\n", netplayhost); + + socketset = SDLNet_AllocSocketSet(1); + if (socketset == NULL) { + fprintf(stderr, "Couldn't create socket set: %s\n", + SDLNet_GetError()); + return -1; + } + SDLNet_TCP_AddSocket(socketset, tcpsock); + + return 1; + } else { + /* server */ + + SDLNet_ResolveHost(&serverIP, NULL, Port); + printf("Server IP: %x, %d\n", serverIP.host, serverIP.port); + servsock = SDLNet_TCP_Open(&serverIP); + if (servsock == NULL) { + cleanup(); + fprintf(stderr, "Couldn't create server socket: %s\n", + SDLNet_GetError()); + return -1; + } + + socketset = SDLNet_AllocSocketSet(2); + if (socketset == NULL) { + fprintf(stderr, "Couldn't create socket set: %s\n", + SDLNet_GetError()); + return -1; + } + SDLNet_TCP_AddSocket(socketset, servsock); + + if (SDLNet_CheckSockets(socketset, ~0)) { + tcpsock = SDLNet_TCP_Accept(servsock); + if (tcpsock == NULL) { + return -1; + } + SDLNet_TCP_AddSocket(socketset, tcpsock); + + printf("OK, connected\n"); + return 1; + } + } + + return -1; +} + +void FCEUD_NetworkClose(void) +{ + cleanup(); +} + +int FCEUD_NetworkRecvData(uint8 *data, uint32 len, int block) +{ + if(block) + { + if(SDLNet_TCP_Recv(tcpsock, (void *) data, len)!=len) + { + cleanup(); + return(0); + } + switch(SDLNet_CheckSockets(socketset,0)) + { + case -1:return(0); + case 0:NoWaiting&=~2;tonowait=0;break; + default:if(tonowait>=3) + NoWaiting|=2; + else tonowait++; + break; + } + return(1); + } + else + { + int t=SDLNet_CheckSockets(socketset,0); + if(t<0) return(0); + if(!t) return(-1); + return(SDLNet_TCP_Recv(tcpsock, (void *) data, len)==len); + } +} + +/* 0 on failure, 1 on success. This function should always block. */ +int FCEUD_NetworkSendData(uint8 *Value, uint32 len) +{ + if (tcpsock) + return(SDLNet_TCP_Send(tcpsock, (void *) Value, len)==len); + return 0; +} diff --git a/drivers/cli/sdl-netplay.h b/drivers/cli/sdl-netplay.h new file mode 100644 index 0000000..48769f6 --- /dev/null +++ b/drivers/cli/sdl-netplay.h @@ -0,0 +1,5 @@ +extern char *netplayhost; +extern int Port; +extern int FDnetplay; +#define netplay FDnetplay + diff --git a/drivers/cli/sdl-sound.c b/drivers/cli/sdl-sound.c new file mode 100644 index 0000000..bbe716b --- /dev/null +++ b/drivers/cli/sdl-sound.c @@ -0,0 +1,148 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include "sdl.h" + +#ifndef DSPSOUND + +//#define BSIZE (32768-1024) + +static int32 BSIZE; +static volatile int16 AudioBuf[32768]; +static volatile uint32 readoffs,writeoffs; +void fillaudio(void *udata, uint8 *stream, int len) +{ + int16 *dest=(int16 *)stream; + + len>>=1; + while(len) + { + *dest=AudioBuf[readoffs]; + dest++; + readoffs=(readoffs+1)&32767; + len--; + } +} + +void WriteSound(int32 *Buffer, int Count, int NoWaiting) +{ + while(Count) + { + while(writeoffs==((readoffs-BSIZE)&32767)) + if(NoWaiting) + return; + AudioBuf[writeoffs]=*Buffer; + writeoffs=(writeoffs+1)&32767; + Buffer++; + Count--; + } +} + +int InitSound(void) +{ + if(_sound) + { + SDL_AudioSpec spec; + + if(_lbufsize<_ebufsize) + { + puts("Ack, lbufsize must not be smaller than ebufsize!"); + return(0); + } + if(_lbufsize<6 || _lbufsize>13) + { + puts("lbufsize out of range"); + return(0); + } + if(_ebufsize<5) + { + puts("ebufsize out of range"); + return(0); + } + memset(&spec,0,sizeof(spec)); + if(SDL_InitSubSystem(SDL_INIT_AUDIO)<0) + { + puts(SDL_GetError()); + return(0); + } + if(_sound==1) _sound=44100; + spec.freq=_sound; + spec.format=AUDIO_S16; + spec.channels=1; + spec.samples=1<<_ebufsize; + spec.callback=fillaudio; + spec.userdata=0; + + if(SDL_OpenAudio(&spec,0)<0) + { + puts(SDL_GetError()); + SDL_QuitSubSystem(SDL_INIT_AUDIO); + return(0); + } + FCEUI_Sound(_sound); + BSIZE=32768-(1<<_lbufsize); + SDL_PauseAudio(0); + return(1); + } + return(0); +} + +void SilenceSound(int n) +{ + SDL_PauseAudio(n); + +} + +void KillSound(void) +{ + SDL_CloseAudio(); + SDL_QuitSubSystem(SDL_INIT_AUDIO); +} + +#else +#include "../common/unixdsp.h" + +void WriteSound(int32 *Buffer, int Count, int NoWaiting) +{ + WriteUNIXDSPSound(Buffer, Count, NoWaiting); +} + +int InitSound(void) +{ + if(_sound) + { + int rate; + if(_sound==1) + _sound=48000; + rate=_sound; + if(InitUNIXDSPSound(&rate,_f8bit?0:1,8,8)) + { + FCEUI_Sound(rate); + return(1); + } + } + return(0); +} +void KillSound(void) +{ + KillUNIXDSPSound(); +} +#endif diff --git a/drivers/cli/sdl-video.c b/drivers/cli/sdl-video.c new file mode 100644 index 0000000..5ca240c --- /dev/null +++ b/drivers/cli/sdl-video.c @@ -0,0 +1,228 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#include "sdl.h" +#include "../common/vidblit.h" + +#define _sline srendline +#define _eline erendline + +SDL_Surface *screen; + +static int tlines; +static int inited=0; + +static int exs,eys,eefx; +#define NWIDTH (256-((eoptions&EO_CLIPSIDES)?16:0)) +#define NOFFSET (eoptions&EO_CLIPSIDES?8:0) + +static void CleanSurface(void) +{ + uint32 x; + + x=screen->pitch*screen->h; + + if(SDL_MUSTLOCK(screen)) + SDL_LockSurface(screen); + + memset((uint8*)screen->pixels, 0x80, x); + + if(SDL_MUSTLOCK(screen)) + SDL_UnlockSurface(screen); + + SDL_UpdateRect(screen, 0, 0, 0, 0); +} + +static int paletterefresh; + +void KillVideo(void) +{ + if(inited&1) + { + SDL_QuitSubSystem(SDL_INIT_VIDEO); + } + inited=0; +} + +int InitVideo(void) +{ + const SDL_VideoInfo *vinf; + int flags=0; + + #ifdef BROKEN + if(_fullscreen && _fshack) + setenv("SDL_VIDEODRIVER",_fshack,1); + else + { + if(!_fshacksave) + unsetenv("SDL_VIDEODRIVER"); + else + setenv("SDL_VIDEODRIVER",_fshacksave,1); + } + #endif + if(SDL_InitSubSystem(SDL_INIT_VIDEO)==-1) + { + puts(SDL_GetError()); + return(0); + } + inited|=1; + + SDL_ShowCursor(0); + tlines=_eline-_sline+1; + + vinf=SDL_GetVideoInfo(); + + if(vinf->hw_available) + flags|=SDL_HWSURFACE; + + if(_fullscreen) + flags|=SDL_FULLSCREEN; + flags|=SDL_HWPALETTE; + + if(_fullscreen) + { + exs=_xscalefs; + eys=_yscalefs; + eefx=_efxfs; + if(_xrespixels; + + if(_fullscreen) + { + xo=(((screen->w-NWIDTH*exs))>>1); + dest+=xo; + if(screen->h>(tlines*eys)) + { + yo=((screen->h-tlines*eys)>>1); + dest+=yo*screen->pitch; + } + } + + Blit8To8(XBuf+NOFFSET,dest, NWIDTH, tlines, screen->pitch,exs,eys,eefx); + + if(SDL_MUSTLOCK(screen)) + SDL_UnlockSurface(screen); + + SDL_UpdateRect(screen, xo, yo, NWIDTH*exs, tlines*eys); +} + +uint32 PtoV(uint16 x, uint16 y) +{ + if(_fullscreen) + { + + } + else + { + if(eoptions&EO_CLIPSIDES) + x+=8; + y+=srendline; + } + return(x|(y<<16)); +} diff --git a/drivers/cli/sdl-video.h b/drivers/cli/sdl-video.h new file mode 100644 index 0000000..036c933 --- /dev/null +++ b/drivers/cli/sdl-video.h @@ -0,0 +1 @@ +uint32 PtoV(uint16 x, uint16 y); diff --git a/drivers/cli/sdl.c b/drivers/cli/sdl.c new file mode 100644 index 0000000..dfa5d40 --- /dev/null +++ b/drivers/cli/sdl.c @@ -0,0 +1,215 @@ +#include +#include +#include + +#include "sdl.h" +#include "sdl-video.h" +#ifdef NETWORK +#include "unix-netplay.h" +#endif + +DSETTINGS Settings; +CFGSTRUCT DriverConfig[]={ + AC(_xscale), + AC(_yscale), + AC(_xscalefs), + AC(_yscalefs), + AC(_efx), + AC(_efxfs), + AC(_sound), + #ifdef DSPSOUND + AC(_f8bit), + #else + AC(_ebufsize), + AC(_lbufsize), + #endif + AC(_fullscreen), + AC(_xres), + AC(_yres), + ACA(joyBMap), + ACA(joyAMap), + ACA(joy), + //ACS(_fshack), + ENDCFGSTRUCT +}; + +//-fshack x Set the environment variable SDL_VIDEODRIVER to \"x\" when +// entering full screen mode and x is not \"0\". + +char *DriverUsage= +"-xres x Set horizontal resolution to x for full screen mode.\n\ +-yres x Set vertical resolution to x for full screen mode.\n\ +-xscale(fs) x Multiply width by x.\n\ +-yscale(fs) x Multiply height by x.\n\ +-efx(fs) x Enable scanlines effect if x is non zero. yscale must be >=2\n\ + and preferably a multiple of 2.\n\ +-fs x Select full screen mode if x is non zero.\n\ +-joyx y Use joystick y as virtual joystick x.\n\ +-sound x Sound.\n\ + 0 = Disabled.\n\ + Otherwise, x = playback rate.\n\ +" +#ifdef DSPSOUND +"-f8bit x Force 8-bit sound.\n\ + 0 = Disabled.\n\ + 1 = Enabled.\n\ +" +#else +"-lbufsize x Internal FCE Ultra sound buffer size. Size = 2^x samples.\n\ +-ebufsize x External SDL sound buffer size. Size = 2^x samples.\n\ +" +#endif +"-connect s Connect to server 's' for TCP/IP network play.\n\ +-server Be a host/server for TCP/IP network play.\n\ +-netport x Use TCP/IP port x for network play."; + +static int docheckie[2]={0,0}; +ARGPSTRUCT DriverArgs[]={ + {"-joy1",0,&joy[0],0},{"-joy2",0,&joy[1],0}, + {"-joy3",0,&joy[2],0},{"-joy4",0,&joy[3],0}, + {"-xscale",0,&_xscale,0}, + {"-yscale",0,&_yscale,0}, + {"-efx",0,&_efx,0}, + {"-xscalefs",0,&_xscalefs,0}, + {"-yscalefs",0,&_yscalefs,0}, + {"-efxfs",0,&_efxfs,0}, + {"-xres",0,&_xres,0}, + {"-yres",0,&_yres,0}, + {"-fs",0,&_fullscreen,0}, + //{"-fshack",0,&_fshack,0x4001}, + {"-sound",0,&_sound,0}, + #ifdef DSPSOUND + {"-f8bit",0,&_f8bit,0}, + #else + {"-lbufsize",0,&_lbufsize,0}, + {"-ebufsize",0,&_ebufsize,0}, + #endif + #ifdef NETWORK + {"-connect",&docheckie[0],&netplayhost,0x4001}, + {"-server",&docheckie[1],0,0}, + {"-netport",0,&Port,0}, + #endif + {0,0,0,0} +}; + +static void SetDefaults(void) +{ + _xres=320; + _yres=240; + _fullscreen=0; + _sound=48000; + #ifdef DSPSOUND + _f8bit=0; + #else + _lbufsize=10; + _ebufsize=8; + #endif + _xscale=_yscale=_xscalefs=_yscalefs=1; + _efx=_efxfs=0; + //_fshack=_fshacksave=0; + memset(joy,0,sizeof(joy)); +} + +void DoDriverArgs(void) +{ + int x; + + #ifdef BROKEN + if(_fshack) + { + if(_fshack[0]=='0') + if(_fshack[1]==0) + { + free(_fshack); + _fshack=0; + } + } + #endif + + #ifdef NETWORK + if(docheckie[0]) + netplay=2; + else if(docheckie[1]) + netplay=1; + + if(netplay) + FCEUI_SetNetworkPlay(netplay); + #endif + + for(x=0;x<4;x++) + if(!joy[x]) + { + memset(joyBMap[x],0,sizeof(joyBMap[0])); + memset(joyAMap[x],0,sizeof(joyAMap[0])); + } +} +int InitMouse(void) +{ + return(0); +} +void KillMouse(void){} +void GetMouseData(uint32 *d) +{ + int x,y; + uint32 t; + + t=SDL_GetMouseState(&x,&y); + d[2]=0; + if(t&SDL_BUTTON(1)) + d[2]|=1; + if(t&SDL_BUTTON(3)) + d[2]|=2; + t=PtoV(x,y); + d[0]=t&0xFFFF; + d[1]=(t>>16)&0xFFFF; +} + +int InitKeyboard(void) +{ + return(1); +} + +int UpdateKeyboard(void) +{ + return(1); +} + +void KillKeyboard(void) +{ + +} + +char *GetKeyboard(void) +{ + SDL_PumpEvents(); + return(SDL_GetKeyState(0)); +} +#include "unix-basedir.h" + +int main(int argc, char *argv[]) +{ + puts("\nStarting FCE Ultra "VERSION_STRING"...\n"); + if(SDL_Init(0)) + { + printf("Could not initialize SDL: %s.\n", SDL_GetError()); + return(-1); + } + SetDefaults(); + + #ifdef BROKEN + if(getenv("SDL_VIDEODRIVER")) + { + if((_fshacksave=malloc(strlen(getenv("SDL_VIDEODRIVER"))+1))) + strcpy(_fshacksave,getenv("SDL_VIDEODRIVER")); + } + else + _fshacksave=0; + #endif + + { + int ret=CLImain(argc,argv); + SDL_Quit(); + return(ret); + } +} + diff --git a/drivers/cli/sdl.h b/drivers/cli/sdl.h new file mode 100644 index 0000000..a235951 --- /dev/null +++ b/drivers/cli/sdl.h @@ -0,0 +1,47 @@ +#include +#include "../../driver.h" +#include "../common/args.h" +#include "../common/config.h" +#include "main.h" + +typedef struct { + int xres; + int yres; + int xscale,yscale; + int xscalefs,yscalefs; + int efx,efxfs; + int fullscreen; + int sound; + #ifdef DSPSOUND + int f8bit; + #else + int lbufsize,ebufsize; + #endif + int joy[4]; + int joyAMap[4][2]; + int joyBMap[4][4]; + char *fshack; + char *fshacksave; +} DSETTINGS; + +extern DSETTINGS Settings; + +#define _xres Settings.xres +#define _yres Settings.yres +#define _fullscreen Settings.fullscreen +#define _sound Settings.sound +#define _f8bit Settings.f8bit +#define _xscale Settings.xscale +#define _yscale Settings.yscale +#define _xscalefs Settings.xscalefs +#define _yscalefs Settings.yscalefs +#define _efx Settings.efx +#define _efxfs Settings.efxfs +#define _ebufsize Settings.ebufsize +#define _lbufsize Settings.lbufsize +#define _fshack Settings.fshack +#define _fshacksave Settings.fshacksave + +#define joyAMap Settings.joyAMap +#define joyBMap Settings.joyBMap +#define joy Settings.joy diff --git a/drivers/cli/svga-video.c b/drivers/cli/svga-video.c new file mode 100644 index 0000000..67f4ecb --- /dev/null +++ b/drivers/cli/svga-video.c @@ -0,0 +1,497 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 1998 \Firebug\ + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include + +#define inportb inb +#define outportb(port, value) outb(value, port) +#define outportw(port, value) outw(value, port) + +#include "main.h" +#include "svgalib.h" +#include "svga-video.h" + + +int vmode=1; + +#ifdef FRAMESKIP +int FCEUDfskip=0; +#endif + +static int vidready=0; +static int conlock=0; + +void LockConsole(void) +{ + if(!conlock) + { + vga_lockvc(); + conlock=1; + FCEUI_DispMessage("Console locked."); + } +} + +void UnlockConsole(void) +{ + if(conlock) + { + vga_unlockvc(); + conlock=0; + FCEUI_DispMessage("Console unlocked."); + } +} + +void SetBorder(void) +{ + if(!conlock) + vga_lockvc(); + inportb(0x3da); + outportb(0x3c0,(0x11|0x20)); + outportb(0x3c0,0x80); + if(!conlock) + vga_unlockvc(); +} + +#include "vgatweak.c" + +void TweakVGA(int VGAMode) +{ + int I; + + if(!conlock) + vga_lockvc(); + + outportb(0x3C8,0x00); + for(I=0;I<768;I++) outportb(0x3C9,0x00); + + outportb(0x3D4,0x11); + I=inportb(0x3D5)&0x7F; + outportb(0x3D4,0x11); + outportb(0x3D5,I); + + switch(VGAMode) + { + case 1: for(I=0;I<25;I++) VGAPortSet(v256x240[I]);break; + case 2: for(I=0;I<25;I++) VGAPortSet(v256x256[I]);break; + case 3: for(I=0;I<25;I++) VGAPortSet(v256x256S[I]);break; + case 6: for(I=0;I<25;I++) VGAPortSet(v256x224S[I]);break; + case 8: for(I=0;I<25;I++) VGAPortSet(v256x224_103[I]);break; + default: break; + } + + outportb(0x3da,0); + if(!conlock) + vga_unlockvc(); +} + + +static uint8 palettedbr[256],palettedbg[256],palettedbb[256]; + +static void FlushPalette(void) +{ + int x; + for(x=0;x<256;x++) + { + int z=x; + if(vmode==4 || vmode==5 || vmode==7) z^=0x80; + vga_setpalette(z,palettedbr[x]>>2,palettedbg[x]>>2,palettedbb[x]>>2); + } +} + +void FCEUD_SetPalette(uint8 index, uint8 r, uint8 g, uint8 b) +{ + palettedbr[index]=r; + palettedbg[index]=g; + palettedbb[index]=b; + + if(vidready) + { + if(vmode==4 || vmode==5 || vmode==7) index^=0x80; + vga_setpalette(index,r>>2,g>>2,b>>2); + } +} + + +void FCEUD_GetPalette(uint8 i, uint8 *r, uint8 *g, uint8 *b) +{ + *r=palettedbr[i]; + *g=palettedbg[i]; + *b=palettedbb[i]; +} + +static void vcfix(void) +{ + int z; + + if(!conlock) + vga_lockvc(); + z=inportb(0x3cc); + if(!conlock) + vga_unlockvc(); + if(z!=0xe3 && z!=0xe7) // Common value in all tweaked video modes(and not in 320x200 mode). + { + TweakVGA(vmode); + SetBorder(); + FlushPalette(); + } +} + +static uint8 *ScreenLoc; + +int InitVideo(void) +{ + #ifdef DUMMY + return(1); + #endif + vidready=0; + + if(vmode<=3 || vmode==6 || vmode==8) + { + if(vga_getcurrentchipset()==FBDEV) + { + puts("Tweaked VGA video modes will not work. Using a 320x240 video mode instead..."); + vmode=7; + } + } + + switch(vmode) + { + default: + case 1: + case 2: + case 3: + case 6: + case 8: + vga_setmode(G320x200x256); + vidready|=1; + ScreenLoc=vga_getgraphmem(); + TweakVGA(vmode); + SetBorder(); + memset(ScreenLoc,128,256*256); + break; + case 4: + case 5: + if(!(vga_getmodeinfo(G640x480x256)->flags & CAPABLE_LINEAR)) + { + puts("Video: No linear addressing mode available!"); + return 0; + } + if(vga_setmode(G640x480x256)==-1) + { + puts("Video: Could not set 640x480x8bpp video mode!"); + return 0; + } + vidready|=1; + + vga_setpage(0); + if(vga_setlinearaddressing()!=-1) + ScreenLoc=vga_getgraphmem(); + else + { + puts("Video: Could not set linear addressing!"); + return 0; + } + memset(ScreenLoc,0,640*480); + break; + case 7: + if(!(vga_getmodeinfo(G320x240x256V)->flags & CAPABLE_LINEAR)) + { + puts("Video: No linear addressing mode available!"); + return 0; + } + if(vga_setmode(G320x240x256V)==-1) + { + puts("Video: Could not set 320x240x8bpp video mode!"); + return 0; + } + vidready|=1; + + vga_setpage(0); + if(vga_setlinearaddressing()!=-1) + ScreenLoc=vga_getgraphmem(); + else + { + puts("Video: Could not set linear addressing!"); + return 0; + } + memset(ScreenLoc,0,320*240); + break; + } + vidready|=2; + FlushPalette(); // Needed for cheat console code(and it isn't a bad thing to do anyway...). + return 1; +} + +void KillVideo(void) +{ + if(vidready) + { + vga_setmode(TEXT); + vidready=0; + } +} + + +void BlitScreen(uint8 *XBuf) +{ + static int conto=0; + uint8 *dest; + int tlines; + #ifdef DUMMY + return; + #endif + #ifdef FRAMESKIP + FCEUI_FrameSkip(FCEUDfskip); + #endif + + if(doptions&DO_VSYNC && !NoWaiting) + { + vga_waitretrace(); + } + + tlines=erendline-srendline+1; + + dest=ScreenLoc; + + if(vmode!=4 && vmode!=5 && vmode!=7) + { + conto=(conto+1)&0x3F; + if(!conto) vcfix(); + } + switch(vmode) + { + case 1:dest+=(((240-tlines)>>1)<<8);break; + case 2: + case 3:dest+=(((256-tlines)>>1)<<8);break; + case 4: + case 5:dest+=(((240-tlines)>>1)*640+((640-512)>>1));break; + case 8: + case 6:if(tlines>224) tlines=224;dest+=(((224-tlines)>>1)<<8);break; + case 7:dest+=(((240-tlines)>>1)*320)+32;break; + } + + XBuf+=(srendline<<8)+(srendline<<4); + + if(eoptions&EO_CLIPSIDES) + { + if(vmode==5) + { + asm volatile( + "xorl %%edx, %%edx\n\t" + "ckoop1:\n\t" + "movb $120,%%al \n\t" + "ckoop2:\n\t" + "movb 1(%%esi),%%dl\n\t" + "shl $16,%%edx\n\t" + "movb (%%esi),%%dl\n\t" + "xorl $0x00800080,%%edx\n\t" + "movl %%edx,(%%edi)\n\t" + "addl $2,%%esi\n\t" + "addl $4,%%edi\n\t" + "decb %%al\n\t" + "jne ckoop2\n\t" + + "addl $32,%%esi\n\t" + "addl $800,%%edi\n\t" + "decb %%bl\n\t" + "jne ckoop1\n\t" + : + : "S" (XBuf+8), "D" (dest+8), "b" (tlines) + : "%al", "%edx", "%cc" ); + } + else if(vmode==4) + { + asm volatile( + "cyoop1:\n\t" + "movb $120,%%al \n\t" + "cyoop2:\n\t" + "movb 1(%%esi),%%dh\n\t" + "movb %%dh,%%dl\n\t" + "shl $16,%%edx\n\t" + "movb (%%esi),%%dl\n\t" + "movb %%dl,%%dh\n\t" // Ugh + "xorl $0x80808080,%%edx\n\t" + "movl %%edx,(%%edi)\n\t" + "addl $2,%%esi\n\t" + "addl $4,%%edi\n\t" + "decb %%al\n\t" + "jne cyoop2\n\t" + + "addl $32,%%esi\n\t" + "addl $800,%%edi\n\t" + "decb %%bl\n\t" + "jne cyoop1\n\t" + : + : "S" (XBuf+8), "D" (dest+8), "b" (tlines) + : "%al", "%edx", "%cc" ); + } + else if(vmode==7) + { + asm volatile( + "cgoop81:\n\t" + "movl $30,%%eax\n\t" + "cgoop82:\n\t" + "movl (%%esi),%%edx\n\t" + "movl 4(%%esi),%%ecx\n\t" + "xorl $0x80808080,%%edx\n\t" + "xorl $0x80808080,%%ecx\n\t" + "movl %%edx,(%%edi)\n\t" + "movl %%ecx,4(%%edi)\n\t" + "addl $8,%%esi\n\t" + "addl $8,%%edi\n\t" + "decl %%eax\n\t" + "jne cgoop82\n\t" + "addl $80,%%edi\n\t" + "addl $32,%%esi\n\t" + "decb %%bl\n\t" + "jne cgoop81\n\t" + : + : "S" (XBuf+8), "D" (dest+8), "b" (tlines) + : "%eax","%cc","%edx","%ecx" ); + } + else + { + asm volatile( + "cgoop1:\n\t" + "movl $30,%%eax\n\t" + "cgoop2:\n\t" + "movl (%%esi),%%edx\n\t" + "movl 4(%%esi),%%ecx\n\t" + "movl %%edx,(%%edi)\n\t" + "movl %%ecx,4(%%edi)\n\t" + "addl $8,%%esi\n\t" + "addl $8,%%edi\n\t" + "decl %%eax\n\t" + "jne cgoop2\n\t" + "addl $32,%%esi\n\t" + "addl $16,%%edi\n\t" + "decb %%bl\n\t" + "jne cgoop1\n\t" + : + : "S" (XBuf+8), "D" (dest+8), "b" (tlines) + : "%eax","%cc","%edx","%ecx" ); + } + } + else + { + if(vmode==5) + { + asm volatile( + "xorl %%edx, %%edx\n\t" + "koop1:\n\t" + "movb $128,%%al \n\t" + "koop2:\n\t" + "movb 1(%%esi),%%dl\n\t" + "shl $16,%%edx\n\t" + "movb (%%esi),%%dl\n\t" + "xorl $0x00800080,%%edx\n\t" + "movl %%edx,(%%edi)\n\t" + "addl $2,%%esi\n\t" + "addl $4,%%edi\n\t" + "decb %%al\n\t" + "jne koop2\n\t" + + "addl $16,%%esi\n\t" + "addl $768,%%edi\n\t" + "decb %%bl\n\t" + "jne koop1\n\t" + : + : "S" (XBuf), "D" (dest), "b" (tlines) + : "%al", "%edx", "%cc" ); + } + else if(vmode==4) + { + asm volatile( + "yoop1:\n\t" + "movb $128,%%al \n\t" + "yoop2:\n\t" + "movb 1(%%esi),%%dh\n\t" + "movb %%dh,%%dl\n\t" + "shl $16,%%edx\n\t" + "movb (%%esi),%%dl\n\t" + "movb %%dl,%%dh\n\t" // Ugh + "xorl $0x80808080,%%edx\n\t" + "movl %%edx,(%%edi)\n\t" + "addl $2,%%esi\n\t" + "addl $4,%%edi\n\t" + "decb %%al\n\t" + "jne yoop2\n\t" + + "addl $16,%%esi\n\t" + "addl $768,%%edi\n\t" + "decb %%bl\n\t" + "jne yoop1\n\t" + : + : "S" (XBuf), "D" (dest), "b" (tlines) + : "%al", "%edx", "%cc" ); + } + else if(vmode==7) + { + asm volatile( + "goop81:\n\t" + "movl $32,%%eax\n\t" + "goop82:\n\t" + "movl (%%esi),%%edx\n\t" + "movl 4(%%esi),%%ecx\n\t" + "xorl $0x80808080,%%edx\n\t" + "xorl $0x80808080,%%ecx\n\t" + "movl %%edx,(%%edi)\n\t" + "movl %%ecx,4(%%edi)\n\t" + "addl $8,%%esi\n\t" + "addl $8,%%edi\n\t" + "decl %%eax\n\t" + "jne goop82\n\t" + "addl $64,%%edi\n\t" + "addl $16,%%esi\n\t" + "decb %%bl\n\t" + "jne goop81\n\t" + : + : "S" (XBuf), "D" (dest), "b" (tlines) + : "%eax","%cc","%edx","%ecx" ); + } + else + { + asm volatile( + "goop1:\n\t" + "movl $32,%%eax\n\t" + "goop2:\n\t" + "movl (%%esi),%%edx\n\t" + "movl 4(%%esi),%%ecx\n\t" + "movl %%edx,(%%edi)\n\t" + "movl %%ecx,4(%%edi)\n\t" + "addl $8,%%esi\n\t" + "addl $8,%%edi\n\t" + "decl %%eax\n\t" + "jne goop2\n\t" + "addl $16,%%esi\n\t" + "decb %%bl\n\t" + "jne goop1\n\t" + : + : "S" (XBuf), "D" (dest), "b" (tlines) + : "%eax","%cc","%edx","%ecx" ); + } + } +} + + diff --git a/drivers/cli/svga-video.h b/drivers/cli/svga-video.h new file mode 100644 index 0000000..e0a991b --- /dev/null +++ b/drivers/cli/svga-video.h @@ -0,0 +1,32 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +extern int vmode; + +#ifdef FRAMESKIP +extern int FCEUDfskip; +#endif + +void LockConsole(void); +void UnlockConsole(void); +int InitVideo(void); +void KillVideo(void); +void FCEUD_BlitScreen(uint8 *XBuf); + diff --git a/drivers/cli/svgalib.c b/drivers/cli/svgalib.c new file mode 100644 index 0000000..7d293e9 --- /dev/null +++ b/drivers/cli/svgalib.c @@ -0,0 +1,178 @@ +#include +#include +#include +#include +#include +#include + +#include "../../driver.h" +#include "../common/args.h" +#include "../common/config.h" +#include "../common/unixdsp.h" + +#include "svgalib.h" +#include "svga-video.h" +#include "lnx-joystick.h" +#include "unix-netplay.h" + +static int soundo=48000; +static int f8bit=0; +static int sfragsize=7,snfrags=8; + +int doptions=0; + +CFGSTRUCT DriverConfig[]={ + NAC("sound",soundo), + AC(doptions), + AC(f8bit), + AC(vmode), + NACA("joybmap",joyBMap), + ACA(joy), + AC(snfrags), + AC(sfragsize), + ENDCFGSTRUCT +}; + + +char *DriverUsage= +"-vmode x Select video mode(all are 8 bpp).\n\ + 1 = 256x240 5 = 640x480(\"1 per 4\")\n\ + 2 = 256x256 6 = 256x224(with scanlines)\n\ + 3 = 256x256(with scanlines) 7 = 320x240\n\ + 4 = 640x480(with scanlines) 8 = 256x224\n\ +-vsync x Wait for the screen's vertical retrace before updating the\n\ + screen. Refer to the documentation for caveats.\n\ + 0 = Disabled.\n\ + 1 = Enabled.\n\ +-joyx y Joystick mapped to virtual joystick x(1-4).\n\ + 0 = Disabled, reset configuration.\n\ + Otherwise, y(1-inf) = joystick number.\n\ +-sound x Sound.\n\ + 0 = Disabled.\n\ + Otherwise, x = playback rate.\n\ +-sfragsize x Set sound fragment size to 2^x samples.\n\ +-snfrags x Set number of sound fragments to x.\n\ +-f8bit x Force 8-bit sound.\n\ + 0 = Disabled.\n\ + 1 = Enabled.\n\ +-connect s Connect to server 's' for TCP/IP network play.\n\ +-server Be a host/server for TCP/IP network play.\n\ +-netport x Use TCP/IP port x for network play."; + + +static int docheckie[2]={0,0}; +ARGPSTRUCT DriverArgs[]={ + {"-joy1",0,&joy[0],0},{"-joy2",0,&joy[1],0}, + {"-joy3",0,&joy[2],0},{"-joy4",0,&joy[3],0}, + {"-snfrags",0,&snfrags,0},{"-sfragsize",0,&sfragsize,0}, + {"-vmode",0,&vmode,0}, + {"-vsync",0,&doptions,0x8000|DO_VSYNC}, + {"-sound",0,&soundo,0}, + {"-f8bit",0,&f8bit,0}, + {"-connect",&docheckie[0],&netplayhost,0x4001}, + {"-server",&docheckie[1],0,0}, + {"-netport",0,&Port,0}, + {0,0,0,0} +}; + +void DoDriverArgs(void) +{ + int x; + + if(docheckie[0]) + netplay=2; + else if(docheckie[1]) + netplay=1; + + if(netplay) + FCEUI_SetNetworkPlay(netplay); + + for(x=0;x<4;x++) + if(!joy[x]) memset(joyBMap[x],0,4*sizeof(int)); +} + +int InitSound(void) +{ + if(soundo) + { + int rate; + if(soundo==1) + soundo=48000; + rate=soundo; + if(InitUNIXDSPSound(&rate,f8bit?0:1,sfragsize,snfrags)) + { + FCEUI_Sound(rate); + return(1); + } + } + return(0); +} + +void WriteSound(int32 *Buffer, int Count, int NoWaiting) +{ + WriteUNIXDSPSound(Buffer,Count,NoWaiting); +} + +void KillSound(void) +{ + KillUNIXDSPSound(); +} + +int InitMouse(void) +{ + vga_setmousesupport(1); + mouse_setxrange(0,260); + mouse_setyrange(0,260); + mouse_setscale(1); + return(1); +} + +void KillMouse(void) +{ + mouse_close(); +} + +void GetMouseData(uint32 *MouseData) +{ + int z; + mouse_update(); + MouseData[0]=mouse_getx(); + MouseData[1]=mouse_gety(); + z=mouse_getbutton(); + MouseData[2]=((z&MOUSE_LEFTBUTTON)?1:0)|((z&MOUSE_RIGHTBUTTON)?2:0); +} + +#include "unix-basedir.h" + +int InitKeyboard(void) +{ + if(keyboard_init()==-1) + { + puts("Error initializing keyboard."); + return 0; + } + keyboard_translatekeys(TRANSLATE_CURSORKEYS | TRANSLATE_DIAGONAL); + return 1; +} + +int UpdateKeyboard(void) +{ + return(keyboard_update()); +} + +char *GetKeyboard(void) +{ + return(keyboard_getstate()); +} + +void KillKeyboard(void) +{ + keyboard_close(); +} + +int main(int argc, char *argv[]) +{ + puts("\nStarting FCE Ultra "VERSION_STRING"...\n"); + vga_init(); + return(CLImain(argc,argv)); +} diff --git a/drivers/cli/svgalib.h b/drivers/cli/svgalib.h new file mode 100644 index 0000000..8f18b74 --- /dev/null +++ b/drivers/cli/svgalib.h @@ -0,0 +1,2 @@ +#define DO_VSYNC 1 +extern int doptions; diff --git a/drivers/cli/throttle.c b/drivers/cli/throttle.c new file mode 100644 index 0000000..604cef7 --- /dev/null +++ b/drivers/cli/throttle.c @@ -0,0 +1,41 @@ +#include +#include "main.h" +#include "throttle.h" + +static uint64 tfreq; +static uint64 desiredfps; + +void RefreshThrottleFPS(void) +{ + desiredfps=FCEUI_GetDesiredFPS()>>8; + tfreq=1000000; + tfreq<<=16; /* Adjustment for fps returned from FCEUI_GetDesiredFPS(). */ +} + +static uint64 GetCurTime(void) +{ + uint64 ret; + struct timeval tv; + + gettimeofday(&tv,0); + ret=(uint64)tv.tv_sec*1000000; + ret+=tv.tv_usec; + return(ret); +} + +void SpeedThrottle(void) +{ + static uint64 ttime,ltime; + + waiter: + + ttime=GetCurTime(); + + if( (ttime-ltime) < (tfreq/desiredfps) ) + goto waiter; + if( (ttime-ltime) >= (tfreq*4/desiredfps)) + ltime=ttime; + else + ltime+=tfreq/desiredfps; +} + diff --git a/drivers/cli/throttle.h b/drivers/cli/throttle.h new file mode 100644 index 0000000..0b0ad9f --- /dev/null +++ b/drivers/cli/throttle.h @@ -0,0 +1,2 @@ +void RefreshThrottleFPS(void); +void SpeedThrottle(void); diff --git a/drivers/cli/unix-basedir.h b/drivers/cli/unix-basedir.h new file mode 100644 index 0000000..4f6808c --- /dev/null +++ b/drivers/cli/unix-basedir.h @@ -0,0 +1,15 @@ +#include +void GetBaseDirectory(char *BaseDirectory) +{ + char *ol; + + ol=getenv("HOME"); + BaseDirectory[0]=0; + if(ol) + { + strncpy(BaseDirectory,ol,2047); + BaseDirectory[2047]=0; + strcat(BaseDirectory,"/.fceultra"); + } +} + diff --git a/drivers/cli/unix-netplay.c b/drivers/cli/unix-netplay.c new file mode 100644 index 0000000..d5d1cc4 --- /dev/null +++ b/drivers/cli/unix-netplay.c @@ -0,0 +1,158 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef socklen_t +#define socklen_t int +#endif + +static int Socket=-1; +#include "main.h" +#include "unix-netplay.h" + +char *netplayhost=0; +int Port=0xFCE; +int netplay=0; + +int FCEUD_NetworkConnect(void) +{ + struct sockaddr_in sockn; + int TSocket; + + memset(&sockn,0,sizeof(sockn)); + sockn.sin_family=AF_INET; + sockn.sin_port=htons(Port); + + if((TSocket=socket(AF_INET, SOCK_STREAM, 0))<0) + { + puts("Error creating socket."); + return(0); + } + + if(netplay==1) /* Be a server. */ + { + sockn.sin_addr.s_addr=INADDR_ANY; + if(bind(TSocket, (struct sockaddr *)&sockn, sizeof(sockn))<0) + { + close(TSocket); + puts("Error binding to socket."); + return(0); + } + if(listen(TSocket, 1)<0) + { + puts("Error listening on socket."); + close(TSocket); + return(0); + } + { + socklen_t len=sizeof(sockn); + + printf("Accepting connection on port %d...\n",Port); + if((Socket=accept(TSocket,(struct sockaddr *)&sockn,&len))<0 ) + { + puts("Error accepting a connection."); + close(TSocket); + return(0); + } + close(TSocket); + } + + } + else /* Connect as a client if not a server. */ + { + struct hostent *Host; + + if((sockn.sin_addr.s_addr=inet_addr(netplayhost))==INADDR_NONE) + { + if(!(Host=gethostbyname(netplayhost))) + { + puts("Error getting network host entry."); + return(0); + } + memcpy(&sockn.sin_addr,Host->h_addr,Host->h_length); + } + printf("Attempting to connect to %s...\n",netplayhost); + if( connect(TSocket, (struct sockaddr *)&sockn, sizeof(sockn)) <0 ) + { + puts("Error connecting to remote host."); + close(TSocket); + return(0); + } + Socket=TSocket; + } + return(1); +} + +/* 0 on failure, 1 on success, -1 if it would block and blocking is not + specified. +*/ + +int FCEUD_NetworkRecvData(uint8 *data, uint32 len, int block) +{ + if(block) + { + int t; + uint8 temp[32]; + t=recv(Socket,temp,32,MSG_PEEK|MSG_DONTWAIT); + if(t==-1) + { + if(errno!=EAGAIN) return(0); + } + else if(t==32) + NoWaiting|=2; + else + NoWaiting&=~2; + return(recv(Socket,data,len,0)==len); + } + else + { + int t=recv(Socket,data,len,MSG_DONTWAIT); + if(t==-1) + { + if(errno==EAGAIN) // Would block + return(-1); + return(0); + } + return(1); + } +} + +/* 0 on failure, 1 on success. This function should always block. */ + +int FCEUD_NetworkSendData(uint8 *Value, uint32 len) +{ + return(send(Socket,Value,len,0)==len); +} + +void FCEUD_NetworkClose(void) +{ + if(Socket>0) + close(Socket); + Socket=-1; +} + diff --git a/drivers/cli/unix-netplay.h b/drivers/cli/unix-netplay.h new file mode 100644 index 0000000..48769f6 --- /dev/null +++ b/drivers/cli/unix-netplay.h @@ -0,0 +1,5 @@ +extern char *netplayhost; +extern int Port; +extern int FDnetplay; +#define netplay FDnetplay + diff --git a/drivers/cli/usage.h b/drivers/cli/usage.h new file mode 100644 index 0000000..425a907 --- /dev/null +++ b/drivers/cli/usage.h @@ -0,0 +1,56 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +void ShowUsage(char *prog) +{ +printf("\nUsage is as follows:\n%s filename\n\n",prog); +puts("Options:"); +puts(DriverUsage); +puts("-cpalette x Load a custom global palette from file x.\n\ +-ntsccol x Emulate an NTSC's TV's colors.\n\ + 0 = Disabled.\n\ + 1 = Enabled.\n\ +-pal Emulate a PAL NES.\n\ +-soundvol x Sound volume. x is an integral percentage value.\n\ +-inputx str Select device mapped to virtual input port x(1-2).\n\ + str may be: none, gamepad, zapper, powerpada, powerpadb,\n\ + arkanoid\n\ +-fcexp str Select Famicom expansion port device.\n\ + str may be: none, shadow, arkanoid, 4player, fkb\n\ +-nofs x Disables Four-Score emulation if x is 1.\n\ +-gg Enable Game Genie emulation.\n\ +-no8lim x Disables the 8 sprites per scanline limitation.\n\ + 0 = Limitation enabled.\n\ + 1 = Limitation disabled.\n\ +-subase x Save extra game data files under the base directory if enabled.\n\ + 0 = Disabled.\n\ + 1 = Enabled.\n\ +-snapname x Selects what type of file name snapshots will have.\n\ + 0 = Numeric(0.png)\n\ + 1 = File base and numeric(mario-0.png)\n\ +-nothrottle x Disable artificial speed throttling if x is non-zero.\n\ +-clipsides x Clip leftmost and rightmost 8 columns of pixels of video output.\n\ + 0 = No clipping.\n\ + 1 = Clipping.\n\ +-slstart x Set the first drawn emulated scanline. Valid values for x are\n\ + 0 through 239.\n\ +-slend x Set the last drawn emulated scanline. Valid values for x are\n\ + 0 through 239."); +} diff --git a/drivers/cli/vgatweak.c b/drivers/cli/vgatweak.c new file mode 100644 index 0000000..a04cf94 --- /dev/null +++ b/drivers/cli/vgatweak.c @@ -0,0 +1,168 @@ +/* This file is "#include"d from dos-video.c and svga-video.c */ + +typedef struct { + uint8 p; + uint8 i; + uint8 v; +} vgareg; + +vgareg v256x224_103[25] = +{ + { 0xc2, 0x0, 0xe7}, + { 0xd4, 0x0, 0x45}, + { 0xd4, 0x1, 0x3f}, + { 0xd4, 0x2, 0x40}, + { 0xd4, 0x3, 0x86}, + { 0xd4, 0x4, 0x3f}, + { 0xd4, 0x5, 0x10}, + { 0xd4, 0x6, 0xcd}, + { 0xd4, 0x7, 0x1f}, + { 0xd4, 0x8, 0x0}, + { 0xd4, 0x9, 0x41}, + { 0xd4, 0x10, 0xc0}, + { 0xd4, 0x11, 0xac}, + { 0xd4, 0x12, 0xbf}, + { 0xd4, 0x13, 0x20}, + { 0xd4, 0x14, 0x40}, // + { 0xd4, 0x15, 0xe7}, + { 0xd4, 0x16, 0x06}, // + { 0xd4, 0x17, 0xa3}, + { 0xc4, 0x1, 0x1}, + { 0xc4, 0x4, 0xe}, // + { 0xce, 0x5, 0x40}, + { 0xce, 0x6, 0x5}, + { 0xc0, 0x10, 0x41}, + { 0xc0, 0x13, 0x0}, +}; + +vgareg v256x240[25] = +{ + { 0xc2, 0x0, 0xe3}, + { 0xd4, 0x0, 0x4f}, + { 0xd4, 0x1, 0x3f}, + { 0xd4, 0x2, 0x40}, + { 0xd4, 0x3, 0x92}, + { 0xd4, 0x4, 0x44}, + { 0xd4, 0x5, 0x10}, + { 0xd4, 0x6, 0x0a}, + { 0xd4, 0x7, 0x3e}, + { 0xd4, 0x8, 0x00}, + { 0xd4, 0x9, 0x41}, + { 0xd4, 0x10, 0xea}, + { 0xd4, 0x11, 0xac}, + { 0xd4, 0x12, 0xdf}, + { 0xd4, 0x13, 0x20}, + { 0xd4, 0x14, 0x40}, + { 0xd4, 0x15, 0xe7}, + { 0xd4, 0x16, 0x06}, + { 0xd4, 0x17, 0xa3}, + { 0xc4, 0x1, 0x1}, + { 0xc4, 0x4, 0xe}, + { 0xce, 0x5, 0x40}, + { 0xce, 0x6, 0x5}, + { 0xc0, 0x10, 0x41}, + { 0xc0, 0x13, 0x0} +}; + +vgareg v256x224S[25] = +{ + { 0xc2, 0x0, 0xe3}, + { 0xd4, 0x0, 0x5f}, + { 0xd4, 0x1, 0x3f}, + { 0xd4, 0x2, 0x40}, + { 0xd4, 0x3, 0x82}, + { 0xd4, 0x4, 0x4e}, + { 0xd4, 0x5, 0x96}, + { 0xd4, 0x6, 0x5}, + { 0xd4, 0x7, 0x1}, + { 0xd4, 0x8, 0x0}, + { 0xd4, 0x9, 0x40}, + { 0xd4, 0x10, 0xea}, + { 0xd4, 0x11, 0xac}, + { 0xd4, 0x12, 0xdf}, + { 0xd4, 0x13, 0x20}, + { 0xd4, 0x14, 0x40}, + { 0xd4, 0x15, 0xe7}, + { 0xd4, 0x16, 0x0}, + { 0xd4, 0x17, 0xe3}, + { 0xc4, 0x1, 0x1}, + { 0xc4, 0x4, 0xe}, + { 0xce, 0x5, 0x40}, + { 0xce, 0x6, 0x5}, + { 0xc0, 0x10, 0x41}, + { 0xc0, 0x13, 0x0} +}; + +vgareg v256x256[25] = +{ + { 0xc2, 0x0, 0xe7}, + { 0xd4, 0x0, 0x5f}, + { 0xd4, 0x1, 0x3f}, + { 0xd4, 0x2, 0x40}, + { 0xd4, 0x3, 0x82}, + { 0xd4, 0x4, 0x4a}, + { 0xd4, 0x5, 0x9a}, + { 0xd4, 0x6, 0x23}, + { 0xd4, 0x7, 0xb2}, + { 0xd4, 0x8, 0x0}, + { 0xd4, 0x9, 0x61}, + { 0xd4, 0x10, 0xa}, + { 0xd4, 0x11, 0xac}, + { 0xd4, 0x12, 0xff}, + { 0xd4, 0x13, 0x20}, + { 0xd4, 0x14, 0x40}, + { 0xd4, 0x15, 0x7}, + { 0xd4, 0x16, 0x1a}, + { 0xd4, 0x17, 0xa3}, + { 0xc4, 0x1, 0x1}, + { 0xc4, 0x4, 0xe}, + { 0xce, 0x5, 0x40}, + { 0xce, 0x6, 0x5}, + { 0xc0, 0x10, 0x41}, + { 0xc0, 0x13, 0x0} +}; + +vgareg v256x256S[25] = +{ + { 0xc2, 0x00, 0xe7},{ 0xd4, 0x00, 0x5F},{ 0xd4, 0x01, 0x3f}, + { 0xd4, 0x02, 0x40},{ 0xd4, 0x03, 0x82},{ 0xd4, 0x04, 0x4a}, + { 0xd4, 0x05, 0x9a},{ 0xd4, 0x06, 0x25},{ 0xd4, 0x07, 0x15}, + { 0xd4, 0x08, 0x00},{ 0xd4, 0x09, 0x60},{ 0xd4, 0x10, 0x0a}, + { 0xd4, 0x11, 0xac},{ 0xd4, 0x12, 0xff},{ 0xd4, 0x13, 0x20}, + { 0xd4, 0x14, 0x40},{ 0xd4, 0x15, 0x07},{ 0xd4, 0x16, 0x1a}, + { 0xd4, 0x17, 0xa3},{ 0xc4, 0x01, 0x01},{ 0xc4, 0x04, 0x0e}, + { 0xce, 0x05, 0x40},{ 0xce, 0x06, 0x05},{ 0xc0, 0x10, 0x41}, + { 0xc0, 0x13, 0x00} +}; + +static void VGAPortSet(vgareg R) +{ + int p,i,v; + + p=0x300|R.p; + i=R.i; + v=R.v; + + switch(p) + { + case 0x3C0: inportb(0x3DA); + outportb(0x3C0,i); + outportb(0x3C0,v); + break; + case 0x3C2: + case 0x3C3: + default: outportb(p, v); + break; + case 0x3C4: if(i==1) + { + outportw(0x3c4,0x100); + outportw(0x3c4,(v<<8)|1); + outportw(0x3c4,0x300); + break; + } + case 0x3CE: + case 0x3D4: outportw(p,i|(v<<8)); + break; + } +} + diff --git a/drivers/common/args.c b/drivers/common/args.c new file mode 100644 index 0000000..7c6c8fe --- /dev/null +++ b/drivers/common/args.c @@ -0,0 +1,91 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/****************************************************************/ +/* FCE Ultra */ +/* */ +/* This file contains code for parsing command-line */ +/* options. */ +/* */ +/****************************************************************/ + +#include +#include +#include + +#include "../../types.h" +#include "args.h" + +void ParseEA(int x, int argc, char *argv[], ARGPSTRUCT *argsps) +{ + int y=0; + + do + { + if(!argsps[y].name) + { + ParseEA(x,argc,argv,(void *)argsps[y].var); + y++; + continue; + } + if(!strcmp(argv[x],argsps[y].name)) // A match. + { + if(argsps[y].subs) + { + if((x+1)>=argc) + break; + if(argsps[y].substype&0x8000) + { + *(int *)argsps[y].subs&=~(argsps[y].substype&(~0x8000)); + *(int *)argsps[y].subs|=atoi(argv[x+1])?(argsps[y].substype&(~0x8000)):0; + } + else + switch(argsps[y].substype&(~0x4000)) + { + case 0: // Integer + *(int *)argsps[y].subs=atoi(argv[x+1]); + break; + case 1: // String + if(argsps[y].substype&0x4000) + { + if(*(char **)argsps[y].subs) + free(*(char **)argsps[y].subs); + if(!( *(char **)argsps[y].subs=malloc(strlen(argv[x+1])+1) )) + break; + } + strcpy(*(char **)argsps[y].subs,argv[x+1]); + break; + } + } + if(argsps[y].var) + *argsps[y].var=1; + } + y++; + } while(argsps[y].var || argsps[y].subs); +} + +void ParseArguments(int argc, char *argv[], ARGPSTRUCT *argsps) +{ + int x; + + for(x=0;x +#include +#include "../../driver.h" + +static void GetString(char *s) +{ + int x; + fgets(s,256,stdin); + + for(x=0;x<256;x++) + if(s[x]=='\n') + { + s[x]=0; + break; + } +} + +/* Get unsigned 16-bit integer from stdin in hex. */ +static uint32 GetH16(unsigned int def) +{ + char buf[32]; + + fgets(buf,32,stdin); + if(buf[0]=='\n') + return(def); + if(buf[0]=='$') + sscanf(buf+1,"%04x",&def); + else + sscanf(buf,"%04x",&def); + return def; +} + +/* Get unsigned 8-bit integer from stdin in decimal. */ +static uint8 Get8(unsigned int def) +{ + char buf[32]; + + fgets(buf,32,stdin); + if(buf[0]=='\n') + return(def); + sscanf(buf,"%d",&def); + return def; +} + +static int GetYN(int def) +{ + char buf[32]; + printf("(Y/N)[%s]: ",def?"Y":"N"); + fgets(buf,32,stdin); + if(buf[0]=='y' || buf[0]=='Y') + return(1); + if(buf[0]=='n' || buf[0]=='N') + return(0); + return(def); +} + +/* +** Begin list code. +** +*/ +static int listcount; +static int listids[16]; +static int listsel; +static int mordoe; + +void BeginListShow(void) +{ + listcount=0; + listsel=-1; + mordoe=0; +} + +/* Hmm =0 for in list choices, hmm=1 for end of list choices. */ +/* Return equals 0 to continue, -1 to stop, otherwise a number. */ +int ListChoice(int hmm) +{ + char buf[32]; + + if(!hmm) + { + int num=0; + + tryagain: + printf(" <'Enter' to continue, (S)top, or enter a number.> "); + fgets(buf,32,stdin); + if(buf[0]=='s' || buf[0]=='S') return(-1); + if(buf[0]=='\n') return(0); + if(!sscanf(buf,"%d",&num)) + return(0); + if(num<1) goto tryagain; + return(num); + } + else + { + int num=0; + + tryagain2: + printf(" <'Enter' to make no selection or enter a number.> "); + fgets(buf,32,stdin); + if(buf[0]=='\n') return(0); + if(!sscanf(buf,"%d",&num)) + return(0); + if(num<1) goto tryagain2; + return(num); + } +} + +int EndListShow(void) +{ + if(mordoe) + { + int r=ListChoice(1); + if(r>0 && r<=listcount) + listsel=listids[r-1]; + } + return(listsel); +} + +/* Returns 0 to stop listing, 1 to continue. */ +int AddToList(char *text, uint32 id) +{ + if(listcount==16) + { + int t=ListChoice(0); + mordoe=0; + if(t==-1) return(0); // Stop listing. + else if(t>0 && t<17) + { + listsel=listids[t-1]; + return(0); + } + listcount=0; + } + mordoe=1; + listids[listcount]=id; + printf("%2d) %s\n",listcount+1,text); + listcount++; + return(1); +} + +/* +** +** End list code. +**/ + +typedef struct MENU { + char *text; + void *action; + int type; // 0 for menu, 1 for function. +} MENU; + +static void SetOC(void) +{ + FCEUI_CheatSearchSetCurrentAsOriginal(); +} + +static void UnhideEx(void) +{ + FCEUI_CheatSearchShowExcluded(); +} + +static void ToggleCheat(int num) +{ + uint32 A; + int s; + + FCEUI_GetCheat(num,0,&A,0,&s); + s^=1; + FCEUI_SetCheat(num,0,-1,-1,s); + printf("Cheat for address $%04x %sabled.\n",(unsigned int)A,s?"en":"dis"); +} + +static void ModifyCheat(int num) +{ + char *name; + char buf[256]; + uint32 A; + uint8 V; + int s; + int t; + + FCEUI_GetCheat(num, &name, &A, &V, &s); + + printf("Name [%s]: ",name); + GetString(buf); + + /* This obviously doesn't allow for cheats with no names. Bah. Who wants + nameless cheats anyway... + */ + + if(buf[0]) + name=buf; // Change name when FCEUI_SetCheat() is called. + else + name=0; // Don't change name when FCEUI_SetCheat() is called. + + printf("Address [$%04x]: ",(unsigned int)A); + A=GetH16(A); + + printf("Value [%03d]: ",(unsigned int)V); + V=Get8(V); + + printf("Enable [%s]: ",s?"Y":"N"); + t=getchar(); + if(t=='Y' || t=='y') s=1; + else if(t=='N' || t=='n') s=0; + + FCEUI_SetCheat(num,name,A,V,s); +} + +static void AddCheatParam(uint32 A, uint8 V) +{ + char name[256]; + + printf("Name: "); + GetString(name); + printf("Address [$%04x]: ",(unsigned int)A); + A=GetH16(A); + printf("Value [%03d]: ",(unsigned int)V); + V=Get8(V); + printf("Add cheat \"%s\" for address $%04x with value %03d?",name,(unsigned int)A,(unsigned int)V); + if(GetYN(0)) + { + if(FCEUI_AddCheat(name,A,V)) + puts("Cheat added."); + else + puts("Error adding cheat."); + } +} + +static void AddCheat(void) +{ + AddCheatParam(0,0); +} + +static int lid; +static int clistcallb(char *name, uint32 a, uint8 v, int s) +{ + char tmp[512]; + int ret; + + sprintf(tmp,"%s $%04x:%03d - %s",s?"*":" ",(unsigned int)a,(unsigned int)v,name); + ret=AddToList(tmp,lid); + lid++; + return(ret); +} + +static void ListCheats(void) +{ + int which; + lid=0; + + BeginListShow(); + FCEUI_ListCheats(clistcallb); + which=EndListShow(); + if(which>=0) + { + char tmp[32]; + printf(" <(T)oggle status, (M)odify, or (D)elete this cheat.> "); + fgets(tmp,32,stdin); + switch(tolower(tmp[0])) + { + case 't':ToggleCheat(which); + break; + case 'd':if(!FCEUI_DelCheat(which)) + puts("Error deleting cheat!"); + else + puts("Cheat has been deleted."); + break; + case 'm':ModifyCheat(which); + break; + } + } +} + +static void ResetSearch(void) +{ + FCEUI_CheatSearchBegin(); + puts("Done."); +} + +static int srescallb(uint32 a, uint8 last, uint8 current) +{ + char tmp[13]; + sprintf(tmp, "$%04x:%03d:%03d",(unsigned int)a,(unsigned int)last,(unsigned int)current); + return(AddToList(tmp,a)); +} + +static void ShowRes(void) +{ + int n=FCEUI_CheatSearchGetCount(); + printf(" %d results:\n",n); + if(n) + { + int which; + BeginListShow(); + FCEUI_CheatSearchGet(srescallb); + which=EndListShow(); + if(which>=0) + AddCheatParam(which,0); + } +} + +static int ShowShortList(char *moe[], int n, int def) +{ + int x,c; + unsigned int baa; + char tmp[16]; + + red: + for(x=0;x ",def+1); + fgets(tmp,256,stdin); + if(tmp[0]=='\n') + return def; + c=tolower(tmp[0]); + baa=c-'1'; + + if(baa "); + fgets(buf,32,stdin); + c=tolower(buf[0]); + if(c=='\n') + goto recommand; + else if(c=='d') + goto redisplay; + else if(c=='x') + { + return; + } + else if(sscanf(buf,"%d",&c)) + { + if(c>x) goto invalid; + if(men[c-1].type) + { + void (*func)(void)=men[c-1].action; + func(); + } + else + DoMenu(men[c-1].action); /* Mmm...recursivey goodness. */ + goto redisplay; + } + else + { + invalid: + puts("Invalid command.\n"); + goto recommand; + } + + } +} + +void DoConsoleCheatConfig(void) +{ + MENU *curmenu=MainMenu; + + DoMenu(curmenu); +} diff --git a/drivers/common/cheat.h b/drivers/common/cheat.h new file mode 100644 index 0000000..6422569 --- /dev/null +++ b/drivers/common/cheat.h @@ -0,0 +1,21 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +void DoConsoleCheatConfig(void); diff --git a/drivers/common/config.c b/drivers/common/config.c new file mode 100644 index 0000000..6f87f55 --- /dev/null +++ b/drivers/common/config.c @@ -0,0 +1,151 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/****************************************************************/ +/* FCE Ultra */ +/* */ +/* This file contains routines for reading/writing the */ +/* configuration file. */ +/* */ +/****************************************************************/ + +#include +#include +#include + +#include "../../types.h" +#include "config.h" + +static int FReadString(FILE *fp, char *str, int n) +{ + int x=0,z; + for(;;) + { + z=fgetc(fp); + str[x]=z; + x++; + if(z<=0) break; + if(x>=n) return 0; + } + if(z<0) return 0; + return 1; +} + +static void GetValueR(FILE *fp, char *str, void *v, int c) +{ + char buf[256]; + int s; + + while(FReadString(fp,buf,256)) + { + fread(&s,1,4,fp); + if(!strcmp(str, buf)) + { + if(!c) // String, allocate some memory. + { + if(!(*(char **)v=malloc(s))) + goto gogl; + fread(*(char **)v,1,s,fp); + continue; + } + else if(s>c || s +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../types.h" + +static int format; +static int dspfd; + +// fsize is in samples, not bytes(gets translated before ioctl()) +int InitUNIXDSPSound(int *rate, int bits, int fsize, int nfrags) +{ + int x; + + printf(" Opening /dev/dsp..."); + dspfd=open("/dev/dsp",O_WRONLY); + if(dspfd==-1) goto __disperror; + + if(!bits) goto skip16check; + x=AFMT_S16_LE; + format=0; + printf("\n Setting format to 16-bit, signed, LSB first..."); + if(ioctl(dspfd,SNDCTL_DSP_SETFMT,&x)==-1) + { + skip16check: + x=AFMT_U8; + printf("\n Setting format to 8-bit, unsigned..."); + if(ioctl(dspfd,SNDCTL_DSP_SETFMT,&x)==-1) goto __disperror; + format=1; + } + + printf("\n Setting fragment size to %d samples and number of fragments to %d...",1<65535) + { + printf(" Sample rate is out of the acceptable range(8192-65535).\n"); + close(dspfd); + return(0); + } + return 1; + __disperror: + printf("ERROR\n"); + return 0; +} + +void KillUNIXDSPSound(void) +{ + close(dspfd); +} + +static int16 MBuffer[2048]; +void WriteUNIXDSPSound(int32 *Buffer, int Count, int noblocking) +{ + int P,c; + int32 *src=Buffer; + + if(format) + { + uint8 *dest=(uint8 *)MBuffer; + for(P=Count;P;P--,dest++,src++) + *dest=(uint8)((*src)>>8)^128; + c=Count; + } + else + { + int16 *dest=MBuffer; + for(P=Count;P;P--,dest++,src++) + *dest=*src; + c=Count<<1; + } + +// noblocking=!noblocking; // speed testing + if(noblocking) + { + struct audio_buf_info ai; + if(!ioctl(dspfd,SNDCTL_DSP_GETOSPACE,&ai)) + if(ai.bytes + +#include "../../types.h" + +static uint32 CBM[3]; +static uint32 *palettetranslate=0; +static int Bpp; // BYTES per pixel + + +int InitBlitToHigh(int b, uint32 rmask, uint32 gmask, uint32 bmask) +{ + Bpp=b; + + if(Bpp<=1 || Bpp>4) + return(0); + + if(Bpp==2) + palettetranslate=malloc(65536*4); + else if(Bpp>=3) + palettetranslate=malloc(256*4); + if(!palettetranslate) + return(0); + + CBM[0]=rmask; + CBM[1]=gmask; + CBM[2]=bmask; + return(1); +} + +void KillBlitToHigh(void) +{ + free(palettetranslate); +} + +void SetPaletteBlitToHigh(uint8 *src) +{ + int cshiftr[3]; + int cshiftl[3]; + int a,x,z,y; + + cshiftl[0]=cshiftl[1]=cshiftl[2]=-1; + for(a=0;a<3;a++) + { + for(x=0,y=-1,z=0;x<32;x++) + { + if(CBM[a]&(1<>cshiftr[0])<>cshiftr[1])<>cshiftr[2])<>8)<<2)]>>cshiftr[0])<>8)<<2)+1]>>cshiftr[1])<>8)<<2)+2]>>cshiftr[2])<>1); + do + { + for(x=xr;x;x--,src++) + { + int too=xscale; + do + { + *(uint8 *)dest=*(uint8 *)src; + dest++; + } while(--too); + } + src-=xr; + dest+=pinc; + } while(--doo); + //src-=xr*(yscale-(yscale>>1)); + dest+=pitch*(yscale>>1); + + src+=xr; + } + + } + else + { + for(y=yr;y;y--,/*dest+=pinc,*/src+=272-xr) + { + int doo=yscale; + do + { + for(x=xr;x;x--,src++) + { + int too=xscale; + do + { + *(uint8 *)dest=*(uint8 *)src; + dest++; + } while(--too); + } + src-=xr; + dest+=pinc; + } while(--doo); + src+=xr; + } + } + + } + else + { + for(y=yr;y;y--,dest+=pinc,src+=272-xr) + for(x=xr;x;x-=4,dest+=4,src+=4) + *(uint32 *)dest=*(uint32 *)src; + } +} + +void Blit8ToHigh(uint8 *src, uint8 *dest, int xr, int yr, int pitch) +{ + int x,y; + int pinc; + + switch(Bpp) + { + case 4: + pinc=pitch-(xr<<2); + for(y=yr;y;y--) + { + for(x=xr;x;x--) + { + *(uint32 *)dest=palettetranslate[(uint32)*src]; + dest+=4; + src++; + } + dest+=pinc; + src+=16; + } + break; + + case 3: + pinc=pitch-(xr+xr+xr); + for(y=yr;y;y--) + { + for(x=xr;x;x--) + { + uint32 tmp; + tmp=palettetranslate[(uint32)*src]; + *(uint16*)dest=(uint16)tmp; + *&dest[2]=(uint8)(tmp>>16); + dest+=3; + src++; + } + dest+=pinc; + src+=16; + } + break; + + case 2: + pinc=pitch-(xr<<1); + for(y=yr;y;y--) + { + for(x=xr>>1;x;x--) + { + *(uint32 *)dest=palettetranslate[*(uint16 *)src]; + dest+=4; + src+=2; + } + dest+=pinc; + src+=16; + } + break; + } +} diff --git a/drivers/common/vidblit.h b/drivers/common/vidblit.h new file mode 100644 index 0000000..a84588c --- /dev/null +++ b/drivers/common/vidblit.h @@ -0,0 +1,25 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +int InitBlitToHigh(int b, uint32 rmask, uint32 gmask, uint32 bmask); +void SetPaletteBlitToHigh(uint8 *src); +void KillBlitToHigh(void); +void Blit8ToHigh(uint8 *src, uint8 *dest, int xr, int yr, int pitch); +void Blit8To8(uint8 *src, uint8 *dest, int xr, int yr, int pitch, int xscale, int yscale, int efx); diff --git a/drivers/win/cheat.c b/drivers/win/cheat.c new file mode 100644 index 0000000..3b53c2f --- /dev/null +++ b/drivers/win/cheat.c @@ -0,0 +1,471 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "common.h" + +#include "cheat.h" + +static int selcheat; +static int scheatmethod=0; +static uint8 cheatval1=0; +static uint8 cheatval2=0; + +static void ConfigAddCheat(HWND wnd); + + +static uint16 StrToU16(char *s) +{ + unsigned int ret=0; + sscanf(s,"%4x",&ret); + return ret; +} + +static uint8 StrToU8(char *s) +{ + unsigned int ret=0; + sscanf(s,"%d",&ret); + return ret; +} + + +/* Need to be careful where these functions are used. */ +static char *U16ToStr(uint16 a) +{ + static char TempArray[16]; + sprintf(TempArray,"%04X",a); + return TempArray; +} + +static char *U8ToStr(uint8 a) +{ + static char TempArray[16]; + sprintf(TempArray,"%03d",a); + return TempArray; +} + + +static HWND RedoCheatsWND; +static int RedoCheatsCallB(char *name, uint32 a, uint8 v, int s) +{ + SendDlgItemMessage(RedoCheatsWND,101,LB_ADDSTRING,0,(LPARAM)(LPSTR)name); + return(1); +} + +static void RedoCheatsLB(HWND hwndDlg) +{ + SendDlgItemMessage(hwndDlg,101,LB_RESETCONTENT,0,0); + RedoCheatsWND=hwndDlg; + FCEUI_ListCheats(RedoCheatsCallB); +} + + +BOOL CALLBACK CheatsConCallB(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case WM_INITDIALOG: + RedoCheatsLB(hwndDlg); + break; + case WM_CLOSE: + case WM_QUIT: goto gornk; + case WM_COMMAND: + switch(HIWORD(wParam)) + { + case BN_CLICKED: + if(selcheat>=0) + { + if(LOWORD(wParam)==107) + FCEUI_SetCheat(selcheat,0,-1,-1,1); + else if(LOWORD(wParam)==108) + FCEUI_SetCheat(selcheat,0,-1,-1,0); + } + break; + case EN_KILLFOCUS: + if(selcheat>=0) + { + char TempArray[256]; + int32 t; + + GetDlgItemText(hwndDlg,LOWORD(wParam),TempArray,256); + switch(LOWORD(wParam)) + { + case 102:FCEUI_SetCheat(selcheat,TempArray,-1,-1,-1); + SendDlgItemMessage(hwndDlg,101,LB_INSERTSTRING,selcheat,(LPARAM)(LPCTSTR)TempArray); + SendDlgItemMessage(hwndDlg,101,LB_DELETESTRING,selcheat+1,0); + SendDlgItemMessage(hwndDlg,101,LB_SETCURSEL,selcheat,0); + break; + case 103:t=StrToU16(TempArray); + FCEUI_SetCheat(selcheat,0,t,-1,-1); + break; + case 104:t=StrToU8(TempArray); + FCEUI_SetCheat(selcheat,0,-1,t,-1); + break; + } + } + break; + } + + switch(LOWORD(wParam)) + { + case 101: + if(HIWORD(wParam)==LBN_SELCHANGE) + { + char *s; + uint32 a; + uint8 b; + int status; + + selcheat=SendDlgItemMessage(hwndDlg,101,LB_GETCURSEL,0,(LPARAM)(LPSTR)0); + if(selcheat<0) break; + + FCEUI_GetCheat(selcheat,&s,&a,&b,&status); + SetDlgItemText(hwndDlg,102,(LPTSTR)s); + SetDlgItemText(hwndDlg,103,(LPTSTR)U16ToStr(a)); + SetDlgItemText(hwndDlg,104,(LPTSTR)U8ToStr(b)); + + CheckRadioButton(hwndDlg,107,108,status?107:108); + } + break; + } + + if(!(wParam>>16)) + switch(wParam&0xFFFF) + { + case 106: + if(selcheat>=0) + { + FCEUI_DelCheat(selcheat); + SendDlgItemMessage(hwndDlg,101,LB_DELETESTRING,selcheat,0); + selcheat=-1; + SetDlgItemText(hwndDlg,102,(LPTSTR)""); + SetDlgItemText(hwndDlg,103,(LPTSTR)""); + SetDlgItemText(hwndDlg,104,(LPTSTR)""); + CheckRadioButton(hwndDlg,107,108,0); // Is this correct? + } + break; + case 105: + ConfigAddCheat(hwndDlg); + RedoCheatsLB(hwndDlg); + break; + case 1: + gornk: + EndDialog(hwndDlg,0); + break; + } + } + return 0; +} + + +void ConfigCheats(HWND hParent) +{ + if(!GI) + { + FCEUD_PrintError("You must have a game loaded before you can manipulate cheats."); + return; + } + + if(GI->type==GIT_NSF) + { + FCEUD_PrintError("Sorry, you can't cheat with NSFs."); + return; + } + + selcheat=-1; + DialogBox(fceu_hInstance,"CHEATS",hParent,CheatsConCallB); +} + + + + + + +HWND cfcallbw; + +int cfcallb(uint32 a, uint8 last, uint8 current) +{ + char temp[16]; + + sprintf(temp,"%04X:%03d:%03d",(unsigned int)a,last,current); + SendDlgItemMessage(cfcallbw,108,LB_ADDSTRING,0,(LPARAM)(LPSTR)temp); + return(1); +} + +static int scrollindex; +static int scrollnum; +static int scrollmax; + +int cfcallbinsert(uint32 a, uint8 last, uint8 current) +{ + char temp[16]; + + sprintf(temp,"%04X:%03d:%03d",(unsigned int)a,last,current); + SendDlgItemMessage(cfcallbw,108,LB_INSERTSTRING,13,(LPARAM)(LPSTR)temp); + return(1); +} + +int cfcallbinsertt(uint32 a, uint8 last, uint8 current) +{ + char temp[16]; + + sprintf(temp,"%04X:%03d:%03d",(unsigned int)a,last,current); + SendDlgItemMessage(cfcallbw,108,LB_INSERTSTRING,0,(LPARAM)(LPSTR)temp); + return(1); +} + + +void AddTheThing(HWND hwndDlg, char *s, int a, int v) +{ + if(FCEUI_AddCheat(s,a,v)) + MessageBox(hwndDlg,"Cheat Added","Cheat Added",MB_OK); +} + + +static void DoGet(void) +{ + int n=FCEUI_CheatSearchGetCount(); + int t; + scrollnum=n; + scrollindex=-32768; + + SendDlgItemMessage(cfcallbw,108,LB_RESETCONTENT,0,0); + FCEUI_CheatSearchGetRange(0,13,cfcallb); + + t=-32768+n-1-13; + if(t<-32768) + t=-32768; + scrollmax=t; + SendDlgItemMessage(cfcallbw,120,SBM_SETRANGE,-32768,t); + SendDlgItemMessage(cfcallbw,120,SBM_SETPOS,-32768,1); +} + + +BOOL CALLBACK AddCheatCallB(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + static int lbfocus; + static HWND hwndLB; + cfcallbw=hwndDlg; + + + switch(uMsg) + { + case WM_VSCROLL: + if(scrollnum>13) + { + switch((int)LOWORD(wParam)) + { + case SB_TOP: + scrollindex=-32768; + SendDlgItemMessage(hwndDlg,120,SBM_SETPOS,scrollindex,1); + SendDlgItemMessage(hwndDlg,108,LB_RESETCONTENT,13,0); + FCEUI_CheatSearchGetRange(scrollindex+32768,scrollindex+32768+13,cfcallb); + break; + case SB_BOTTOM: + scrollindex=scrollmax; + SendDlgItemMessage(hwndDlg,120,SBM_SETPOS,scrollindex,1); + SendDlgItemMessage(hwndDlg,108,LB_RESETCONTENT,13,0); + FCEUI_CheatSearchGetRange(scrollindex+32768,scrollindex+32768+13,cfcallb); + break; + case SB_LINEUP: + if(scrollindex>-32768) + { + scrollindex--; + SendDlgItemMessage(hwndDlg,120,SBM_SETPOS,scrollindex,1); + SendDlgItemMessage(hwndDlg,108,LB_DELETESTRING,13,0); + FCEUI_CheatSearchGetRange(scrollindex+32768,scrollindex+32768,cfcallbinsertt); + } + break; + + case SB_PAGEUP: + scrollindex-=14; + if(scrollindex<-32768) scrollindex=-32768; + SendDlgItemMessage(hwndDlg,120,SBM_SETPOS,scrollindex,1); + SendDlgItemMessage(hwndDlg,108,LB_RESETCONTENT,13,0); + FCEUI_CheatSearchGetRange(scrollindex+32768,scrollindex+32768+13,cfcallb); + break; + + case SB_LINEDOWN: + if(scrollindexscrollmax) + scrollindex=scrollmax; + SendDlgItemMessage(hwndDlg,120,SBM_SETPOS,scrollindex,1); + SendDlgItemMessage(hwndDlg,108,LB_RESETCONTENT,0,0); + FCEUI_CheatSearchGetRange(scrollindex+32768,scrollindex+32768+13,cfcallb); + break; + + case SB_THUMBPOSITION: + case SB_THUMBTRACK: + scrollindex=(short int)HIWORD(wParam); + SendDlgItemMessage(hwndDlg,120,SBM_SETPOS,scrollindex,1); + SendDlgItemMessage(hwndDlg,108,LB_RESETCONTENT,0,0); + FCEUI_CheatSearchGetRange(32768+scrollindex,32768+scrollindex+13,cfcallb); + break; + } + + } + break; + + case WM_INITDIALOG: + SetDlgItemText(hwndDlg,110,(LPTSTR)U8ToStr(cheatval1)); + SetDlgItemText(hwndDlg,111,(LPTSTR)U8ToStr(cheatval2)); + DoGet(); + CheckRadioButton(hwndDlg,115,118,scheatmethod+115); + lbfocus=0; + hwndLB=0; + break; + + case WM_VKEYTOITEM: + if(lbfocus) + { + int real; + + real=SendDlgItemMessage(hwndDlg,108,LB_GETCURSEL,0,(LPARAM)(LPSTR)0); + switch((int)LOWORD(wParam)) + { + case VK_UP: + /* mmmm....recursive goodness */ + if(!real) + SendMessage(hwndDlg,WM_VSCROLL,SB_LINEUP,0); + return(-1); + break; + case VK_DOWN: + if(real==13) + SendMessage(hwndDlg,WM_VSCROLL,SB_LINEDOWN,0); + return(-1); + break; + case VK_PRIOR: + SendMessage(hwndDlg,WM_VSCROLL,SB_PAGEUP,0); + break; + case VK_NEXT: + SendMessage(hwndDlg,WM_VSCROLL,SB_PAGEDOWN,0); + break; + case VK_HOME: + SendMessage(hwndDlg,WM_VSCROLL,SB_TOP,0); + break; + case VK_END: + SendMessage(hwndDlg,WM_VSCROLL,SB_BOTTOM,0); + break; + } + return(-2); + } + break; + + case WM_CLOSE: + case WM_QUIT: goto gornk; + case WM_COMMAND: + switch(LOWORD(wParam)) + { + case 108: + switch(HIWORD(wParam)) + { + case LBN_SELCHANGE: + { + char TempArray[32]; + SendDlgItemMessage(hwndDlg,108,LB_GETTEXT,SendDlgItemMessage(hwndDlg,108,LB_GETCURSEL,0,(LPARAM)(LPSTR)0),(LPARAM)(LPCTSTR)TempArray); + TempArray[4]=0; + SetDlgItemText(hwndDlg,201,(LPTSTR)TempArray); + } + break; + case LBN_SETFOCUS: + lbfocus=1; + break; + case LBN_KILLFOCUS: + lbfocus=0; + break; + } + break; + } + + switch(HIWORD(wParam)) + { + case BN_CLICKED: + if(LOWORD(wParam)>=115 && LOWORD(wParam)<=118) + scheatmethod=LOWORD(wParam)-115; + break; + case EN_CHANGE: + { + char TempArray[256]; + GetDlgItemText(hwndDlg,LOWORD(wParam),TempArray,256); + switch(LOWORD(wParam)) + { + case 110:cheatval1=StrToU8(TempArray);break; + case 111:cheatval2=StrToU8(TempArray);break; + } + } + break; + } + + + if(!(wParam>>16)) + switch(wParam&0xFFFF) + { + case 112: + FCEUI_CheatSearchBegin(); + DoGet(); + break; + case 113: + FCEUI_CheatSearchEnd(scheatmethod,cheatval1,cheatval2); + DoGet(); + break; + case 114: + FCEUI_CheatSearchSetCurrentAsOriginal(); + DoGet(); + break; + case 107: + FCEUI_CheatSearchShowExcluded(); + DoGet(); + break; + case 105: + { + int a,v; + char temp[256]; + + GetDlgItemText(hwndDlg,201,temp,4+1); + a=StrToU16(temp); + GetDlgItemText(hwndDlg,202,temp,3+1); + v=StrToU8(temp); + + GetDlgItemText(hwndDlg,200,temp,256); + AddTheThing(hwndDlg,temp,a,v); + } + break; + case 106: + gornk: + EndDialog(hwndDlg,0); + break; + } + } + return 0; +} + +static void ConfigAddCheat(HWND wnd) +{ + DialogBox(fceu_hInstance,"ADDCHEAT",wnd,AddCheatCallB); +} diff --git a/drivers/win/cheat.h b/drivers/win/cheat.h new file mode 100644 index 0000000..b3cd3cc --- /dev/null +++ b/drivers/win/cheat.h @@ -0,0 +1 @@ +void ConfigCheats(HWND hParent); diff --git a/drivers/win/common.h b/drivers/win/common.h new file mode 100644 index 0000000..91b8d3d --- /dev/null +++ b/drivers/win/common.h @@ -0,0 +1,20 @@ +#include +#include +#include + +#ifndef WIN32 +#define WIN32 +#endif +#undef WINNT +#define NONAMELESSUNION + +#define DIRECTSOUND_VERSION 0x0700 +#define DIRECTDRAW_VERSION 0x0700 +#define DIRECTINPUT_VERSION 0x700 +#include "../../driver.h" + +extern HWND hAppWnd; +extern HINSTANCE fceu_hInstance; + +extern int NoWaiting; +extern FCEUGI *GI; diff --git a/drivers/win/config.c b/drivers/win/config.c new file mode 100644 index 0000000..761a7a9 --- /dev/null +++ b/drivers/win/config.c @@ -0,0 +1,132 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/****************************************************************/ +/* FCE Ultra */ +/* */ +/* This file contains code to interface to the standard */ +/* FCE Ultra configuration file saving/loading code. */ +/* */ +/****************************************************************/ +#include "../common/config.h" + +static CFGSTRUCT fceuconfig[]={ + + ACS(rfiles[0]), + ACS(rfiles[1]), + ACS(rfiles[2]), + ACS(rfiles[3]), + ACS(rfiles[4]), + ACS(rfiles[5]), + ACS(rfiles[6]), + ACS(rfiles[7]), + ACS(rfiles[8]), + ACS(rfiles[9]), + + AC(ntsccol),AC(ntsctint),AC(ntschue), + + AC(soundsleep), + NAC("palyo",palyo), + NAC("genie",genie), + NAC("fs",fullscreen), + NAC("vgamode",vmod), + NAC("sound",soundo), + + ACS(gfsdir), + + NACS("odcheats",DOvers[0]), + NACS("odmisc",DOvers[1]), + NACS("odnonvol",DOvers[2]), + NACS("odstates",DOvers[3]), + NACS("odsnaps",DOvers[4]), + NACS("odbase",DOvers[5]), + + NAC("winsizemul",winsizemul), + + AC(soundrate), + AC(soundbuftime), + AC(soundoptions), + AC(soundvolume), + + NAC("eoptions",eoptions), + NACA("cpalette",cpalette), + + ACA(joy), + ACA(joyA),ACA(joyB),ACA(joySelect),ACA(joyStart), + + AC(joyOptions), + ACA(joyUp),ACA(joyDown),ACA(joyLeft),ACA(joyRight), + + NACA("InputType",UsrInputType), + NAC("keyben",keybEnable), + + NACA("keybm0",keyBMap[0]), + NACA("keybm1",keyBMap[1]), + NACA("keybm2",keyBMap[2]), + NACA("keybm3",keyBMap[3]), + NACA("ppasc0",powerpadsc[0]), + NACA("ppasc1",powerpadsc[1]), + + NAC("ppaside",powerpadside), + NAC("vmcx",vmodes[0].x), + NAC("vmcy",vmodes[0].y), + NAC("vmcb",vmodes[0].bpp), + NAC("vmcf",vmodes[0].flags), + NAC("vmcxs",vmodes[0].xscale), + NAC("vmcys",vmodes[0].yscale), + + NAC("srendline",srendlinen), + NAC("erendline",erendlinen), + NAC("srendlinep",srendlinep), + NAC("erendlinep",erendlinep), + + AC(UsrInputTypeFC), + AC(winsync), + AC(fssync), + AC(NoFourScore), + ACA(fkbmap), + ENDCFGSTRUCT +}; + +static void SaveConfig(char *filename) +{ + DriverInterface(DES_GETNTSCTINT,&ntsctint); + DriverInterface(DES_GETNTSCHUE,&ntschue); + SaveFCEUConfig(filename,fceuconfig); +} + +static void LoadConfig(char *filename) +{ + DriverInterface(DES_GETNTSCTINT,&ntsctint); + DriverInterface(DES_GETNTSCHUE,&ntschue); + LoadFCEUConfig(filename,fceuconfig); + DriverInterface(DES_NTSCCOL,&ntsccol); + DriverInterface(DES_SETNTSCTINT,&ntsctint); + DriverInterface(DES_SETNTSCHUE,&ntschue); + + palyo&=1; + FCEUI_SetVidSystem(palyo); + genie&=1; + FCEUI_SetGameGenie(genie); + fullscreen&=1; + soundo&=1; + FCEUI_SetSoundVolume(soundvolume); +} + diff --git a/drivers/win/input.c b/drivers/win/input.c new file mode 100644 index 0000000..aea3f47 --- /dev/null +++ b/drivers/win/input.c @@ -0,0 +1,288 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "common.h" +#include + +#include "input.h" +#include "keyboard.h" +#include "joystick.h" + + +int UsrInputType[2]={SI_GAMEPAD,SI_GAMEPAD}; +int UsrInputTypeFC=SIFC_NONE; +int InputType[2]; +int InputTypeFC; + +int NoFourScore=0; + +static uint32 powerpadbuf[2]; + +LPDIRECTINPUT7 lpDI; + +void FixGIGO(void) +{ + InputType[0]=UsrInputType[0]; + InputType[1]=UsrInputType[1]; + InputTypeFC=UsrInputTypeFC; + + if(GI) + { + if(GI->input[0]>=0) + InputType[0]=GI->input[0]; + if(GI->input[1]>=0) + InputType[1]=GI->input[1]; + if(GI->inputfc>=0) + InputTypeFC=GI->inputfc; + CreateInputStuff(); + } +} + +static uint32 JSreturn; +static uint32 mousedata[3]; + + +static void ConfigGamePad(HWND hParent, int port); + +int InitDInput(void) +{ + HRESULT ddrval; + + ddrval=DirectInputCreateEx(fceu_hInstance,DIRECTINPUT_VERSION,&IID_IDirectInput7,(LPVOID *)&lpDI,0); + if(ddrval!=DI_OK) + { + FCEUD_PrintError("DirectInput: Error creating DirectInput object."); + return 0; + } + return 1; +} + +static int screenmode=0; +void InputScreenChanged(int fs) +{ + int x; + for(x=0;x<2;x++) + if(InputType[x]==SI_ZAPPER) + FCEUI_SetInput(x,SI_ZAPPER,mousedata,fs); + if(InputTypeFC==SIFC_SHADOW) + FCEUI_SetInputFC(SIFC_SHADOW,mousedata,fs); + screenmode=fs; +} + +void InitInputStuff(void) +{ + KeyboardInitialize(); + InitJoysticks(hAppWnd); +} + +void CreateInputStuff(void) +{ + void *InputDPtr=0; + int x; + int TAttrib; + + for(x=0;x<2;x++) + { + TAttrib=0; + switch(InputType[x]) + { + case SI_GAMEPAD:InputDPtr=((uint8 *)&JSreturn)+(x<<1); + break; + case SI_POWERPAD:InputDPtr=&powerpadbuf[x];break; + case SI_ARKANOID:InputDPtr=mousedata;break; + case SI_ZAPPER:InputDPtr=mousedata; + TAttrib=screenmode; + break; + } + FCEUI_SetInput(x,InputType[x],InputDPtr,TAttrib); + } + + TAttrib=0; + switch(InputTypeFC) + { + case SIFC_SHADOW:InputDPtr=mousedata;TAttrib=screenmode;break; + case SIFC_ARKANOID:InputDPtr=mousedata;break; + case SIFC_FKB:InputDPtr=fkbkeys;memset(fkbkeys,0,sizeof(fkbkeys));break; + } + FCEUI_SetInputFC(InputTypeFC,InputDPtr,TAttrib); + FCEUI_DisableFourScore(NoFourScore); +} + +void DestroyInput(void) +{ + KillJoysticks(); + KeyboardClose(); +} + +void FCEUD_UpdateInput(void) +{ + int x; + uint32 JS; + int t=0; + + KeyboardUpdate(); + + for(x=0;x<2;x++) + switch(InputType[x]) + { + case SI_GAMEPAD:t|=1;break; + case SI_ARKANOID:t|=2;break; + case SI_ZAPPER:t|=2;break; + case SI_POWERPAD:powerpadbuf[x]=UpdatePPadData(x);break; + } + switch(InputTypeFC) + { + case SIFC_ARKANOID:t|=2;break; + case SIFC_SHADOW:t|=2;break; + case SIFC_FKB: if(cidisabled) UpdateFKB();break; + } + if(t&1) + { + JS=KeyboardDodo(); + if(joy[0]|joy[1]|joy[2]|joy[3]) + JS|=(uint32)GetJSOr(); + JSreturn=(JS&0xFF000000)|(JS&0xFF)|((JS&0xFF0000)>>8)|((JS&0xFF00)<<8); + } + if(t&2) + GetMouseData(&mousedata[0], &mousedata[1], &mousedata[2]); +} + + +BOOL CALLBACK InputConCallB(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + static void (*boopar[5])(HWND hParent, int port)={0,ConfigGamePad,0,ConfigKeyboardiePowerpad,0}; + static void (*boopar2[5])(HWND hParent)={0,0,0,0,ConfigFKB}; + static char *strn[5]={"","Gamepad","Zapper","Power Pad","Arkanoid Paddle"}; + static char *strf[5]={"","Arkanoid Paddle","Hyper Shot gun","4-Player Adapter","Family Keyboard"}; + int x; + + switch(uMsg) { + case WM_INITDIALOG: + for(x=0;x<2;x++) + { + int y; + + for(y=0;y<5;y++) + SendDlgItemMessage(hwndDlg,104+x,CB_ADDSTRING,0,(LPARAM)(LPSTR)strn[y]); + + SendDlgItemMessage(hwndDlg,104+x,CB_SETCURSEL,UsrInputType[x],(LPARAM)(LPSTR)0); + EnableWindow(GetDlgItem(hwndDlg,106+x),boopar[InputType[x]]?1:0); + SetDlgItemText(hwndDlg,200+x,(LPTSTR)strn[InputType[x]]); + } + + + { + int y; + for(y=0;y<5;y++) + SendDlgItemMessage(hwndDlg,110,CB_ADDSTRING,0,(LPARAM)(LPSTR)strf[y]); + SendDlgItemMessage(hwndDlg,110,CB_SETCURSEL,UsrInputTypeFC,(LPARAM)(LPSTR)0); + EnableWindow(GetDlgItem(hwndDlg,111),boopar2[InputTypeFC]?1:0); + SetDlgItemText(hwndDlg,202,(LPTSTR)strf[InputTypeFC]); + } + + break; + case WM_CLOSE: + case WM_QUIT: goto gornk; + case WM_COMMAND: + if(HIWORD(wParam)==CBN_SELENDOK) + { + switch(LOWORD(wParam)) + { + case 104: + case 105:UsrInputType[LOWORD(wParam)-104]=InputType[LOWORD(wParam)-104]=SendDlgItemMessage(hwndDlg,LOWORD(wParam),CB_GETCURSEL,0,(LPARAM)(LPSTR)0); + EnableWindow( GetDlgItem(hwndDlg,LOWORD(wParam)+2),boopar[InputType[LOWORD(wParam)-104]]?1:0); + SetDlgItemText(hwndDlg,200+LOWORD(wParam)-104,(LPTSTR)strn[InputType[LOWORD(wParam)-104]]); + break; + case 110:UsrInputTypeFC=InputTypeFC=SendDlgItemMessage(hwndDlg,110,CB_GETCURSEL,0,(LPARAM)(LPSTR)0); + EnableWindow(GetDlgItem(hwndDlg,111),boopar2[InputTypeFC]?1:0); + SetDlgItemText(hwndDlg,202,(LPTSTR)strf[InputTypeFC]); + break; + + } + + } + if(!(wParam>>16)) + switch(wParam&0xFFFF) + { + case 111: + if(boopar2[InputTypeFC]) + boopar2[InputTypeFC](hwndDlg); + break; + + case 107: + case 106: + { + int t=(wParam&0xFFFF)-106; + if(boopar[InputType[t]]) + boopar[InputType[t]](hwndDlg,t); + } + break; + case 1: + gornk: + EndDialog(hwndDlg,0); + break; + } + } + return 0; +} + +void ConfigInput(HWND hParent) +{ + DialogBox(fceu_hInstance,"INPUTCONFIG",hParent,InputConCallB); + CreateInputStuff(); +} + + +static int porttemp; + +BOOL CALLBACK GPConCallB(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) { + case WM_INITDIALOG: + if(NoFourScore) + CheckDlgButton(hwndDlg,200,BST_CHECKED); + break; + case WM_CLOSE: + case WM_QUIT: goto gornk; + case WM_COMMAND: + if(!(wParam>>16)) + switch(wParam&0xFFFF) + { + case 107:ConfigJoystickies(hwndDlg, porttemp);break; + case 106:ConfigKeyboardie(hwndDlg, porttemp);break; + case 1: + gornk: + NoFourScore=0; + if(IsDlgButtonChecked(hwndDlg,200)==BST_CHECKED) + NoFourScore=1; + EndDialog(hwndDlg,0); + break; + } + } + return 0; +} + +static void ConfigGamePad(HWND hParent, int port) +{ + porttemp=port; + DialogBox(fceu_hInstance,"GAMEPADCONFIG",hParent,GPConCallB); +} + + diff --git a/drivers/win/input.h b/drivers/win/input.h new file mode 100644 index 0000000..559f1af --- /dev/null +++ b/drivers/win/input.h @@ -0,0 +1,25 @@ +void ConfigInput(HWND hParent); +int InitDInput(void); +void CreateInputStuff(void); +void InitInputStuff(void); +void DestroyInput(void); +void InputScreenChanged(int fs); +void FixGIGO(void); + +extern LPDIRECTINPUT7 lpDI; + +#define JOY_A 1 +#define JOY_B 2 +#define JOY_SELECT 4 +#define JOY_START 8 +#define JOY_UP 0x10 +#define JOY_DOWN 0x20 +#define JOY_LEFT 0x40 +#define JOY_RIGHT 0x80 + + +extern int InputType[2]; +extern int InputTypeFC; +extern int NoFourScore; +extern int UsrInputType[2]; +extern int UsrInputTypeFC; diff --git a/drivers/win/joystick.c b/drivers/win/joystick.c new file mode 100644 index 0000000..375b7af --- /dev/null +++ b/drivers/win/joystick.c @@ -0,0 +1,407 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "common.h" +#include + +#include "input.h" +#include "joystick.h" + + +HRESULT ddrval; + +static GUID joyGUID[64]; + +static int joycounter; + +static LPDIRECTINPUTDEVICE7 lpJoy[4]={0,0,0,0}; + +int joyOptions[4]={0,0,0,0}; +int joyA[4]={1,1,1,1}; +int joyB[4]={0,0,0,0}; +int joySelect[4]={2,2,2,2}; +int joyStart[4]={3,3,3,3}; +int joyUp[4]={4,4,4,4}; +int joyDown[4]={5,5,5,5}; +int joyLeft[4]={6,6,6,6}; +int joyRight[4]={7,7,7,7}; + +int joy[4]={0,0,0,0}; + +static int JoyXMax[4]; +static int JoyXMin[4]; + +static int JoyYMax[4]; +static int JoyYMin[4]; + +static DIJOYSTATE2 JoyStatus; + +static void ShowDIJErr(int w, char *s) +{ + char tempo[128]; + sprintf(tempo,"DirectInput: Joystick %d: %s",w+1,s); + FCEUD_PrintError(tempo); +} + +static void JoyAutoRestore(HRESULT ddrval,LPDIRECTINPUTDEVICE7 lpJJoy) +{ + switch(ddrval) + { + case DIERR_INPUTLOST: + case DIERR_NOTACQUIRED: + IDirectInputDevice7_Acquire(lpJJoy); + break; + } +} + +static int GetJoystickButton(int x) +{ + int errc=0; + int z; + + if(lpJoy[x]) + { + doagaino: + if(errc>8) return(-1); + + ddrval=IDirectInputDevice7_Poll(lpJoy[x]); + if(ddrval!=DI_OK && ddrval!=DI_NOEFFECT) {JoyAutoRestore(ddrval,lpJoy[x]);errc++;goto doagaino;} + + ddrval=IDirectInputDevice7_GetDeviceState(lpJoy[x],sizeof(JoyStatus),&JoyStatus); + if(ddrval!=DI_OK) {JoyAutoRestore(ddrval,lpJoy[x]);errc++;goto doagaino;} + + for(z=0;z<128;z++) + if(JoyStatus.rgbButtons[z]&0x80) + return z; + } + return(-1); +} + +uint32 GetJSOr(void) +{ + unsigned long ret; + int x; + ret=0; + + for(x=0;x<4;x++) + { + if(lpJoy[x]) + { + + ddrval=IDirectInputDevice7_Poll(lpJoy[x]); + if(ddrval!=DI_OK && ddrval!=DI_NOEFFECT) JoyAutoRestore(ddrval,lpJoy[x]); + + ddrval=IDirectInputDevice7_GetDeviceState(lpJoy[x],sizeof(JoyStatus),&JoyStatus); + if(ddrval!=DI_OK) JoyAutoRestore(ddrval,lpJoy[x]); + + if(joyOptions[x]&1) + { + if(JoyStatus.rgbButtons[joyUp[x]&127]&0x80) ret|=JOY_UP<<(x<<3); + if(JoyStatus.rgbButtons[joyDown[x]&127]&0x80) ret|=JOY_DOWN<<(x<<3); + if(JoyStatus.rgbButtons[joyLeft[x]&127]&0x80) ret|=JOY_LEFT<<(x<<3); + if(JoyStatus.rgbButtons[joyRight[x]&127]&0x80) ret|=JOY_RIGHT<<(x<<3); + } + else + { + if(JoyStatus.lX>=JoyXMax[x]) + ret|=JOY_RIGHT<<(x<<3); + else if(JoyStatus.lX<=JoyXMin[x]) + ret|=JOY_LEFT<<(x<<3); + + if(JoyStatus.lY>=JoyYMax[x]) + ret|=JOY_DOWN<<(x<<3); + else if(JoyStatus.lY<=JoyYMin[x]) + ret|=JOY_UP<<(x<<3); + } + if(JoyStatus.rgbButtons[joyA[x]&127]&0x80) ret|=1<<(x<<3); + if(JoyStatus.rgbButtons[joyB[x]&127]&0x80) ret|=2<<(x<<3); + if(JoyStatus.rgbButtons[joySelect[x]&127]&0x80) ret|=4<<(x<<3); + if(JoyStatus.rgbButtons[joyStart[x]&127]&0x80) ret|=8<<(x<<3); + } + } + + return ret; +} + +static void KillJoystick(int w) +{ + if(lpJoy[w]) + { + IDirectInputDevice7_Unacquire(lpJoy[w]); + IDirectInputDevice7_Release(lpJoy[w]); + lpJoy[w]=0; + } +} + +void KillJoysticks(void) +{ + int x; + for(x=0;x<4;x++) + KillJoystick(x); +} + +static BOOL CALLBACK JoystickFound(LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef) +{ + if(joycounter<64) + { + joyGUID[joycounter]=lpddi->guidInstance; + joycounter++; + if(pvRef) + { + SendDlgItemMessage(pvRef,106,CB_ADDSTRING,0,(LPARAM)(LPSTR)lpddi->tszProductName); + SendDlgItemMessage(pvRef,112,CB_ADDSTRING,0,(LPARAM)(LPSTR)lpddi->tszProductName); + } + return DIENUM_CONTINUE; + } + else + return 0; +} + +void InitJoystick(int w, HWND wnd) +{ + if(joy[w]) + { + if(joy[w]>joycounter) + { + ShowDIJErr(w,"Not found."); + joy[w]=0; + return; + } + ddrval=IDirectInput7_CreateDeviceEx(lpDI,&joyGUID[joy[w]-1],&IID_IDirectInputDevice7,(LPVOID *)&lpJoy[w],0); + if(ddrval != DI_OK) + { + ShowDIJErr(w,"Error creating device."); + joy[w]=0; + return; + } + ddrval=IDirectInputDevice7_SetCooperativeLevel(lpJoy[w],wnd,DISCL_FOREGROUND|DISCL_NONEXCLUSIVE); + if (ddrval != DI_OK) + { + ShowDIJErr(w,"Error setting cooperative level."); + KillJoystick(w); + joy[w]=0; + return; + } + ddrval=IDirectInputDevice7_SetDataFormat(lpJoy[w],&c_dfDIJoystick2); + if (ddrval != DI_OK) + { + ShowDIJErr(w,"Error setting data format."); + KillJoystick(w); + joy[w]=0; + return; + } + + { + DIPROPRANGE diprg; + int r; + + memset(&diprg,0,sizeof(DIPROPRANGE)); + diprg.diph.dwSize=sizeof(DIPROPRANGE); + diprg.diph.dwHeaderSize=sizeof(DIPROPHEADER); + diprg.diph.dwHow=DIPH_BYOFFSET; + diprg.diph.dwObj=DIJOFS_X; + ddrval=IDirectInputDevice7_GetProperty(lpJoy[w],DIPROP_RANGE,&diprg.diph); + if(ddrval!=DI_OK) + { + ShowDIJErr(w,"Error getting X axis range."); + joy[w]=0; + KillJoystick(w); + joy[w]=0; + return; + } + r=diprg.lMax-diprg.lMin; + JoyXMax[w]=diprg.lMax-(r>>2); + JoyXMin[w]=diprg.lMin+(r>>2); + + memset(&diprg,0,sizeof(DIPROPRANGE)); + diprg.diph.dwSize=sizeof(DIPROPRANGE); + diprg.diph.dwHeaderSize=sizeof(DIPROPHEADER); + diprg.diph.dwHow=DIPH_BYOFFSET; + diprg.diph.dwObj=DIJOFS_Y; + ddrval=IDirectInputDevice7_GetProperty(lpJoy[w],DIPROP_RANGE,&diprg.diph); + if(ddrval!=DI_OK) + { + ShowDIJErr(w,"Error getting X axis range."); + KillJoystick(w); + joy[w]=0; + return; + } + r=diprg.lMax-diprg.lMin; + JoyYMax[w]=diprg.lMax-(r>>2); + JoyYMin[w]=diprg.lMin+(r>>2); + } + + } +} + +void InitJoysticks(HWND wnd) +{ + int x; + + joycounter=0; + IDirectInput7_EnumDevices(lpDI, DIDEVTYPE_JOYSTICK,JoystickFound,0,DIEDFL_ATTACHEDONLY); + + for(x=0;x<4;x++) + InitJoystick(x,wnd); +} + + +static int joyconport; + +static BOOL CALLBACK JoyConCallB(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + char tempo[64]; + int x; + static int bid; + + switch(uMsg) { + case WM_TIMER: + if(bid>=200 && bid<=215) + { + int z; + + /* GetJoystickButton() makes sure there is a joystick, + so we don't need to here. + */ + if(bid<=207) + { + if( (z=GetJoystickButton(joyconport))!=-1) + SetDlgItemInt(hwndDlg,bid,z,0); + } + else + { + if( (z=GetJoystickButton(2+joyconport))!=-1) + SetDlgItemInt(hwndDlg,bid,z,0); + } + } + break; + case WM_INITDIALOG: + bid=0; + SetTimer(hwndDlg,666,20,0); /* Every 20ms(50x a second).*/ + + InitJoysticks(hwndDlg); + + SendDlgItemMessage(hwndDlg,106,CB_ADDSTRING,0,(LPARAM)(LPSTR)""); + SendDlgItemMessage(hwndDlg,112,CB_ADDSTRING,0,(LPARAM)(LPSTR)""); + + sprintf(tempo,"Virtual Gamepad %d",joyconport+1); + SetDlgItemText(hwndDlg,102,tempo); + sprintf(tempo,"Virtual Gamepad %d",joyconport+3); + SetDlgItemText(hwndDlg,104,tempo); + + for(x=0;x<=2;x+=2) + { + SetDlgItemInt(hwndDlg,200+(x<<2),joySelect[x+joyconport],0); + SetDlgItemInt(hwndDlg,201+(x<<2),joyStart[x+joyconport],0); + SetDlgItemInt(hwndDlg,202+(x<<2),joyB[x+joyconport],0); + SetDlgItemInt(hwndDlg,203+(x<<2),joyA[x+joyconport],0); + + SetDlgItemInt(hwndDlg,204+(x<<2),joyUp[x+joyconport],0); + SetDlgItemInt(hwndDlg,205+(x<<2),joyDown[x+joyconport],0); + SetDlgItemInt(hwndDlg,206+(x<<2),joyLeft[x+joyconport],0); + SetDlgItemInt(hwndDlg,207+(x<<2),joyRight[x+joyconport],0); + + } + joycounter=0; + IDirectInput7_EnumDevices(lpDI, DIDEVTYPE_JOYSTICK,JoystickFound,hwndDlg,DIEDFL_ATTACHEDONLY); + + SendDlgItemMessage(hwndDlg,106,CB_SETCURSEL,joy[0+joyconport],(LPARAM)(LPSTR)0); + SendDlgItemMessage(hwndDlg,112,CB_SETCURSEL,joy[2+joyconport],(LPARAM)(LPSTR)0); + + if(joyOptions[joyconport]&1) + CheckDlgButton(hwndDlg,300,BST_CHECKED); + if(joyOptions[joyconport+2]&1) + CheckDlgButton(hwndDlg,301,BST_CHECKED); + break; + case WM_CLOSE: + case WM_QUIT: goto gornk; + case WM_COMMAND: + if(HIWORD(wParam)==EN_SETFOCUS) + { + bid=LOWORD(wParam); + } + else if(HIWORD(wParam)==EN_KILLFOCUS) + { + bid=0; + } + else if(HIWORD(wParam)==CBN_SELENDOK) + { + switch(LOWORD(wParam)) + { + case 106: + KillJoystick(joyconport); + joy[0+(joyconport)]=SendDlgItemMessage(hwndDlg,106,CB_GETCURSEL,0,(LPARAM)(LPSTR)0); + InitJoystick(joyconport,hwndDlg); + SendDlgItemMessage(hwndDlg,106,CB_SETCURSEL,joy[0+joyconport],(LPARAM)(LPSTR)0); + break; + case 112: + KillJoystick(2+joyconport); + joy[2+(joyconport)]=SendDlgItemMessage(hwndDlg,112,CB_GETCURSEL,0,(LPARAM)(LPSTR)0); + InitJoystick(2+joyconport,hwndDlg); + SendDlgItemMessage(hwndDlg,112,CB_SETCURSEL,joy[2+joyconport],(LPARAM)(LPSTR)0); + break; + } + } + + if(!(wParam>>16)) + switch(wParam&0xFFFF) + { + case 1: + gornk: + + KillTimer(hwndDlg,666); + KillJoysticks(); + + for(x=0;x<=2;x+=2) + { + joySelect[x+(joyconport)]=GetDlgItemInt(hwndDlg,200+(x<<2),0,0); + joyStart[x+(joyconport)]=GetDlgItemInt(hwndDlg,201+(x<<2),0,0); + joyB[x+(joyconport)]=GetDlgItemInt(hwndDlg,202+(x<<2),0,0); + joyA[x+(joyconport)]=GetDlgItemInt(hwndDlg,203+(x<<2),0,0); + + joyUp[x+(joyconport)]=GetDlgItemInt(hwndDlg,204+(x<<2),0,0); + joyDown[x+(joyconport)]=GetDlgItemInt(hwndDlg,205+(x<<2),0,0); + joyLeft[x+(joyconport)]=GetDlgItemInt(hwndDlg,206+(x<<2),0,0); + joyRight[x+(joyconport)]=GetDlgItemInt(hwndDlg,207+(x<<2),0,0); + } + if(IsDlgButtonChecked(hwndDlg,300)==BST_CHECKED) + joyOptions[joyconport]|=1; + else + joyOptions[joyconport]&=~1; + if(IsDlgButtonChecked(hwndDlg,301)==BST_CHECKED) + joyOptions[joyconport+2]|=1; + else + joyOptions[joyconport+2]&=~1; + EndDialog(hwndDlg,0); + break; + } + } + return 0; +} + +void ConfigJoystickies(HWND hParent, int port) +{ + joyconport=port; + + KillJoysticks(); + DialogBox(fceu_hInstance,"JOYCONFIG",hParent,JoyConCallB); + InitJoysticks(hAppWnd); +} + diff --git a/drivers/win/joystick.h b/drivers/win/joystick.h new file mode 100644 index 0000000..a5cdb40 --- /dev/null +++ b/drivers/win/joystick.h @@ -0,0 +1,16 @@ +void ConfigJoystickies(HWND hParent, int port); +void InitJoysticks(HWND wnd); +void KillJoysticks(void); +uint32 GetJSOr(void); + +extern int joyOptions[4]; +extern int joyA[4]; +extern int joyB[4]; +extern int joySelect[4]; +extern int joyStart[4]; +extern int joyUp[4]; +extern int joyDown[4]; +extern int joyLeft[4]; +extern int joyRight[4]; +extern int joy[4]; + diff --git a/drivers/win/keyboard.c b/drivers/win/keyboard.c new file mode 100644 index 0000000..b521081 --- /dev/null +++ b/drivers/win/keyboard.c @@ -0,0 +1,458 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "common.h" +#include + + +#include "input.h" +#include "keyboard.h" + +#include "keyscan.h" + + +HRESULT ddrval; + +static LPDIRECTINPUTDEVICE7 lpdid=0; +static int porttemp; + + +int keyBMap[4][8]={ + {SCAN_LEFTALT,SCAN_LEFTCONTROL,SCAN_TAB,SCAN_ENTER,SCAN_BL_CURSORUP,SCAN_BL_CURSORDOWN,SCAN_BL_CURSORLEFT,SCAN_BL_CURSORRIGHT}, + {SCAN_LEFTALT,SCAN_LEFTCONTROL,SCAN_TAB,SCAN_ENTER,SCAN_BL_CURSORUP,SCAN_BL_CURSORDOWN,SCAN_BL_CURSORLEFT,SCAN_BL_CURSORRIGHT}, + {SCAN_LEFTALT,SCAN_LEFTCONTROL,SCAN_TAB,SCAN_ENTER,SCAN_BL_CURSORUP,SCAN_BL_CURSORDOWN,SCAN_BL_CURSORLEFT,SCAN_BL_CURSORRIGHT}, + {SCAN_LEFTALT,SCAN_LEFTCONTROL,SCAN_TAB,SCAN_ENTER,SCAN_BL_CURSORUP,SCAN_BL_CURSORDOWN,SCAN_BL_CURSORLEFT,SCAN_BL_CURSORRIGHT} + }; +int keybEnable=1; + +int powerpadside=0; +int powerpadsc[2][12]={ + { + SCAN_O,SCAN_P,SCAN_BRACKET_LEFT, + SCAN_BRACKET_RIGHT,SCAN_K,SCAN_L,SCAN_SEMICOLON,SCAN_APOSTROPHE, + SCAN_M,SCAN_COMMA,SCAN_PERIOD,SCAN_SLASH + }, + { + SCAN_O,SCAN_P,SCAN_BRACKET_LEFT, + SCAN_BRACKET_RIGHT,SCAN_K,SCAN_L,SCAN_SEMICOLON,SCAN_APOSTROPHE, + SCAN_M,SCAN_COMMA,SCAN_PERIOD,SCAN_SLASH + } + }; + + + + +void KeyboardClose(void) +{ + if(lpdid) IDirectInputDevice7_Unacquire(lpdid); + lpdid=0; +} + +static char keys[256]; +static void KeyboardUpdateState(void) +{ + ddrval=IDirectInputDevice7_GetDeviceState(lpdid,256,keys); + switch(ddrval) + { + case DIERR_INPUTLOST: + case DIERR_NOTACQUIRED: + IDirectInputDevice7_Acquire(lpdid); + break; + } +} + +int KeyboardInitialize(void) +{ + + if(lpdid) + return(1); + + ddrval=IDirectInput7_CreateDeviceEx(lpDI, &GUID_SysKeyboard,&IID_IDirectInputDevice7, (LPVOID *)&lpdid,0); + if(ddrval != DI_OK) + { + FCEUD_PrintError("DirectInput: Error creating keyboard device."); + return 0; + } + + ddrval=IDirectInputDevice7_SetCooperativeLevel(lpdid, hAppWnd,DISCL_FOREGROUND|DISCL_NONEXCLUSIVE); + if(ddrval != DI_OK) + { + FCEUD_PrintError("DirectInput: Error setting keyboard cooperative level."); + return 0; + } + + ddrval=IDirectInputDevice7_SetDataFormat(lpdid,&c_dfDIKeyboard); + if(ddrval != DI_OK) + { + FCEUD_PrintError("DirectInput: Error setting keyboard data format."); + return 0; + } + + ddrval=IDirectInputDevice7_Acquire(lpdid); + if(ddrval != DI_OK) + { + FCEUD_PrintError("DirectInput: Error acquiring keyboard."); + return 0; + } + return 1; +} + +static int DIPS=0; +static uint8 keyonce[256]; +#define KEY(__a) keys[SCAN_##__a] +#define keyonly(__a,__z) {if(KEY(__a)){if(!keyonce[SCAN_##__a]) {keyonce[SCAN_##__a]=1;__z}} else{keyonce[SCAN_##__a]=0;}} +int cidisabled=0; + +void KeyboardUpdate(void) +{ + KeyboardUpdateState(); + + if(InputTypeFC==SIFC_FKB && cidisabled) + return; + + NoWaiting&=~1; + if(KEY(GRAVE)) + NoWaiting|=1; + + if(GI) + { + if(GI->type==GIT_FDS) + { + keyonly(S,DriverInterface(DES_FDSSELECT,0);) + keyonly(I,DriverInterface(DES_FDSINSERT,0);) + keyonly(E,DriverInterface(DES_FDSEJECT,0);) + } + + if(GI->type!=GIT_NSF) + { + keyonly(F5,FCEUI_SaveState();) + keyonly(F7,FCEUI_LoadState();) + } + keyonly(F9,FCEUI_SaveSnapshot();) + + if(GI->type==GIT_VSUNI) + { + keyonly(C,DriverInterface(DES_VSUNICOIN,0);) + keyonly(V,DIPS^=1;DriverInterface(DES_VSUNITOGGLEDIPVIEW,0);) + if(!(DIPS&1)) goto DIPSless; + keyonly(1,DriverInterface(DES_VSUNIDIPSET,(void *)1);) + keyonly(2,DriverInterface(DES_VSUNIDIPSET,(void *)2);) + keyonly(3,DriverInterface(DES_VSUNIDIPSET,(void *)3);) + keyonly(4,DriverInterface(DES_VSUNIDIPSET,(void *)4);) + keyonly(5,DriverInterface(DES_VSUNIDIPSET,(void *)5);) + keyonly(6,DriverInterface(DES_VSUNIDIPSET,(void *)6);) + keyonly(7,DriverInterface(DES_VSUNIDIPSET,(void *)7);) + keyonly(8,DriverInterface(DES_VSUNIDIPSET,(void *)8);) + } + else + { + keyonly(H,DriverInterface(DES_NTSCSELHUE,0);) + keyonly(T,DriverInterface(DES_NTSCSELTINT,0);) + if(KEY(KP_MINUS) || KEY(MINUS)) DriverInterface(DES_NTSCDEC,0); + if(KEY(KP_PLUS) || KEY(EQUAL)) DriverInterface(DES_NTSCINC,0); + + DIPSless: + keyonly(0,FCEUI_SelectState(0);) + keyonly(1,FCEUI_SelectState(1);) + keyonly(2,FCEUI_SelectState(2);) + keyonly(3,FCEUI_SelectState(3);) + keyonly(4,FCEUI_SelectState(4);) + keyonly(5,FCEUI_SelectState(5);) + keyonly(6,FCEUI_SelectState(6);) + keyonly(7,FCEUI_SelectState(7);) + keyonly(8,FCEUI_SelectState(8);) + keyonly(9,FCEUI_SelectState(9);) + } + } +} + +uint32 KeyboardDodo(void) +{ + uint32 JS=0; + + + if(GI) + if(GI->type!=GIT_NSF) + { + int x,y,u,b; + + for(u=0;u<4;u++) + { + if(keybEnable&(1<=0;b--) + if(keys[tmpo[b]]) JS|=(1<>16)) + { + wParam&=0xFFFF; + if((wParam>=600 && wParam<=607) || (wParam>=610 && wParam<=617)) + inkeyloop=wParam; + else switch(wParam) + { + case 1: + gornk: + for(x=0;x<2;x++) + { + for(y=0;y<8;y++) + keyBMap[porttemp+(x<<1)][y]=GetDlgItemInt(hwndDlg,600+y+x*10,0,0); + + if(IsDlgButtonChecked(hwndDlg,320+x)==BST_CHECKED) + keybEnable|=(1<<((x<<1)+porttemp)); + else + keybEnable&=~(1<<((x<<1)+porttemp)); + } + + EndDialog(hwndDlg,0); + break; + } + } + } + return 0; +} + +static BOOL CALLBACK KeyPPConCallB(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + int x; + char tempo[64]; + + switch(uMsg) { + case WM_USER+666: + if(inkeyloop) + { + SetDlgItemInt(hwndDlg,inkeyloop,lParam,0); + inkeyloop=0; + } + break; + case WM_INITDIALOG: + for(x=0;x<12;x++) + SetDlgItemInt(hwndDlg,500+x,powerpadsc[porttemp][x],0); + CheckDlgButton(hwndDlg,300+((powerpadside>>porttemp)&1),BST_CHECKED); + sprintf(tempo,"Virtual Power Pad %d",porttemp+1); + SetDlgItemText(hwndDlg,302,tempo); + break; + case WM_CLOSE: + case WM_QUIT: goto gornk; + case WM_COMMAND: + if(!(wParam>>16)) + { + wParam&=0xFFFF; + if(wParam>=500 && wParam<=511) + inkeyloop=wParam; + else switch(wParam) + { + case 1: + gornk: + for(x=0;x<12;x++) + powerpadsc[porttemp][x]=GetDlgItemInt(hwndDlg,500+x,0,0); + powerpadside&=~(1<>16)) + { + wParam&=0xFFFF; + if(wParam>=300 && wParam<=371) + inkeyloop=wParam; + else switch(wParam) + { + case 1: + gornk: + EndDialog(hwndDlg,0); + break; + } + } + } + return 0; +} + +static HHOOK hHook; +static LRESULT CALLBACK FilterFunc(int nCode, WORD wParam, DWORD lParam) +{ + MSG FAR *ptrMsg; + LPARAM tmpo; + + if(nCode>=0) + { + if(nCode==MSGF_DIALOGBOX) + { + ptrMsg=(MSG FAR *)lParam; + if(ptrMsg->message==WM_KEYDOWN || ptrMsg->message==WM_SYSKEYDOWN) + { + tmpo=((ptrMsg->lParam>>16)&0x7F)|((ptrMsg->lParam>>17)&0x80); + PostMessage(GetParent(ptrMsg->hwnd),WM_USER+666,0,tmpo); + if(inkeyloop) return 1; + } + } + } + return CallNextHookEx(hHook,nCode,wParam,lParam); +} + + +void ConfigKeyboardie(HWND hParent, int port) +{ + porttemp=port; + + hHook=SetWindowsHookEx(WH_MSGFILTER,(HOOKPROC)FilterFunc,fceu_hInstance,GetCurrentThreadId()); + DialogBox(fceu_hInstance,"KEYCONFIG",hParent,KeyConCallB); + UnhookWindowsHookEx(hHook); +} + +void ConfigKeyboardiePowerpad(HWND hParent, int port) +{ + porttemp=port; + + hHook=SetWindowsHookEx(WH_MSGFILTER,(HOOKPROC)FilterFunc,fceu_hInstance,GetCurrentThreadId()); + DialogBox(fceu_hInstance,"KEYPPCONFIG",hParent,KeyPPConCallB); + UnhookWindowsHookEx(hHook); +} + +void ConfigFKB(HWND hParent) +{ + hHook=SetWindowsHookEx(WH_MSGFILTER,(HOOKPROC)FilterFunc,fceu_hInstance,GetCurrentThreadId()); + DialogBox(fceu_hInstance,"FKBCONFIG",hParent,FKBConCallB); + UnhookWindowsHookEx(hHook); +} diff --git a/drivers/win/keyboard.h b/drivers/win/keyboard.h new file mode 100644 index 0000000..f118fc6 --- /dev/null +++ b/drivers/win/keyboard.h @@ -0,0 +1,22 @@ +void KeyboardClose(void); +int KeyboardInitialize(void); +void KeyboardUpdate(void); +uint32 KeyboardDodo(void); +uint32 UpdatePPadData(int w); +void UpdateFKB(void); + + +void ConfigFKB(HWND hParent); +void ConfigKeyboardie(HWND hParent, int port); +void ConfigKeyboardiePowerpad(HWND hParent, int port); + +extern int cidisabled; + +/* Config stuff: */ +extern int keyBMap[4][8]; +extern int keybEnable; +extern int powerpadside; +extern int powerpadsc[2][12]; + +extern int fkbmap[0x48]; +extern uint8 fkbkeys[0x48]; diff --git a/drivers/win/keyscan.h b/drivers/win/keyscan.h new file mode 100644 index 0000000..689d780 --- /dev/null +++ b/drivers/win/keyscan.h @@ -0,0 +1,125 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define SCAN_GRAVE 0x29 +#define SCAN_1 0x02 +#define SCAN_2 0x03 +#define SCAN_3 0x04 +#define SCAN_4 0x05 +#define SCAN_5 0x06 +#define SCAN_6 0x07 +#define SCAN_7 0x08 +#define SCAN_8 0x09 +#define SCAN_9 0x0A +#define SCAN_0 0x0B +#define SCAN_MINUS 0x0C +#define SCAN_EQUAL 0x0D +#define SCAN_BACKSLASH 0x2B +#define SCAN_BACKSPACE 0x0E +#define SCAN_TAB 0x0F +#define SCAN_Q 0x10 +#define SCAN_W 0x11 +#define SCAN_E 0x12 +#define SCAN_R 0x13 +#define SCAN_T 0x14 +#define SCAN_Y 0x15 +#define SCAN_U 0x16 +#define SCAN_I 0x17 +#define SCAN_O 0x18 +#define SCAN_P 0x19 +#define SCAN_BRACKET_LEFT 0x1A +#define SCAN_BRACKET_RIGHT 0x1B +#define SCAN_LOWBACKSLASH 0x2B +#define SCAN_CAPSLOCK 0x3A +#define SCAN_A 0x1E +#define SCAN_S 0x1F +#define SCAN_D 0x20 +#define SCAN_F 0x21 +#define SCAN_G 0x22 +#define SCAN_H 0x23 +#define SCAN_J 0x24 +#define SCAN_K 0x25 +#define SCAN_L 0x26 +#define SCAN_SEMICOLON 0x27 +#define SCAN_APOSTROPHE 0x28 +#define SCAN_ENTER 0x1C +#define SCAN_LEFTSHIFT 0x2A +#define SCAN_Z 0x2C +#define SCAN_X 0x2D +#define SCAN_C 0x2E +#define SCAN_V 0x2F +#define SCAN_B 0x30 +#define SCAN_N 0x31 +#define SCAN_M 0x32 +#define SCAN_COMMA 0x33 +#define SCAN_PERIOD 0x34 +#define SCAN_SLASH 0x35 +#define SCAN_RIGHTSHIFT 0x36 +#define SCAN_LEFTCONTROL 0x1D +#define SCAN_LEFTALT 0x38 +#define SCAN_SPACE 0x39 + +#define SCAN_RIGHTALT (0x38|0x80) +#define SCAN_RIGHTCONTROL (0x1D|0x80) +#define SCAN_BL_INSERT (0x52|0x80) +#define SCAN_BL_DELETE (0x53|0x80) +#define SCAN_BL_CURSORLEFT (0x4B|0x80) +#define SCAN_BL_HOME (0x47|0x80) +#define SCAN_BL_END (0x4F|0x80) +#define SCAN_BL_CURSORUP (0x48|0x80) +#define SCAN_BL_CURSORDOWN (0x50|0x80) +#define SCAN_BL_PAGEUP (0x49|0x80) +#define SCAN_BL_PAGEDOWN (0x51|0x80) +#define SCAN_BL_CURSORRIGHT (0x4D|0x80) + +#define SCAN_SCROLLLOCK 0x46 +/* Keys in the key pad area. */ +#define SCAN_NUMLOCK 0x45 +#define SCAN_HOME 0x47 +#define SCAN_CURSORLEFT 0x4B +#define SCAN_END 0x4F +#define SCAN_SLASH 0x35 +#define SCAN_CURSORUP 0x48 +#define SCAN_CENTER 0x4C +#define SCAN_CURSORDOWN 0x50 +#define SCAN_INSERT 0x52 +#define SCAN_ASTERISK 0x37 +#define SCAN_PAGEUP 0x49 +#define SCAN_CURSORRIGHT 0x4D +#define SCAN_PAGEDOWN 0x51 +#define SCAN_KP_DELETE 0x53 +#define SCAN_KP_MINUS 0x4A +#define SCAN_KP_PLUS 0x4E +#define SCAN_KP_ENTER 0x1C + +#define SCAN_ESCAPE 0x01 +#define SCAN_F1 0x3B +#define SCAN_F2 0x3C +#define SCAN_F3 0x3D +#define SCAN_F4 0x3E +#define SCAN_F5 0x3F +#define SCAN_F6 0x40 +#define SCAN_F7 0x41 +#define SCAN_F8 0x42 +#define SCAN_F9 0x43 +#define SCAN_F10 0x44 +#define SCAN_F11 0x57 +#define SCAN_F12 0x58 + diff --git a/drivers/win/main.c b/drivers/win/main.c new file mode 100644 index 0000000..2faf4eb --- /dev/null +++ b/drivers/win/main.c @@ -0,0 +1,338 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "common.h" + +#include +#include +#include +#include +#include +#include +#include +#include // For directories configuration dialog. + +#include "input.h" +#include "joystick.h" +#include "keyboard.h" +#include "cheat.h" + + +#define EO_BGRUN 1 + +#define EO_CPALETTE 4 +#define EO_NOSPRLIM 8 +#define EO_BSAV 16 +#define EO_FSAFTERLOAD 32 +#define EO_FOAFTERSTART 64 +#define EO_NOTHROTTLE 128 +#define EO_CLIPSIDES 256 +#define EO_SNAPNAME 512 + +/* EO_USERFORCE is something I've been playing with. + The code for it isn't finished. +*/ +#define EO_USERFORCE 1024 + + +#define VNSCLIP ((eoptions&EO_CLIPSIDES)?8:0) +#define VNSWID ((eoptions&EO_CLIPSIDES)?240:256) + +static int eoptions=EO_BGRUN; + +void ResetVideo(void); +void ShowCursorAbs(int w); +void HideFWindow(int h); +int SetMainWindowStuff(void); +int GetClientAbsRect(LPRECT lpRect); +void UpdateFCEUWindow(void); + + +HWND hAppWnd=0; +HINSTANCE fceu_hInstance; + +HRESULT ddrval; + +FCEUGI *GI=0; + +// cheats, misc, nonvol, states, snaps, base +static char *DOvers[6]={0,0,0,0,0,0}; +static char *defaultds[5]={"cheats","gameinfo","sav","fcs","snaps"}; + +static char TempArray[2048]; +static char BaseDirectory[2048]; + +void SetDirs(void) +{ + int x; + static int jlist[5]= + {FCEUIOD_CHEATS,FCEUIOD_MISC,FCEUIOD_NV,FCEUIOD_STATE,FCEUIOD_SNAPS}; + + for(x=0;x<5;x++) + FCEUI_SetDirOverride(jlist[x], DOvers[x]); + if(DOvers[5]) + FCEUI_SetBaseDirectory(DOvers[5]); + else + FCEUI_SetBaseDirectory(BaseDirectory); + FCEUI_SaveExtraDataUnderBase(eoptions&EO_BSAV); +} +/* Remove empty, unused directories. */ +void RemoveDirs(void) +{ + int x; + + for(x=0;x<5;x++) + if(!DOvers[x]) + { + sprintf(TempArray,"%s\\%s",DOvers[5]?DOvers[5]:BaseDirectory,defaultds[x]); + RemoveDirectory(TempArray); + } +} + +void CreateDirs(void) +{ + int x; + + for(x=0;x<5;x++) + if(!DOvers[x]) + { + sprintf(TempArray,"%s\\%s",DOvers[5]?DOvers[5]:BaseDirectory,defaultds[x]); + CreateDirectory(TempArray,0); + } +} + +static char *gfsdir=0; +void GetBaseDirectory(void) +{ + int x; + BaseDirectory[0]=0; + GetModuleFileName(0,(LPTSTR)BaseDirectory,2047); + + for(x=strlen(BaseDirectory);x>=0;x--) + { + if(BaseDirectory[x]=='\\' || BaseDirectory[x]=='/') + {BaseDirectory[x]=0;break;} + } +} + +static int exiting=0; +int BlockingCheck(void) +{ + MSG msg; + + while( PeekMessage( &msg, 0, 0, 0, PM_NOREMOVE ) ) { + if( GetMessage( &msg, 0, 0, 0)>0 ) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + if(exiting) return(0); + + return(1); +} + +int NoWaiting=0; +static int fullscreen=0; +static int soundflush=0; +static int soundsleep=0; +static int genie=0; +static int palyo=0; +static int windowedfailed; +static int winsizemul=1; +static int winwidth,winheight; + +static volatile int nofocus=0; +static volatile int userpause=0; + +#define SO_FORCE8BIT 1 +#define SO_SECONDARY 2 +#define SO_GFOCUS 4 +#define SO_D16VOL 8 + +static int soundrate=44100; +static int soundbuftime=46; +static int soundbufsize; +static int soundoptions=0; +static int soundvolume=100; + +static unsigned int srendline,erendline; +static unsigned int srendlinen=8; +static unsigned int erendlinen=239; +static unsigned int srendlinep=0; +static unsigned int erendlinep=239; + + +static unsigned int totallines; + +static void FixFL(void) +{ + FCEUI_GetCurrentVidSystem(&srendline,&erendline); + totallines=erendline-srendline+1; +} + +static void UpdateRendBounds(void) +{ + FCEUI_SetRenderedLines(srendlinen,erendlinen,srendlinep,erendlinep); + FixFL(); +} + +static uint8 cpalette[192]; +static int vmod=1; +static int soundo=1; +static int ntsccol=0,ntsctint,ntschue; + +void FCEUD_PrintError(char *s) +{ + if(fullscreen) ShowCursorAbs(1); + MessageBox(0,s,"FCE Ultra Error",MB_ICONERROR|MB_OK|MB_SETFOREGROUND|MB_TOPMOST); + if(fullscreen)ShowCursorAbs(0); +} + +void ShowAboutBox(void) +{ + sprintf(TempArray,"FCE Ultra "VERSION_STRING"\n\nhttp://fceultra.sourceforge.net\n\n"__TIME__"\n"__DATE__"\n""gcc "__VERSION__); + MessageBox(hAppWnd,TempArray,"About FCE Ultra",MB_OK); +} + +void DoFCEUExit(void) +{ + exiting=1; + if(GI) + { + FCEUI_CloseGame(); + GI=0; + } +} + +static int changerecursive=0; + +#include "throttle.c" + +#include "netplay.c" +#include "sound.c" +#include "video.c" +#include "window.c" +#include "config.c" + + +int DriverInitialize(void) +{ + if(!InitializeDDraw()) + return(0); + + if(soundo) + soundo=InitSound(); + + SetVideoMode(fullscreen); + InitInputStuff(); /* Initialize DInput interfaces. */ + CreateInputStuff(); /* Create and set virtual NES/FC devices. */ + return 1; +} + +static void DriverKill(void) +{ + sprintf(TempArray,"%s/fceu.cfg",BaseDirectory); + SaveConfig(TempArray); + DestroyInput(); + ResetVideo(); + if(soundo) TrashSound(); + CloseWave(); + ByebyeWindow(); +} + + +int main(int argc,char *argv[]) +{ + char *t; + + if(!FCEUI_Initialize()) + goto doexito; + + fceu_hInstance=GetModuleHandle(0); + + GetBaseDirectory(); + + sprintf(TempArray,"%s\\fceu.cfg",BaseDirectory); + LoadConfig(TempArray); + FixGIGO(); /* Since a game doesn't have to be + loaded before the GUI can be used, make + sure the temporary input type variables + are set. + */ + + + CreateDirs(); + SetDirs(); + + DoVideoConfigFix(); + DoMiscConfigFix(); + + if(eoptions&EO_CPALETTE) + FCEUI_SetPaletteArray(cpalette); + + t=0; + if(argc>1) + t=argv[1]; + if(!t) fullscreen=0; + + CreateMainWindow(); + + if(!InitDInput()) + goto doexito; + + if(!DriverInitialize()) + goto doexito; + + InitSpeedThrottle(); + UpdateMenu(); + + if(t) + ALoad(t); + else if(eoptions&EO_FOAFTERSTART) + LoadNewGamey(hAppWnd); + + doloopy: + UpdateFCEUWindow(); + if(GI) + { + FCEUI_Emulate(); + RedrawWindow(hAppWnd,0,0,RDW_ERASE|RDW_INVALIDATE); + StopSound(); + } + Sleep(50); + if(!exiting) + goto doloopy; + + doexito: + DriverKill(); + return(0); +} + +void FCEUD_Update(uint8 *XBuf, int32 *Buffer, int Count) +{ + FCEUD_BlitScreen(XBuf); + if(Count) + FCEUD_WriteSoundData(Buffer,Count); + FCEUD_UpdateInput(); +} + diff --git a/drivers/win/netplay.c b/drivers/win/netplay.c new file mode 100644 index 0000000..6c7ac45 --- /dev/null +++ b/drivers/win/netplay.c @@ -0,0 +1,415 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +static char netplayhost[256]={0}; +static int netplayport=0xFCE; + +static int netplayon=0; +static int netplaytype=0; + +static HWND hwndns=0; + +static SOCKET Socket=INVALID_SOCKET; +static int wsainit=0; + +static volatile int abortnetplay=0; +static volatile int concommand=0; + +static void WSE(char *ahh) +{ + char tmp[256]; + sprintf(tmp,"Winsock: %s",ahh); + FCEUD_PrintError(tmp); +} + +int SetBlockingSock(SOCKET Socko) +{ + unsigned long t; + t=1; + if(ioctlsocket(Socko,FIONBIO,&t)) + { + WSE("Error setting socket to non-blocking mode!\n"); + return 0; + } + return 1; +} + +BOOL CALLBACK BoogaDooga(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) { + case WM_USER+1: + if(WSAGETASYNCERROR(lParam)) + concommand=1; + else + concommand=2; + break; + case WM_USER: + if(WSAGETSELECTEVENT(lParam)==FD_CONNECT) + { + if(WSAGETSELECTERROR(lParam)) + concommand=1; + else + concommand=2; + } + break; + case WM_INITDIALOG: + if(!netplaytype) SetDlgItemText(hwndDlg,100,(LPTSTR)"Waiting for a connection..."); + else SetDlgItemText(hwndDlg,100,(LPTSTR)"Attempting to establish a connection..."); + break; + case WM_CLOSE: + case WM_QUIT: goto gornk; + case WM_COMMAND: + if(!(wParam>>16)) + switch(wParam&0xFFFF) + { + case 1: + gornk: + abortnetplay=1; + EndDialog(hwndDlg,0); + break; + } + } + return 0; + +} + +static void CloseNSDialog(void) +{ + if(hwndns) + { + SendMessage(hwndns,WM_COMMAND,1,0); + hwndns=0; + } +} + +void CreateStatusDialog(void) +{ + hwndns=CreateDialog(fceu_hInstance,"NETSTAT",hAppWnd,BoogaDooga); +} + +void FCEUD_NetworkClose(void) +{ + CloseNSDialog(); + if(Socket!=INVALID_SOCKET) + { + closesocket(Socket); + Socket=INVALID_SOCKET; + } + if(wsainit) + { + WSACleanup(); + wsainit=0; + } + /* Make sure blocking is returned to normal once network play is stopped. */ + NoWaiting&=~2; +} + +int FCEUD_NetworkConnect(void) +{ + WSADATA WSAData; + SOCKADDR_IN sockin; /* I want to play with fighting robots. */ + SOCKET TSocket; + + if(WSAStartup(MAKEWORD(1,1),&WSAData)) + { + FCEUD_PrintError("Error initializing Windows Sockets."); + return(0); + } + wsainit=1; + concommand=abortnetplay=0; + + if( (TSocket=socket(AF_INET,SOCK_STREAM,0))==INVALID_SOCKET) + { + WSE("Error creating socket."); + FCEUD_NetworkClose(); + return(0); + } + + memset(&sockin,0,sizeof(sockin)); + sockin.sin_family=AF_INET; + sockin.sin_port=htons(netplayport); + + if(!netplaytype) /* Act as a server. */ + { + + int sockin_len; + sockin.sin_addr.s_addr=INADDR_ANY; + + sockin_len=sizeof(sockin); + + if(bind(TSocket,(struct sockaddr *)&sockin,sizeof(sockin))==SOCKET_ERROR) + { + WSE("Error binding to socket."); + closesocket(TSocket); + FCEUD_NetworkClose(); + return(0); + } + + if(listen(TSocket,1)==SOCKET_ERROR) + { + WSE("Error listening on socket."); + closesocket(TSocket); + FCEUD_NetworkClose(); + return(0); + } + + CreateStatusDialog(); + if(!SetBlockingSock(TSocket)) + { + closesocket(TSocket); + FCEUD_NetworkClose(); + return(0); + } + + while( (Socket=accept(TSocket,(struct sockaddr *) &sockin,(int *)&sockin_len)) == + INVALID_SOCKET) + { + if(abortnetplay || WSAGetLastError()!=WSAEWOULDBLOCK) + { + if(!abortnetplay) + WSE("Error accepting connection."); + closesocket(TSocket); + FCEUD_NetworkClose(); + return(0); + } + else + BlockingCheck(); + } + + if(!SetBlockingSock(Socket)) + { + FCEUD_NetworkClose(); + return(0); + } + + } + else /* We're a client... */ + { + char phostentb[MAXGETHOSTSTRUCT]; + unsigned long hadr; + + hadr=inet_addr(netplayhost); + + CreateStatusDialog(); + + if(hadr!=INADDR_NONE) + sockin.sin_addr.s_addr=hadr; + else + { + if(!WSAAsyncGetHostByName(hwndns,WM_USER+1,(const char *)netplayhost,phostentb,MAXGETHOSTSTRUCT)) + { + ghosterr: + WSE("Error getting host network information."); + closesocket(TSocket); + FCEUD_NetworkClose(); + return(0); + } + while(concommand!=2) + { + BlockingCheck(); + if(concommand==1 || abortnetplay) + goto ghosterr; + } + memcpy((char *)&sockin.sin_addr,((PHOSTENT)phostentb)->h_addr,((PHOSTENT)phostentb)->h_length); + } + concommand=0; + + if(WSAAsyncSelect(TSocket,hwndns,WM_USER,FD_CONNECT|FD_CLOSE)==SOCKET_ERROR) + { + eventnoterr: + WSE("Error setting event notification on socket."); + closesocket(TSocket); + FCEUD_NetworkClose(); + return(0); + } + + if(!SetBlockingSock(TSocket)) + { + closesocket(TSocket); + FCEUD_NetworkClose(); + return(0); + } + + if(connect(TSocket,(PSOCKADDR)&sockin,sizeof(sockin))==SOCKET_ERROR) + { + if(WSAGetLastError()!=WSAEWOULDBLOCK) + { + cerrav: + WSE("Error connecting to remote host."); + + cerra: + closesocket(TSocket); + FCEUD_NetworkClose(); + return(0); + } + + while(concommand!=2) + { + BlockingCheck(); + if(abortnetplay) goto cerra; + if(concommand==1) goto cerrav; + } + } + if(WSAAsyncSelect(TSocket,hAppWnd,WM_USER,0)==SOCKET_ERROR) + goto eventnoterr; + Socket=TSocket; + + } + CloseNSDialog(); + return(1); +} + + +int FCEUD_NetworkSendData(uint8 *data, uint32 len) +{ + int erc; + + while((erc=send(Socket,data,len,0))) + { + if(erc!=SOCKET_ERROR) + { + len-=erc; + data+=erc; + if(!len) + return(1); /* All data sent. */ + + if(!BlockingCheck()) return(0); + } + else + { + if(WSAGetLastError()==WSAEWOULDBLOCK) + { + if(!BlockingCheck()) return(0); + continue; + } + return(0); + } + } + return(0); +} + +int FCEUD_NetworkRecvData(uint8 *data, uint32 len, int block) +{ + int erc; + + if(block) // TODO: Add code elsewhere to handle sound buffer underruns. + { + while((erc=recv(Socket,data,len,0))!=len) + { + if(!erc) + return(0); + if(WSAGetLastError()==WSAEWOULDBLOCK) + { + if(!BlockingCheck()) return(0); + continue; + } + return(0); + } + + { + char buf[24]; + if(recv(Socket,buf,24,MSG_PEEK)==SOCKET_ERROR) + { + if(WSAGetLastError()==WSAEWOULDBLOCK) + NoWaiting&=~2; + else + return(0); + } + else + NoWaiting|=2; /* We're the client and we're lagging behind. + disable blocking(particularly sound...) to *try* + to catch up. + */ + } + + return 1; + } + + else /* We're the server. See if there's any new data + from player 2. If not, then return(-1). + */ + { + erc=recv(Socket,data,len,0); + if(!erc) + return(0); + if(erc==SOCKET_ERROR) + { + if(WSAGetLastError()==WSAEWOULDBLOCK) + return(-1); + return(0); // Some other(bad) error occurred. + } + return(1); + } // end else to if(block) +} + +BOOL CALLBACK NetConCallB(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) { + case WM_INITDIALOG: + + CheckDlgButton(hwndDlg,100,netplayon?BST_CHECKED:BST_UNCHECKED); + CheckRadioButton(hwndDlg,101,102,101+netplaytype); + SetDlgItemInt(hwndDlg,107,netplayport,0); + + if(netplayhost[0]) + SetDlgItemText(hwndDlg,104,netplayhost); + break; + case WM_CLOSE: + case WM_QUIT: goto gornk; + case WM_COMMAND: + if(!(wParam>>16)) + switch(wParam&0xFFFF) + { + case 1: + gornk: + + netplayport=GetDlgItemInt(hwndDlg,107,0,0); + + if(IsDlgButtonChecked(hwndDlg,100)==BST_CHECKED) + netplayon=1; + else + netplayon=0; + + if(IsDlgButtonChecked(hwndDlg,101)==BST_CHECKED) + netplaytype=0; + else + netplaytype=1; + + GetDlgItemText(hwndDlg,104,netplayhost,255); + netplayhost[255]=0; + + EndDialog(hwndDlg,0); + break; + } + } + return 0; +} + + +static void ConfigNetplay(void) +{ + DialogBox(fceu_hInstance,"NETPLAYCONFIG",hAppWnd,NetConCallB); + + if(netplayon) + FCEUI_SetNetworkPlay(netplaytype+1); + else + FCEUI_SetNetworkPlay(0); +} + diff --git a/drivers/win/res.res b/drivers/win/res.res new file mode 100644 index 0000000..e7e533d Binary files /dev/null and b/drivers/win/res.res differ diff --git a/drivers/win/sound.c b/drivers/win/sound.c new file mode 100644 index 0000000..d9c29c5 --- /dev/null +++ b/drivers/win/sound.c @@ -0,0 +1,457 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +FILE *soundlog=0; +void WriteWaveData(int32 *Buffer, int Count); +DWORD WINAPI DSThread(LPVOID lpParam); +LPDIRECTSOUND ppDS=0; +LPDIRECTSOUNDBUFFER ppbuf=0; +LPDIRECTSOUNDBUFFER ppbufsec=0; +LPDIRECTSOUNDBUFFER ppbufw; + +DSBUFFERDESC DSBufferDesc; +WAVEFORMATEX wfa; +WAVEFORMATEX wf; + +static int DSBufferSize=0; +static int bittage; + +void TrashSound(void) +{ + FCEUI_Sound(0); + if(ppbufsec) + { + IDirectSoundBuffer_Stop(ppbufsec); + IDirectSoundBuffer_Release(ppbufsec); + ppbufsec=0; + } + if(ppbuf) + { + IDirectSoundBuffer_Stop(ppbuf); + IDirectSoundBuffer_Release(ppbuf); + ppbuf=0; + } + if(ppDS) + { + IDirectSound_Release(ppDS); + ppDS=0; + } +} + + + static VOID *feegle[2]; + static DWORD dook[2]; + static DWORD writepos=0,playpos=0,lplaypos=0; +void CheckDStatus(void) +{ + DWORD status; + status=0; + IDirectSoundBuffer_GetStatus(ppbufw, &status); + + if(status&DSBSTATUS_BUFFERLOST) + { + IDirectSoundBuffer_Restore(ppbufw); + } + + if(!(status&DSBSTATUS_PLAYING)) + { + lplaypos=0; + writepos=((soundbufsize)<>8))^128; + } + else + { + for(P=0;P=DSBufferSize) + if(playpos=(playpos+(soundbufsize<>=1; + if(stime>=5) + Sleep(stime); + k=1; + } + } + else if(soundsleep==2) + { + int stime; + stime=writepos-(playpos+(soundbufsize<>=1; + if(stime>=2) + Sleep(stime); + } + } + BlockingCheck(); + if(!soundo || NoWaiting) return; + goto ilicpo; + } + + if(netplaytype && netplayon) + { + if(writepos<=playpos+128) + writepos=playpos+(soundbufsize<>16)) + switch(wParam&0xFFFF) + { + case 1: + gornk: + soundoptions=0; + if(IsDlgButtonChecked(hwndDlg,122)==BST_CHECKED) + soundoptions|=SO_FORCE8BIT; + if(IsDlgButtonChecked(hwndDlg,123)==BST_CHECKED) + soundoptions|=SO_SECONDARY; + if(IsDlgButtonChecked(hwndDlg,124)==BST_CHECKED) + soundoptions|=SO_GFOCUS; + if(IsDlgButtonChecked(hwndDlg,126)==BST_CHECKED) + soundo=1; + else + soundo=0; + x=GetDlgItemInt(hwndDlg,200,0,0); + if(x<8192 || x>65535) + { + FCEUD_PrintError("Sample rate is out of range(8192-65535)."); + break; + } + else + soundrate=x; + + soundvolume=200-SendDlgItemMessage(hwndDlg,500,TBM_GETPOS,0,0); + FCEUI_SetSoundVolume(soundvolume); + soundsleep=SendDlgItemMessage(hwndDlg,129,CB_GETCURSEL,0,(LPARAM)(LPSTR)0); + EndDialog(hwndDlg,0); + break; + } + } + return 0; +} + + +void ConfigSound(void) +{ + int backo=soundo,sr=soundrate; + int so=soundoptions; + + DialogBox(fceu_hInstance,"SOUNDCONFIG",hAppWnd,SoundConCallB); + + if(((backo?1:0)!=(soundo?1:0))) + { + if(!soundo) + TrashSound(); + else + soundo=InitSound(); + } + else if(( soundoptions!=so || (sr!=soundrate)) && soundo) + { + TrashSound(); + soundo=InitSound(); + } + soundbufsize=(soundbuftime*soundrate/1000); +} + + +void StopSound(void) +{ + if(soundo) + IDirectSoundBuffer_Stop(ppbufw); +} + +#include "wave.c" diff --git a/drivers/win/throttle.c b/drivers/win/throttle.c new file mode 100644 index 0000000..1ccf271 --- /dev/null +++ b/drivers/win/throttle.c @@ -0,0 +1,73 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +static uint64 tmethod,tfreq; +static uint64 desiredfps; + +static void RefreshThrottleFPS(void) +{ + desiredfps=FCEUI_GetDesiredFPS()>>8; +} + +static uint64 GetCurTime(void) +{ + if(tmethod) + { + uint64 tmp; + + /* Practically, LARGE_INTEGER and uint64 differ only by signness and name. */ + QueryPerformanceCounter((LARGE_INTEGER*)&tmp); + + return(tmp); + } + else + return((uint64)GetTickCount()); + +} + +static void InitSpeedThrottle(void) +{ + tmethod=0; + if(QueryPerformanceFrequency((LARGE_INTEGER*)&tfreq)) + { + tmethod=1; + } + else + tfreq=1000; + tfreq<<=16; /* Adjustment for fps returned from FCEUI_GetDesiredFPS(). */ +} + + +static void SpeedThrottle(void) +{ + static uint64 ttime,ltime; + + waiter: + + ttime=GetCurTime(); + + + if( (ttime-ltime) < (tfreq/desiredfps) ) + goto waiter; + if( (ttime-ltime) >= (tfreq*4/desiredfps)) + ltime=ttime; + else + ltime+=tfreq/desiredfps; +} diff --git a/drivers/win/video.c b/drivers/win/video.c new file mode 100644 index 0000000..a0438a0 --- /dev/null +++ b/drivers/win/video.c @@ -0,0 +1,1174 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +static int RecalcCustom(void); + +#define VF_DDSTRETCHED 1 + +#define VEF_LOSTSURFACE 1 +#define VEF____INTERNAL 2 + +#define VMDF_DXBLT 1 +#define VMDF_STRFS 2 + +typedef struct { + int x; + int y; + int bpp; + int flags; + int xscale; + int yscale; + RECT srect; + RECT drect; +} vmdef; + +// left, top, right, bottom +static vmdef vmodes[11]={ + {320,240,8,0,1,1}, //0 + {320,240,8,0,1,1}, //1 + {512,384,8,0,1,1}, //2 + {640,480,8,0,1,1}, //3 + {640,480,8,0,1,1}, //4 + {640,480,8,0,1,1}, //5 + {640,480,8,VMDF_DXBLT,2,2}, //6 + {1024,768,8,VMDF_DXBLT,4,3}, //7 + {1280,1024,8,VMDF_DXBLT,5,4}, //8 + {1600,1200,8,VMDF_DXBLT,6,5}, //9 + {800,600,8,VMDF_DXBLT|VMDF_STRFS,0,0} //10 + }; +static DDCAPS caps; +static int mustrestore=0; +static DWORD CBM[3]; + +static int bpp; +static int vflags; +static int veflags; + +int fssync=0; +int winsync=0; + +static uint32 *palettetranslate=0; + +PALETTEENTRY color_palette[256]; +static int PaletteChanged=0; + +LPDIRECTDRAWCLIPPER lpClipper=0; +LPDIRECTDRAW lpDD=0; +LPDIRECTDRAW4 lpDD4=0; +LPDIRECTDRAWPALETTE lpddpal; + +DDSURFACEDESC2 ddsd; + +DDSURFACEDESC2 ddsdback; +LPDIRECTDRAWSURFACE4 lpDDSPrimary=0; +LPDIRECTDRAWSURFACE4 lpDDSDBack=0; +LPDIRECTDRAWSURFACE4 lpDDSBack=0; + +static void ShowDDErr(char *s) +{ + char tempo[512]; + sprintf(tempo,"DirectDraw: %s",s); + FCEUD_PrintError(tempo); +} + +int RestoreDD(int w) +{ + if(w) + { + if(!lpDDSBack) return 0; + if(IDirectDrawSurface4_Restore(lpDDSBack)!=DD_OK) return 0; + } + else + { + if(!lpDDSPrimary) return 0; + if(IDirectDrawSurface4_Restore(lpDDSPrimary)!=DD_OK) return 0; + } + veflags|=1; + return 1; +} + +void FCEUD_SetPalette(unsigned char index, unsigned char r, unsigned char g, unsigned char b) +{ + color_palette[index].peRed=r; + color_palette[index].peGreen=g; + color_palette[index].peBlue=b; + PaletteChanged=1; +} + +void FCEUD_GetPalette(unsigned char i, unsigned char *r, unsigned char *g, unsigned char *b) +{ + *r=color_palette[i].peRed; + *g=color_palette[i].peGreen; + *b=color_palette[i].peBlue; +} + +int InitializeDDraw(void) +{ + ddrval = DirectDrawCreate(NULL, &lpDD, NULL); + if (ddrval != DD_OK) + { + ShowDDErr("Error creating DirectDraw object."); + return 0; + } + + ddrval = IDirectDraw_QueryInterface(lpDD,&IID_IDirectDraw4,(LPVOID *)&lpDD4); + IDirectDraw_Release(lpDD); + + if (ddrval != DD_OK) + { + ShowDDErr("Error querying interface."); + return 0; + } + + caps.dwSize=sizeof(caps); + if(IDirectDraw4_GetCaps(lpDD4,&caps,0)!=DD_OK) + { + ShowDDErr("Error getting capabilities."); + return 0; + } + return 1; +} + +static int GetBPP(void) +{ + DDPIXELFORMAT ddpix; + + memset(&ddpix,0,sizeof(ddpix)); + ddpix.dwSize=sizeof(ddpix); + + ddrval=IDirectDrawSurface4_GetPixelFormat(lpDDSPrimary,&ddpix); + if (ddrval != DD_OK) + { + ShowDDErr("Error getting primary surface pixel format."); + return 0; + } + + if(ddpix.dwFlags&DDPF_RGB) + { + bpp=ddpix.DUMMYUNIONNAMEN(1).dwRGBBitCount; + CBM[0]=ddpix.DUMMYUNIONNAMEN(2).dwRBitMask; + CBM[1]=ddpix.DUMMYUNIONNAMEN(3).dwGBitMask; + CBM[2]=ddpix.DUMMYUNIONNAMEN(4).dwBBitMask; + } + else + { + ShowDDErr("RGB data not valid."); + return 0; + } + if(bpp==15) bpp=16; + + return 1; +} + +static int InitBPPStuff(void) +{ + if(bpp==16) + palettetranslate=malloc(65536*4); + else if(bpp>=24) + palettetranslate=malloc(256*4); + else if(bpp==8) + { + ddrval=IDirectDraw4_CreatePalette( lpDD4, DDPCAPS_8BIT|DDPCAPS_ALLOW256|DDPCAPS_INITIALIZE,color_palette,&lpddpal,NULL); + if (ddrval != DD_OK) + { + ShowDDErr("Error creating palette object."); + return 0; + } + ddrval=IDirectDrawSurface4_SetPalette(lpDDSPrimary, lpddpal); + if (ddrval != DD_OK) + { + ShowDDErr("Error setting palette object."); + return 0; + } + } + return 1; +} + +int SetVideoMode(int fs) +{ + if(!lpDD4) // DirectDraw not initialized + return(1); + + if(fs) + if(!vmod) + if(!RecalcCustom()) + return(0); + + vflags=0; + veflags=1; + PaletteChanged=1; + + ResetVideo(); + + if(!fs) + { + ShowCursorAbs(1); + windowedfailed=1; + HideFWindow(0); + + ddrval = IDirectDraw4_SetCooperativeLevel ( lpDD4, hAppWnd, DDSCL_NORMAL); + if (ddrval != DD_OK) + { + ShowDDErr("Error setting cooperative level."); + return 1; + } + + /* Beginning */ + memset(&ddsd,0,sizeof(ddsd)); + ddsd.dwSize = sizeof(ddsd); + ddsd.dwFlags = DDSD_CAPS; + ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + + ddrval = IDirectDraw4_CreateSurface ( lpDD4, &ddsd, &lpDDSPrimary,(IUnknown FAR*)NULL); + if (ddrval != DD_OK) + { + ShowDDErr("Error creating primary surface."); + return 1; + } + + memset(&ddsdback,0,sizeof(ddsdback)); + ddsdback.dwSize=sizeof(ddsdback); + ddsdback.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH; + ddsdback.ddsCaps.dwCaps= DDSCAPS_OFFSCREENPLAIN; + + ddsdback.dwWidth=256; + ddsdback.dwHeight=240; + + /* If no blit hardware is present, make sure buffer is created + in system memory. + */ + if(!(caps.dwCaps&DDCAPS_BLT)) + ddsdback.ddsCaps.dwCaps|=DDSCAPS_SYSTEMMEMORY; + + ddrval = IDirectDraw4_CreateSurface ( lpDD4, &ddsdback, &lpDDSBack, (IUnknown FAR*)NULL); + if (ddrval != DD_OK) + { + ShowDDErr("Error creating secondary surface."); + return 0; + } + + if(!GetBPP()) + return 0; + + if(bpp!=16 && bpp!=24 && bpp!=32) + { + ShowDDErr("Current bit depth not supported!"); + return 0; + } + + if(!InitBPPStuff()) + return 0; + + ddrval=IDirectDraw4_CreateClipper(lpDD4,0,&lpClipper,0); + if (ddrval != DD_OK) + { + ShowDDErr("Error creating clipper."); + return 0; + } + + ddrval=IDirectDrawClipper_SetHWnd(lpClipper,0,hAppWnd); + if (ddrval != DD_OK) + { + ShowDDErr("Error setting clipper window."); + return 0; + } + ddrval=IDirectDrawSurface4_SetClipper(lpDDSPrimary,lpClipper); + if (ddrval != DD_OK) + { + ShowDDErr("Error attaching clipper to primary surface."); + return 0; + } + + windowedfailed=0; + SetMainWindowStuff(); + } + else + { + HideFWindow(1); + + ddrval = IDirectDraw4_SetCooperativeLevel ( lpDD4, hAppWnd,DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | DDSCL_ALLOWREBOOT); + if (ddrval != DD_OK) + { + ShowDDErr("Error setting cooperative level."); + return 0; + } + + ddrval = IDirectDraw4_SetDisplayMode(lpDD4, vmodes[vmod].x, vmodes[vmod].y,vmodes[vmod].bpp,0,0); + if (ddrval != DD_OK) + { + ShowDDErr("Error setting display mode."); + return 0; + } + if(vmodes[vmod].flags&VMDF_DXBLT) + { + memset(&ddsdback,0,sizeof(ddsdback)); + ddsdback.dwSize=sizeof(ddsdback); + ddsdback.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH; + ddsdback.ddsCaps.dwCaps= DDSCAPS_OFFSCREENPLAIN; + + ddsdback.dwWidth=256; //vmodes[vmod].srect.right; + ddsdback.dwHeight=240; //vmodes[vmod].srect.bottom; + + if(!(caps.dwCaps&DDCAPS_BLT)) + ddsdback.ddsCaps.dwCaps|=DDSCAPS_SYSTEMMEMORY; + + ddrval = IDirectDraw4_CreateSurface ( lpDD4, &ddsdback, &lpDDSBack, (IUnknown FAR*)NULL); + if(ddrval!=DD_OK) + { + ShowDDErr("Error creating secondary surface."); + return 0; + } + } + + // create foreground surface + + memset(&ddsd,0,sizeof(ddsd)); + ddsd.dwSize = sizeof(ddsd); + + ddsd.dwFlags = DDSD_CAPS; + ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + + if(fssync==2) // Double buffering. + { + ddsd.dwFlags |= DDSD_BACKBUFFERCOUNT; + ddsd.dwBackBufferCount = 1; + ddsd.ddsCaps.dwCaps |= DDSCAPS_COMPLEX | DDSCAPS_FLIP; + } + + ddrval = IDirectDraw4_CreateSurface ( lpDD4, &ddsd, &lpDDSPrimary,(IUnknown FAR*)NULL); + if (ddrval != DD_OK) + { + ShowDDErr("Error creating primary surface."); + return 0; + } + + if(fssync==2) + { + DDSCAPS2 tmp; + + memset(&tmp,0,sizeof(tmp)); + tmp.dwCaps=DDSCAPS_BACKBUFFER; + + if(IDirectDrawSurface4_GetAttachedSurface(lpDDSPrimary,&tmp,&lpDDSDBack)!=DD_OK) + { + ShowDDErr("Error getting attached surface."); + return 0; + } + } + + if(!GetBPP()) + return 0; + if(!InitBPPStuff()) + return 0; + + mustrestore=1; + ShowCursorAbs(0); + } + + InputScreenChanged(fs); + fullscreen=fs; + return 1; +} + +static void BlitScreenWindow(uint8 *XBuf); +static void BlitScreenFull(uint8 *XBuf); + +void FCEUD_BlitScreen(uint8 *XBuf) +{ + doagain: + + UpdateFCEUWindow(); + + if(!(eoptions&EO_BGRUN)) + while(nofocus) + { + Sleep(50); + BlockingCheck(); + } + + + /* This complex statement deserves some explanation. + Make sure this special speed throttling hasn't been disabled by the user + first. Second, we don't want to throttle the speed if the fast-forward + button is pressed down(or during certain network play conditions). + + Now, if we're at this point, we'll throttle speed if sound is disabled. + Otherwise, it gets a bit more complicated. We'll throttle speed if focus + to FCE Ultra has been lost and we're writing to the primary sound buffer + because our sound code won't block. Blocking does seem to work when + writing to a secondary buffer, so we won't throttle when a secondary + buffer is used. + */ + + if(!(eoptions&EO_NOTHROTTLE)) + if(!NoWaiting) + if(!soundo || (soundo && nofocus && !(soundoptions&SO_SECONDARY)) ) + SpeedThrottle(); + + if(fullscreen) + { + if(fssync==1 && !NoWaiting) + IDirectDraw4_WaitForVerticalBlank(lpDD4,DDWAITVB_BLOCKBEGIN,0); + + BlitScreenFull(XBuf); + } + else + { + if(winsync && !NoWaiting) + IDirectDraw4_WaitForVerticalBlank(lpDD4,DDWAITVB_BLOCKBEGIN,0); + + if(!windowedfailed) + BlitScreenWindow(XBuf); + } + if(userpause) + { + StopSound(); + Sleep(50); + BlockingCheck(); + goto doagain; + } +} + +static INLINE void BlitVidHi(uint8 *src, uint8 *dest, /*int xr,*/ int yr, int pitch) +{ + int x,y; + int pinc; + + if(!(eoptions&EO_CLIPSIDES)) + switch(bpp) + { + case 32: + + pinc=pitch-(256<<2); + for(y=yr;y;y--) + { + for(x=256;x;x--) + { + *(uint32 *)dest=palettetranslate[(uint32)*src]; + dest+=4; + src++; + } + dest+=pinc; + src+=16; + } + break; + + case 24: + pinc=pitch-(256*3); + for(y=yr;y;y--) + { + for(x=256;x;x--) + { + uint32 tmp; + tmp=palettetranslate[(uint32)*src]; + *(uint16*)dest=(uint16)tmp; + *&dest[2]=(uint8)(tmp>>16); + dest+=3; + src++; + } + dest+=pinc; + src+=16; + } + break; + + case 16: + pinc=pitch-(256<<1); + for(y=yr;y;y--) + { + for(x=256>>1;x;x--) + { + *(unsigned long *)dest=palettetranslate[*(unsigned short *)src]; + dest+=4; + src+=2; + } + dest+=pinc; + src+=16; + } + break; + } + else + switch(bpp) + { + case 32: + + pinc=pitch-(240<<2); + for(y=yr;y;y--) + { + for(x=240;x;x--) + { + *(uint32 *)dest=palettetranslate[(uint32)*src]; + dest+=4; + src++; + } + dest+=pinc; + src+=32; + } + break; + + case 24: + pinc=pitch-(240*3); + for(y=yr;y;y--) + { + for(x=240;x;x--) + { + uint32 tmp; + tmp=palettetranslate[(uint32)*src]; + *(uint16*)dest=(uint16)tmp; + *&dest[2]=(uint8)(tmp>>16); + dest+=3; + src++; + } + dest+=pinc; + src+=32; + } + break; + case 16: + pinc=pitch-(240<<1); + for(y=yr;y;y--) + { + for(x=240>>1;x;x--) + { + *(unsigned long *)dest=palettetranslate[*(unsigned short *)src]; + dest+=4; + src+=2; + } + dest+=pinc; + src+=32; + } + break; + } +} + +static INLINE void FixPaletteHi(void) +{ + int x; + + switch(bpp) + { + case 16:{ + int cshiftr[3]; + int cshiftl[3]; + int a,x,z,y; + + cshiftl[0]=cshiftl[1]=cshiftl[2]=-1; + for(a=0;a<3;a++) + { + for(x=0,y=-1,z=0;x<16;x++) + { + if(CBM[a]&(1<>cshiftr[0])<>cshiftr[1])<>cshiftr[2])<>8].peRed>>cshiftr[0])<>8].peGreen>>cshiftr[1])<>8].peBlue>>cshiftr[2])<=16) + FixPaletteHi(); + else + for(x=0;x<=0x80;x+=0x80) + { + ddrval=IDirectDrawPalette_SetEntries(lpddpal,0,0x80^x,128,&color_palette[x]); + if(ddrval!=DD_OK) + { + if(ddrval==DDERR_SURFACELOST) RestoreDD(0); + return; + } + } + PaletteChanged=0; + } + + if(vmodes[vmod].flags&VMDF_DXBLT) + { + ddrval=IDirectDrawSurface4_Lock(lpDDSBack,NULL,&ddsdback, 0, NULL); + if(ddrval!=DD_OK) + { + if(ddrval==DDERR_SURFACELOST) RestoreDD(1); + return; + } + ScreenLoc=ddsdback.lpSurface; + pitch=ddsdback.DUMMYUNIONNAMEN(1).lPitch; + + srect.top=0; + srect.left=0; + srect.right=VNSWID; + srect.bottom=totallines; + if(vmodes[vmod].flags&VMDF_STRFS) + { + drect.top=0; + drect.left=0; + drect.right=vmodes[vmod].x; + drect.bottom=vmodes[vmod].y; + } + else + { + drect.top=(vmodes[vmod].y-(totallines*vmodes[vmod].yscale))>>1; + drect.bottom=drect.top+(totallines*vmodes[vmod].yscale); + drect.left=(vmodes[vmod].x-VNSWID*vmodes[vmod].xscale)>>1; + drect.right=drect.left+VNSWID*vmodes[vmod].xscale; + } + } + else + { + ddrval=IDirectDrawSurface4_Lock(lpDDSVPrimary,NULL,&ddsd, 0, NULL); + if(ddrval!=DD_OK) + { + if(ddrval==DDERR_SURFACELOST) RestoreDD(0); + return; + } + + ScreenLoc=ddsd.lpSurface; + pitch=ddsd.DUMMYUNIONNAMEN(1).lPitch; + } + + if(veflags&1) + { + if(vmodes[vmod].flags&VMDF_DXBLT) + { + veflags|=2; + memset((char *)ScreenLoc,0,pitch*srect.bottom); + } + else + { + memset((char *)ScreenLoc,0,pitch*vmodes[vmod].y); + } + PaletteChanged=1; + veflags&=~1; + } + + if(vmod==5) + { + if(eoptions&EO_CLIPSIDES) + { + asm volatile( + "xorl %%edx, %%edx\n\t" + "akoop1:\n\t" + "movb $120,%%al \n\t" + "akoop2:\n\t" + "movb 1(%%esi),%%dl\n\t" + "shl $16,%%edx\n\t" + "movb (%%esi),%%dl\n\t" + "xorl $0x00800080,%%edx\n\t" + "movl %%edx,(%%edi)\n\t" + "addl $2,%%esi\n\t" + "addl $4,%%edi\n\t" + "decb %%al\n\t" + "jne akoop2\n\t" + "addl $32,%%esi\n\t" + "addl %%ecx,%%edi\n\t" + "decb %%bl\n\t" + "jne akoop1\n\t" + : + : "S" (XBuf+srendline*272+VNSCLIP), "D" (ScreenLoc+((240-totallines)/2)*pitch+(640-(VNSWID<<1))/2),"b" (totallines), "c" ((pitch-VNSWID)<<1) + : "%al", "%edx", "%cc" ); + } + else + { + asm volatile( + "xorl %%edx, %%edx\n\t" + "koop1:\n\t" + "movb $128,%%al \n\t" + "koop2:\n\t" + "movb 1(%%esi),%%dl\n\t" + "shl $16,%%edx\n\t" + "movb (%%esi),%%dl\n\t" + "xorl $0x00800080,%%edx\n\t" + "movl %%edx,(%%edi)\n\t" + "addl $2,%%esi\n\t" + "addl $4,%%edi\n\t" + "decb %%al\n\t" + "jne koop2\n\t" + "addl $16,%%esi\n\t" + "addl %%ecx,%%edi\n\t" + "decb %%bl\n\t" + "jne koop1\n\t" + : + : "S" (XBuf+srendline*272), "D" (ScreenLoc+((240-totallines)/2)*pitch+(640-512)/2),"b" (totallines), "c" (pitch-512+pitch) + : "%al", "%edx", "%cc" ); + } + } + else if(vmod==4) + { + if(eoptions&EO_CLIPSIDES) + { + asm volatile( + "ayoop1:\n\t" + "movb $120,%%al \n\t" + "ayoop2:\n\t" + "movb 1(%%esi),%%dh\n\t" + "movb %%dh,%%dl\n\t" + "shl $16,%%edx\n\t" + "movb (%%esi),%%dl\n\t" + "movb %%dl,%%dh\n\t" // Ugh + "xorl $0x80808080,%%edx\n\t" + "movl %%edx,(%%edi)\n\t" + "addl $2,%%esi\n\t" + "addl $4,%%edi\n\t" + "decb %%al\n\t" + "jne ayoop2\n\t" + "addl $32,%%esi\n\t" + "addl %%ecx,%%edi\n\t" + "decb %%bl\n\t" + "jne ayoop1\n\t" + : + : "S" (XBuf+srendline*272+VNSCLIP), "D" (ScreenLoc+((240-totallines)/2)*pitch+(640-(VNSWID<<1))/2),"b" (totallines), "c" ((pitch-VNSWID)<<1) + : "%al", "%edx", "%cc" ); + } + else + { + asm volatile( + "yoop1:\n\t" + "movb $128,%%al \n\t" + "yoop2:\n\t" + "movb 1(%%esi),%%dh\n\t" + "movb %%dh,%%dl\n\t" + "shl $16,%%edx\n\t" + "movb (%%esi),%%dl\n\t" + "movb %%dl,%%dh\n\t" // Ugh + "xorl $0x80808080,%%edx\n\t" + "movl %%edx,(%%edi)\n\t" + "addl $2,%%esi\n\t" + "addl $4,%%edi\n\t" + "decb %%al\n\t" + "jne yoop2\n\t" + "addl $16,%%esi\n\t" + "addl %%ecx,%%edi\n\t" + "decb %%bl\n\t" + "jne yoop1\n\t" + : + : "S" (XBuf+srendline*272), "D" (ScreenLoc+((240-totallines)/2)*pitch+(640-512)/2),"b" (totallines), "c" (pitch-512+pitch) + : "%al", "%edx", "%cc" ); + } + } + else + { + if(!(vmodes[vmod].flags&VMDF_DXBLT)) + { + ScreenLoc+=((vmodes[vmod].x-VNSWID)>>1)*(bpp>>3)+(((vmodes[vmod].y-totallines)>>1))*pitch; + } + if(bpp>=16) + { + BlitVidHi(XBuf+srendline*272+VNSCLIP, ScreenLoc, /*VNSWID,*/ totallines, pitch); + } + else + { + XBuf+=srendline*272+VNSCLIP; + if(eoptions&EO_CLIPSIDES) + { + for(y=totallines;y;y--) + { + for(x=60;x;x--) + { + *(long *)ScreenLoc=(*(long *)XBuf)^0x80808080; + ScreenLoc+=4; + XBuf+=4; + } + ScreenLoc+=pitch-240; + XBuf+=32; + } + } + else + { + for(y=totallines;y;y--) + { + for(x=64;x;x--) + { + *(long *)ScreenLoc=(*(long *)XBuf)^0x80808080; + ScreenLoc+=4; + XBuf+=4; + } + ScreenLoc+=pitch-256; + XBuf+=16; + } + } + } + } + + if(vmodes[vmod].flags&VMDF_DXBLT) + { + IDirectDrawSurface4_Unlock(lpDDSBack, NULL); + + if(veflags&2) + { + if(IDirectDrawSurface4_Lock(lpDDSVPrimary,NULL,&ddsd, 0, NULL)==DD_OK) + { + memset(ddsd.lpSurface,0,ddsd.DUMMYUNIONNAMEN(1).lPitch*vmodes[vmod].y); + IDirectDrawSurface4_Unlock(lpDDSVPrimary, NULL); + veflags&=~2; + } + } + + + if(IDirectDrawSurface4_Blt(lpDDSVPrimary, &drect,lpDDSBack,&srect,DDBLT_ASYNC,0)!=DD_OK) + { + ddrval=IDirectDrawSurface4_Blt(lpDDSVPrimary, &drect,lpDDSBack,&srect,DDBLT_WAIT,0); + if(ddrval!=DD_OK) + { + if(ddrval==DDERR_SURFACELOST) + { + RestoreDD(0); + RestoreDD(1); + } + return; + } + + } + } + else + IDirectDrawSurface4_Unlock(lpDDSVPrimary, NULL); + if(fssync==2) + { + IDirectDrawSurface4_Flip(lpDDSPrimary,0,0); + + } +} + +void ResetVideo(void) +{ + ShowCursorAbs(1); + if(palettetranslate) {free(palettetranslate);palettetranslate=0;} + if(lpDD4) + if(mustrestore) + {IDirectDraw4_RestoreDisplayMode(lpDD4);mustrestore=0;} + if(lpDDSBack) {IDirectDrawSurface4_Release(lpDDSBack);lpDDSBack=0;} + if(lpDDSPrimary) {IDirectDrawSurface4_Release(lpDDSPrimary);lpDDSPrimary=0;} + if(lpClipper) {IDirectDrawClipper_Release(lpClipper);lpClipper=0;} +} + +static int RecalcCustom(void) +{ + vmodes[0].flags&=~VMDF_DXBLT; + + if(vmodes[0].flags&VMDF_STRFS) + { + vmodes[0].flags|=VMDF_DXBLT; + + vmodes[0].srect.top=srendline; + vmodes[0].srect.left=VNSCLIP; + vmodes[0].srect.right=256-VNSCLIP; + vmodes[0].srect.bottom=erendline+1; + + vmodes[0].drect.top=vmodes[0].drect.left=0; + vmodes[0].drect.right=vmodes[0].x; + vmodes[0].drect.bottom=vmodes[0].y; + } + else if(vmodes[0].xscale!=1 || vmodes[0].yscale!=1) + { + vmodes[0].flags|=VMDF_DXBLT; + if(VNSWID*vmodes[0].xscale>vmodes[0].x) + { + FCEUD_PrintError("Scaled width is out of range. Reverting to no horizontal scaling."); + vmodes[0].xscale=1; + } + if(totallines*vmodes[0].yscale>vmodes[0].y) + { + FCEUD_PrintError("Scaled height is out of range. Reverting to no vertical scaling."); + vmodes[0].yscale=1; + } + + vmodes[0].srect.left=VNSCLIP; + vmodes[0].srect.top=srendline; + vmodes[0].srect.right=256-VNSCLIP; + vmodes[0].srect.bottom=erendline+1; + + vmodes[0].drect.top=(vmodes[0].y-(totallines*vmodes[0].yscale))>>1; + vmodes[0].drect.bottom=vmodes[0].drect.top+totallines*vmodes[0].yscale; + + vmodes[0].drect.left=(vmodes[0].x-(VNSWID*vmodes[0].xscale))>>1; + vmodes[0].drect.right=vmodes[0].drect.left+VNSWID*vmodes[0].xscale; + } + + if(vmodes[0].x>3)-1,(LPARAM)(LPSTR)0); + + SetDlgItemInt(hwndDlg,200,vmodes[0].x,0); + SetDlgItemInt(hwndDlg,201,vmodes[0].y,0); + + SetDlgItemInt(hwndDlg,302,vmodes[0].xscale,0); + SetDlgItemInt(hwndDlg,303,vmodes[0].yscale,0); + CheckRadioButton(hwndDlg,300,301,(vmodes[0].flags&VMDF_STRFS)?301:300); + if(eoptions&EO_FSAFTERLOAD) + CheckDlgButton(hwndDlg,102,BST_CHECKED); + + if(eoptions&EO_CLIPSIDES) + CheckDlgButton(hwndDlg,106,BST_CHECKED); + + SetDlgItemInt(hwndDlg,500,srendlinen,0); + SetDlgItemInt(hwndDlg,501,erendlinen,0); + + SetDlgItemInt(hwndDlg,502,srendlinep,0); + SetDlgItemInt(hwndDlg,503,erendlinep,0); + + + SetDlgItemInt(hwndDlg,103,winsizemul,0); + + SendDlgItemMessage(hwndDlg,104,CB_ADDSTRING,0,(LPARAM)(LPSTR)""); + SendDlgItemMessage(hwndDlg,105,CB_ADDSTRING,0,(LPARAM)(LPSTR)""); + + SendDlgItemMessage(hwndDlg,104,CB_ADDSTRING,0,(LPARAM)(LPSTR)"Wait for VBlank"); + SendDlgItemMessage(hwndDlg,105,CB_ADDSTRING,0,(LPARAM)(LPSTR)"Wait for VBlank"); + + SendDlgItemMessage(hwndDlg,105,CB_ADDSTRING,0,(LPARAM)(LPSTR)"Double Buffering"); + + SendDlgItemMessage(hwndDlg,104,CB_SETCURSEL,winsync,(LPARAM)(LPSTR)0); + SendDlgItemMessage(hwndDlg,105,CB_SETCURSEL,fssync,(LPARAM)(LPSTR)0); + break; + case WM_CLOSE: + case WM_QUIT: goto gornk; + case WM_COMMAND: + if(!(wParam>>16)) + switch(wParam&0xFFFF) + { + case 1: + gornk: + + if(IsDlgButtonChecked(hwndDlg,106)==BST_CHECKED) + eoptions|=EO_CLIPSIDES; + else + eoptions&=~EO_CLIPSIDES; + + srendlinen=GetDlgItemInt(hwndDlg,500,0,0); + erendlinen=GetDlgItemInt(hwndDlg,501,0,0); + srendlinep=GetDlgItemInt(hwndDlg,502,0,0); + erendlinep=GetDlgItemInt(hwndDlg,503,0,0); + + + if(erendlinen>239) erendlinen=239; + if(srendlinen>erendlinen) srendlinen=erendlinen; + + if(erendlinep>239) erendlinep=239; + if(srendlinep>erendlinen) srendlinep=erendlinep; + + UpdateRendBounds(); + + if(IsDlgButtonChecked(hwndDlg,301)==BST_CHECKED) + vmodes[0].flags|=VMDF_STRFS; + else + vmodes[0].flags&=~VMDF_STRFS; + + vmod=SendDlgItemMessage(hwndDlg,100,CB_GETCURSEL,0,(LPARAM)(LPSTR)0); + vmodes[0].x=GetDlgItemInt(hwndDlg,200,0,0); + vmodes[0].y=GetDlgItemInt(hwndDlg,201,0,0); + vmodes[0].bpp=(SendDlgItemMessage(hwndDlg,202,CB_GETCURSEL,0,(LPARAM)(LPSTR)0)+1)<<3; + + vmodes[0].xscale=GetDlgItemInt(hwndDlg,302,0,0); + vmodes[0].yscale=GetDlgItemInt(hwndDlg,303,0,0); + + if(IsDlgButtonChecked(hwndDlg,101)==BST_CHECKED) + fullscreen=1; + else + fullscreen=0; + if(IsDlgButtonChecked(hwndDlg,102)==BST_CHECKED) + eoptions|=EO_FSAFTERLOAD; + else + eoptions&=~EO_FSAFTERLOAD; + + { + int t=GetDlgItemInt(hwndDlg,103,0,0); + if(t>0 && t<60) + winsizemul=t; + } + winsync=SendDlgItemMessage(hwndDlg,104,CB_GETCURSEL,0,(LPARAM)(LPSTR)0); + fssync=SendDlgItemMessage(hwndDlg,105,CB_GETCURSEL,0,(LPARAM)(LPSTR)0); + EndDialog(hwndDlg,0); + break; + } + } + return 0; +} + +static void SetFSVideoMode(void) +{ + changerecursive=1; + if(!SetVideoMode(1)) + SetVideoMode(0); + changerecursive=0; +} + + + +void ConfigVideo(void) +{ + DialogBox(fceu_hInstance,"VIDEOCONFIG",hAppWnd,VideoConCallB); + UpdateRendBounds(); + if(fullscreen) + SetFSVideoMode(); + else + SetMainWindowStuff(); +} + +void DoVideoConfigFix(void) +{ + UpdateRendBounds(); +} + + +#ifdef moo + if(!vmod) + { + if(vmodes[0].x>8)&0xFF,soundlog); + fputc((s>>16)&0xFF,soundlog); + fputc((s>>24)&0xFF,soundlog); + + fseek(soundlog,0x28,SEEK_SET); + s=wsize; + fputc(s&0xFF,soundlog); + fputc((s>>8)&0xFF,soundlog); + fputc((s>>16)&0xFF,soundlog); + fputc((s>>24)&0xFF,soundlog); + + fclose(soundlog); + soundlog=0; + return 1; +} +int WriteWaveHeader(FILE *fp) +{ + int r; + fputs("RIFF",fp); + fseek(fp,4,SEEK_CUR); // Skip size + fputs("WAVEfmt ",fp); + fputc(0x10,fp); + fputc(0,fp); + fputc(0,fp); + fputc(0,fp); + + fputc(1,fp); // PCM + fputc(0,fp); + fputc(1,fp); // Monophonic + fputc(0,fp); + + r=44100; + fputc(r&0xFF,fp); + fputc((r>>8)&0xFF,fp); + fputc((r>>16)&0xFF,fp); + fputc((r>>24)&0xFF,fp); + r<<=1; + fputc(r&0xFF,fp); + fputc((r>>8)&0xFF,fp); + fputc((r>>16)&0xFF,fp); + fputc((r>>24)&0xFF,fp); + fputc(2,fp); + fputc(0,fp); + fputc(16,fp); + fputc(0,fp); + + fputs("data",fp); + fseek(fp,4,SEEK_CUR); + + return 1; +} + +int StartSoundLog(char *str) +{ + wsize=0; + soundlog=fopen(str,"wb"); + if(soundlog) + return WriteWaveHeader(soundlog); + else + return 0; +} +int CreateSoundSave(void) +{ + const char filter[]="MS WAVE(*.wav)\0*.wav\0"; + char nameo[2048]; + OPENFILENAME ofn; + + if(soundlog) + { + CloseWave(); + return 0; + } + + memset(&ofn,0,sizeof(ofn)); + ofn.lStructSize=sizeof(ofn); + ofn.hInstance=fceu_hInstance; + ofn.lpstrTitle="Log Sound As..."; + ofn.lpstrFilter=filter; + nameo[0]=0; + ofn.lpstrFile=nameo; + ofn.nMaxFile=256; + ofn.Flags=OFN_EXPLORER|OFN_FILEMUSTEXIST|OFN_HIDEREADONLY; + if(GetSaveFileName(&ofn)) + return StartSoundLog(nameo); + return 0; +} diff --git a/drivers/win/window.c b/drivers/win/window.c new file mode 100644 index 0000000..6ccd4e0 --- /dev/null +++ b/drivers/win/window.c @@ -0,0 +1,855 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +static void ConfigMisc(void); +static void ConfigPalette(void); +static void ConfigDirectories(void); + +static HMENU fceumenu=0; +static HMENU recentmenu; + +static int tog=0; + +void ShowCursorAbs(int w) +{ + static int stat=0; + if(w) + { + if(stat==-1) {stat++; ShowCursor(1);} + } + else + { + if(stat==0) {stat--; ShowCursor(0);} + } +} + + +RECT *CalcWindowSize(void) +{ + static RECT al; + al.left=0; + al.right=VNSWID*winsizemul; + al.top=0; + al.bottom=totallines*winsizemul; + + AdjustWindowRectEx(&al,GetWindowLong(hAppWnd,GWL_STYLE),GetMenu(hAppWnd)!=NULL,GetWindowLong(hAppWnd,GWL_EXSTYLE)); + + al.right-=al.left; + al.left=0; + al.bottom-=al.top; + al.top=0; + + return(&al); +} + +void UpdateMenu(void) +{ + static int *polo[2]={&genie,&palyo}; + static int polo2[2]={310,311}; + int x; + + for(x=0;x<2;x++) + CheckMenuItem(fceumenu,polo2[x],*polo[x]?MF_CHECKED:MF_UNCHECKED); + if(eoptions&EO_BGRUN) + CheckMenuItem(fceumenu,301,MF_CHECKED); + else + CheckMenuItem(fceumenu,301,MF_UNCHECKED); +} + +char *rfiles[10]={0,0,0,0,0,0,0,0,0,0}; + +void UpdateRMenu(void) +{ + MENUITEMINFO moo; + int x; + + moo.cbSize=sizeof(moo); + moo.fMask=MIIM_SUBMENU|MIIM_STATE; + + GetMenuItemInfo(GetSubMenu(fceumenu,0),102,FALSE,&moo); + moo.hSubMenu=recentmenu; + moo.fState=rfiles[0]?MFS_ENABLED:MFS_GRAYED; + + SetMenuItemInfo(GetSubMenu(fceumenu,0),102,FALSE,&moo); + + for(x=0;x<10;x++) + RemoveMenu(recentmenu,600+x,MF_BYCOMMAND); + for(x=9;x>=0;x--) + { + char tmp[128+5]; + if(!rfiles[x]) continue; + + moo.cbSize=sizeof(moo); + moo.fMask=MIIM_DATA|MIIM_ID|MIIM_TYPE; + + if(strlen(rfiles[x])<128) + { + sprintf(tmp,"&%d. %s",(x+1)%10,rfiles[x]); + } + else + sprintf(tmp,"&%d. %s",(x+1)%10,rfiles[x]+strlen(rfiles[x])-127); + + moo.cch=strlen(tmp); + moo.fType=0; + moo.wID=600+x; + moo.dwTypeData=tmp; + InsertMenuItem(recentmenu,0,1,&moo); + } + DrawMenuBar(hAppWnd); +} + +void AddRecent(char *fn) +{ + int x; + + for(x=0;x<10;x++) + if(rfiles[x]) + if(!strcmp(rfiles[x],fn)) // Item is already in list. + { + int y; + char *tmp; + + tmp=rfiles[x]; // Save pointer. + for(y=x;y;y--) + rfiles[y]=rfiles[y-1]; // Move items down. + + rfiles[0]=tmp; // Put item on top. + UpdateRMenu(); + return; + } + + if(rfiles[9]) free(rfiles[9]); + for(x=9;x;x--) rfiles[x]=rfiles[x-1]; + rfiles[0]=malloc(strlen(fn)+1); + strcpy(rfiles[0],fn); + UpdateRMenu(); +} + +void HideMenu(int h) +{ + if(h) + { + SetMenu(hAppWnd,0); + } + else + { + SetMenu(hAppWnd,fceumenu); + } +} + +static LONG WindowXC=1<<30,WindowYC; +void HideFWindow(int h) +{ + LONG desa; + + if(h) + { + RECT bo; + GetWindowRect(hAppWnd,&bo); + WindowXC=bo.left; + WindowYC=bo.top; + + SetMenu(hAppWnd,0); + desa=WS_POPUP|WS_CLIPSIBLINGS; + } + else + { + desa=WS_OVERLAPPEDWINDOW|WS_CLIPSIBLINGS; + HideMenu(tog); + } + + SetWindowLong(hAppWnd,GWL_STYLE,desa|(GetWindowLong(hAppWnd,GWL_STYLE)&WS_VISIBLE)); + SetWindowPos(hAppWnd,0,0,0,0,0,SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOCOPYBITS|SWP_NOMOVE|SWP_NOREPOSITION|SWP_NOSIZE|SWP_NOZORDER); +} + +void ToggleHideMenu(void) +{ + if(!fullscreen) + { + tog^=1; + HideMenu(tog); + SetMainWindowStuff(); + } +} + +static void ALoad(char *nameo) +{ + if((GI=FCEUI_LoadGame(nameo))) + { + FixFL(); + FixGIGO(); + SetMainWindowStuff(); + AddRecent(nameo); + RefreshThrottleFPS(); + if(eoptions&EO_FSAFTERLOAD) + SetFSVideoMode(); + } + else + StopSound(); +} + +void LoadNewGamey(HWND hParent) +{ + const char filter[]="All usable files(*.nes,*.nsf,*.fds,*.unf,*.zip,*.gz)\0*.nes;*.nsf;*.fds;*.unf;*.zip;*.gz\0All non-compressed usable files(*.nes,*.nsf,*.fds,*.unf)\0*.nes;*.nsf;*.fds;*.unf\0All files (*.*)\0*.*\0"; + char nameo[2048]; + OPENFILENAME ofn; + memset(&ofn,0,sizeof(ofn)); + ofn.lStructSize=sizeof(ofn); + ofn.hInstance=fceu_hInstance; + ofn.lpstrTitle="FCE Ultra Open File..."; + ofn.lpstrFilter=filter; + nameo[0]=0; + ofn.hwndOwner=hParent; + ofn.lpstrFile=nameo; + ofn.nMaxFile=256; + ofn.Flags=OFN_EXPLORER|OFN_FILEMUSTEXIST|OFN_HIDEREADONLY; //OFN_EXPLORER|OFN_ENABLETEMPLATE|OFN_ENABLEHOOK; + ofn.lpstrInitialDir=gfsdir; + if(GetOpenFileName(&ofn)) + { + if(gfsdir) free(gfsdir); + if((gfsdir=malloc(ofn.nFileOffset+1))) + { + strncpy(gfsdir,ofn.lpstrFile,ofn.nFileOffset); + gfsdir[ofn.nFileOffset]=0; + } + ALoad(nameo); + } +} + +static uint32 mousex,mousey,mouseb; +void GetMouseData(uint32 *x, uint32 *y, uint32 *b) +{ + *x=mousex; + *y=mousey; + if(!fullscreen) + { + if(eoptions&EO_USERFORCE) + { + RECT t; + GetClientRect(hAppWnd,&t); + + *x=*x*VNSWID/(t.right?t.right:1); + *y=*y*totallines/(t.bottom?t.bottom:1); + } + else + { + *x/=winsizemul; + *y/=winsizemul; + } + *x+=VNSCLIP; + } + + *y+=srendline; + *b=((mouseb==MK_LBUTTON)?1:0)|((mouseb==MK_RBUTTON)?2:0); +} + +static int sizchange=0; +static int vchanged=0; + +LRESULT FAR PASCAL AppWndProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam) +{ + switch(msg) { + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + mouseb=wParam; + goto proco; + case WM_MOUSEMOVE: + { + mousex=LOWORD(lParam); + mousey=HIWORD(lParam); + } + goto proco; + case WM_SIZING: + sizchange=1; + goto proco; + case WM_DISPLAYCHANGE: + if(!fullscreen && !changerecursive) + vchanged=1; + goto proco; + case WM_DROPFILES: + { + UINT len; + char *ftmp; + + len=DragQueryFile((HANDLE)wParam,0,0,0)+1; + if((ftmp=malloc(len))) + { + DragQueryFile((HANDLE)wParam,0,ftmp,len); + ALoad(ftmp); + free(ftmp); + } + } + break; + case WM_COMMAND: + if(!(wParam>>16)) + { + wParam&=0xFFFF; + if(wParam>=600 && wParam<=609) + { + if(rfiles[wParam-600]) ALoad(rfiles[wParam-600]); + } + switch(wParam) + { + case 300:ToggleHideMenu();break; + case 301:eoptions^=EO_BGRUN;UpdateMenu();break; + + case 310:genie^=1;FCEUI_SetGameGenie(genie);UpdateMenu();break; + case 311:palyo^=1; + FCEUI_SetVidSystem(palyo); + RefreshThrottleFPS(); + UpdateMenu(); + FixFL(); + SetMainWindowStuff(); + break; + + case 320:StopSound();ConfigDirectories();break; + case 321:StopSound();ConfigInput(hWnd);break; + case 322:ConfigMisc();break; + case 323:StopSound();ConfigNetplay();break; + case 324:StopSound();ConfigPalette();break; + case 325:StopSound();ConfigSound();break; + case 326:ConfigVideo();break; + + case 200:DriverInterface(DES_RESET,0);break; + case 201:DriverInterface(DES_POWER,0);break; + case 202:ConfigCheats(hWnd);break; + + case 100:StopSound(); + LoadNewGamey(hWnd); + break; + case 101:if(GI) + { + FCEUI_CloseGame(); + GI=0; + } + break; + case 110:FCEUI_SaveState();break; + case 111:FCEUI_LoadState();break; + + case 120: + { + MENUITEMINFO mi; + char *str; + + StopSound(); + if(CreateSoundSave()) + str="Stop Sound Logging"; + else + str="Log Sound As..."; + memset(&mi,0,sizeof(mi)); + mi.fMask=MIIM_DATA|MIIM_TYPE; + mi.cbSize=sizeof(mi); + GetMenuItemInfo(fceumenu,120,0,&mi); + mi.fMask=MIIM_DATA|MIIM_TYPE; + mi.cbSize=sizeof(mi); + mi.dwTypeData=str; + mi.cch=strlen(str); + SetMenuItemInfo(fceumenu,120,0,&mi); + } + break; + case 130:DoFCEUExit();break; + + case 400:StopSound();ShowAboutBox();break; + } + } + break; + + + case WM_SYSCOMMAND: + if(wParam==SC_KEYMENU) + if(GI && InputTypeFC==SIFC_FKB && cidisabled) + break; + goto proco; + case WM_SYSKEYDOWN: + if(GI && InputTypeFC==SIFC_FKB && cidisabled) + break; /* Hopefully this won't break DInput... */ + + if(fullscreen || tog) + { + if(wParam==VK_MENU) + break; + } + if(wParam==VK_F10) + { + if(!(lParam&0x40000000)) + DriverInterface(DES_RESET,0); + break; + } + goto proco; + + case WM_KEYDOWN: + if(GI) + { + /* Only disable command keys if a game is loaded(and the other + conditions are right, of course). */ + if(InputTypeFC==SIFC_FKB) + { + if(wParam==VK_SCROLL) + { + cidisabled^=1; + FCEUI_DispMessage("Family Keyboard %sabled.",cidisabled?"en":"dis"); + } + if(cidisabled) + break; /* Hopefully this won't break DInput... */ + } + if(GI->type==GIT_NSF) + switch(wParam) + { + case VK_UP:DriverInterface(DES_NSFINC,0);break; + case VK_DOWN:DriverInterface(DES_NSFDEC,0);break; + case VK_RETURN:DriverInterface(DES_NSFRES,0);break; + + case VK_LEFT:if(!(lParam&0x40000000)) + DriverInterface(DES_NSFDEC,0); break; + case VK_RIGHT:if(!(lParam&0x40000000)) + DriverInterface(DES_NSFINC,0); break; + } + + } + if(!(lParam&0x40000000)) + switch( wParam ) + { + case VK_F11:DriverInterface(DES_POWER,0);break; + case VK_F12:DoFCEUExit();break; + case VK_F2:userpause^=1;break; + case VK_F3:ToggleHideMenu();break; + case VK_F4: UpdateMenu(); + changerecursive=1; + if(!SetVideoMode(fullscreen^1)) + SetVideoMode(fullscreen); + changerecursive=0; + break; + } + goto proco; + + + case WM_NCRBUTTONDOWN: + case WM_NCMBUTTONDOWN:StopSound();goto proco; + case WM_NCLBUTTONDOWN:StopSound();goto proco; + + case WM_ENTERMENULOOP:StopSound();goto proco; + case WM_CLOSE: + case WM_DESTROY: + case WM_QUIT:DoFCEUExit();break; + case WM_ACTIVATEAPP: + if((BOOL)wParam) + { + nofocus=0; + } + else + { + nofocus=1; + } + default: + proco: + return DefWindowProc(hWnd,msg,wParam,lParam); + } + return 0; +} + +void UpdateFCEUWindow(void) +{ + int w,h; + RECT wrect; + + if(vchanged && !fullscreen && !changerecursive && !nofocus) + { + SetVideoMode(0); + vchanged=0; + } + + if(sizchange && !fullscreen && !(eoptions&EO_USERFORCE)) + { + GetWindowRect(hAppWnd,&wrect); + h=wrect.bottom-wrect.top; + w=wrect.right-wrect.left; + if(w!=winwidth) + winsizemul=(w-(winwidth-VNSWID*winsizemul)+(VNSWID>>1))>>8; + else + if(h!=winheight) + winsizemul=(h-(winheight-totallines*winsizemul)+(totallines>>1))>>8; + + if(winsizemul<1) + winsizemul=1; + SetMainWindowStuff(); + } + sizchange=0; + + BlockingCheck(); +} + +void ByebyeWindow(void) +{ + SetMenu(hAppWnd,0); + DestroyMenu(fceumenu); + DestroyWindow(hAppWnd); +} + +int CreateMainWindow(void) +{ + WNDCLASSEX winclass; + RECT tmp; + + memset(&winclass,0,sizeof(winclass)); + winclass.cbSize=sizeof(WNDCLASSEX); + winclass.style=CS_OWNDC|CS_HREDRAW|CS_VREDRAW|CS_SAVEBITS; + winclass.lpfnWndProc=AppWndProc; + winclass.cbClsExtra=0; + winclass.cbWndExtra=0; + winclass.hInstance=fceu_hInstance; + winclass.hIcon=LoadIcon(fceu_hInstance, "ICON_1"); + winclass.hIconSm=LoadIcon(fceu_hInstance, "ICON_1"); + winclass.hCursor=LoadCursor(NULL, IDC_ARROW); + winclass.hbrBackground=GetStockObject(BLACK_BRUSH); + //winclass.lpszMenuName="FCEUMENU"; + winclass.lpszClassName="FCEULTRA"; + + if(!RegisterClassEx(&winclass)) + return FALSE; + + AdjustWindowRectEx(&tmp,WS_OVERLAPPEDWINDOW,1,0); + + fceumenu=LoadMenu(fceu_hInstance,"FCEUMENU"); + recentmenu=CreateMenu(); + UpdateRMenu(); + + hAppWnd = CreateWindowEx(0,"FCEULTRA","FCE Ultra", + WS_OVERLAPPEDWINDOW|WS_CLIPSIBLINGS, /* Style */ + CW_USEDEFAULT,CW_USEDEFAULT,256,240, /* X,Y ; Width, Height */ + NULL,fceumenu,fceu_hInstance,NULL ); + DragAcceptFiles(hAppWnd, 1); + SetMainWindowStuff(); + return 1; +} + + +int SetMainWindowStuff(void) +{ + RECT *srect; + RECT tmp; + + GetWindowRect(hAppWnd,&tmp); + + if(WindowXC!=(1<<30)) + { + /* Subtracting and adding for if(eoptions&EO_USERFORCE) below. */ + tmp.bottom-=tmp.top; + tmp.bottom+=WindowYC; + + tmp.right-=tmp.left; + tmp.right+=WindowXC; + + + tmp.left=WindowXC; + tmp.top=WindowYC; + WindowXC=1<<30; + } + + if(eoptions&EO_USERFORCE) + { + SetWindowPos(hAppWnd,HWND_TOP,tmp.left,tmp.top,0,0,SWP_NOSIZE|SWP_SHOWWINDOW); + winwidth=tmp.right-tmp.left; + winheight=tmp.bottom-tmp.top; + } + else + { + srect=CalcWindowSize(); + SetWindowPos(hAppWnd,HWND_TOP,tmp.left,tmp.top,srect->right,srect->bottom,SWP_SHOWWINDOW); + winwidth=srect->right; + winheight=srect->bottom; + } + + + ShowWindow(hAppWnd, SW_SHOWNORMAL); + return 1; +} + +int GetClientAbsRect(LPRECT lpRect) +{ + POINT point; + point.x=point.y=0; + if(!ClientToScreen(hAppWnd,&point)) return 0; + + lpRect->top=point.y; + lpRect->left=point.x; + + if(eoptions&EO_USERFORCE) + { + RECT al; + + GetClientRect(hAppWnd,&al); + + lpRect->right=point.x+al.right; + lpRect->bottom=point.y+al.bottom; + } + else + { + lpRect->right=point.x+VNSWID*winsizemul; + lpRect->bottom=point.y+totallines*winsizemul; + } + return 1; +} + + +void LoadPaletteFile(void) +{ + FILE *fp; + const char filter[]="All usable files(*.pal)\0*.pal\0All files (*.*)\0*.*\0"; + char nameo[2048]; + OPENFILENAME ofn; + memset(&ofn,0,sizeof(ofn)); + ofn.lStructSize=sizeof(ofn); + ofn.hInstance=fceu_hInstance; + ofn.lpstrTitle="FCE Ultra Open Palette File..."; + ofn.lpstrFilter=filter; + nameo[0]=0; + ofn.lpstrFile=nameo; + ofn.nMaxFile=256; + ofn.Flags=OFN_EXPLORER|OFN_FILEMUSTEXIST|OFN_HIDEREADONLY; + ofn.lpstrInitialDir=0; + if(GetOpenFileName(&ofn)) + { + if((fp=fopen(nameo,"rb"))) + { + fread(cpalette,1,192,fp); + fclose(fp); + FCEUI_SetPaletteArray(cpalette); + eoptions|=EO_CPALETTE; + } + else + FCEUD_PrintError("Error opening palette file!"); + } +} + +BOOL CALLBACK PaletteConCallB(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) { + case WM_INITDIALOG: + if(ntsccol) + CheckDlgButton(hwndDlg,100,BST_CHECKED); + SendDlgItemMessage(hwndDlg,500,TBM_SETRANGE,1,MAKELONG(0,128)); + SendDlgItemMessage(hwndDlg,501,TBM_SETRANGE,1,MAKELONG(0,128)); + DriverInterface(DES_GETNTSCTINT,&ntsctint); + DriverInterface(DES_GETNTSCHUE,&ntschue); + SendDlgItemMessage(hwndDlg,500,TBM_SETPOS,1,ntsctint); + SendDlgItemMessage(hwndDlg,501,TBM_SETPOS,1,ntschue); + break; + case WM_CLOSE: + case WM_QUIT: goto gornk; + case WM_COMMAND: + if(!(wParam>>16)) + switch(wParam&0xFFFF) + { + case 100:ntsccol^=1;DriverInterface(DES_NTSCCOL,&ntsccol);break; + case 200: + LoadPaletteFile(); + break; + case 201:FCEUI_SetPaletteArray(0); + eoptions&=~EO_CPALETTE; + break; + case 1: + gornk: + ntsctint=SendDlgItemMessage(hwndDlg,500,TBM_GETPOS,0,(LPARAM)(LPSTR)0); + ntschue=SendDlgItemMessage(hwndDlg,501,TBM_GETPOS,0,(LPARAM)(LPSTR)0); + EndDialog(hwndDlg,0); + break; + } + } + return 0; +} + +static void ConfigPalette(void) +{ + DialogBox(fceu_hInstance,"PALCONFIG",hAppWnd,PaletteConCallB); + DriverInterface(DES_SETNTSCTINT,&ntsctint); + DriverInterface(DES_SETNTSCHUE,&ntschue); +} + + +static BOOL CALLBACK MiscConCallB(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) { + case WM_INITDIALOG: + if(eoptions&EO_NOSPRLIM) + CheckDlgButton(hwndDlg,100,BST_CHECKED); + if(eoptions&EO_FOAFTERSTART) + CheckDlgButton(hwndDlg,102,BST_CHECKED); + if(eoptions&EO_SNAPNAME) + CheckDlgButton(hwndDlg,103,BST_CHECKED); + if(eoptions&EO_NOTHROTTLE) + CheckDlgButton(hwndDlg,101,BST_CHECKED); + break; + case WM_CLOSE: + case WM_QUIT: goto gornk; + case WM_COMMAND: + if(!(wParam>>16)) + switch(wParam&0xFFFF) + { + case 1: + gornk: + if(IsDlgButtonChecked(hwndDlg,100)==BST_CHECKED) + eoptions|=EO_NOSPRLIM; + else + eoptions&=~EO_NOSPRLIM; + if(IsDlgButtonChecked(hwndDlg,102)==BST_CHECKED) + eoptions|=EO_FOAFTERSTART; + else + eoptions&=~EO_FOAFTERSTART; + if(IsDlgButtonChecked(hwndDlg,103)==BST_CHECKED) + eoptions|=EO_SNAPNAME; + else + eoptions&=~EO_SNAPNAME; + if(IsDlgButtonChecked(hwndDlg,101)==BST_CHECKED) + eoptions|=EO_NOTHROTTLE; + else + eoptions&=~EO_NOTHROTTLE; + EndDialog(hwndDlg,0); + break; + } + } + return 0; +} + +void DoMiscConfigFix(void) +{ + FCEUI_DisableSpriteLimitation(eoptions&EO_NOSPRLIM); + FCEUI_SetSnapName(eoptions&EO_SNAPNAME); +} + +static void ConfigMisc(void) +{ + DialogBox(fceu_hInstance,"MISCCONFIG",hAppWnd,MiscConCallB); + DoMiscConfigFix(); +} + +static int BrowseForFolder(HWND hParent, char *htext, char *buf) +{ + BROWSEINFO bi; + LPCITEMIDLIST pidl; + int ret=1; + + buf[0]=0; + + memset(&bi,0,sizeof(bi)); + + bi.hwndOwner=hParent; + bi.lpszTitle=htext; + bi.ulFlags=BIF_RETURNONLYFSDIRS; + + if(FAILED(CoInitialize(0))) + return(0); + + if(!(pidl=SHBrowseForFolder(&bi))) + { + ret=0; + goto end1; + } + + if(!SHGetPathFromIDList(pidl,buf)) + { + ret=0; + goto end2; + } + + end2: + /* This probably isn't the best way to free the memory... */ + CoTaskMemFree((PVOID)pidl); + + end1: + CoUninitialize(); + return(ret); +} + +static BOOL CALLBACK DirConCallB(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + int x; + + switch(uMsg){ + case WM_INITDIALOG: + for(x=0;x<6;x++) + SetDlgItemText(hwndDlg,100+x,DOvers[x]); + + if(eoptions&EO_BSAV) + CheckDlgButton(hwndDlg,300,BST_CHECKED); + break; + case WM_CLOSE: + case WM_QUIT: goto gornk; + case WM_COMMAND: + if(!(wParam>>16)) + { + if((wParam&0xFFFF)>=200 && (wParam&0xFFFF)<=205) + { + static char *helpert[6]={"Cheats","Miscellaneous","Nonvolatile Game Data","Save States","Screen Snapshots","Base Directory"}; + char name[MAX_PATH]; + + if(BrowseForFolder(hwndDlg,helpert[((wParam&0xFFFF)-200)],name)) + SetDlgItemText(hwndDlg,100+((wParam&0xFFFF)-200),name); + } + else switch(wParam&0xFFFF) + { + case 1: + gornk: + + RemoveDirs(); // Remove empty directories. + + for(x=0;x<6;x++) + { + LONG len; + len=SendDlgItemMessage(hwndDlg,100+x,WM_GETTEXTLENGTH,0,0); + if(len<=0) + { + if(DOvers[x]) free(DOvers[x]); + DOvers[x]=0; + continue; + } + len++; // Add 1 for null character. + if(!(DOvers[x]=malloc(len))) + continue; + if(!GetDlgItemText(hwndDlg,100+x,DOvers[x],len)) + { + free(DOvers[x]); + DOvers[x]=0; + continue; + } + + } + if(IsDlgButtonChecked(hwndDlg,300)==BST_CHECKED) + eoptions|=EO_BSAV; + else + eoptions&=~EO_BSAV; + + CreateDirs(); // Create needed directories. + SetDirs(); // Set the directories in the core. + EndDialog(hwndDlg,0); + break; + } + } + } + return 0; +} + + + +static void ConfigDirectories(void) +{ + DialogBox(fceu_hInstance,"DIRCONFIG",hAppWnd,DirConCallB); +} diff --git a/endian.c b/endian.c new file mode 100644 index 0000000..8cd5b70 --- /dev/null +++ b/endian.c @@ -0,0 +1,71 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Contains file I/O functions that write/read data */ +/* LSB first. */ + + +#include +#include "types.h" +#include "endian.h" + +static uint8 s[4]; + +int write16(uint16 b, FILE *fp) +{ + s[0]=b; + s[1]=b>>8; + return((fwrite(s,1,2,fp)<2)?0:2); +} + +int write32(uint32 b, FILE *fp) +{ + s[0]=b; + s[1]=b>>8; + s[2]=b>>16; + s[3]=b>>24; + return((fwrite(s,1,4,fp)<4)?0:4); +} + +int read32(void *Bufo, FILE *fp) +{ + uint32 buf; + if(fread(&buf,1,4,fp)<4) + return 0; + #ifdef LSB_FIRST + *(uint32*)Bufo=buf; + #else + *(uint32*)Bufo=((buf&0xFF)<<24)|((buf&0xFF00)<<8)|((buf&0xFF0000)>>8)|((buf&0xFF000000)>>24); + #endif + return 1; +} + +int read16(char *d, FILE *fp) +{ + #ifdef LSB_FIRST + return((fread(d,1,2,fp)<2)?0:2); + #else + int ret; + ret=fread(d+1,1,1,fp); + ret+=fread(d,1,1,fp); + return ret<2?0:2; + #endif +} + diff --git a/endian.h b/endian.h new file mode 100644 index 0000000..23ee4ed --- /dev/null +++ b/endian.h @@ -0,0 +1,3 @@ +int write16(uint16 b, FILE *fp); +int write32(uint32 b, FILE *fp); +int read32(void *Bufo, FILE *fp); diff --git a/fce.c b/fce.c new file mode 100644 index 0000000..653a3f6 --- /dev/null +++ b/fce.c @@ -0,0 +1,1565 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 1998 BERO + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#include "types.h" +#include "x6502.h" +#include "fce.h" +#include "sound.h" +#include "svga.h" +#include "netplay.h" +#include "general.h" +#include "endian.h" +#include "version.h" +#include "memory.h" + +#include "cart.h" +#include "nsf.h" +#include "fds.h" +#include "ines.h" +#include "unif.h" +#include "cheat.h" + +#include "state.h" +#include "video.h" +#include "input.h" +#include "file.h" +#include "crc32.h" + +#define Pal (PALRAM) + +static void FetchSpriteData(void); +static void FASTAPASS(1) RefreshLine(uint8 *target); +static void PRefreshLine(void); +static void FASTAPASS(1) RefreshSprite(uint8 *target); +static void ResetPPU(void); +static void PowerPPU(void); + +uint64 timestampbase=0; + +int MMC5Hack; +uint32 MMC5HackVROMMask; +uint8 *MMC5HackExNTARAMPtr; +uint8 *MMC5HackVROMPTR; +uint8 MMC5HackCHRMode=0; +uint8 MMC5HackSPMode; +uint8 MMC5HackSPScroll; +uint8 MMC5HackSPPage; + +uint8 *MMC5SPRVPage[8]; +uint8 *MMC5BGVPage[8]; + + +uint8 VRAMBuffer,PPUGenLatch; + +uint8 *vnapage[4]; +uint8 PPUNTARAM; +uint8 PPUCHRRAM; + +/* Color deemphasis emulation. Joy... */ +static uint8 deemp=0; +static int deempcnt[8]; + +static int tosprite=256; + +FCEUGI FCEUGameInfo; +void (*GameInterface)(int h); + +void FP_FASTAPASS(1) (*PPU_hook)(uint32 A); + +void (*GameStateRestore)(int version); +void (*GameHBIRQHook)(void); + +readfunc ARead[0x10000]; +writefunc BWrite[0x10000]; +static readfunc *AReadG; +static writefunc *BWriteG; +static int RWWrap=0; + +DECLFW(BNull) +{ + +} + +DECLFR(ANull) +{ + return(X.DB); +} + +int AllocGenieRW(void) +{ + if(!(AReadG=FCEU_malloc(0x8000*sizeof(readfunc)))) + return 0; + if(!(BWriteG=FCEU_malloc(0x8000*sizeof(writefunc)))) + return 0; + RWWrap=1; + return 1; +} + +void FlushGenieRW(void) +{ + int32 x; + + if(RWWrap) + { + for(x=0;x<0x8000;x++) + { + ARead[x+0x8000]=AReadG[x]; + BWrite[x+0x8000]=BWriteG[x]; + } + free(AReadG); + free(BWriteG); + AReadG=0; + BWriteG=0; + RWWrap=0; + } +} + +readfunc FASTAPASS(1) GetReadHandler(int32 a) +{ + if(a>=0x8000 && RWWrap) + return AReadG[a-0x8000]; + else + return ARead[a]; +} + +void FASTAPASS(3) SetReadHandler(int32 start, int32 end, readfunc func) +{ + int32 x; + + if(!func) + func=ANull; + + if(RWWrap) + for(x=end;x>=start;x--) + { + if(x>=0x8000) + AReadG[x-0x8000]=func; + else + ARead[x]=func; + } + else + + for(x=end;x>=start;x--) + ARead[x]=func; +} + +writefunc FASTAPASS(1) GetWriteHandler(int32 a) +{ + if(RWWrap && a>=0x8000) + return BWriteG[a-0x8000]; + else + return BWrite[a]; +} + +void FASTAPASS(3) SetWriteHandler(int32 start, int32 end, writefunc func) +{ + int32 x; + + if(!func) + func=BNull; + + if(RWWrap) + for(x=end;x>=start;x--) + { + if(x>=0x8000) + BWriteG[x-0x8000]=func; + else + BWrite[x]=func; + } + else + for(x=end;x>=start;x--) + BWrite[x]=func; +} + +uint8 vtoggle=0; +uint8 XOffset=0; + +uint32 TempAddr,RefreshAddr; + +static int maxsprites=8; + +/* scanline is equal to the current visible scanline we're on. */ + +int scanline; +static uint32 scanlines_per_frame; + +uint8 PPU[4]; +uint8 PPUSPL; + +uint8 GameMemBlock[131072]; +uint8 NTARAM[0x800],PALRAM[0x20],SPRAM[0x100],SPRBUF[0x100]; +uint8 RAM[0x800]; + +uint8 PAL=0; + +#define MMC5SPRVRAMADR(V) &MMC5SPRVPage[(V)>>10][(V)] +#define MMC5BGVRAMADR(V) &MMC5BGVPage[(V)>>10][(V)] +#define VRAMADR(V) &VPage[(V)>>10][(V)] + +static DECLFW(BRAML) +{ + RAM[A]=V; +} + +static DECLFW(BRAMH) +{ + RAM[A&0x7FF]=V; +} + +static DECLFR(ARAML) +{ + return RAM[A]; +} + +static DECLFR(ARAMH) +{ + return RAM[A&0x7FF]; +} + + +static DECLFR(A2002) +{ + uint8 ret; + ret = PPU_status; + vtoggle=0; + PPU_status&=0x7F; + return ret|(PPUGenLatch&0x1F); +} + +static DECLFR(A200x) +{ + return PPUGenLatch; +} + +static DECLFR(A2007) +{ + uint8 ret; + uint32 tmp=RefreshAddr&0x3FFF; + + PPUGenLatch=ret=VRAMBuffer; + if(PPU_hook) PPU_hook(tmp); + if(tmp<0x2000) + { + VRAMBuffer=VPage[tmp>>10][tmp]; + } + else + { + VRAMBuffer=vnapage[(tmp>>10)&0x3][tmp&0x3FF]; + } + + if (INC32) RefreshAddr+=32; + else RefreshAddr++; + if(PPU_hook) PPU_hook(RefreshAddr&0x3fff); + return ret; +} + +static DECLFW(B2000) +{ + PPUGenLatch=V; + PPU[0]=V; + TempAddr&=0xF3FF; + TempAddr|=(V&3)<<10; +} + +static DECLFW(B2001) +{ + PPUGenLatch=V; + PPU[1]=V; + if(V&0xE0) + deemp=V>>5; + //printf("$%04x:$%02x, %d\n",X.PC,V,scanline); +} + +static DECLFW(B2002) +{ + PPUGenLatch=V; +} + +static DECLFW(B2003) +{ + PPUGenLatch=V; + PPU[3]=V; + PPUSPL=V&0x7; +} + +static DECLFW(B2004) +{ + PPUGenLatch=V; + //SPRAM[PPU[3]++]=V; + if(PPUSPL&8) + { + if(PPU[3]>=8) + SPRAM[PPU[3]]=V; + } + else + { + //printf("$%02x:$%02x\n",PPUSPL,V); + SPRAM[PPUSPL]=V; + PPUSPL++; + } + PPU[3]++; +} + +static DECLFW(B2005) +{ + uint32 tmp=TempAddr; + + PPUGenLatch=V; + if (!vtoggle) + { + tmp&=0xFFE0; + tmp|=V>>3; + XOffset=V&7; + } + else + { + tmp&=0x8C1F; + tmp|=((V&~0x7)<<2); + tmp|=(V&7)<<12; + } + + TempAddr=tmp; + vtoggle^=1; +} + +static DECLFW(B2006) +{ + PPUGenLatch=V; + if(!vtoggle) + { + TempAddr&=0x00FF; + TempAddr|=(V&0x3f)<<8; + } + else + { + TempAddr&=0xFF00; + TempAddr|=V; + RefreshAddr=TempAddr; + + if(PPU_hook) + PPU_hook(RefreshAddr); + } + vtoggle^=1; +} + +static DECLFW(B2007) +{ + uint32 tmp=RefreshAddr&0x3FFF; + + PPUGenLatch=V; + if(tmp>=0x3F00) + { + // hmmm.... + if(!(tmp&0xf)) + PALRAM[0x00]=PALRAM[0x04]=PALRAM[0x08]=PALRAM[0x0C]= + PALRAM[0x10]=PALRAM[0x14]=PALRAM[0x18]=PALRAM[0x1c]=V&0x3f; + else if(tmp&3) PALRAM[(tmp&0x1f)]=V&0x3f; + } + else if(tmp<0x2000) + { + if(PPUCHRRAM&(1<<(tmp>>10))) + VPage[tmp>>10][tmp]=V; + } + else + { + if(PPUNTARAM&(1<<((tmp&0xF00)>>10))) + vnapage[((tmp&0xF00)>>10)][tmp&0x3FF]=V; + } + if (INC32) RefreshAddr+=32; + else RefreshAddr++; + if(PPU_hook) PPU_hook(RefreshAddr&0x3fff); +} + +static DECLFW(B4014) +{ + uint32 t=V<<8; + int x; + for(x=0;x<256;x++) + B2004(0x2004,X.DB=ARead[t+x](t+x)); + X6502_AddCycles(512); +} + +static void FASTAPASS(1) BGRender(uint8 *target) +{ + uint32 tem; + RefreshLine(target); + if(!(PPU[1]&2)) + { + tem=Pal[0]|(Pal[0]<<8)|(Pal[0]<<16)|(Pal[0]<<24); + tem|=0x40404040; + *(uint32 *)target=*(uint32 *)(target+4)=tem; + } +} + +#ifdef FRAMESKIP +static int FSkip=0; +void FCEUI_FrameSkip(int x) +{ + FSkip=x; +} +#endif + +/* This is called at the beginning of each visible scanline */ +static void Loop6502(void) +{ + uint32 tem; + int x; + uint8 *target=XBuf+(scanline<<8)+(scanline<<4)+8; + + if(ScreenON || SpriteON) + { + /* PRefreshLine() will not get called on skipped frames. This + could cause a problem, but the solution would be rather complex, + due to the current sprite 0 hit code. + */ + #ifdef FRAMESKIP + if(!FSkip) + { + #endif + if(ScreenON) + { + if(scanline>=FSettings.FirstSLine && scanline<=FSettings.LastSLine) + BGRender(target); + else + { + if(PPU_hook) + PRefreshLine(); + } + } + else + { + tem=Pal[0]|(Pal[0]<<8)|(Pal[0]<<16)|(Pal[0]<<24); + tem|=0x40404040; + FCEU_dwmemset(target,tem,264); + } + #ifdef FRAMESKIP + } + #endif + if (SpriteON && scanline) + RefreshSprite(target); + #ifdef FRAMESKIP + if(!FSkip) + { + #endif + if(PPU[1]&0x01) + { + for(x=63;x>=0;x--) + *(uint32 *)&target[x<<2]=(*(uint32*)&target[x<<2])&0xF0F0F0F0; + } + if((PPU[1]>>5)==0x7) + for(x=63;x>=0;x--) + *(uint32 *)&target[x<<2]=((*(uint32*)&target[x<<2])&0x3f3f3f3f)|0x40404040; + else if(PPU[1]&0xE0) + for(x=63;x>=0;x--) + *(uint32 *)&target[x<<2]=(*(uint32*)&target[x<<2])|0xC0C0C0C0; + else + for(x=63;x>=0;x--) + *(uint32 *)&target[x<<2]=(*(uint32*)&target[x<<2])&0x3f3f3f3f; + + #ifdef FRAMESKIP + } + #endif + } + else + { + tem=Pal[0]|(Pal[0]<<8)|(Pal[0]<<16)|(Pal[0]<<24); + FCEU_dwmemset(target,tem,256); + } + if(InputScanlineHook) + InputScanlineHook(target, scanline); +} + +#define PAL(c) ((c)+cc) + + +static void PRefreshLine(void) +{ + uint32 vofs; + uint8 X1; + + vofs = 0; + if (BGAdrHI) vofs = 0x1000; + + vofs+=(RefreshAddr>>12)&7; + + for(X1=33;X1;X1--) + { + register uint8 no; + register uint8 zz2; + zz2=(uint8)((RefreshAddr>>10)&3); + PPU_hook(0x2000|(RefreshAddr&0xFFF)); + no = vnapage[zz2][(RefreshAddr&0x3ff)]; + PPU_hook((no<<4)+vofs); + if((RefreshAddr&0x1f)==0x1f) + RefreshAddr^=0x41F; + else + RefreshAddr++; + } +} + +/* Total of 33 tiles(32 + 1 extra) */ +static void FASTAPASS(1) RefreshLine(uint8 *target) +{ + uint32 vofs; + int X1; + register uint8 *P=target; + + vofs=0; + + Pal[0]|=64; + Pal[4]|=64; + Pal[8]|=64; + Pal[0xC]|=64; + + vofs=((PPU[0]&0x10)<<8) | ((RefreshAddr>>12)&7); + P-=XOffset; + + /* This high-level graphics MMC5 emulation code was written + for MMC5 carts in "CL" mode. It's probably not totally + correct for carts in "SL" mode. + */ + if(MMC5Hack && geniestage!=1) + { + if(MMC5HackCHRMode==0 && (MMC5HackSPMode&0x80)) + { + int8 tochange; + + tochange=MMC5HackSPMode&0x1F; + + for(X1=33;X1;X1--,P+=8) + { + uint8 *C; + register uint8 cc,zz,zz2; + uint32 vadr; + + if((tochange<=0 && MMC5HackSPMode&0x40) || + (tochange>0 && !(MMC5HackSPMode&0x40))) + { + uint8 xs,ys; + + xs=33-X1; + ys=((scanline>>3)+MMC5HackSPScroll)&0x1F; + if(ys>=0x1E) ys-=0x1E; + vadr=(MMC5HackExNTARAMPtr[xs|(ys<<5)]<<4)+(vofs&7); + + C = MMC5HackVROMPTR+vadr; + C += ((MMC5HackSPPage & 0x3f & MMC5HackVROMMask) << 12); + + cc=MMC5HackExNTARAMPtr[0x3c0+(xs>>2)+((ys&0x1C)<<1)]; + cc=((cc >> ((xs&2) + ((ys&0x2)<<1))) &3) <<2; + } + else + { + zz=RefreshAddr&0x1F; + zz2=(RefreshAddr>>10)&3; + vadr=(vnapage[zz2][RefreshAddr&0x3ff]<<4)+vofs; + C = MMC5BGVRAMADR(vadr); + cc=vnapage[zz2][0x3c0+(zz>>2)+((RefreshAddr&0x380)>>4)]; + cc=((cc >> ((zz&2) + ((RefreshAddr&0x40)>>4))) &3) <<2; + } + #include "fceline.h" + + if((RefreshAddr&0x1f)==0x1f) + RefreshAddr^=0x41F; + else + RefreshAddr++; + tochange--; + } + } + else if(MMC5HackCHRMode==1 && (MMC5HackSPMode&0x80)) + { + int8 tochange; + + tochange=MMC5HackSPMode&0x1F; + + for(X1=33;X1;X1--,P+=8) + { + uint8 *C; + register uint8 cc; + register uint8 zz2; + uint32 vadr; + + if((tochange<=0 && MMC5HackSPMode&0x40) || + (tochange>0 && !(MMC5HackSPMode&0x40))) + { + uint8 xs,ys; + + xs=33-X1; + ys=((scanline>>3)+MMC5HackSPScroll)&0x1F; + if(ys>=0x1E) ys-=0x1E; + vadr=(MMC5HackExNTARAMPtr[xs|(ys<<5)]<<4)+(vofs&7); + + C = MMC5HackVROMPTR+vadr; + C += ((MMC5HackSPPage & 0x3f & MMC5HackVROMMask) << 12); + + cc=MMC5HackExNTARAMPtr[0x3c0+(xs>>2)+((ys&0x1C)<<1)]; + cc=((cc >> ((xs&2) + ((ys&0x2)<<1))) &3) <<2; + } + else + { + C=MMC5HackVROMPTR; + zz2=(RefreshAddr>>10)&3; + vadr = (vnapage[zz2][RefreshAddr & 0x3ff] << 4) + vofs; + C += (((MMC5HackExNTARAMPtr[RefreshAddr & 0x3ff]) & 0x3f & + MMC5HackVROMMask) << 12) + (vadr & 0xfff); + vadr = (MMC5HackExNTARAMPtr[RefreshAddr & 0x3ff] & 0xC0)>> 4; + cc = vadr; + } + #include "fceline.h" + if((RefreshAddr&0x1f)==0x1f) + RefreshAddr^=0x41F; + else + RefreshAddr++; + tochange--; + } + } + + else if(MMC5HackCHRMode==1) + { + for(X1=33;X1;X1--,P+=8) + { + uint8 *C; + register uint8 cc; + register uint8 zz2; + uint32 vadr; + + C=MMC5HackVROMPTR; + zz2=(RefreshAddr>>10)&3; + vadr = (vnapage[zz2][RefreshAddr & 0x3ff] << 4) + vofs; + C += (((MMC5HackExNTARAMPtr[RefreshAddr & 0x3ff]) & 0x3f & + MMC5HackVROMMask) << 12) + (vadr & 0xfff); + vadr = (MMC5HackExNTARAMPtr[RefreshAddr & 0x3ff] & 0xC0)>> 4; + cc = vadr; + + #include "fceline.h" + if((RefreshAddr&0x1f)==0x1f) + RefreshAddr^=0x41F; + else + RefreshAddr++; + } + } + else + { + for(X1=33;X1;X1--,P+=8) + { + uint8 *C; + register uint8 cc,zz,zz2; + uint32 vadr; + + zz=RefreshAddr&0x1F; + zz2=(RefreshAddr>>10)&3; + vadr=(vnapage[zz2][RefreshAddr&0x3ff]<<4)+vofs; + C = MMC5BGVRAMADR(vadr); + cc=vnapage[zz2][0x3c0+(zz>>2)+((RefreshAddr&0x380)>>4)]; + cc=((cc >> ((zz&2) + ((RefreshAddr&0x40)>>4))) &3) <<2; + + #include "fceline.h" + + if((RefreshAddr&0x1f)==0x1f) + RefreshAddr^=0x41F; + else + RefreshAddr++; + } + } + } // End if(MMC5Hack) + + else if(PPU_hook) + { + for(X1=33;X1;X1--,P+=8) + { + uint8 *C; + register uint8 cc,zz,zz2; + uint32 vadr; + + zz=RefreshAddr&0x1F; + zz2=(RefreshAddr>>10)&3; + PPU_hook(0x2000|(RefreshAddr&0xFFF)); + cc=vnapage[zz2][0x3c0+(zz>>2)+((RefreshAddr&0x380)>>4)]; + cc=((cc >> ((zz&2) + ((RefreshAddr&0x40)>>4))) &3) <<2; + vadr=(vnapage[zz2][RefreshAddr&0x3ff]<<4)+vofs; + C = VRAMADR(vadr); + + #include "fceline.h" + + PPU_hook(vadr); + + if((RefreshAddr&0x1f)==0x1f) + RefreshAddr^=0x41F; + else + RefreshAddr++; + } + } + else + { + for(X1=33;X1;X1--,P+=8) + { + uint8 *C; + register uint8 cc,zz,zz2; + uint32 vadr; + + zz=RefreshAddr&0x1F; + zz2=(RefreshAddr>>10)&3; + vadr=(vnapage[zz2][RefreshAddr&0x3ff]<<4)+vofs; + C = VRAMADR(vadr); + cc=vnapage[zz2][0x3c0+(zz>>2)+((RefreshAddr&0x380)>>4)]; + cc=((cc >> ((zz&2) + ((RefreshAddr&0x40)>>4))) &3) <<2; + #include "fceline.h" + + if((RefreshAddr&0x1f)==0x1f) + RefreshAddr^=0x41F; + else + RefreshAddr++; + } + } + + #undef vofs + + Pal[0]&=63; + Pal[4]&=63; + Pal[8]&=63; + Pal[0xC]&=63; +} + +static INLINE void Fixit2(void) +{ + if(ScreenON || SpriteON) + { + uint32 rad=RefreshAddr; + rad&=0xFBE0; + rad|=TempAddr&0x041f; + RefreshAddr=rad; + //PPU_hook(RefreshAddr,-1); + } +} + +static INLINE void Fixit1(void) +{ + if(ScreenON || SpriteON) + { + uint32 rad=RefreshAddr; + + if((rad&0x7000)==0x7000) + { + rad^=0x7000; + if((rad&0x3E0)==0x3A0) + { + rad^=0x3A0; + rad^=0x800; + } + else + { + if((rad&0x3E0)==0x3e0) + rad^=0x3e0; + else rad+=0x20; + } + } + else + rad+=0x1000; + RefreshAddr=rad; + //PPU_hook(RefreshAddr,-1); + } +} + +/* This is called at the beginning of all h-blanks on visible lines. */ +static void DoHBlank(void) +{ + if(ScreenON || SpriteON) + FetchSpriteData(); + if(GameHBIRQHook && (ScreenON || SpriteON)) + { + X6502_Run(12); + GameHBIRQHook(); + X6502_Run(25-12); + Fixit2(); + X6502_Run(85-25); + } + else + { + X6502_Run(25); // Tried 65, caused problems with Slalom(maybe others) + Fixit2(); + X6502_Run(85-25); + } + //PPU_hook(0,-1); + //fprintf(stderr,"%3d: $%04x\n",scanline,RefreshAddr); +} + +#define V_FLIP 0x80 +#define H_FLIP 0x40 +#define SP_BACK 0x20 + +typedef struct { + uint8 y,no,atr,x; +} SPR; + +typedef struct { + uint8 ca[2],atr,x; +} SPRB; + +uint8 sprlinebuf[256+8]; + +void FCEUI_DisableSpriteLimitation(int a) +{ + maxsprites=a?64:8; +} + +static uint8 nosprites,SpriteBlurp; + +static void FetchSpriteData(void) +{ + SPR *spr; + uint8 H; + int n,vofs; + + spr=(SPR *)SPRAM; + H=8; + + nosprites=SpriteBlurp=0; + + vofs=(unsigned int)(PPU[0]&0x8&(((PPU[0]&0x20)^0x20)>>2))<<9; + H+=(PPU[0]&0x20)>>2; + + if(!PPU_hook) + for(n=63;n>=0;n--,spr++) + { + if((unsigned int)(scanline-spr->y)>=H) continue; + + if(nospritesy); + + if (Sprite16) + vadr = ((spr->no&1)<<12) + ((spr->no&0xFE)<<4); + else + vadr = (spr->no<<4)+vofs; + + if (spr->atr&V_FLIP) + { + vadr+=7; + vadr-=t; + vadr+=(PPU[0]&0x20)>>1; + vadr-=t&8; + } + else + { + vadr+=t; + vadr+=t&8; + } + + /* Fix this geniestage hack */ + if(MMC5Hack && geniestage!=1) C = MMC5SPRVRAMADR(vadr); + else C = VRAMADR(vadr); + + + dst.ca[0]=C[0]; + dst.ca[1]=C[8]; + dst.x=spr->x; + dst.atr=spr->atr; + + + *(uint32 *)&SPRBUF[nosprites<<2]=*(uint32 *)&dst; + } + + nosprites++; + } + else + { + PPU_status|=0x20; + break; + } + } + else + for(n=63;n>=0;n--,spr++) + { + if((unsigned int)(scanline-spr->y)>=H) continue; + + if(nospritesy); + + if (Sprite16) + vadr = ((spr->no&1)<<12) + ((spr->no&0xFE)<<4); + else + vadr = (spr->no<<4)+vofs; + + if (spr->atr&V_FLIP) + { + vadr+=7; + vadr-=t; + vadr+=(PPU[0]&0x20)>>1; + vadr-=t&8; + } + else + { + vadr+=t; + vadr+=t&8; + } + + if(MMC5Hack) C = MMC5SPRVRAMADR(vadr); + else C = VRAMADR(vadr); + dst.ca[0]=C[0]; + PPU_hook(vadr); + dst.ca[1]=C[8]; + PPU_hook(vadr|8); + dst.x=spr->x; + dst.atr=spr->atr; + + + *(uint32 *)&SPRBUF[nosprites<<2]=*(uint32 *)&dst; + } + + nosprites++; + } + else + { + PPU_status|=0x20; + break; + } + } +} + +static void FASTAPASS(1) RefreshSprite(uint8 *target) +{ + int n; + SPRB *spr; + uint8 *P=target; + + if(!nosprites) return; + #ifdef FRAMESKIP + if(FSkip) + { + if(!SpriteBlurp) + { + nosprites=0; + return; + } + else + nosprites=1; + } + #endif + + FCEU_dwmemset(sprlinebuf,0x80808080,256); + nosprites--; + spr = (SPRB*)SPRBUF+nosprites; + + for(n=nosprites;n>=0;n--,spr--) + { + register uint8 J,atr,c1,c2; + int x=spr->x; + uint8 *C; + uint8 *VB; + + P+=x; + + c1=((spr->ca[0]>>1)&0x55)|(spr->ca[1]&0xAA); + c2=(spr->ca[0]&0x55)|((spr->ca[1]<<1)&0xAA); + + J=spr->ca[0]|spr->ca[1]; + atr=spr->atr; + + if(J) + { + if(n==0 && SpriteBlurp && !(PPU_status&0x40)) + { + int z,ze=x+8; + if(ze>256) {ze=256;} + if(ScreenON && (scanlineFSettings.LastSLine + #ifdef FRAMESKIP + || FSkip + #endif + )) + BGRender(target); + + if(!(atr&H_FLIP)) + { + for(z=x;z>(z-x))) + { + if(!(target[z]&64)) + tosprite=z; + } + } + } + else + { + for(z=x;z>=2;c2>>=2; + if (J&0x08) C[3]=VB[c1&3]|0x40;; + if (J&0x04) C[2]=VB[c2&3]|0x40;; + c1>>=2;c2>>=2; + if (J&0x20) C[5]=VB[c1&3]|0x40;; + if (J&0x10) C[4]=VB[c2&3]|0x40;; + c1>>=2;c2>>=2; + if (J&0x80) C[7]=VB[c1]|0x40;; + if (J&0x40) C[6]=VB[c2]|0x40;; + } else { + if (J&0x02) C[6]=VB[c1&3]|0x40; + if (J&0x01) C[7]=VB[c2&3]|0x40; + c1>>=2;c2>>=2; + if (J&0x08) C[4]=VB[c1&3]|0x40; + if (J&0x04) C[5]=VB[c2&3]|0x40; + c1>>=2;c2>>=2; + if (J&0x20) C[2]=VB[c1&3]|0x40; + if (J&0x10) C[3]=VB[c2&3]|0x40; + c1>>=2;c2>>=2; + if (J&0x80) *C=VB[c1]|0x40; + if (J&0x40) C[1]=VB[c2]|0x40; + } + } else { + if (atr&H_FLIP) + { + if (J&0x02) C[1]=VB[(c1&3)]; + if (J&0x01) *C=VB[(c2&3)]; + c1>>=2;c2>>=2; + if (J&0x08) C[3]=VB[(c1&3)]; + if (J&0x04) C[2]=VB[(c2&3)]; + c1>>=2;c2>>=2; + if (J&0x20) C[5]=VB[(c1&3)]; + if (J&0x10) C[4]=VB[(c2&3)]; + c1>>=2;c2>>=2; + if (J&0x80) C[7]=VB[c1]; + if (J&0x40) C[6]=VB[c2]; + }else{ + if (J&0x02) C[6]=VB[(c1&3)]; + if (J&0x01) C[7]=VB[(c2&3)]; + c1>>=2;c2>>=2; + if (J&0x08) C[4]=VB[(c1&3)]; + if (J&0x04) C[5]=VB[(c2&3)]; + c1>>=2;c2>>=2; + if (J&0x20) C[2]=VB[(c1&3)]; + if (J&0x10) C[3]=VB[(c2&3)]; + c1>>=2;c2>>=2; + if (J&0x80) *C=VB[c1]; + if (J&0x40) C[1]=VB[c2]; + } + } + } + P-=x; + } + + nosprites=0; + #ifdef FRAMESKIP + if(FSkip) return; + #endif + + { + uint8 n=((PPU[1]&4)^4)<<1; + loopskie: + { + uint32 t=*(uint32 *)(sprlinebuf+n); + if(t!=0x80808080) + { + #ifdef LSB_FIRST + if(!(t&0x80)) + { + if(!(t&0x40)) // Normal sprite + P[n]=sprlinebuf[n]; + else if(P[n]&64) // behind bg sprite + P[n]=sprlinebuf[n]; + } + + if(!(t&0x8000)) + { + if(!(t&0x4000)) // Normal sprite + P[n+1]=(sprlinebuf+1)[n]; + else if(P[n+1]&64) // behind bg sprite + P[n+1]=(sprlinebuf+1)[n]; + } + + if(!(t&0x800000)) + { + if(!(t&0x400000)) // Normal sprite + P[n+2]=(sprlinebuf+2)[n]; + else if(P[n+2]&64) // behind bg sprite + P[n+2]=(sprlinebuf+2)[n]; + } + + if(!(t&0x80000000)) + { + if(!(t&0x40000000)) // Normal sprite + P[n+3]=(sprlinebuf+3)[n]; + else if(P[n+3]&64) // behind bg sprite + P[n+3]=(sprlinebuf+3)[n]; + } + #else + if(!(t&0x80000000)) + { + if(!(t&0x40)) // Normal sprite + P[n]=sprlinebuf[n]; + else if(P[n]&64) // behind bg sprite + P[n]=sprlinebuf[n]; + } + + if(!(t&0x800000)) + { + if(!(t&0x4000)) // Normal sprite + P[n+1]=(sprlinebuf+1)[n]; + else if(P[n+1]&64) // behind bg sprite + P[n+1]=(sprlinebuf+1)[n]; + } + + if(!(t&0x8000)) + { + if(!(t&0x400000)) // Normal sprite + P[n+2]=(sprlinebuf+2)[n]; + else if(P[n+2]&64) // behind bg sprite + P[n+2]=(sprlinebuf+2)[n]; + } + + if(!(t&0x80)) + { + if(!(t&0x40000000)) // Normal sprite + P[n+3]=(sprlinebuf+3)[n]; + else if(P[n+3]&64) // behind bg sprite + P[n+3]=(sprlinebuf+3)[n]; + } + #endif + } + } + n+=4; + if(n) goto loopskie; + } +} + +void ResetMapping(void) +{ + int x; + + SetReadHandler(0x0000,0xFFFF,ANull); + SetWriteHandler(0x0000,0xFFFF,BNull); + + SetReadHandler(0,0x7FF,ARAML); + SetWriteHandler(0,0x7FF,BRAML); + + SetReadHandler(0x800,0x1FFF,ARAMH); /* Part of a little */ + SetWriteHandler(0x800,0x1FFF,BRAMH); /* hack for a small speed boost. */ + + for(x=0x2000;x<0x4000;x+=8) + { + ARead[x]=A200x; + BWrite[x]=B2000; + ARead[x+1]=A200x; + BWrite[x+1]=B2001; + ARead[x+2]=A2002; + BWrite[x+2]=B2002; + ARead[x+3]=A200x; + BWrite[x+3]=B2003; + ARead[x+4]=A200x; + BWrite[x+4]=B2004; + ARead[x+5]=A200x; + BWrite[x+5]=B2005; + ARead[x+6]=A200x; + BWrite[x+6]=B2006; + ARead[x+7]=A2007; + BWrite[x+7]=B2007; + } + + BWrite[0x4014]=B4014; + SetNESSoundMap(); + InitializeInput(); +} + +int GameLoaded=0; +void CloseGame(void) +{ + if(GameLoaded) + { + if(FCEUGameInfo.type!=GIT_NSF) + FlushGameCheats(); + #ifdef NETWORK + if(FSettings.NetworkPlay) KillNetplay(); + #endif + GameInterface(GI_CLOSE); + CloseGenie(); + GameLoaded=0; + } +} + +void ResetGameLoaded(void) +{ + if(GameLoaded) CloseGame(); + GameStateRestore=0; + PPU_hook=0; + GameHBIRQHook=0; + GameExpSound.Fill=0; + GameExpSound.RChange=0; + if(GameExpSound.Kill) + GameExpSound.Kill(); + GameExpSound.Kill=0; + MapIRQHook=0; + MMC5Hack=0; + PAL&=1; + pale=0; + + FCEUGameInfo.name=0; + FCEUGameInfo.type=GIT_CART; + FCEUGameInfo.vidsys=GIV_USER; + FCEUGameInfo.input[0]=FCEUGameInfo.input[1]=-1; + FCEUGameInfo.inputfc=-1; +} + +FCEUGI *FCEUI_LoadGame(char *name) +{ + int fp; + + Exit=1; + ResetGameLoaded(); + + fp=FCEU_fopen(name,"rb"); + if(!fp) + { + FCEU_PrintError("Error opening \"%s\"!",name); + return 0; + } + + GetFileBase(name); + if(iNESLoad(name,fp)) + goto endlseq; + if(NSFLoad(fp)) + goto endlseq; + if(FDSLoad(name,fp)) + goto endlseq; + if(UNIFLoad(name,fp)) + goto endlseq; + + FCEU_PrintError("An error occurred while loading the file."); + FCEU_fclose(fp); + return 0; + + endlseq: + FCEU_fclose(fp); + GameLoaded=1; + + FCEU_ResetVidSys(); + if(FCEUGameInfo.type!=GIT_NSF) + if(FSettings.GameGenie) + OpenGenie(); + + PowerNES(); + #ifdef NETWORK + if(FSettings.NetworkPlay) InitNetplay(); + #endif + SaveStateRefresh(); + if(FCEUGameInfo.type!=GIT_NSF) + { + LoadGamePalette(); + LoadGameCheats(); + } + + FCEU_ResetPalette(); + Exit=0; + return(&FCEUGameInfo); +} + + +void FCEU_ResetVidSys(void) +{ + int w; + + if(FCEUGameInfo.vidsys==GIV_NTSC) + w=0; + else if(FCEUGameInfo.vidsys==GIV_PAL) + w=1; + else + w=FSettings.PAL; + + if(w) + { + PAL=1; + scanlines_per_frame=312; + FSettings.FirstSLine=FSettings.UsrFirstSLine[1]; + FSettings.LastSLine=FSettings.UsrLastSLine[1]; + } + else + { + PAL=0; + scanlines_per_frame=262; + FSettings.FirstSLine=FSettings.UsrFirstSLine[0]; + FSettings.LastSLine=FSettings.UsrLastSLine[0]; + } + SetSoundVariables(); +} + +int FCEUI_Initialize(void) +{ + if(!InitVirtualVideo()) + return 0; + memset(&FSettings,0,sizeof(FSettings)); + FSettings.UsrFirstSLine[0]=8; + FSettings.UsrFirstSLine[1]=0; + FSettings.UsrLastSLine[0]=FSettings.UsrLastSLine[1]=239; + FSettings.SoundVolume=65536; // 100% + return 1; +} + +#define harko 0xe //0x9 +static INLINE void Thingo(void) +{ + Loop6502(); + + if(tosprite>=256) + { + X6502_Run(256-harko); + Fixit1(); + X6502_Run(harko); + } + else + { + if(tosprite<=240) + { + X6502_Run(tosprite); + PPU[2]|=0x40; + X6502_Run(256-tosprite-harko); + Fixit1(); + X6502_Run(harko); + } + else + { + X6502_Run(256-harko); + Fixit1(); + X6502_Run(tosprite-(256-harko)); + PPU[2]|=0x40; + X6502_Run(256-tosprite); + } + tosprite=256; + } + DoHBlank(); +} +#undef harko + +void EmLoop(void) +{ + for(;;) + { + ApplyPeriodicCheats(); + X6502_Run(256+85); + + PPU[2]|=0x80; + PPU[3]=PPUSPL=0; /* Not sure if this is correct. According + to Matt Conte and my own tests, it is. Timing is probably + off, though. NOTE: Not having this here + breaks a Super Donkey Kong game. */ + + X6502_Run(12); /* I need to figure out the true nature and length + of this delay. + */ + if(FCEUGameInfo.type==GIT_NSF) + TriggerNMINSF(); + else if(VBlankON) + TriggerNMI(); + + X6502_Run((scanlines_per_frame-242)*(256+85)-12); + + PPU_status&=0x1f; + + X6502_Run(256); + { + static int kook=0; + if(ScreenON || SpriteON) + if(GameHBIRQHook) + GameHBIRQHook(); + + X6502_Run(85-kook); + kook=(kook+1)&1; + } + + if(ScreenON || SpriteON) + { + RefreshAddr=TempAddr; + if(PPU_hook) PPU_hook(RefreshAddr&0x3fff); + } + if(FCEUGameInfo.type==GIT_NSF) + X6502_Run((256+85)*240); + else + { + int x,max,maxref; + + deemp=PPU[1]>>5; + for(scanline=0;scanline<240;scanline++) + { + deempcnt[deemp]++; + Thingo(); + } + for(x=1,max=0,maxref=0;x<7;x++) + { + if(deempcnt[x]>max) + { + max=deempcnt[x]; + maxref=x; + } + deempcnt[x]=0; + } + //FCEU_DispMessage("%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x %d",deempcnt[0],deempcnt[1],deempcnt[2],deempcnt[3],deempcnt[4],deempcnt[5],deempcnt[6],deempcnt[7],maxref); + //memset(deempcnt,0,sizeof(deempcnt)); + SetNESDeemph(maxref,0); + } + + { + int ssize; + + ssize=FlushEmulateSound(); + + #ifdef FRAMESKIP + if(FSkip) + { + FCEU_PutImageDummy(); + FSkip--; + FCEUD_Update(0,WaveFinal,ssize); + } + else + #endif + { + FCEU_PutImage(); + FCEUD_Update(XBuf+8,WaveFinal,ssize); + } + UpdateInput(); + } + + if(Exit) + { + CloseGame(); + break; + } + + } +} + +#ifdef FPS +#include +uint64 frcount; +#endif +void FCEUI_Emulate(void) +{ + #ifdef FPS + uint64 starttime,end; + struct timeval tv; + frcount=0; + gettimeofday(&tv,0); + starttime=((uint64)tv.tv_sec*1000000)+tv.tv_usec; + #endif + EmLoop(); + + #ifdef FPS + // Probably won't work well on Windows port; for + // debugging/speed testing. + { + uint64 w; + int i,frac; + gettimeofday(&tv,0); + end=((uint64)tv.tv_sec*1000000)+tv.tv_usec; + w=frcount*10000000000LL/(end-starttime); + i=w/10000; + frac=w-i*10000; + printf("Average FPS: %d.%04d\n",i,frac); + } + #endif + +} + +void FCEUI_CloseGame(void) +{ + Exit=1; +} + +static void ResetPPU(void) +{ + VRAMBuffer=PPU[0]=PPU[1]=PPU[2]=PPU[3]=0; + PPUSPL=0; + PPUGenLatch=0; + RefreshAddr=TempAddr=0; + vtoggle = 0; +} + +static void PowerPPU(void) +{ + memset(NTARAM,0x00,0x800); + memset(PALRAM,0x00,0x20); + memset(SPRAM,0x00,0x100); + ResetPPU(); +} + +void ResetNES(void) +{ + if(!GameLoaded || (FCEUGameInfo.type==GIT_NSF)) return; + GameInterface(GI_RESETM2); + ResetSound(); + ResetPPU(); + X6502_Reset(); +} + +void PowerNES(void) +{ + if(!GameLoaded) return; + + FCEU_CheatResetRAM(); + FCEU_CheatAddRAM(2,0,RAM); + + GeniePower(); + + memset(RAM,0x00,0x800); + ResetMapping(); + GameInterface(GI_POWER); + PowerSound(); + PowerPPU(); + timestampbase=0; + X6502_Power(); +} + diff --git a/fce.h b/fce.h new file mode 100644 index 0000000..ba90d75 --- /dev/null +++ b/fce.h @@ -0,0 +1,81 @@ +#ifndef _FCEH +extern int GameLoaded; +void ResetGameLoaded(void); + +#define DECLFR(x) uint8 FP_FASTAPASS(1) x (uint32 A) +#define DECLFW(x) void FP_FASTAPASS(2) x (uint32 A, uint8 V) + +void FASTAPASS(3) SetReadHandler(int32 start, int32 end, readfunc func); +void FASTAPASS(3) SetWriteHandler(int32 start, int32 end, writefunc func); +writefunc FASTAPASS(1) GetWriteHandler(int32 a); +readfunc FASTAPASS(1) GetReadHandler(int32 a); + +int AllocGenieRW(void); +void FlushGenieRW(void); + +void FCEU_ResetVidSys(void); + +void ResetMapping(void); +void ResetNES(void); +void PowerNES(void); + + +extern uint64 timestampbase; +extern uint32 MMC5HackVROMMask; +extern uint8 *MMC5HackExNTARAMPtr; +extern int MMC5Hack; +extern uint8 *MMC5HackVROMPTR; +extern uint8 MMC5HackCHRMode; +extern uint8 MMC5HackSPMode; +extern uint8 MMC5HackSPScroll; +extern uint8 MMC5HackSPPage; + +extern uint8 RAM[0x800],SPRAM[0x100],NTARAM[0x800],PALRAM[0x20],SPRAM[0x100],SPRBUF[0x100]; +extern uint8 GameMemBlock[131072]; + +extern uint32 RefreshAddr,TempAddr; +extern uint8 vtoggle,XOffset,VRAMBuffer,PPUGenLatch; +extern uint8 PPU[4]; + +extern int scanline; +extern uint8 *vnapage[4]; + +extern uint8 PPUNTARAM; +extern uint8 PPUCHRRAM; +extern uint8 VPAL[8]; +extern uint8 PAL; + +extern readfunc ARead[0x10000]; +extern writefunc BWrite[0x10000]; + +#define VBlankON (PPU[0]&0x80) /* Generate VBlank NMI */ +#define SpHitON (PPU[0]&0x40) +#define Sprite16 (PPU[0]&0x20) /* Sprites 8x16/8x8 */ +#define BGAdrHI (PPU[0]&0x10) /* BG pattern adr $0000/$1000 */ +#define SpAdrHI (PPU[0]&0x08) /* Sprite pattern adr $0000/$1000 */ +#define INC32 (PPU[0]&0x04) /* auto increment 1/32 */ +#define NameTable (PPU[0]&0x3) /* name table $2000/$2400/$2800/$2C00 */ + +#define SpriteON (PPU[1]&0x10) /* Show Sprite */ +#define ScreenON (PPU[1]&0x08) /* Show screen */ +#define PPU_status (PPU[2]) + + +extern void (*GameInterface)(int h); +extern void FP_FASTAPASS(1) (*PPU_hook)(uint32 A); +extern void (*GameHBIRQHook)(void); +extern void (*GameStateRestore)(int version); + +#define GI_RESETM2 1 +#define GI_POWER 2 +#define GI_CLOSE 3 + +#include "git.h" +extern FCEUGI FCEUGameInfo; +extern int GameAttributes; + + +#endif + +#define _FCEH + diff --git a/fceline.h b/fceline.h new file mode 100644 index 0000000..5ba0ab7 --- /dev/null +++ b/fceline.h @@ -0,0 +1,112 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 1998 BERO + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifdef C80x86 +{ + int dummy,dummy1,dummy2; + __asm__ __volatile__( + "xorl %%edx,%%edx\n\t" + "movb (%%esi),%%cl\n\t" + "movb 8(%%esi),%%dl\n\t" + "movl %%ebx,%%esi\n\t" + "addl %%eax,%%esi\n\t" + "xorl %%ebx,%%ebx\n\t" + "movb %%cl,%%bl\n\t" + "movb %%dl,%%al\n\t" + "shrb $1,%%bl\n\t" + "andb $0xaa,%%al\n\t" + "andb $0x55,%%bl\n\t" + + "andb $0x55,%%cl\n\t" + "shlb $1,%%dl\n\t" + "andb $0xaa,%%dl\n\t" + "orb %%al, %%bl\n\t" // Stick c1 into bl + "orb %%cl, %%dl\n\t" // Stick c2 into dl + "xorl %%eax, %%eax\n\t" + "xorl %%ecx, %%ecx\n\t" + /* At this point, bl contains c1, and dl contains c2 */ + /* and edi contains P, esi contains VRAM[] */ + /* al will be used for zz, cl will be used for zz2 */ + "movb %%bl,%%al\n\t" + "movb %%dl,%%cl\n\t" + "andb $3,%%al\n\t" + "andb $3,%%cl\n\t" + "movb (%%esi,%%eax),%%bh\n\t" + "movb (%%esi,%%ecx),%%dh\n\t" + "movb %%bh,6(%%edi)\n\t" + "movb %%dh,7(%%edi)\n\t" + + "movb %%bl,%%al\n\t" + "movb %%dl,%%cl\n\t" + "shrb $2,%%al\n\t" + "shrb $2,%%cl\n\t" + "andb $3,%%al\n\t" + "andb $3,%%cl\n\t" + "movb (%%esi,%%eax),%%bh\n\t" + "movb (%%esi,%%ecx),%%dh\n\t" + "movb %%bh,4(%%edi)\n\t" + "movb %%dh,5(%%edi)\n\t" + + "movb %%bl,%%al\n\t" + "movb %%dl,%%cl\n\t" + "shrb $4,%%al\n\t" + "shrb $4,%%cl\n\t" + "andb $3,%%al\n\t" + "andb $3,%%cl\n\t" + "movb (%%esi,%%eax),%%bh\n\t" + "movb (%%esi,%%ecx),%%dh\n\t" + "movb %%bh,2(%%edi)\n\t" + "movb %%dh,3(%%edi)\n\t" + +// "movb %%bl,%%al\n\t" +// "movb %%dl,%%cl\n\t" + "xorb %%bh,%%bh\n\t" + "xorb %%dh,%%dh\n\t" + "shrb $6,%%bl\n\t" + "shrb $6,%%dl\n\t" + "movb (%%esi,%%ebx),%%al\n\t" + "movb (%%esi,%%edx),%%cl\n\t" + "movb %%al,0(%%edi)\n\t" + "movb %%cl,1(%%edi)\n\t" + : "=S" (dummy), "=a" (dummy1), "=b" (dummy2) + : "D" (P), "S" (C), "a" (cc), "b" (PALRAM) + : "%ecx", "%edx" + ); + +} +#else + { + uint8 *S=PALRAM+cc; + register uint8 c1,c2; + + c1=((C[0]>>1)&0x55)|(C[8]&0xAA); + c2=(C[0]&0x55)|((C[8]<<1)&0xAA); + + P[6]=S[c1&3]; + P[7]=S[c2&3]; + P[4]=S[(c1>>2)&3]; + P[5]=S[(c2>>2)&3]; + P[2]=S[(c1>>4)&3]; + P[3]=S[(c2>>4)&3]; + + P[0]=S[c1>>6]; + P[1]=S[c2>>6]; + } +#endif diff --git a/fds.c b/fds.c new file mode 100644 index 0000000..07445e7 --- /dev/null +++ b/fds.c @@ -0,0 +1,709 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#include "types.h" +#include "x6502.h" +#include "version.h" +#include "fce.h" +#include "fds.h" +#include "svga.h" +#include "sound.h" +#include "general.h" +#include "state.h" +#include "file.h" +#include "memory.h" +#include "cart.h" + +/* TODO: Add code to put a delay in between the time a disk is inserted + and the when it can be successfully read/written to. This should + prevent writes to wrong places OR add code to prevent disk ejects + when the virtual motor is on(mmm...virtual motor). +*/ + +static DECLFR(FDSRead4030); +static DECLFR(FDSRead4031); +static DECLFR(FDSRead4032); +static DECLFR(FDSRead4033); +static DECLFW(FDSWrite4020); +static DECLFW(FDSWrite4021); +static DECLFW(FDSWrite4022); +static DECLFW(FDSWrite4023); +static DECLFW(FDSWrite4024); +static DECLFW(FDSWrite4025); +static DECLFW(FDSWaveWrite); +static DECLFR(FDSWaveRead); + +static DECLFR(FDSSRead); +static DECLFW(FDSSWrite); +static DECLFR(FDSBIOSRead); +static DECLFR(FDSRAMRead); +static DECLFW(FDSRAMWrite); +static void FDSInit(void); +static void FDSReset(void); +static void FP_FASTAPASS(1) FDSFix(int a); + +#define FDSRAM GameMemBlock +#define mapbyte1 (GameMemBlock+32768) +#define mapbyte2 (GameMemBlock+32768+8) +#define mapbyte3 (GameMemBlock+32768+16) +#define mapbyte4 (GameMemBlock+32768+24) // 8 bytes +#define CHRRAM (GameMemBlock+32768+32+64) + +#define IRQLatch (*(int32*)(CHRRAM+8192)) +#define IRQCount (*(int32*)(CHRRAM+8192+4)) +#define IRQa (*(CHRRAM+8192+8)) + +static void FDSClose(void); +static uint8 header[16]; +#define writeskip mapbyte2[0] + +static char FDSSaveName[2048]; + +uint8 FDSBIOS[8192]; +uint8 *diskdata[4]={0,0,0,0}; + +#define SideWrite mapbyte2[1] +#define DiskPtr (*(uint32*)(mapbyte2+4)) +#define dsr0 mapbyte2[2] +#define dsr1 mapbyte2[3] + +#define DC_INC 1 + +#define DiskSeekIRQ (*(int32*)(mapbyte3+4)) +#define SelectDisk mapbyte3[0] +#define InDisk mapbyte3[1] + +static void FDSReset(void) +{ + memset(mapbyte1,0,8); + memset(mapbyte2,0,8); + memset(mapbyte3+4,0,4); + memset(mapbyte4,0,8); +} + +void FDSGI(int h) +{ + switch(h) + { + case GI_CLOSE: FDSClose();break; + case GI_POWER: FDSReset();FDSInit();break; + } +} + +static void FDSStateRestore(int version) +{ + setmirror(((mapbyte1[5]&8)>>3)^1); +} + +void FDSSound(); +void FDSSoundReset(void); +void FDSSoundStateAdd(void); +static void RenderSound(void); + +static void FDSInit(void) +{ + dsr0=0; + dsr1=0x41; + setmirror(1); + if(InDisk!=255) + dsr1&=0xFE; + + setprg8r(0,0xe000,0); // BIOS + setprg32r(1,0x6000,0); // 32KB RAM + setchr8(0); // 8KB CHR RAM + + MapIRQHook=FDSFix; + GameStateRestore=FDSStateRestore; + + SetReadHandler(0x4030,0x4030,FDSRead4030); + SetReadHandler(0x4031,0x4031,FDSRead4031); + SetReadHandler(0x4032,0x4032,FDSRead4032); + SetReadHandler(0x4033,0x4033,FDSRead4033); + + SetWriteHandler(0x4020,0x4020,FDSWrite4020); + SetWriteHandler(0x4021,0x4021,FDSWrite4021); + SetWriteHandler(0x4022,0x4022,FDSWrite4022); + SetWriteHandler(0x4023,0x4023,FDSWrite4023); + SetWriteHandler(0x4024,0x4024,FDSWrite4024); + SetWriteHandler(0x4025,0x4025,FDSWrite4025); + + SetWriteHandler(0x6000,0xdfff,FDSRAMWrite); + SetReadHandler(0x6000,0xdfff,FDSRAMRead); + SetReadHandler(0xE000,0xFFFF,FDSBIOSRead); + + IRQCount=IRQLatch=IRQa=0; + + FDSSoundReset(); +} + +void FDSControl(int what) +{ + switch(what) + { + case FDS_IDISK:dsr1&=0xFE; + if(InDisk==255) + { + FCEU_DispMessage("Disk %d Side %s Inserted", + SelectDisk>>1,(SelectDisk&1)?"B":"A"); + InDisk=SelectDisk; + } + else + FCEU_DispMessage("Jamming disks is a BAD IDEA"); + break; + case FDS_EJECT: + if(InDisk!=255) + FCEU_DispMessage("Disk Ejected"); + else + FCEU_DispMessage("Cannot Eject Air"); + dsr1|=1; + InDisk=255; + break; + case FDS_SELECT: + if(InDisk!=255) + { + FCEU_DispMessage("Eject disk before selecting."); + break; + } + SelectDisk=((SelectDisk+1)%header[4])&3; + FCEU_DispMessage("Disk %d Side %s Selected", + SelectDisk>>1,(SelectDisk&1)?"B":"A"); + break; + } +} + +static void FP_FASTAPASS(1) FDSFix(int a) +{ + if(IRQa) + { + IRQCount-=a; + if(IRQCount<=0) + { + IRQa=0; + dsr0|=1; + dsr0&=~2; + IRQCount=0xFFFF; + X6502_IRQBegin(FCEU_IQEXT); + } + } + if(DiskSeekIRQ>0) + { + DiskSeekIRQ-=a; + if(DiskSeekIRQ<=0) + { + if(mapbyte1[5]&0x80) + { + dsr0&=~1; + dsr0|=2; + TriggerIRQ(); + } + } + } +} + +void DiskControl(int which) +{ +if(mapbyte1[5]&1) + { + switch(which) + { + case DC_INC: + if(DiskPtr<64999) DiskPtr++; + //DiskSeekIRQ=160+100; + //DiskSeekIRQ=140; + //DiskSeekIRQ=160; + DiskSeekIRQ=150; + break; + } + } +} +static DECLFR(FDSRead4030) +{ + X6502_IRQEnd(FCEU_IQEXT); + return dsr0; +} + +static DECLFR(FDSRead4031) +{ + static uint8 z=0; + if(InDisk!=255) + { + z=diskdata[InDisk][DiskPtr]; + DiskControl(DC_INC); + } + return z; +} + +static DECLFR(FDSRead4032) +{ + return dsr1; +} + +static DECLFR(FDSRead4033) +{ + return 0x80; // battery +} + +static DECLFW(FDSRAMWrite) +{ + (FDSRAM-0x6000)[A]=V; +} + +static DECLFR(FDSBIOSRead) +{ + return (FDSBIOS-0xE000)[A]; +} + +static DECLFR(FDSRAMRead) +{ + return (FDSRAM-0x6000)[A]; +} + +/* Begin FDS sound */ + +#define FDSClock (1789772.7272727272727272/8) + +typedef struct { + int64 cycles; // Cycles per PCM sample + int64 count; // Cycle counter + int64 envcount; // Envelope cycle counter + uint32 b19shiftreg60; + uint32 b24adder66; + uint32 b24latch68; + uint32 b17latch76; + int32 clockcount; // Counter to divide frequency by 8. + uint8 b8shiftreg88; // Modulation register. + uint8 amplitude[2]; // Current amplitudes. + uint8 mwave[0x20]; // Modulation waveform + uint8 cwave[0x40]; // Game-defined waveform(carrier) + uint8 SPSG[0xB]; +} FDSSOUND; + +static FDSSOUND fdso; + +#define SPSG fdso.SPSG +#define b19shiftreg60 fdso.b19shiftreg60 +#define b24adder66 fdso.b24adder66 +#define b24latch68 fdso.b24latch68 +#define b17latch76 fdso.b17latch76 +#define b8shiftreg88 fdso.b8shiftreg88 +#define clockcount fdso.clockcount +#define amplitude fdso.amplitude + +void FDSSoundStateAdd(void) +{ + AddExState(fdso.cwave,64,0,"WAVE"); + AddExState(fdso.mwave,32,0,"MWAV"); + AddExState(amplitude,2,0,"AMPL"); + AddExState(SPSG,0xB,0,"SPSG"); + + AddExState(&b8shiftreg88,1,0,"B88"); + + AddExState(&clockcount, 4, 1, "CLOC"); + AddExState(&b19shiftreg60,4,1,"B60"); + AddExState(&b24adder66,4,1,"B66"); + AddExState(&b24latch68,4,1,"B68"); + AddExState(&b17latch76,4,1,"B76"); + +} + +static DECLFR(FDSSRead) +{ + switch(A&0xF) + { + case 0x0:return(amplitude[0]|(X.DB&0xC0)); + case 0x2:return(amplitude[1]|(X.DB&0xC0)); + } + return(X.DB); +} + +static DECLFW(FDSSWrite) +{ + if(FSettings.SndRate) + RenderSound(); + A-=0x4080; + switch(A) + { + case 0x0: + case 0x4: + if(!(V&0x80)) + { + // if(V&0x40) amplitude[(A&0xF)>>2]=0; + // else amplitude[(A&0xF)>>2]=0x3F; + } + else + amplitude[(A&0xF)>>2]=V&0x3F; + break; + case 0x7: b17latch76=0;SPSG[0x5]=0;break; + case 0x8: + //printf("%d:$%02x\n",SPSG[0x5],V); + fdso.mwave[SPSG[0x5]&0x1F]=V&0x7; + SPSG[0x5]=(SPSG[0x5]+1)&0x1F; + break; + } + //if(A>=0x7 && A!=0x8 && A<=0xF) + //if(A==0xA || A==0x9) printf("$%04x:$%02x\n",A,V); + SPSG[A]=V; +} + +// $4080 - Fundamental wave amplitude data register 92 +// $4082 - Fundamental wave frequency data register 58 +// $4083 - Same as $4082($4083 is the upper 4 bits). + +// $4084 - Modulation amplitude data register 78 +// $4086 - Modulation frequency data register 72 +// $4087 - Same as $4086($4087 is the upper 4 bits) + + +static void DoEnv() +{ + int x; + + for(x=0;x<2;x++) + if(!(SPSG[x<<2]&0x80) && !(SPSG[0x9+x]&0x80)) + { + static int counto[2]={0,0}; + + if(counto[x]<=0) + { + if(SPSG[x<<2]&0x40) + { + if(amplitude[x]<0x3F) + amplitude[x]++; + } + else + { + if(amplitude[x]>0) + amplitude[x]--; + } + counto[x]=(SPSG[x<<2]&0x3F); + } + else + counto[x]--; + } +} + +static DECLFR(FDSWaveRead) +{ + return(fdso.cwave[A&0x3f]|(X.DB&0xC0)); +} + +static DECLFW(FDSWaveWrite) +{ + if(SPSG[0x9]&0x80) + fdso.cwave[A&0x3f]=V&0x3F; +} + +static INLINE void ClockRise(void) +{ + if(!clockcount) + { + b19shiftreg60=(SPSG[0x2]|((SPSG[0x3]&0xF)<<8)); + b17latch76=(SPSG[0x6]|((SPSG[0x07]&0x3)<<8))+b17latch76; + + if(!(SPSG[0x7]&0x80)) + { + b8shiftreg88=(amplitude[1]*((fdso.mwave[(b17latch76>>11)&0x1F]&7))); + //b8shiftreg88=((fdso.mwave[(b17latch76>>11)&0x1F]&7))|(amplitude[1]<<3); + } + else + { b8shiftreg88=0;} + } + else + { + b19shiftreg60<<=1; + b8shiftreg88>>=1; + } + b24adder66=(b24latch68+b19shiftreg60)&0xFFFFFF; +} + +static INLINE void ClockFall(void) +{ +// if(!(SPSG[0x7]&0x80)) + { + if(!(b8shiftreg88&1)) + b24latch68=b24adder66; + } + clockcount=(clockcount+1)&7; +} + +static INLINE int32 FDSDoSound(void) +{ + fdso.count+=fdso.cycles; + if(fdso.count>=((int64)1<<40)) + { + dogk: + fdso.count-=(int64)1<<40; + ClockRise(); + ClockFall(); + } + if(fdso.count>=32768) goto dogk; + + fdso.envcount-=fdso.cycles; + if(fdso.envcount<=0) + { + // Fix this? + fdso.envcount+=((int64)1<<40)*FDSClock/1024; + DoEnv(); + } + + // Might need to emulate applying the amplitude to the waveform a bit better... + return (fdso.cwave[b24latch68>>18]*amplitude[0]); +} + +static int32 FBC=0; + +static void RenderSound(void) +{ + int32 end, start; + int32 x; + + start=FBC; + end=(timestamp<<16)/soundtsinc; + if(end<=start) + return; + FBC=end; + + if(!(SPSG[0x9]&0x80)) + for(x=start;x>4]+=t>>3; + } +} + +void FDSSound(int c) +{ + RenderSound(); + FBC=c; +} + +static void FDS_ESI(void) +{ + if(FSettings.SndRate) + { + fdso.cycles=((int64)1<<40)*FDSClock; + fdso.cycles/=FSettings.SndRate OVERSAMPLE; + } +// fdso.cycles=(int64)32768*FDSClock/(FSettings.SndRate OVERSAMPLE); + SetReadHandler(0x4040,0x407f,FDSWaveRead); + SetWriteHandler(0x4040,0x407f,FDSWaveWrite); + SetWriteHandler(0x4080,0x408A,FDSSWrite); + SetReadHandler(0x4090,0x4092,FDSSRead); +} + +void FDSSoundReset(void) +{ + memset(&fdso,0,sizeof(fdso)); + FDS_ESI(); + GameExpSound.Fill=FDSSound; + GameExpSound.RChange=FDS_ESI; +} + + +static DECLFW(FDSWrite4020) +{ + X6502_IRQEnd(FCEU_IQEXT); + IRQLatch&=0xFF00; + IRQLatch|=V; + mapbyte1[0]=V; +} +static DECLFW(FDSWrite4021) +{ + X6502_IRQEnd(FCEU_IQEXT); + IRQLatch&=0xFF; + IRQLatch|=V<<8; + mapbyte1[1]=V; +} +static DECLFW(FDSWrite4022) +{ + X6502_IRQEnd(FCEU_IQEXT); + IRQCount=IRQLatch; + IRQa=V&2; + mapbyte1[2]=V; +} +static DECLFW(FDSWrite4023) +{ + mapbyte1[3]=V; +} +static DECLFW(FDSWrite4024) +{ + if(InDisk!=255 && !(mapbyte1[5]&0x4) && mapbyte1[3]&0x1) + { + if(DiskPtr>=0 && DiskPtr<65000) + { + if(writeskip) writeskip--; + else if(DiskPtr>=2) + { + SideWrite|=1<>3)&1)^1); +} +static void FreeFDSMemory(void) +{ + int x; + + for(x=0;x4) header[4]=4; + if(!header[4]) header[4]|=1; + for(x=0;x +#include +#include + +#ifdef ZLIB + #include + #include "zlib/unzip.h" +#endif + +#include "types.h" +#include "file.h" +#include "endian.h" +#include "memory.h" +#include "driver.h" + +static void *desctable[8]={0,0,0,0,0,0,0,0}; +static int x; + +#ifdef ZLIB + +typedef struct { + uint8 *data; + uint32 size; + uint32 location; +} ZIPWRAP; + +void *MakeZipWrap(void *tz) +{ + unz_file_info ufo; + ZIPWRAP *tmp; + + if(!(tmp=FCEU_malloc(sizeof(ZIPWRAP)))) + goto doret; + + unzGetCurrentFileInfo(tz,&ufo,0,0,0,0,0,0); + + tmp->location=0; + tmp->size=ufo.uncompressed_size; + if(!(tmp->data=FCEU_malloc(ufo.uncompressed_size))) + { + tmp=0; + goto doret; + } + + unzReadCurrentFile(tz,tmp->data,ufo.uncompressed_size); + + doret: + + unzCloseCurrentFile(tz); + unzClose(tz); + + return tmp; +} +#endif + +#ifndef __GNUC__ + #define strcasecmp strcmp +#endif + +int FASTAPASS(2) FCEU_fopen(char *path, char *mode) +{ + void *t; + + #ifdef ZLIB + unzFile tz; + if((tz=unzOpen(path))) // If it's not a zip file, use regular file handlers. + // Assuming file type by extension usually works, + // but I don't like it. :) + { + if(unzGoToFirstFile(tz)==UNZ_OK) + { + for(;;) + { + char tempu[512]; // Longer filenames might be possible, but I don't + // think people would name files that long in zip files... + unzGetCurrentFileInfo(tz,0,tempu,512,0,0,0,0); + tempu[511]=0; + if(strlen(tempu)>=4) + { + char *za=tempu+strlen(tempu)-4; + if(!strcasecmp(za,".nes") || !strcasecmp(za,".fds") || + !strcasecmp(za,".nsf") || !strcasecmp(za,".unf") || + !strcasecmp(za,".nez")) + break; + } + if(strlen(tempu)>=5) + { + if(!strcasecmp(tempu+strlen(tempu)-5,".unif")) + break; + } + if(unzGoToNextFile(tz)!=UNZ_OK) + { + if(unzGoToFirstFile(tz)!=UNZ_OK) goto zpfail; + break; + } + } + if(unzOpenCurrentFile(tz)!=UNZ_OK) + goto zpfail; + } + else + { + zpfail: + unzClose(tz); + return 0; + } + + for(x=0;x<8;x++) + if(!desctable[x]) + { + if(!(desctable[x]=MakeZipWrap(tz))) + return(0); + return((x+1)|0x8000); + } + } +#endif + + #ifdef ZLIB + if((t=fopen(path,"rb"))) + { + uint32 magic; + + magic=fgetc(t); + magic|=fgetc(t)<<8; + magic|=fgetc(t)<<16; + + fclose(t); + + if(magic==0x088b1f) + { + if((t=gzopen(path,mode))) + for(x=0;x<8;x++) + if(!desctable[x]) + { + desctable[x]=t; + return((x+1)|0x4000); + } + } + } + #endif + + if((t=fopen(path,mode))) + { + fseek(t,0,SEEK_SET); + for(x=0;x<8;x++) + if(!desctable[x]) + { + desctable[x]=t; + return(x+1); + } + } + return 0; +} + +int FASTAPASS(1) FCEU_fclose(int stream) +{ + #ifdef ZLIB + if(stream&0x4000) + { + gzclose(desctable[(stream&255)-1]); + desctable[(stream&255)-1]=0; + } + else if(stream&0x8000) + { + free(((ZIPWRAP*)desctable[(stream&255)-1])->data); + free(desctable[(stream&255)-1]); + desctable[(stream&255)-1]=0; + } + else // close zip file + { + #endif + fclose(desctable[stream-1]); + desctable[stream-1]=0; + #ifdef ZLIB + } + #endif + return 1; +} + +size_t FASTAPASS(3) FCEU_fread(void *ptr, size_t size, size_t nmemb, int stream) +{ + #ifdef ZLIB + if(stream&0x4000) + { + return gzread(desctable[(stream&255)-1],ptr,size*nmemb); + } + else if(stream&0x8000) + { + ZIPWRAP *wz; + uint32 total=size*nmemb; + + wz=(ZIPWRAP*)desctable[(stream&255)-1]; + if(wz->location>=wz->size) return 0; + + if((wz->location+total)>wz->size) + { + int ak=wz->size-wz->location; + memcpy((uint8*)ptr,wz->data+wz->location,ak); + wz->location=wz->size; + return(ak/size); + } + else + { + memcpy((uint8*)ptr,wz->data+wz->location,total); + wz->location+=total; + return nmemb; + } + } + else + { + #endif + return fread(ptr,size,nmemb,desctable[stream-1]); + #ifdef ZLIB + } + #endif +} + +size_t FASTAPASS(3) FCEU_fwrite(void *ptr, size_t size, size_t nmemb, int stream) +{ + #ifdef ZLIB + if(stream&0x4000) + { + return gzwrite(desctable[(stream&255)-1],ptr,size*nmemb); + } + else if(stream&0x8000) + { + return 0; + } + else + #endif + return fwrite(ptr,size,nmemb,desctable[stream-1]); +} + +int FASTAPASS(3) FCEU_fseek(int stream, long offset, int whence) +{ + #ifdef ZLIB + if(stream&0x4000) + { + return gzseek(desctable[(stream&255)-1],offset,whence); + } + else if(stream&0x8000) + { + ZIPWRAP *wz; + wz=(ZIPWRAP*)desctable[(stream&255)-1]; + + switch(whence) + { + case SEEK_SET:if(offset>=wz->size) + return(-1); + wz->location=offset;break; + case SEEK_CUR:if(offset+wz->location>wz->size) + return (-1); + wz->location+=offset; + break; + } + return 0; + } + else + #endif + return fseek(desctable[stream-1],offset,whence); +} + +long FASTAPASS(1) FCEU_ftell(int stream) +{ + #ifdef ZLIB + if(stream&0x4000) + { + return gztell(desctable[(stream&255)-1]); + } + else if(stream&0x8000) + { + return (((ZIPWRAP *)desctable[(stream&255)-1])->location); + } + else + #endif + return ftell(desctable[stream-1]); +} + +void FASTAPASS(1)FCEU_rewind(int stream) +{ + #ifdef ZLIB + if(stream&0x4000) + { + gzrewind(desctable[(stream&255)-1]); + } + else if(stream&0x8000) + { + ((ZIPWRAP *)desctable[(stream&255)-1])->location=0; + } + else + #endif + #ifdef _WIN32_WCE + fseek(desctable[stream-1],0,SEEK_SET); + #else + rewind(desctable[stream-1]); + #endif +} + +int FASTAPASS(2) FCEU_read32(void *Bufo, int stream) +{ + #ifdef ZLIB + if(stream&0xC000) + { + uint8 t[4]; + #ifndef LSB_FIRST + uint8 x[4]; + #endif + if(stream&0x8000) + { + ZIPWRAP *wz; + wz=(ZIPWRAP*)desctable[(stream&255)-1]; + if(wz->location+4>wz->size) + {return 0;} + *(uint32 *)t=*(uint32 *)(wz->data+wz->location); + wz->location+=4; + } + else if(stream&0x4000) + gzread(desctable[(stream&255)-1],&t,4); + #ifndef LSB_FIRST + x[0]=t[3]; + x[1]=t[2]; + x[2]=t[1]; + x[3]=t[0]; + *(uint32*)Bufo=*(uint32*)x; + #else + *(uint32*)Bufo=*(uint32*)t; + #endif + return 1; + } + else + #endif + { + return read32(Bufo,desctable[stream-1]); + } +} + +int FASTAPASS(1) FCEU_fgetc(int stream) +{ + #ifdef ZLIB + if(stream&0x4000) + return gzgetc(desctable[(stream&255)-1]); + else if(stream&0x8000) + { + ZIPWRAP *wz; + wz=(ZIPWRAP*)desctable[(stream&255)-1]; + if(wz->locationsize) + return wz->data[wz->location++]; + return EOF; + } + else +#endif + return fgetc(desctable[stream-1]); +} + +long FASTAPASS(1) FCEU_fgetsize(int stream) +{ + #ifdef ZLIB + if(stream&0x4000) + { + int x,t; + t=gztell(desctable[(stream&255)-1]); + gzrewind(desctable[(stream&255)-1]); + for(x=0;gzgetc(desctable[(stream&255)-1]) != EOF; x++); + gzseek(desctable[(stream&255)-1],t,SEEK_SET); + return(x); + } + else if(stream&0x8000) + return ((ZIPWRAP*)desctable[(stream&255)-1])->size; + else + #endif + { + long t,r; + t=ftell(desctable[stream-1]); + fseek(desctable[stream-1],0,SEEK_END); + r=ftell(desctable[stream-1]); + fseek(desctable[stream-1],t,SEEK_SET); + return r; + } +} + +int FASTAPASS(1) FCEU_fisarchive(int stream) +{ + #ifdef ZLIB + if(stream&0x8000) + return 1; + #endif + return 0; +} diff --git a/file.h b/file.h new file mode 100644 index 0000000..3c70f97 --- /dev/null +++ b/file.h @@ -0,0 +1,12 @@ +int FASTAPASS(2) FCEU_fopen(char *path, char *mode); +int FASTAPASS(1) FCEU_fclose(int stream); +size_t FASTAPASS(3) FCEU_fread(void *ptr, size_t size, size_t nmemb, int stream); +size_t FASTAPASS(3) FCEU_fwrite(void *ptr, size_t size, size_t nmemb, int stream); +int FASTAPASS(3) FCEU_fseek(int stream, long offset, int whence); +long FASTAPASS(1) FCEU_ftell(int stream); +void FASTAPASS(1) FCEU_rewind(int stream); +int FASTAPASS(2) FCEU_read32(void *Bufo, int fp); +int FASTAPASS(1) FCEU_fgetc(int stream); +long FASTAPASS(1) FCEU_fgetsize(int stream); +int FASTAPASS(1) FCEU_fisarchive(int stream); + diff --git a/general.c b/general.c new file mode 100644 index 0000000..b0c1942 --- /dev/null +++ b/general.c @@ -0,0 +1,169 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#include "types.h" + +#include "general.h" +#include "state.h" +#include "version.h" +#include "svga.h" +#include "driver.h" + +char *marray[1]={"Error allocating memory!"}; + +static char BaseDirectory[2048]; +static char FileBase[2048]; +static char FileExt[2048]; +static char FileBaseDirectory[2048]; + +void FCEUI_SetBaseDirectory(char *dir) +{ + strncpy(BaseDirectory,dir,2047); + BaseDirectory[2047]=0; +} + +static char *odirs[FCEUIOD__COUNT]={0,0,0,0,0}; // odirs, odors. ^_^ + +void FCEUI_SetDirOverride(int which, char *n) +{ + odirs[which]=n; + if(which==FCEUIOD_STATE) + SaveStateRefresh(); +} + +/* We should probably use snprintf(), but many C libraries don't seem to + have this function. +*/ +char *FCEU_MakeFName(int type, int id1, char *cd1) +{ + static uint8 ret[2048]; + + ret[0]=0; + switch(type) + { + case FCEUMKF_STATE:if(odirs[FCEUIOD_STATE]) + sprintf(ret,"%s"PSS"%s.fc%d",odirs[FCEUIOD_STATE],FileBase,id1); + else + sprintf(ret,"%s"PSS"fcs"PSS"%s.fc%d",BaseDirectory,FileBase,id1); + break; + case FCEUMKF_SNAP: + if(FSettings.SnapName) + { + if(odirs[FCEUIOD_SNAPS]) + sprintf(ret,"%s"PSS"%s-%d.%s",odirs[FCEUIOD_SNAPS],FileBase,id1,cd1); + else + sprintf(ret,"%s"PSS"snaps"PSS"%s-%d.%s",BaseDirectory,FileBase,id1,cd1); + } + else + { + if(odirs[FCEUIOD_SNAPS]) + sprintf(ret,"%s"PSS"%d.%s",odirs[FCEUIOD_SNAPS],id1,cd1); + else + sprintf(ret,"%s"PSS"snaps"PSS"%d.%s",BaseDirectory,id1,cd1); + } + break; + case FCEUMKF_SAV:if(odirs[FCEUIOD_NV]) + { + sprintf(ret,"%s"PSS"%s.%s",odirs[FCEUIOD_NV],FileBase,cd1); + } + else + { + if(FSettings.SUnderBase) + sprintf(ret,"%s"PSS"sav"PSS"%s.%s",BaseDirectory,FileBase,cd1); + else + sprintf(ret,"%s"PSS"%s.%s",FileBaseDirectory,FileBase,cd1); + } + break; + case FCEUMKF_CHEAT: + if(odirs[FCEUIOD_CHEATS]) + sprintf(ret,"%s"PSS"%s.cht",odirs[FCEUIOD_CHEATS],FileBase); + else + sprintf(ret,"%s"PSS"cheats"PSS"%s.cht",BaseDirectory,FileBase); + break; + case FCEUMKF_GGROM:sprintf(ret,"%s"PSS"gg.rom",BaseDirectory);break; + case FCEUMKF_FDSROM:sprintf(ret,"%s"PSS"disksys.rom",BaseDirectory);break; + case FCEUMKF_PALETTE: + if(odirs[FCEUIOD_MISC]) + sprintf(ret,"%s"PSS"%s.pal",odirs[FCEUIOD_MISC],FileBase); + else + sprintf(ret,"%s"PSS"gameinfo"PSS"%s.pal",BaseDirectory,FileBase); + break; + } + return(ret); +} + +void GetFileBase(char *f) +{ + char *tp1,*tp3; + + #if PSS_STYLE==4 + tp1=((char *)strrchr(f,':')); + #elif PSS_STYLE==1 + tp1=((char *)strrchr(f,'/')); + #else + tp1=((char *)strrchr(f,'\\')); + #if PSS_STYLE!=3 + tp3=((char *)strrchr(f,'/')); + if(tp1=0;x--) + if(n&(1< +#include +#include + +#include "types.h" +#include "x6502.h" +#include "fce.h" +#define INESPRIV +#include "ines.h" +#include "version.h" +#include "svga.h" +#include "general.h" +#include "state.h" +#include "file.h" +#include "memory.h" +#include "cart.h" +#include "crc32.h" +#include "cheat.h" + +static DECLFR(VSRead); + +static uint8 *trainerpoo=0; +static uint8 *ROM=NULL; +uint8 *VROM=NULL; + + +static uint32 ROM_size; +uint32 VROM_size; + +static void CheckVSUni(void); +static int MMC_init(int type); +void (*MapClose)(void); +void (*MapperReset)(void); + +static int MapperNo; +static int SaveGame=0; + +static iNES_HEADER head; + +/* MapperReset() is called when the NES is reset(with the reset button). + Mapperxxx_init is called when the NES has been powered on. +*/ + +static void iNESGI(int h) +{ + switch(h) + { + case GI_RESETM2: + if(MapperReset) + MapperReset(); + break; + case GI_POWER: + SetReadHandler(0x8000,0xFFFF,CartBR); + MMC_init(MapperNo); + break; + case GI_CLOSE: + { + FILE *sp; + + if(ROM) {free(ROM);ROM=0;} + if(VROM) {free(VROM);VROM=0;} + + if(SaveGame) + { + char *soot; + SaveGame=0; + soot=FCEU_MakeFName(FCEUMKF_SAV,0,"sav"); + sp=fopen(soot,"wb"); + if (sp==NULL) + FCEU_PrintError("WRAM file \"%s\" cannot be written to.\n",soot); + else + { + void *ptr; + uint32 amount; + ptr=WRAM; + amount=8192; + + if(MapperNo==1) + { + extern uint8 MMC1WRAMsize; + if(MMC1WRAMsize==2) ptr=WRAM+8192; + } + else if(MapperNo==5) + { + extern uint8 MMC5WRAMsize; + if(MMC5WRAMsize==4) + amount=32768; + } + + fwrite(ptr,1,amount,sp); + fclose(sp); + } + } + if(MapClose) MapClose(); + if(trainerpoo) {free(trainerpoo);trainerpoo=0;} + } + break; + } +} + +uint32 iNESGameCRC32; + +struct CRCMATCH { + uint32 crc; + char *name; +}; + +static void CheckBad(void) +{ + int x; + #define CRCNT 7 + struct CRCMATCH tab[CRCNT]={ + {0x28d183ac,"Antarctic Adventure"}, + {0x7095ac65,"Akumajo Densetsu"}, + {0x1bd7ed5a,"Gradius 2"}, + {0x81c3aa66,"Crisis Force"}, + {0xfe3088df,"Fire Emblem Gaiden"}, + {0xfa8339a5,"Bucky O'Hare"}, + {0x3c539d78,"Ganbare Goemon 2"}, + }; + for(x=0;x=0 || moo[x].input2>=0 || moo[x].inputfc>=0) + { + if(moo[x].crc32==iNESGameCRC32) + { + FCEUGameInfo.input[0]=moo[x].input1; + FCEUGameInfo.input[1]=moo[x].input2; + FCEUGameInfo.inputfc=moo[x].inputfc; + break; + } + x++; + } +} + +struct CHINF { + uint32 crc32; + int32 mapper; + int32 mirror; +}; + +#define SAVCNT 14 +static void CheckHInfo(void) +{ + /* ROM images that have the battery-backed bit set in the header that really + don't have battery-backed RAM is not that big of a problem, so I'll + treat this differently. + */ + + static uint32 savie[SAVCNT]= + { + 0x7cab2e9b,0x3ee43cda,0xe1383deb, /* Mouryou Senki Madara */ + 0x3b3f88f0,0x2545214c, /* Dragon Warrior PRG 0 and 1 */ + 0x8c5a784e, /* DW 2 */ + 0xa86a5318, /* DW 3 */ + 0x506e259d, /* DW 4 */ + 0xcebd2a31,0xb8b88130, /* Final Fantasy */ + 0x466efdc2, /* FF(J) */ + 0xc9556b36, /* FF1+2*/ + 0xd29db3c7, /* FF2 */ + 0x57e220d0, /* FF3 */ + }; + + static struct CHINF moo[]= + { + {0xc68363f6,180,0}, /* Crazy Climber */ + {0xbe939fce,9,1}, /* Punchout*/ + {0x5e66eaea,13,1}, /* Videomation */ + {0xaf5d7aa2,-1,0}, /* Clu Clu Land */ + + {0xc2730c30,34,0}, /* Deadly Towers */ + {0x932ff06e,34,1}, /* Classic Concentration */ + {0x4c7c1af3,34,1}, /* Caesar's Palace */ + {0x9ea1dc76,2,0}, /* Rainbow Islands */ + + {0x9eefb4b4,4,8}, /* Pachi Slot Adventure 2 */ + {0x5337f73c,4,8}, /* Niji no Silk Road */ + {0x7474ac92,4,8}, /* Kabuki: Quantum Fighter */ + + {0x970bd9c2,1,8}, /* Hanjuku Hero */ + + {0xbb7c5f7a,89,8}, /* Mito Koumon or something similar */ + /* Probably a Namco MMC3-workalike */ + {0xa5e6baf9,4,1|4}, /* Dragon Slayer 4 */ + {0xe40b4973,4,1|4}, /* Metro Cross */ + {0xd97c31b0,4,1|4}, /* Rasaaru Ishii no Childs Quest */ + + {0x84382231,9,0}, /* Punch Out (J) */ + + {0xfcdaca80,0,0}, /* Elevator Action */ + {0xe492d45a,0,0}, /* Zippy Race */ + {0x32fa246f,0,0}, /* Tag Team Pro Wrestling */ + {0x6d65cac6,2,0}, /* Terra Cresta */ + {0x28c11d24,2,1}, /* Sukeban Deka */ + {0x02863604,2,1}, /* Sukeban Deka */ + {0x2bb6a0f8,2,1}, /* Sherlock Holmes */ + {0x55773880,2,1}, /* Gilligan's Island */ + {0x419461d0,2,1}, /* Super Cars */ + {0x6e0eb43e,2,1}, /* Puss n Boots */ + {0xfc3e5c86,2,1}, /* Trojan */ + + {0x291bcd7d,1,8}, /* Pachio Kun 2 */ + {0xf74dfc91,1,-1}, /* Win, Lose, or Draw */ + + {0x59280bec,4,8}, /* Jackie Chan */ + + {0xfe364be5,1,8}, /* Deep Dungeon 4 */ + {0xd8ee7669,1,8}, /* Adventures of Rad Gravity */ + {0xa5e8d2cd,1,8}, /* Breakthru */ + {0xf6fa4453,1,8}, /* Bigfoot */ + {0x37ba3261,1,8}, /* Back to the Future 2 and 3 */ + {0x934db14a,1,-1}, /* All-Pro Basketball */ + {0xe94d5181,1,8}, /* Mirai Senshi - Lios */ + {0x7156cb4d,1,8}, /* Muppet Adventure Carnival thingy */ + {0x5b6ca654,1,8}, /* Barbie rev X*/ + {0x57c12280,1,8}, /* Demon Sword */ + + {0xcf322bb3,3,1}, /* John Elway's Quarterback */ + {0x9bde3267,3,1}, /* Adventures of Dino Riki */ + {0x02cc3973,3,8}, /* Ninja Kid */ + {0xb5d28ea2,3,1}, /* Mystery Quest - mapper 3?*/ + {0xbc065fc3,3,1}, /* Pipe Dream */ + + {0x5b837e8d,1,8}, /* Alien Syndrome */ + {0x283ad224,32,8}, /* Ai Sensei no Oshiete */ + {0x5555fca3,32,8}, /* "" "" */ + {0x243a8735,32,0x10|4}, /* Major League */ + + {0x6bc65d7e,66,1}, /* Youkai Club*/ + {0xc2df0a00,66,1}, /* Bio Senshi Dan(hacked) */ + {0xbde3ae9b,66,1}, /* Doraemon */ + {0xd26efd78,66,1}, /* SMB Duck Hunt */ + {0x811f06d9,66,1}, /* Dragon Power */ + + {0x3293afea,66,1}, /* Mississippi Satsujin Jiken */ + {0xe84274c5,66,1}, /* "" "" */ + + + {0x6e68e31a,16,8}, /* Dragon Ball 3*/ + + {0xba51ac6f,78,2}, + {0x3d1c3137,78,8}, + + {0xbda8f8e4,152,8}, /* Gegege no Kitarou 2 */ + {0x026c5fca,152,8}, /* Saint Seiya Ougon Densetsu */ + {0x0f141525,152,8}, /* Arkanoid 2 (Japanese) */ + {0xb1a94b82,152,8}, /* Pocket Zaurus */ + + {0x3f15d20d,153,8}, /* Famicom Jump 2 */ + + {0xbba58be5,70,-1}, /* Family Trainer - Manhattan Police */ + {0x370ceb65,70,-1}, /* Family Trainer - Meiro Dai Sakusen */ + {0xdd8ed0f7,70,1}, /* Kamen Rider Club */ + + {0x90c773c1,118,-1}, /* Goal! 2 */ + {0xb9b4d9e0,118,-1}, /* NES Play Action Football */ + {0x78b657ac,118,-1}, /* Armadillo */ + {0x37b62d04,118,-1}, /* Ys 3 */ + {0x07d92c31,118,-1}, /* RPG Jinsei Game */ + {0x2705eaeb,234,-1}, /* Maxi 15 */ + {0x404b2e8b,4,2}, /* Rad Racer 2 */ + + {0x1356f6a6,4,8}, /* "Cattou Ninden Teyandee" English tranlation. + Should I have even bothered to do this? */ + {0x50fd4fd6,4,8}, /* "" "" */ + + {0xa912b064,51|0x800,8}, /* 11-in-1 Ball Games(has CHR ROM when it shouldn't) */ + {0,-1,-1} + }; + int tofix=0; + int x=0; + + do + { + if(moo[x].crc32==iNESGameCRC32) + { + if(moo[x].mapper>=0) + { + if(moo[x].mapper&0x800 && VROM_size) + { + VROM_size=0; + free(VROM); + tofix|=8; + } + if(MapperNo!=(moo[x].mapper&0xFF)) + { + tofix|=1; + MapperNo=moo[x].mapper&0xFF; + } + } + if(moo[x].mirror>=0) + { + if(moo[x].mirror==8) + { + if(Mirroring==2) /* Anything but hard-wired(four screen). */ + { + tofix|=2; + Mirroring=0; + } + } + else if(Mirroring!=moo[x].mirror) + { + if(Mirroring!=(moo[x].mirror&~4)) + if((moo[x].mirror&~4)<=2) /* Don't complain if one-screen mirroring + needs to be set(the iNES header can't + hold this information). + */ + tofix|=2; + Mirroring=moo[x].mirror; + } + } + break; + } + x++; + } while(moo[x].mirror>=0 || moo[x].mapper>=0); + + for(x=0;x>4); + MapperNo|=(head.ROM_type2&0xF0); + + Mirroring = (head.ROM_type&1); + if(head.ROM_type&8) Mirroring=2; + + if(!(ROM=(uint8 *)FCEU_malloc(ROM_size<<14))) + return 0; + + if (VROM_size) + if(!(VROM=(uint8 *)FCEU_malloc(VROM_size<<13))) + { + free(ROM); + return 0; + } + + memset(ROM,0xFF,ROM_size<<14); + if(VROM_size) memset(VROM,0xFF,VROM_size<<13); + if(head.ROM_type&4) /* Trainer */ + { + if(!(trainerpoo=FCEU_malloc(512))) + return(0); + FCEU_fread(trainerpoo,512,1,fp); + } + ResetCartMapping(); + SetupCartPRGMapping(0,ROM,ROM_size*0x4000,0); + SetupCartPRGMapping(1,WRAM,8192,1); + + FCEU_fread(ROM,0x4000,head.ROM_size,fp); + + if(VROM_size) + FCEU_fread(VROM,0x2000,head.VROM_size,fp); + + printf("File %s loaded.\n",name); + + iNESGameCRC32=CalcCRC32(0,ROM,ROM_size<<14); + if(VROM_size) + iNESGameCRC32=CalcCRC32(iNESGameCRC32,VROM,VROM_size<<13); + printf("\n PRG ROM: %3d x 16k\n CHR ROM: %3d x 8k\n ROM CRC32: %08lx\n Mapper: %d\n Mirroring: %s\n",head.ROM_size,head.VROM_size,iNESGameCRC32,MapperNo,Mirroring==2?"None(Four-screen)":Mirroring?"Vertical":"Horizontal"); + if(head.ROM_type&2) puts(" Battery-backed."); + if(head.ROM_type&4) puts(" Trained."); + puts(""); + + CheckBad(); + SetInput(); + CheckHInfo(); + CheckVSUni(); + //if(MapperNo!=4 && MapperNo!=118 && MapperNo!=119) exit(1); // Testing + + /* Must remain here because above functions might change value of + VROM_size and free(VROM). + */ + if(VROM_size) + SetupCartCHRMapping(0,VROM,VROM_size*0x2000,0); + + if(Mirroring==2) + SetupCartMirroring(4,1,ExtraNTARAM); + else if(Mirroring>=0x10) + SetupCartMirroring(2+(Mirroring&1),1,0); + else + SetupCartMirroring(Mirroring&1,(Mirroring&4)>>2,0); + + if(MapperNo==5) DetectMMC5WRAMSize(); + else if(MapperNo==1) DetectMMC1WRAMSize(); + + if(head.ROM_type&2) + { + char *soot; + + SaveGame=1; + soot=FCEU_MakeFName(FCEUMKF_SAV,0,"sav"); + sp=fopen(soot,"rb"); + if(sp!=NULL) + { + void *ptr; + uint32 amount; + + ptr=WRAM; + amount=8192; + if(MapperNo==1) + { + extern uint8 MMC1WRAMsize; + if(MMC1WRAMsize==2) ptr=WRAM+8192; + } + else if(MapperNo==5) + { + extern uint8 MMC5WRAMsize; + if(MMC5WRAMsize==4) + amount=32768; + } + if(fread(ptr,1,amount,sp)==amount) + printf(" WRAM Save File \"%s\" loaded...\n",soot); + fclose(sp); + } + + } + GameInterface=iNESGI; + return 1; +} + +#include "banksw.h" + + +void FASTAPASS(1) onemir(uint8 V) +{ + if(Mirroring==2) return; + if(V>1) + V=1; + Mirroring=0x10|V; + setmirror(MI_0+V); +} + +void FASTAPASS(1) MIRROR_SET2(uint8 V) +{ + if(Mirroring==2) return; + Mirroring=V; + setmirror(V); +} + +void FASTAPASS(1) MIRROR_SET(uint8 V) +{ + if(Mirroring==2) return; + V^=1; + Mirroring=V; + setmirror(V); +} + +static void NONE_init(void) +{ + ROM_BANK16(0x8000,0); + ROM_BANK16(0xC000,~0); + + if(VROM_size) + VROM_BANK8(0); + else + setvram8(CHRRAM); +} + +static uint8 secdata[2][32]= +{ + { + 0xff, 0xbf, 0xb7, 0x97, 0x97, 0x17, 0x57, 0x4f, + 0x6f, 0x6b, 0xeb, 0xa9, 0xb1, 0x90, 0x94, 0x14, + 0x56, 0x4e, 0x6f, 0x6b, 0xeb, 0xa9, 0xb1, 0x90, + 0xd4, 0x5c, 0x3e, 0x26, 0x87, 0x83, 0x13, 0x00 + }, + { + 0x00, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, + 0x00, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x94, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + } +}; + +static uint8 *secptr; + +static void CheckVSUni(void) +{ + FCEUGameInfo.type=GIT_VSUNI; + + /* FCE Ultra doesn't complain when headers for these games are bad. */ + secptr=0; + switch(iNESGameCRC32) + { + default:FCEUGameInfo.type=0;break; + + case 0xffbef374: pale=1;break; /* Castlevania */ + + case 0x98e3c75a: + case 0x7cff0f84: pale=2;break; /* SMB */ + + case 0xef7af338: pale=2;break; /* Ice Climber */ + case 0xabe1a0c2: FCEUGameInfo.input[0]=SI_ZAPPER;break; /*Duck hunt */ + case 0x16aa4e2d: pale=7;FCEUGameInfo.input[0]=SI_ZAPPER;break; /* hoganal ^_^ */ + case 0x2b85420e: pale=3;break; /* Dr Mario */ + + case 0xfb0ddde7: pale=2;break; + case 0xc95321a8: pale=6;break; /* Excitebike */ + case 0xb1c4c508: pale=1;break; /* Golf */ + + case 0x66471efe: break; + case 0xca3e9b1a: pale=7;break; /* Pinball*/ + + case 0xf735d926: pale=7; break; /* Gradius */ + + case 0x2019fe65: + case 0x31678411:MapperNo=68;pale=7;break; /* Platoon */ + + case 0x74f713b4:pale=4;break; /* Goonies */ + case 0x9ae2baa0:pale=5;break; /* Slalom */ + case 0xe45485a5:secptr=secdata[1];vsdip=0x20;MapperNo=4;break; /* RBI Baseball */ + case 0x21a653c7:vsdip=0x20;MapperNo=4;break; /* Super Sky Kid */ + case 0xe9a6f17d:vsdip=0x20;break; /* Tetris */ + + case 0x159ef3c1:break; /* Star Luster */ + case 0x9768e5e0:break; /* Stroke and Match Golf */ + + /* FCE Ultra doesn't have correct palettes for the following games. */ + case 0x0fa322c2:pale=2;break; /* Clu Clu Land */ + + case 0x766c2cac: /* Soccer */ + case 0x01357944: /* Battle City */ + case 0xb2816bf9:break; /* Battle City (Bootleg) */ + + case 0x832cf592:FCEUGameInfo.input[0]=SI_ZAPPER;break; /* Freedom Force */ + case 0x63abf889:break; /* Ladies Golf */ + case 0x8c0c2df5:pale=2;MapperNo=1;Mirroring=1;break; /* Top Gun */ + case 0x52c501d0:vsdip=0x80;MapperNo=4;secptr=secdata[0];break; + /* TKO Boxing */ + } + + if(MapperNo==99) + Mirroring=2; +} + + +static int VSindex; + +static DECLFR(VSRead) +{ + //printf("$%04x, $%04x\n",A,X.PC); + switch(A) + { + default: + case 0x5e00: VSindex=0;return 0xFF; + case 0x5e01: return(secptr[(VSindex++)&0x1F]); + } +} + +static void DoVSHooks(void) +{ + if(secptr) + SetReadHandler(0x5e00,0x5e01,VSRead); +} + +void (*MapInitTab[256])(void)= +{ + 0, + Mapper1_init,Mapper2_init,Mapper3_init,Mapper4_init, + Mapper5_init,Mapper6_init,Mapper7_init,Mapper8_init, + Mapper9_init,Mapper10_init,Mapper11_init,0, + Mapper13_init,0,Mapper15_init,Mapper16_init, + Mapper17_init,Mapper18_init,Mapper19_init,0, + Mapper21_init,Mapper22_init,Mapper23_init,Mapper24_init, + Mapper25_init,Mapper26_init,0,0, + 0,0,0,Mapper32_init, + Mapper33_init,Mapper34_init,0,0, + 0,0,0,Mapper40_init, + Mapper41_init,Mapper42_init,Mapper43_init,Mapper44_init, + Mapper45_init,Mapper46_init,Mapper47_init,Mapper33_init,Mapper49_init,0,Mapper51_init,Mapper52_init, + 0,0,0,0,0,0,0,0, + 0,0,0,Mapper64_init, + Mapper65_init,Mapper66_init,Mapper67_init,Mapper68_init, + Mapper69_init,Mapper70_init,Mapper71_init,Mapper72_init, + Mapper73_init,0,Mapper75_init,Mapper76_init, + Mapper77_init,Mapper78_init,Mapper79_init,Mapper80_init, + 0,Mapper82_init,Mapper83_init,0, + Mapper85_init,Mapper86_init,Mapper87_init,Mapper88_init, + Mapper89_init,Mapper90_init,0,Mapper92_init, + Mapper93_init,Mapper94_init,Mapper95_init,Mapper96_init, + Mapper97_init,0,Mapper99_init,0, + 0,0,0,0,Mapper105_init,0,0,0, + 0,0,0,Mapper112_init,Mapper113_init,0,0,0, + Mapper117_init,Mapper118_init,Mapper119_init,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,Mapper140_init, + 0,0,0,0,0,0,0,0, + 0,0,Mapper151_init,Mapper152_init,Mapper153_init,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,Mapper180_init, + 0,Mapper182_init,0,Mapper184_init,Mapper185_init,0,0,0, + Mapper189_init,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,Mapper225_init,Mapper226_init,Mapper227_init,Mapper228_init, + Mapper229_init,0,0,Mapper232_init,0,Mapper234_init,Mapper43_init,0, + 0,0,0,Mapper240_init,0,Mapper242_init,0,0, + Mapper245_init,Mapper246_init,0,Mapper248_init,Mapper249_init,Mapper250_init,0,0,0,0,0 +}; + +static DECLFW(BWRAM) +{ + WRAM[A-0x6000]=V; +} + +static DECLFR(AWRAM) +{ + return WRAM[A-0x6000]; +} + +void (*MapStateRestore)(int version); +void iNESStateRestore(int version) +{ + int x; + + if(!MapperNo) return; + + for(x=0;x<4;x++) + setprg8(0x8000+x*8192,PRGBankList[x]); + + if(VROM_size) + for(x=0;x<8;x++) + setchr1(0x400*x,CHRBankList[x]); + + switch(Mirroring) + { + case 0:setmirror(MI_H);break; + case 1:setmirror(MI_V);break; + case 0x12: + case 0x10:setmirror(MI_0);break; + case 0x13: + case 0x11:setmirror(MI_1);break; + } + if(MapStateRestore) MapStateRestore(version); +} + +static int MMC_init(int type) +{ + int x; + + GameStateRestore=iNESStateRestore; + MapClose=0; + MapperReset=0; + MapStateRestore=0; + + setprg8r(1,0x6000,0); + + SetReadHandler(0x6000,0x7FFF,AWRAM); + SetWriteHandler(0x6000,0x7FFF,BWRAM); + FCEU_CheatAddRAM(8,0x6000,WRAM); + + /* This statement represents atrocious code. I need to rewrite + all of the iNES mapper code... */ + IRQCount=IRQLatch=IRQa=0; + if(head.ROM_type&2) + { + extern uint8 MMC5WRAMsize,MMC1WRAMsize; + if(type==5 && MMC5WRAMsize==4) + memset(GameMemBlock+32768,0,sizeof(GameMemBlock)-32768); + else if(type==1 && MMC1WRAMsize==2) + { + memset(GameMemBlock,0,8192); + memset(GameMemBlock+16384,0,sizeof(GameMemBlock)-16384); + } + else + memset(GameMemBlock+8192,0,sizeof(GameMemBlock)-8192); + } + else + memset(GameMemBlock,0,sizeof(GameMemBlock)); + if(head.ROM_type&4) + memcpy(WRAM+4096,trainerpoo,512); + + NONE_init(); + + if(FCEUGameInfo.type==GIT_VSUNI) + DoVSHooks(); + if(type==5) + { + MMC5HackVROMMask=CHRmask4[0]; + MMC5HackExNTARAMPtr=MapperExRAM+0x6000; + MMC5Hack=1; + MMC5HackVROMPTR=VROM; + MMC5HackCHRMode=0; + } + ResetExState(); + AddExState(WRAM, 8192, 0, "WRAM"); + if(type==19 || type==5 || type==6 || type==69 || type==85) + AddExState(MapperExRAM, 32768, 0, "MEXR"); + if((!VROM_size || type==6 || type==19 || type==119) && + (type!=13 && type!=96)) + AddExState(CHRRAM, 8192, 0, "CHRR"); + if(head.ROM_type&8) + AddExState(ExtraNTARAM, 2048, 0, "EXNR"); + + /* Exclude some mappers whose emulation code handle save state stuff + themselves. */ + if(type && type!=13 && type!=96) + { + AddExState(mapbyte1, 32, 0, "MPBY"); + AddExState(&Mirroring, 1, 0, "MIRR"); + AddExState(&IRQCount, 4, 1, "IRQC"); + AddExState(&IRQLatch, 4, 1, "IQL1"); + AddExState(&IRQa, 1, 0, "IRQA"); + AddExState(PRGBankList, 4, 0, "PBL"); + for(x=0;x<8;x++) + { + char tak[8]; + sprintf(tak,"CBL%d",x); + AddExState(&CHRBankList[x], 2, 1,tak); + } + } + + if(MapInitTab[type]) MapInitTab[type](); + else if(type) + { + FCEU_PrintError("iNES mapper #%d is not supported at all.",type); + } + return 1; +} diff --git a/ines.h b/ines.h new file mode 100644 index 0000000..a27cf74 --- /dev/null +++ b/ines.h @@ -0,0 +1,366 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 1998 Bero + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef INESPRIV + +void iNESStateRestore(int version); +extern uint32 iNESGameCRC32; + +extern uint8 *VROM; +extern uint32 VROM_size; + +extern void (*MapStateRestore)(int version); +extern void (*MapClose)(void); +extern void (*MapperReset)(void); + +/* This order is necessary */ +#define WRAM (GameMemBlock) +#define sizeofWRAM 8192 + +#define MapperExRAM (GameMemBlock+sizeofWRAM) +#define sizeofMapperExRAM 32768 +/* for the MMC5 code to work properly. It might be fixed later... */ + + +#define CHRRAM (GameMemBlock+sizeofWRAM+sizeofMapperExRAM) +#define sizeofCHRRAM 8192 + +#define ExtraNTARAM (GameMemBlock+sizeofWRAM+sizeofMapperExRAM+sizeofCHRRAM) +#define sizeofExtraNTARAM 2048 + +#define PRGBankList (ExtraNTARAM+sizeofExtraNTARAM) + +#define mapbyte1 (PRGBankList+4) +#define mapbyte2 (mapbyte1+8) +#define mapbyte3 (mapbyte2+8) +#define mapbyte4 (mapbyte3+8) +uint16 iNESCHRBankList[8]; +int32 iNESIRQLatch,iNESIRQCount; +uint8 iNESMirroring; +uint8 iNESIRQa; + +#define IRQa iNESIRQa +#define Mirroring iNESMirroring +#define IRQCount iNESIRQCount +#define IRQLatch iNESIRQLatch +#define CHRBankList iNESCHRBankList +#else +int iNESLoad(char *name, int fp); +#endif + + typedef struct { + char ID[4]; /*NES^Z*/ + uint8 ROM_size; + uint8 VROM_size; + uint8 ROM_type; + uint8 ROM_type2; + uint8 reserve[8]; + } iNES_HEADER; + +void FASTAPASS(2) VRAM_BANK1(uint32 A, uint8 V); +void FASTAPASS(2) VRAM_BANK4(uint32 A,uint32 V); + +void FASTAPASS(2) VROM_BANK1(uint32 A,uint32 V); +void FASTAPASS(2) VROM_BANK2(uint32 A,uint32 V); +void FASTAPASS(2) VROM_BANK4(uint32 A, uint32 V); +void FASTAPASS(1) VROM_BANK8(uint32 V); +void FASTAPASS(2) ROM_BANK8(uint32 A, uint32 V); +void FASTAPASS(2) ROM_BANK16(uint32 A, uint32 V); +void FASTAPASS(2) ROM_BANK32(uint32 V); + +extern uint8 vmask; +extern uint32 vmask1; +extern uint32 vmask2; +extern uint32 vmask4; +extern uint32 pmask8; +extern uint8 pmask16; +extern uint8 pmask32; + +void FASTAPASS(1) onemir(uint8 V); +void FASTAPASS(1) MIRROR_SET2(uint8 V); +void FASTAPASS(1) MIRROR_SET(uint8 V); + + +void DetectMMC1WRAMSize(void); +void DetectMMC5WRAMSize(void); + +void Mapper0_init(void); +void Mapper1_init(void); +void Mapper2_init(void); +void Mapper3_init(void); +void Mapper4_init(void); +void Mapper5_init(void); +void Mapper6_init(void); +void Mapper7_init(void); +void Mapper8_init(void); +void Mapper9_init(void); +void Mapper10_init(void); +void Mapper11_init(void); +void Mapper12_init(void); +void Mapper13_init(void); +void Mapper14_init(void); +void Mapper15_init(void); +void Mapper16_init(void); +void Mapper17_init(void); +void Mapper18_init(void); +void Mapper19_init(void); +void Mapper20_init(void); +void Mapper21_init(void); +void Mapper22_init(void); +void Mapper23_init(void); +void Mapper24_init(void); +void Mapper25_init(void); +void Mapper26_init(void); +void Mapper27_init(void); +void Mapper28_init(void); +void Mapper29_init(void); +void Mapper30_init(void); +void Mapper31_init(void); +void Mapper32_init(void); +void Mapper33_init(void); +void Mapper34_init(void); +void Mapper35_init(void); +void Mapper36_init(void); +void Mapper37_init(void); +void Mapper38_init(void); +void Mapper39_init(void); +void Mapper40_init(void); +void Mapper41_init(void); +void Mapper42_init(void); +void Mapper43_init(void); +void Mapper44_init(void); +void Mapper45_init(void); +void Mapper46_init(void); +void Mapper47_init(void); +void Mapper48_init(void); +void Mapper49_init(void); +void Mapper50_init(void); +void Mapper51_init(void); +void Mapper52_init(void); +void Mapper53_init(void); +void Mapper54_init(void); +void Mapper55_init(void); +void Mapper56_init(void); +void Mapper57_init(void); +void Mapper58_init(void); +void Mapper59_init(void); +void Mapper60_init(void); +void Mapper61_init(void); +void Mapper62_init(void); +void Mapper63_init(void); +void Mapper64_init(void); +void Mapper65_init(void); +void Mapper66_init(void); +void Mapper67_init(void); +void Mapper68_init(void); +void Mapper69_init(void); +void Mapper70_init(void); +void Mapper71_init(void); +void Mapper72_init(void); +void Mapper73_init(void); +void Mapper74_init(void); +void Mapper75_init(void); +void Mapper76_init(void); +void Mapper77_init(void); +void Mapper78_init(void); +void Mapper79_init(void); +void Mapper80_init(void); +void Mapper81_init(void); +void Mapper82_init(void); +void Mapper83_init(void); +void Mapper84_init(void); +void Mapper85_init(void); +void Mapper86_init(void); +void Mapper87_init(void); +void Mapper88_init(void); +void Mapper89_init(void); +void Mapper90_init(void); +void Mapper91_init(void); +void Mapper92_init(void); +void Mapper93_init(void); +void Mapper94_init(void); +void Mapper95_init(void); +void Mapper96_init(void); +void Mapper97_init(void); +void Mapper98_init(void); +void Mapper99_init(void); +void Mapper100_init(void); +void Mapper101_init(void); +void Mapper102_init(void); +void Mapper103_init(void); +void Mapper104_init(void); +void Mapper105_init(void); +void Mapper106_init(void); +void Mapper107_init(void); +void Mapper108_init(void); +void Mapper109_init(void); +void Mapper110_init(void); +void Mapper111_init(void); +void Mapper112_init(void); +void Mapper113_init(void); +void Mapper114_init(void); +void Mapper115_init(void); +void Mapper116_init(void); +void Mapper117_init(void); +void Mapper118_init(void); +void Mapper119_init(void); +void Mapper120_init(void); +void Mapper121_init(void); +void Mapper122_init(void); +void Mapper123_init(void); +void Mapper124_init(void); +void Mapper125_init(void); +void Mapper126_init(void); +void Mapper127_init(void); +void Mapper128_init(void); +void Mapper129_init(void); +void Mapper130_init(void); +void Mapper131_init(void); +void Mapper132_init(void); +void Mapper133_init(void); +void Mapper134_init(void); +void Mapper135_init(void); +void Mapper136_init(void); +void Mapper137_init(void); +void Mapper138_init(void); +void Mapper139_init(void); +void Mapper140_init(void); +void Mapper141_init(void); +void Mapper142_init(void); +void Mapper143_init(void); +void Mapper144_init(void); +void Mapper145_init(void); +void Mapper146_init(void); +void Mapper147_init(void); +void Mapper148_init(void); +void Mapper149_init(void); +void Mapper150_init(void); +void Mapper151_init(void); +void Mapper152_init(void); +void Mapper153_init(void); +void Mapper154_init(void); +void Mapper155_init(void); +void Mapper156_init(void); +void Mapper157_init(void); +void Mapper158_init(void); +void Mapper159_init(void); +void Mapper160_init(void); +void Mapper161_init(void); +void Mapper162_init(void); +void Mapper163_init(void); +void Mapper164_init(void); +void Mapper165_init(void); +void Mapper166_init(void); +void Mapper167_init(void); +void Mapper168_init(void); +void Mapper169_init(void); +void Mapper170_init(void); +void Mapper171_init(void); +void Mapper172_init(void); +void Mapper173_init(void); +void Mapper174_init(void); +void Mapper175_init(void); +void Mapper176_init(void); +void Mapper177_init(void); +void Mapper178_init(void); +void Mapper179_init(void); +void Mapper180_init(void); +void Mapper181_init(void); +void Mapper182_init(void); +void Mapper183_init(void); +void Mapper184_init(void); +void Mapper185_init(void); +void Mapper186_init(void); +void Mapper187_init(void); +void Mapper188_init(void); +void Mapper189_init(void); +void Mapper190_init(void); +void Mapper191_init(void); +void Mapper192_init(void); +void Mapper193_init(void); +void Mapper194_init(void); +void Mapper195_init(void); +void Mapper196_init(void); +void Mapper197_init(void); +void Mapper198_init(void); +void Mapper199_init(void); +void Mapper200_init(void); +void Mapper201_init(void); +void Mapper202_init(void); +void Mapper203_init(void); +void Mapper204_init(void); +void Mapper205_init(void); +void Mapper206_init(void); +void Mapper207_init(void); +void Mapper208_init(void); +void Mapper209_init(void); +void Mapper210_init(void); +void Mapper211_init(void); +void Mapper212_init(void); +void Mapper213_init(void); +void Mapper214_init(void); +void Mapper215_init(void); +void Mapper216_init(void); +void Mapper217_init(void); +void Mapper218_init(void); +void Mapper219_init(void); +void Mapper220_init(void); +void Mapper221_init(void); +void Mapper222_init(void); +void Mapper223_init(void); +void Mapper224_init(void); +void Mapper225_init(void); +void Mapper226_init(void); +void Mapper227_init(void); +void Mapper228_init(void); +void Mapper229_init(void); +void Mapper230_init(void); +void Mapper231_init(void); +void Mapper232_init(void); +void Mapper233_init(void); +void Mapper234_init(void); +void Mapper235_init(void); +void Mapper236_init(void); +void Mapper237_init(void); +void Mapper238_init(void); +void Mapper239_init(void); +void Mapper240_init(void); +void Mapper241_init(void); +void Mapper242_init(void); +void Mapper243_init(void); +void Mapper244_init(void); +void Mapper245_init(void); +void Mapper246_init(void); +void Mapper247_init(void); +void Mapper248_init(void); +void Mapper249_init(void); +void Mapper250_init(void); +void Mapper251_init(void); +void Mapper252_init(void); +void Mapper253_init(void); +void Mapper254_init(void); +void Mapper255_init(void); + +void VRC6_ESI(int t); +void VRC7_ESI(void); +void Mapper5_ESI(void); +void Mapper69_ESI(void); +void Mapper19_ESI(void); diff --git a/input.c b/input.c new file mode 100644 index 0000000..be6b360 --- /dev/null +++ b/input.c @@ -0,0 +1,330 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 1998 BERO + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "types.h" +#include "x6502.h" + +#include "fce.h" +#include "sound.h" +#include "netplay.h" +#include "svga.h" + +#include "input.h" + +extern INPUTC *FCEU_InitZapper(int w); +extern INPUTC *FCEU_InitPowerpad(int w); +extern INPUTC *FCEU_InitArkanoid(int w); + +extern INPUTCFC *FCEU_InitArkanoidFC(void); +extern INPUTCFC *FCEU_InitSpaceShadow(void); +extern INPUTCFC *FCEU_InitFKB(void); +static uint8 joy_readbit[2]; +static uint16 joy[2]={0,0}; + +extern int coinon; + +static int FSDisable=0; /* Set to 1 if NES-style four-player adapter is disabled. */ +static int JPAttrib[2]={0,0}; +static int JPType[2]={0,0}; +static void *InputDataPtr[2]; + +static int JPAttribFC=0; +static int JPTypeFC=0; +static void *InputDataPtrFC; + +void (*InputScanlineHook)(uint8 *buf, int line); + +static INPUTC DummyJPort={0,0,0,0,0}; +static INPUTC *JPorts[2]={&DummyJPort,&DummyJPort}; +static INPUTCFC *FCExp=0; + +static uint8 FP_FASTAPASS(1) ReadGPVS(int w) +{ + uint8 ret=0; + + if(joy_readbit[w]>=8) + ret=1; + else + { + ret = ((joy[w]>>(joy_readbit[w]))&1); + joy_readbit[w]++; + } + return ret; +} + +static uint8 FP_FASTAPASS(1) ReadGP(int w) +{ + uint8 ret; + //if(JoyMulti) + //{ + //ret = ((joy[w]>>(joy_readbit[w]))&1)| + //(((joy[w]>>(joy_readbit[w]+8))&1)<<1); + //if(joy_readbit[w]>8) ret=0; + //} + ret = ((joy[w]>>(joy_readbit[w]))&1); + if(FSDisable) + { + if(joy_readbit[w]>=8) ret|=1; + } + else + { + if(joy_readbit[w]==19-w) ret|=1; + } + joy_readbit[w]++; + return ret; +} + +static DECLFR(JPRead) +{ + uint8 ret=0; + + if(JPorts[A&1]->Read) + ret|=JPorts[A&1]->Read(A&1); + + if(FCExp) + if(FCExp->Read) + ret=FCExp->Read(A&1,ret); + + ret|=X.DB&0xC0; + return(ret); +} + +static DECLFW(B4016) +{ + if(FCExp) + if(FCExp->Write) + FCExp->Write(V&7); + + if(JPorts[0]->Write) + JPorts[0]->Write(V&1); + if(JPorts[1]->Write) + JPorts[1]->Write(V&1); + + if((PSG[0x16]&1) && (!(V&1))) + { + /* This strobe code is just for convenience. If it were + with the code in input / *.c, it would more accurately represent + what's really going on. But who wants accuracy? ;) + Seriously, though, this shouldn't be a problem. + */ + if(JPorts[0]->Strobe) + JPorts[0]->Strobe(0); + if(JPorts[1]->Strobe) + JPorts[1]->Strobe(1); + if(FCExp) + if(FCExp->Strobe) + FCExp->Strobe(); + } + PSG[0x16]=V; +} + +static void FP_FASTAPASS(1) StrobeGP(int w) +{ + joy_readbit[w]=0; +} + +static INPUTC GPC={ReadGP,0,StrobeGP,0,0,0}; +static INPUTC GPCVS={ReadGPVS,0,StrobeGP,0,0,0}; + +void DrawInput(uint8 *buf) +{ + int x; + + for(x=0;x<2;x++) + if(JPorts[x]->Draw) + JPorts[x]->Draw(x,buf,JPAttrib[x]); + if(FCExp) + if(FCExp->Draw) + FCExp->Draw(buf,JPAttribFC); +} + +void UpdateInput(void) +{ + int x; + + for(x=0;x<2;x++) + { + switch(JPType[x]) + { + case SI_GAMEPAD: + if(!x) joy[0]=*(uint16 *)InputDataPtr[0]; + else joy[1]=*(uint16 *)InputDataPtr[1]; + break; + default: + if(JPorts[x]->Update) + JPorts[x]->Update(x,InputDataPtr[x],JPAttrib[x]); + break; + } + } + if(FCExp) + if(FCExp->Update) + FCExp->Update(InputDataPtrFC,JPAttribFC); + + if(FCEUGameInfo.type==GIT_VSUNI) + { + uint16 t=joy[0]; + joy[0]=(joy[0]&0xC)|(joy[1]&0xF3); + joy[1]=(joy[1]&0xC)|(t&0xF3); + if(coinon) coinon--; + } + #ifdef NETWORK + if(netplay) NetplayUpdate(&joy[0],&joy[1]); + #endif + FlushCommandQueue(); +} + +static DECLFR(VSUNIRead0) +{ + uint8 ret=0; + + if(JPorts[0]->Read) + ret|=(JPorts[0]->Read(0))&1; + + ret|=(vsdip&3)<<3; + if(coinon) + ret|=0x4; + return ret; +} + +static DECLFR(VSUNIRead1) +{ + uint8 ret=0; + + if(JPorts[1]->Read) + ret|=(JPorts[1]->Read(1))&1; + ret|=vsdip&0xFC; + return ret; +} + +static void SLHLHook(uint8 *buf, int line) +{ + int x; + + for(x=0;x<2;x++) + if(JPorts[x]->SLHook) + JPorts[x]->SLHook(x,buf,line); + if(FCExp) + if(FCExp->SLHook) + FCExp->SLHook(buf,line); +} + +static void CheckSLHook(void) +{ + InputScanlineHook=0; + if(JPorts[0]->SLHook || JPorts[1]->SLHook) + InputScanlineHook=SLHLHook; + if(FCExp) + if(FCExp->SLHook) + InputScanlineHook=SLHLHook; +} + +static void FASTAPASS(1) SetInputStuff(int x) +{ + switch(JPType[x]) + { + case SI_GAMEPAD: + if(FCEUGameInfo.type==GIT_VSUNI) + JPorts[x]=&GPCVS; + else + JPorts[x]=&GPC; + break; + case SI_ARKANOID:JPorts[x]=FCEU_InitArkanoid(x);break; + case SI_ZAPPER:JPorts[x]=FCEU_InitZapper(x);break; + case SI_POWERPAD:JPorts[x]=FCEU_InitPowerpad(x);break; + case SI_NONE:JPorts[x]=&DummyJPort;break; + } + + CheckSLHook(); +} + +static uint8 F4ReadBit[2]; +static void StrobeFami4(void) +{ + F4ReadBit[0]=F4ReadBit[1]=0; +} + +static uint8 FP_FASTAPASS(2) ReadFami4(int w, uint8 ret) +{ + ret&=1; + + ret |= ((joy[w]>>(F4ReadBit[w]+8))&1)<<1; + if(F4ReadBit[w]>=8) ret|=2; + else F4ReadBit[w]++; + + return(ret); +} + +static INPUTCFC FAMI4C={ReadFami4,0,StrobeFami4,0,0,0}; +static void SetInputStuffFC(void) +{ + switch(JPTypeFC) + { + case SIFC_NONE:FCExp=0;break; + case SIFC_ARKANOID:FCExp=FCEU_InitArkanoidFC();break; + case SIFC_SHADOW:FCExp=FCEU_InitSpaceShadow();break; + case SIFC_4PLAYER:FCExp=&FAMI4C;memset(&F4ReadBit,0,sizeof(F4ReadBit));break; + case SIFC_FKB:FCExp=FCEU_InitFKB();break; + } + CheckSLHook(); +} + +// VS Unisystem code called after SetInputMap() hooks B4016. Need to +// rewrite code to make this more sane? + +void InitializeInput(void) +{ + memset(joy_readbit,0,sizeof(joy_readbit)); + memset(joy,0,sizeof(joy)); + + if(FCEUGameInfo.type==GIT_VSUNI) + { + SetReadHandler(0x4016,0x4016,VSUNIRead0); + SetReadHandler(0x4017,0x4017,VSUNIRead1); + } + else + SetReadHandler(0x4016,0x4017,JPRead); + SetWriteHandler(0x4016,0x4016,B4016); + + SetInputStuff(0); + SetInputStuff(1); + SetInputStuffFC(); +} + +void FCEUI_SetInput(int port, int type, void *ptr, int attrib) +{ + JPAttrib[port]=attrib; + JPType[port]=type; + InputDataPtr[port]=ptr; + SetInputStuff(port); +} + +void FCEUI_DisableFourScore(int s) +{ + FSDisable=s; +} + +void FCEUI_SetInputFC(int type, void *ptr, int attrib) +{ + JPAttribFC=attrib; + JPTypeFC=type; + InputDataPtrFC=ptr; + SetInputStuffFC(); +} diff --git a/input.h b/input.h new file mode 100644 index 0000000..8cec68a --- /dev/null +++ b/input.h @@ -0,0 +1,23 @@ +typedef struct { + uint8 FP_FASTAPASS(1) (*Read)(int w); + void FP_FASTAPASS(1) (*Write)(uint8 v); + void FP_FASTAPASS(1) (*Strobe)(int w); + void FP_FASTAPASS(3) (*Update)(int w, void *data, int arg); + void FP_FASTAPASS(3) (*SLHook)(int w, uint8 *buf, int line); + void FP_FASTAPASS(3) (*Draw)(int w, uint8 *buf, int arg); +} INPUTC; + +typedef struct { + uint8 FP_FASTAPASS(2) (*Read)(int w, uint8 ret); + void FP_FASTAPASS(1) (*Write)(uint8 v); + void (*Strobe)(void); + void FP_FASTAPASS(2) (*Update)(void *data, int arg); + void FP_FASTAPASS(2) (*SLHook)(uint8 *buf, int line); + void FP_FASTAPASS(2) (*Draw)(uint8 *buf, int arg); +} INPUTCFC; + +void DrawInput(uint8 *buf); +void UpdateInput(void); +void InitializeInput(void); +extern void (*PStrobe[2])(void); +extern void (*InputScanlineHook)(uint8 *buf, int line); diff --git a/input/Makefile b/input/Makefile new file mode 100644 index 0000000..0327a9f --- /dev/null +++ b/input/Makefile @@ -0,0 +1,8 @@ +INPOBJS = input/cursor.o input/powerpad.o input/zapper.o input/arkanoid.o input/shadow.o input/fkb.o + +input/cursor.o: input/cursor.c +input/zapper.o: input/zapper.c +input/powerpad.o: input/powerpad.c +input/arkanoid.o: input/arkanoid.c +input/shadow.o: input/shadow.c +input/fkb.o: input/fkb.c input/fkb.h diff --git a/input/arkanoid.c b/input/arkanoid.c new file mode 100644 index 0000000..b53fd3a --- /dev/null +++ b/input/arkanoid.c @@ -0,0 +1,117 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include "share.h" + +typedef struct { + uint32 mzx,mzb; + uint32 readbit; +} ARK; + +static ARK NESArk[2]; +static ARK FCArk; + +static void StrobeARKFC(void) +{ + FCArk.readbit=0; +} + + +static uint8 FP_FASTAPASS(2) ReadARKFC(int w,uint8 ret) +{ + ret&=~2; + + if(w) + { + if(FCArk.readbit>=8) + ret|=2; + else + { + ret|=((FCArk.mzx>>(7-FCArk.readbit))&1)<<1; + FCArk.readbit++; + } + } + else + ret|=FCArk.mzb<<1; + return(ret); +} + +static uint32 FixX(uint32 x) +{ + x=98+x*144/240; + if(x>242) x=242; + x=~x; + return(x); +} + +static void FP_FASTAPASS(2) UpdateARKFC(void *data, int arg) +{ + uint32 *ptr=data; + FCArk.mzx=FixX(ptr[0]); + FCArk.mzb=ptr[2]?1:0; +} + +static INPUTCFC ARKCFC={ReadARKFC,0,StrobeARKFC,UpdateARKFC,0,0}; + +INPUTCFC *FCEU_InitArkanoidFC(void) +{ + FCArk.mzx=98; + FCArk.mzb=0; + return(&ARKCFC); +} + +static uint8 FP_FASTAPASS(1) ReadARK(int w) +{ + uint8 ret=0; + + if(NESArk[w].readbit>=8) + ret|=1<<4; + else + { + ret|=((NESArk[w].mzx>>(7-NESArk[w].readbit))&1)<<4; + NESArk[w].readbit++; + } + ret|=(NESArk[w].mzb&1)<<3; + return(ret); +} + + +static void FP_FASTAPASS(1) StrobeARK(int w) +{ + NESArk[w].readbit=0; +} + +static void FP_FASTAPASS(3) UpdateARK(int w, void *data, int arg) +{ + uint32 *ptr=data; + NESArk[w].mzx=FixX(ptr[0]); + NESArk[w].mzb=ptr[2]?1:0; +} + +static INPUTC ARKC={ReadARK, 0, StrobeARK, UpdateARK, 0, 0}; + +INPUTC *FCEU_InitArkanoid(int w) +{ + NESArk[w].mzx=98; + NESArk[w].mzb=0; + return(&ARKC); +} diff --git a/input/cursor.c b/input/cursor.c new file mode 100644 index 0000000..cb78d73 --- /dev/null +++ b/input/cursor.c @@ -0,0 +1,45 @@ +#include "share.h" + +static uint8 FCEUcursor[11*19]= +{ + 1,0,0,0,0,0,0,0,0,0,0, + 1,1,0,0,0,0,0,0,0,0,0, + 1,2,1,0,0,0,0,0,0,0,0, + 1,2,2,1,0,0,0,0,0,0,0, + 1,2,2,2,1,0,0,0,0,0,0, + 1,2,2,2,2,1,0,0,0,0,0, + 1,2,2,2,2,2,1,0,0,0,0, + 1,2,2,2,2,2,2,1,0,0,0, + 1,2,2,2,2,2,2,2,1,0,0, + 1,2,2,2,2,2,2,2,2,1,0, + 1,2,2,2,2,2,1,1,1,1,1, + 1,2,2,1,2,2,1,0,0,0,0, + 1,2,1,0,1,2,2,1,0,0,0, + 1,1,0,0,1,2,2,1,0,0,0, + 1,0,0,0,0,1,2,2,1,0,0, + 0,0,0,0,0,1,2,2,1,0,0, + 0,0,0,0,0,0,1,2,2,1,0, + 0,0,0,0,0,0,1,2,2,1,0, + 0,0,0,0,0,0,0,1,1,0,0, +}; + +void FCEU_DrawCursor(uint8 *buf, int xc, int yc) +{ + int x,y; + int c,d; + + if(xc<256 && yc<240) + for(y=0;y<19;y++) + for(x=0;x<11;x++) + { + uint8 a; + a=FCEUcursor[y*11+x]; + if(a) + { + c=(yc+y); + d=(xc+x); + if(d<256 && c<240) + buf[c*272+d]=a+127; + } + } +} diff --git a/input/fkb.c b/input/fkb.c new file mode 100644 index 0000000..c70e73f --- /dev/null +++ b/input/fkb.c @@ -0,0 +1,102 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include "share.h" +#include "fkb.h" +#define AK2(x,y) ( (FKB_##x) | (FKB_##y <<8) ) +#define AK(x) FKB_##x + +static uint8 bufit[0x49]; +static uint8 ksmode; +static uint8 ksindex; + + +static uint16 matrix[9][2][4]= +{ +{{AK(F8),AK(RETURN),AK(BRACKETLEFT),AK(BRACKETRIGHT)}, + {AK(KANA),AK(RIGHTSHIFT),AK(BACKSLASH),AK(STOP)}}, +{{AK(F7),AK(AT),AK(COLON),AK(SEMICOLON)}, + {AK(UNDERSCORE),AK(SLASH),AK(MINUS),AK(CARET)}}, +{{AK(F6),AK(O),AK(L),AK(K)}, + {AK(PERIOD),AK(COMMA),AK(P),AK(0)}}, +{{AK(F5),AK(I),AK(U),AK(J)}, + {AK(M),AK(N),AK(9),AK(8)}}, +{{AK(F4),AK(Y),AK(G),AK(H)}, + {AK(B),AK(V),AK(7),AK(6)}}, +{{AK(F3),AK(T),AK(R),AK(D)}, + {AK(F),AK(C),AK(5),AK(4)}}, +{{AK(F2),AK(W),AK(S),AK(A)}, + {AK(X),AK(Z),AK(E),AK(3)}}, +{{AK(F1),AK(ESCAPE),AK(Q),AK(CONTROL)}, + {AK(LEFTSHIFT),AK(GRAPH),AK(1),AK(2)}}, +{{AK(CLEAR),AK(UP),AK(RIGHT),AK(LEFT)}, + {AK(DOWN),AK(SPACE),AK(DELETE),AK(INSERT)}}, +}; + +static void FP_FASTAPASS(1) FKB_Write(uint8 v) +{ + v>>=1; + if(v&2) + { + if((ksmode&1) && !(v&1)) + ksindex=(ksindex+1)%9; + } + ksmode=v; +} + +static uint8 FP_FASTAPASS(2) FKB_Read(int w, uint8 ret) +{ + //printf("$%04x, %d, %d\n",w+0x4016,ksindex,ksmode&1); + if(w) + { + int x; + + ret&=~0x1E; + for(x=0;x<4;x++) + if(bufit[ matrix[ksindex][ksmode&1][x]&0xFF ] || bufit[ matrix[ksindex][ksmode&1][x]>>8]) + { + ret|=1<<(x+1); + } + ret^=0x1E; + } + return(ret); +} + +static void FKB_Strobe(void) +{ + ksmode=0; + ksindex=0; + //printf("strobe\n"); +} + +static void FP_FASTAPASS(2) FKB_Update(void *data, int arg) +{ + memcpy(bufit+1,data,0x48); +} + +static INPUTCFC FKB={FKB_Read,FKB_Write,FKB_Strobe,FKB_Update,0,0}; + +INPUTCFC *FCEU_InitFKB(void) +{ + memset(bufit,0,sizeof(bufit)); + ksmode=ksindex=0; + return(&FKB); +} diff --git a/input/fkb.h b/input/fkb.h new file mode 100644 index 0000000..ca27b34 --- /dev/null +++ b/input/fkb.h @@ -0,0 +1,72 @@ +#define FKB_F1 0x01 +#define FKB_F2 0x02 +#define FKB_F3 0x03 +#define FKB_F4 0x04 +#define FKB_F5 0x05 +#define FKB_F6 0x06 +#define FKB_F7 0x07 +#define FKB_F8 0x08 +#define FKB_1 0x09 +#define FKB_2 0x0A +#define FKB_3 0x0B +#define FKB_4 0x0C +#define FKB_5 0x0D +#define FKB_6 0x0E +#define FKB_7 0x0F +#define FKB_8 0x10 +#define FKB_9 0x11 +#define FKB_0 0x12 +#define FKB_MINUS 0x13 +#define FKB_CARET 0x14 +#define FKB_BACKSLASH 0x15 +#define FKB_STOP 0x16 +#define FKB_ESCAPE 0x17 +#define FKB_Q 0x18 +#define FKB_W 0x19 +#define FKB_E 0x1A +#define FKB_R 0x1B +#define FKB_T 0x1C +#define FKB_Y 0x1D +#define FKB_U 0x1E +#define FKB_I 0x1F +#define FKB_O 0x20 +#define FKB_P 0x21 +#define FKB_AT 0x22 +#define FKB_BRACKETLEFT 0x23 +#define FKB_RETURN 0x24 +#define FKB_CONTROL 0x25 +#define FKB_A 0x26 +#define FKB_S 0x27 +#define FKB_D 0x28 +#define FKB_F 0x29 +#define FKB_G 0x2A +#define FKB_H 0x2B +#define FKB_J 0x2C +#define FKB_K 0x2D +#define FKB_L 0x2E +#define FKB_SEMICOLON 0x2F +#define FKB_COLON 0x30 +#define FKB_BRACKETRIGHT 0x31 +#define FKB_KANA 0x32 +#define FKB_LEFTSHIFT 0x33 +#define FKB_Z 0x34 +#define FKB_X 0x35 +#define FKB_C 0x36 +#define FKB_V 0x37 +#define FKB_B 0x38 +#define FKB_N 0x39 +#define FKB_M 0x3A +#define FKB_COMMA 0x3B +#define FKB_PERIOD 0x3C +#define FKB_SLASH 0x3D +#define FKB_UNDERSCORE 0x3E +#define FKB_RIGHTSHIFT 0x3F +#define FKB_GRAPH 0x40 +#define FKB_SPACE 0x41 +#define FKB_CLEAR 0x42 +#define FKB_INSERT 0x43 +#define FKB_DELETE 0x44 +#define FKB_UP 0x45 +#define FKB_LEFT 0x46 +#define FKB_RIGHT 0x47 +#define FKB_DOWN 0x48 diff --git a/input/powerpad.c b/input/powerpad.c new file mode 100644 index 0000000..3c613f9 --- /dev/null +++ b/input/powerpad.c @@ -0,0 +1,60 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include "share.h" + + +static uint32 pprsb[2]; +static uint32 pprdata[2]; + +static uint8 FP_FASTAPASS(1) ReadPP(int w) +{ + uint8 ret=0; + ret|=((pprdata[w]>>pprsb[w])&1)<<3; + ret|=((pprdata[w]>>(pprsb[w]+8))&1)<<4; + if(pprsb[w]>=4) + { + ret|=0x10; + if(pprsb[w]>=8) + ret|=0x08; + } + pprsb[w]++; + return ret; +} + +static void FP_FASTAPASS(1) StrobePP(int w) +{ + pprsb[w]=0; +} + +void FP_FASTAPASS(3) UpdatePP(int w, void *data, int arg) +{ + pprdata[w]=*(uint32 *)data; +} + +static INPUTC PPC={ReadPP,0,StrobePP,UpdatePP,0,0}; + +INPUTC *FCEU_InitPowerpad(int w) +{ + pprsb[w]=pprdata[w]=0; + return(&PPC); +} diff --git a/input/shadow.c b/input/shadow.c new file mode 100644 index 0000000..499e640 --- /dev/null +++ b/input/shadow.c @@ -0,0 +1,127 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +#include "share.h" + +typedef struct { + uint32 mzx,mzy,mzb; + int zap_readbit; + int bogo; + uint32 colok; + uint32 coloklast; +} ZAPPER; + +static ZAPPER ZD; + +static void FP_FASTAPASS(2) ZapperThingy(uint8 *buf, int line) +{ + int mzx=ZD.mzx; + + if(line==0) ZD.colok=1<<16; /* Disable it. */ + ZD.coloklast=ZD.colok; + + if((line>=ZD.mzy-3 && line<=ZD.mzy+3) && mzx<256) + { + int a,sum,x; + + for(x=-4;x<4;x++) + { + if((mzx+x)<0 || (mzx+x)>255) continue; + a=buf[mzx+x]&63; + sum=palo[a].r+palo[a].g+palo[a].b; + + if(sum>=100*3) + { + ZD.colok=timestamp+mzx/3; + break; + } + } + } +} + +static INLINE int CheckColor(void) +{ + if( (timestamp>=ZD.coloklast && timestamp<=(ZD.coloklast+10)) || + (timestamp>=ZD.colok && timestamp<=(ZD.colok+10)) ) + return 0; + return 1; +} + +static uint8 FP_FASTAPASS(2) ReadZapper(int w, uint8 ret) +{ + if(w) + { + ret&=~0x18; + if(ZD.bogo) + ret|=0x10; + if(CheckColor()) + ret|=0x8; + } + else + { + //printf("Kayo: %d\n",ZD.zap_readbit); + ret&=~2; + //if(ZD.zap_readbit==4) ret|=ZD.mzb&2; + ret|=(ret&1)<<1; + //ZD.zap_readbit++; + } + return ret; +} + +static void FP_FASTAPASS(2) DrawZapper(uint8 *buf, int arg) +{ + if(arg) + FCEU_DrawCursor(buf, ZD.mzx, ZD.mzy); +} + +static void FP_FASTAPASS(2) UpdateZapper(void *data, int arg) +{ + uint32 *ptr=data; + + if(ZD.bogo) + ZD.bogo--; + if(ptr[2]&1 && (!(ZD.mzb&1))) + ZD.bogo=5; + + ZD.mzx=ptr[0]; + ZD.mzy=ptr[1]; + ZD.mzb=ptr[2]; + + if(ZD.mzx>=256 || ZD.mzy>=240) + ZD.colok=0; +} + +static void StrobeShadow(void) +{ + ZD.zap_readbit=0; +} + +static INPUTCFC SHADOWC={ReadZapper,0,StrobeShadow,UpdateZapper,ZapperThingy,DrawZapper}; + +INPUTCFC *FCEU_InitSpaceShadow(void) +{ + memset(&ZD,0,sizeof(ZAPPER)); + return(&SHADOWC); +} + + diff --git a/input/share.h b/input/share.h new file mode 100644 index 0000000..8e8c333 --- /dev/null +++ b/input/share.h @@ -0,0 +1,7 @@ +#include "../types.h" +#include "../input.h" +#include "../fce.h" +#include "../svga.h" +#include "../x6502.h" + +void FCEU_DrawCursor(uint8 *buf, int xc, int yc); diff --git a/input/zapper.c b/input/zapper.c new file mode 100644 index 0000000..16f9e15 --- /dev/null +++ b/input/zapper.c @@ -0,0 +1,143 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +#include "share.h" + +typedef struct { + uint32 mzx,mzy,mzb; + int zap_readbit; + int bogo; + uint32 colok; + uint32 coloklast; +} ZAPPER; + +static ZAPPER ZD[2]; + +static void FP_FASTAPASS(3) ZapperThingy(int w, uint8 *buf, int line) +{ + int mzx=ZD[w].mzx; + + if(line==0) ZD[w].colok=1<<16; /* Disable it. */ + + ZD[w].coloklast=ZD[w].colok; + + if(ZD[w].mzb&2) return; + if((line>=ZD[w].mzy-3 && line<=ZD[w].mzy+3) && mzx<256) + { + int a,sum,x; + + for(x=-4;x<4;x++) + { + if((mzx+x)<0 || (mzx+x)>255) continue; + a=buf[mzx+x]&63; + sum=palo[a].r+palo[a].g+palo[a].b; + + if(sum>=100*3) + { + ZD[w].colok=timestamp+mzx/3; + break; + } + } + } + +} + +static INLINE int CheckColor(int w) +{ + if( (timestamp>=ZD[w].coloklast && timestamp<=(ZD[w].coloklast+100)) || + (timestamp>=ZD[w].colok && timestamp<=(ZD[w].colok+100)) ) + return 0; + return 1; +} + +static uint8 FP_FASTAPASS(1) ReadZapperVS(int w) +{ + uint8 ret=0; + + if(ZD[w].zap_readbit==4) ret=1; + + if(ZD[w].zap_readbit==7) + { + if(ZD[w].bogo) + ret|=0x1; + } + if(ZD[w].zap_readbit==6) + { + if(!CheckColor(w)) + ret|=0x1; + } + ZD[w].zap_readbit++; + return ret; +} + +static void FP_FASTAPASS(1) StrobeZapperVS(int w) +{ + ZD[w].zap_readbit=0; +} + +static uint8 FP_FASTAPASS(1) ReadZapper(int w) +{ + uint8 ret=0; + if(ZD[w].bogo) + ret|=0x10; + if(CheckColor(w)) + ret|=0x8; + return ret; +} + +static void FASTAPASS(3) DrawZapper(int w, uint8 *buf, int arg) +{ + if(arg) + FCEU_DrawCursor(buf, ZD[w].mzx,ZD[w].mzy); +} + +static void FP_FASTAPASS(3) UpdateZapper(int w, void *data, int arg) +{ + uint32 *ptr=data; + + if(ZD[w].bogo) + ZD[w].bogo--; + if(ptr[2]&3 && (!(ZD[w].mzb&3))) + ZD[w].bogo=5; + + ZD[w].mzx=ptr[0]; + ZD[w].mzy=ptr[1]; + ZD[w].mzb=ptr[2]; + + if(ZD[w].mzb&2 || ZD[w].mzx>=256 || ZD[w].mzy>=240) + ZD[w].colok=0; +} + +static INPUTC ZAPC={ReadZapper,0,0,UpdateZapper,ZapperThingy,DrawZapper}; +static INPUTC ZAPVSC={ReadZapperVS,0,StrobeZapperVS,UpdateZapper,ZapperThingy,DrawZapper}; + +INPUTC *FCEU_InitZapper(int w) +{ + memset(&ZD[w],0,sizeof(ZAPPER)); + if(FCEUGameInfo.type==GIT_VSUNI) + return(&ZAPVSC); + else + return(&ZAPC); +} + + diff --git a/mappers/105.c b/mappers/105.c new file mode 100644 index 0000000..977e9f2 --- /dev/null +++ b/mappers/105.c @@ -0,0 +1,131 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 1998 BERO + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + +#define MMC1_reg mapbyte1 +#define MMC1_buf mapbyte2[0] +#define MMC1_sft mapbyte3[0] +#define lastn mapbyte2[1] + +#define NWCDIP 0xE + +static void FP_FASTAPASS(1) NWCIRQHook(int a) +{ + if(!(MMC1_reg[1]&0x10)) + { + IRQCount+=a; + if((IRQCount|(NWCDIP<<25))>=0x3e000000) + { + IRQCount=0; + X6502_IRQBegin(FCEU_IQEXT); + } + } +} + + +static void MMC1PRG(void) +{ + if(MMC1_reg[1]&8) + { + switch(MMC1_reg[0]&0xC) + { + case 0xC: ROM_BANK16(0x8000,8+(MMC1_reg[3]&7)); + ROM_BANK16(0xC000,15); + break; + case 0x8: ROM_BANK16(0xC000,8+((MMC1_reg[3])&7)); + ROM_BANK16(0x8000,8); + break; + case 0x0: + case 0x4: + ROM_BANK16(0x8000,8+(MMC1_reg[3]&6)); + ROM_BANK16(0xc000,8+((MMC1_reg[3]&6)+1)); + break; + } + } + else + { + ROM_BANK32((MMC1_reg[1]>>1)&3); + } +} + +DECLFW(Mapper105_write) +{ + int n=(A>>13)-4; + + if (V&0x80) + { + MMC1_sft=MMC1_buf=0; + return; + } + + if(lastn!=n) + { + MMC1_sft=MMC1_buf=0; + } + lastn=n; + + //MMC1_reg[n]&=~((1)<<(MMC1_sft)); + MMC1_buf|=(V&1)<<(MMC1_sft++); + + if (MMC1_sft==5) + { + if(n==3) V&=0xF; + else V&=0x1F; + + MMC1_reg[n]=V=MMC1_buf; + MMC1_sft = MMC1_buf=0; + + switch(n){ + case 0: + switch(MMC1_reg[0]&3) + { + case 2: MIRROR_SET(0);break; + case 3: MIRROR_SET(1);break; + case 0: onemir(0);break; + case 1: onemir(1);break; + } + MMC1PRG(); + break; + case 1: + if(MMC1_reg[1]&0x10) + {IRQCount=0;X6502_IRQEnd(FCEU_IQEXT);} + MMC1PRG(); + break; + case 3: + MMC1PRG(); + break; + } + } +} + + +void Mapper105_init(void) +{ + int i; + for(i=0;i<4;i++) MMC1_reg[i]=0; + MMC1_sft = MMC1_buf =0; + MMC1_reg[0]=0xC; + ROM_BANK32(0); + SetWriteHandler(0x8000,0xFFFF,Mapper105_write); + MapIRQHook=NWCIRQHook; +} + diff --git a/mappers/112.c b/mappers/112.c new file mode 100644 index 0000000..bfa4595 --- /dev/null +++ b/mappers/112.c @@ -0,0 +1,52 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + + + +DECLFW(Mapper112_write) +{ +switch(A) +{ + case 0xe000:MIRROR_SET(V&1);break; + case 0x8000:mapbyte1[0]=V;break; + case 0xa000:switch(mapbyte1[0]) + { + case 0:ROM_BANK8(0x8000,V);break; + case 1:ROM_BANK8(0xA000,V);break; + case 2: V&=0xFE;VROM_BANK1(0,V); + VROM_BANK1(0x400,(V+1));break; + case 3: V&=0xFE;VROM_BANK1(0x800,V); + VROM_BANK1(0xC00,(V+1));break; + case 4:VROM_BANK1(0x1000,V);break; + case 5:VROM_BANK1(0x1400,V);break; + case 6:VROM_BANK1(0x1800,V);break; + case 7:VROM_BANK1(0x1c00,V);break; + } + break; + } +} + +void Mapper112_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper112_write); +} + diff --git a/mappers/113.c b/mappers/113.c new file mode 100644 index 0000000..66548b5 --- /dev/null +++ b/mappers/113.c @@ -0,0 +1,47 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + +/* I'm getting the feeling this is another "jam two different bank + switching hardwares into one mapper". +*/ + +/* HES 4-in-1 */ +DECLFW(Mapper113_write) +{ + ROM_BANK32((V>>3)&7); + VROM_BANK8(V&7); +} + + +/* Deathbots */ +DECLFW(Mapper113_writeh) +{ + ROM_BANK32(V&0xF); +} + + +void Mapper113_init(void) +{ + ROM_BANK32(0); + SetWriteHandler(0x4020,0x7fff,Mapper113_write); + SetWriteHandler(0x8000,0xffff,Mapper113_writeh); +} diff --git a/mappers/117.c b/mappers/117.c new file mode 100644 index 0000000..aa0ea5f --- /dev/null +++ b/mappers/117.c @@ -0,0 +1,67 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + + + +DECLFW(Mapper117_write) +{ + switch(A) + { + case 0xc003:IRQCount=V;break; + case 0xc001:IRQa=V;break; + case 0xa000:VROM_BANK1(0x0000,V);break; + case 0xa001:VROM_BANK1(0x0400,V);break; + case 0xa002:VROM_BANK1(0x0800,V);break; + case 0xa003:VROM_BANK1(0x0c00,V);break; + case 0xa004:VROM_BANK1(0x1000,V);break; + case 0xa005:VROM_BANK1(0x1400,V);break; + case 0xa006:VROM_BANK1(0x1800,V);break; + case 0xa007:VROM_BANK1(0x1c00,V);break; + case 0x8000:ROM_BANK8(0x8000,V);break; + case 0x8001:ROM_BANK8(0xa000,V);break; + case 0x8002:ROM_BANK8(0xc000,V);break; + case 0x8003:ROM_BANK8(0xe000,V);break; + } +} + +static void Mapper117_hb(void) +{ + if(IRQa) + { + if(IRQCount<=0) + { + IRQa=0; + TriggerIRQ(); + } + else + { + IRQCount--; + } + } +} + +void Mapper117_init(void) +{ + GameHBIRQHook=Mapper117_hb; + SetWriteHandler(0x8000,0xffff,Mapper117_write); +} + diff --git a/mappers/15.c b/mappers/15.c new file mode 100644 index 0000000..05146af --- /dev/null +++ b/mappers/15.c @@ -0,0 +1,87 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 1998 BERO + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + + + + +DECLFW(Mapper15_write) +{ +switch(A) + { + case 0x8000: + if(V&0x80) + { + ROM_BANK8(0x8000,(V<<1)+1); + ROM_BANK8(0xA000,(V<<1)); + ROM_BANK8(0xC000,(V<<1)+2); + ROM_BANK8(0xE000,(V<<1)+1); + } + else + { + ROM_BANK16(0x8000,V); + ROM_BANK16(0xC000,V+1); + } + MIRROR_SET((V>>6)&1); + break; + case 0x8001: + MIRROR_SET(0); + ROM_BANK16(0x8000,V); + ROM_BANK16(0xc000,~0); + break; + case 0x8002: + if(V&0x80) + { + ROM_BANK8(0x8000,((V<<1)+1)); + ROM_BANK8(0xA000,((V<<1)+1)); + ROM_BANK8(0xC000,((V<<1)+1)); + ROM_BANK8(0xE000,((V<<1)+1)); + } + else + { + ROM_BANK8(0x8000,(V<<1)); + ROM_BANK8(0xA000,(V<<1)); + ROM_BANK8(0xC000,(V<<1)); + ROM_BANK8(0xE000,(V<<1)); + } + break; + case 0x8003: + MIRROR_SET((V>>6)&1); + if(V&0x80) + { + ROM_BANK8(0xC000,(V<<1)+1); + ROM_BANK8(0xE000,(V<<1)); + } + else + { + ROM_BANK16(0xC000,V); + } + break; + } +} + +void Mapper15_init(void) +{ + ROM_BANK32(0); + SetWriteHandler(0x8000,0xFFFF,Mapper15_write); +} + diff --git a/mappers/151.c b/mappers/151.c new file mode 100644 index 0000000..cc59c6c --- /dev/null +++ b/mappers/151.c @@ -0,0 +1,41 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + + + +DECLFW(Mapper151_write) +{ + switch(A&0xF000) + { + case 0x8000:ROM_BANK8(0x8000,V);break; + case 0xA000:ROM_BANK8(0xA000,V);break; + case 0xC000:ROM_BANK8(0xC000,V);break; + case 0xe000:VROM_BANK4(0x0000,V);break; + case 0xf000:VROM_BANK4(0x1000,V);break; + } +} + +void Mapper151_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper151_write); +} + diff --git a/mappers/16.c b/mappers/16.c new file mode 100644 index 0000000..3742c34 --- /dev/null +++ b/mappers/16.c @@ -0,0 +1,129 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 1998 BERO + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + +static void FP_FASTAPASS(1) BandaiIRQHook(int a) +{ + if(IRQa) + { + IRQCount-=a; + if(IRQCount<0) + { + X6502_IRQBegin(FCEU_IQEXT); + //printf("IRQ: %d, %d\n",scanline,timestamp); + IRQa=0; + IRQCount=0xFFFF; + } + } +} + +static DECLFW(Mapper16_write) +{ + A&=0xF; + + if(A<=0x7) + VROM_BANK1(A<<10,V); + else if(A==0x8) + ROM_BANK16(0x8000,V); + else switch(A) { + case 0x9: switch(V&3) { + case 0x00:MIRROR_SET2(1);break; + case 0x01:MIRROR_SET2(0);break; + case 0x02:onemir(0);break; + case 0x03:onemir(1);break; + } + break; + case 0xA:X6502_IRQEnd(FCEU_IQEXT); + IRQa=V&1; + IRQCount=IRQLatch; + break; + case 0xB:IRQLatch&=0xFF00; + IRQLatch|=V; + break; + case 0xC:IRQLatch&=0xFF; + IRQLatch|=V<<8; + break; + case 0xD: break;/* Serial EEPROM control port */ + } +} + +// Famicom jump 2: +// 0-7: Lower bit of data selects which 256KB PRG block is in use. +// This seems to be a hack on the developers' part, so I'll make emulation +// of it a hack(I think the current PRG block would depend on whatever the +// lowest bit of the CHR bank switching register that corresponds to the +// last CHR address read). + +static void PRGO(void) +{ + uint32 base=(mapbyte1[0]&1)<<4; + ROM_BANK16(0x8000,(mapbyte2[0]&0xF)|base); + ROM_BANK16(0xC000,base|0xF); +} + +static DECLFW(Mapper153_write) +{ + A&=0xF; + if(A<=0x7) + { + mapbyte1[A&7]=V; + PRGO(); + } + else if(A==0x8) + { + mapbyte2[0]=V; + PRGO(); + } + else switch(A) { + case 0x9: switch(V&3) { + case 0x00:MIRROR_SET2(1);break; + case 0x01:MIRROR_SET2(0);break; + case 0x02:onemir(0);break; + case 0x03:onemir(1);break; + } + break; + case 0xA:X6502_IRQEnd(FCEU_IQEXT); + IRQa=V&1; + IRQCount=IRQLatch; + break; + case 0xB:IRQLatch&=0xFF00; + IRQLatch|=V; + break; + case 0xC:IRQLatch&=0xFF; + IRQLatch|=V<<8; + break; + } +} + +void Mapper16_init(void) +{ + MapIRQHook=BandaiIRQHook; + SetWriteHandler(0x6000,0xFFFF,Mapper16_write); +} + +void Mapper153_init(void) +{ + MapIRQHook=BandaiIRQHook; + SetWriteHandler(0x8000,0xFFFF,Mapper153_write); + /* This mapper/board seems to have WRAM at $6000-$7FFF, so I'll let the + main ines code take care of that memory region. */ +} diff --git a/mappers/17.c b/mappers/17.c new file mode 100644 index 0000000..66b85d4 --- /dev/null +++ b/mappers/17.c @@ -0,0 +1,74 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + + + +static void FP_FASTAPASS(1) FFEIRQHook(int a) +{ + if(IRQa) + { + IRQCount+=a; + + if(IRQCount>=0x10000) + { + TriggerIRQ(); + IRQa=0; + IRQCount=0; + } + } +} + + +DECLFW(Mapper17_write) +{ + switch(A){ + default: + break; + case 0x42FE: + onemir((V>>4)&1); + break; + case 0x42FF: + MIRROR_SET((V>>4)&1); + break; + case 0x4501:IRQa=V&1;break; + case 0x4502:IRQCount&=0xFF00;IRQCount|=V;break; + case 0x4503:IRQCount&=0x00FF;IRQCount|=V<<8;IRQa=1;break; + case 0x4504: ROM_BANK8(0x8000,V);break; + case 0x4505: ROM_BANK8(0xA000,V);break; + case 0x4506: ROM_BANK8(0xC000,V);break; + case 0x4507: ROM_BANK8(0xE000,V);break; + case 0x4510: VROM_BANK1(0x0000,V);break; + case 0x4511: VROM_BANK1(0x0400,V);break; + case 0x4512: VROM_BANK1(0x0800,V);break; + case 0x4513: VROM_BANK1(0x0C00,V);break; + case 0x4514: VROM_BANK1(0x1000,V);break; + case 0x4515: VROM_BANK1(0x1400,V);break; + case 0x4516: VROM_BANK1(0x1800,V);break; + case 0x4517: VROM_BANK1(0x1C00,V);break; + } +} + +void Mapper17_init(void) +{ +MapIRQHook=FFEIRQHook; +SetWriteHandler(0x4020,0x5fff,Mapper17_write); +} diff --git a/mappers/18.c b/mappers/18.c new file mode 100644 index 0000000..f77e831 --- /dev/null +++ b/mappers/18.c @@ -0,0 +1,80 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + +#define K4buf mapbyte2 +#define K4buf2 mapbyte3 + +void FP_FASTAPASS(1) JalecoIRQHook(int a) +{ + if(IRQa && IRQCount) + { + IRQCount-=a; + if(IRQCount<=0) + { + X6502_IRQBegin(FCEU_IQEXT); + IRQCount=0; + IRQa=0; + } + } +} + +DECLFW(Mapper18_write) +{ + A&=0xF003; + if(A>=0x8000 && A<=0x9001) + { + int x=((A>>1)&1)|((A-0x8000)>>11); + + K4buf2[x]&=(0xF0)>>((A&1)<<2); + K4buf2[x]|=(V&0xF)<<((A&1)<<2); + ROM_BANK8(0x8000+(x<<13),K4buf2[x]); + } + else if(A>=0xa000 && A<=0xd003) + { + int x=((A>>1)&1)|((A-0xA000)>>11); + + K4buf[x]&=(0xF0)>>((A&1)<<2); + K4buf[x]|=(V&0xF)<<((A&1)<<2); + VROM_BANK1(x<<10,K4buf[x]); + } + else switch(A) + { + case 0xe000:IRQLatch&=0xFFF0;IRQLatch|=(V&0x0f);break; + case 0xe001:IRQLatch&=0xFF0F;IRQLatch|=(V&0x0f)<<4;break; + case 0xe002:IRQLatch&=0xF0FF;IRQLatch|=(V&0x0f)<<8;break; + case 0xe003:IRQLatch&=0x0FFF;IRQLatch|=(V&0x0f)<<12;break; + case 0xf000:IRQCount=IRQLatch; + break; + case 0xf001:IRQa=V&1; + X6502_IRQEnd(FCEU_IQEXT); + break; + case 0xf002:MIRROR_SET2(V&1); + if(V&2) onemir(0); + break; + } +} + +void Mapper18_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper18_write); + MapIRQHook=JalecoIRQHook; +} diff --git a/mappers/180.c b/mappers/180.c new file mode 100644 index 0000000..bd877c8 --- /dev/null +++ b/mappers/180.c @@ -0,0 +1,14 @@ +#include "mapinc.h" + + + +DECLFW(Mapper180_write) +{ +ROM_BANK16(0xC000,V); +} + +void Mapper180_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper180_write); +} + diff --git a/mappers/182.c b/mappers/182.c new file mode 100644 index 0000000..5fe0750 --- /dev/null +++ b/mappers/182.c @@ -0,0 +1,48 @@ +#include "mapinc.h" + +DECLFW(Mapper182_write) +{ + switch(A&0xf003) + { + case 0xe003:IRQCount=V;IRQa=1;break; + case 0x8001:MIRROR_SET(V&1);break; + case 0xA000:mapbyte1[0]=V;break; + case 0xC000: + switch(mapbyte1[0]&7) + { + case 0:VROM_BANK2(0x0000,V>>1);break; + case 1:VROM_BANK1(0x1400,V);break; + case 2:VROM_BANK2(0x0800,V>>1);break; + case 3:VROM_BANK1(0x1c00,V);break; + case 4:ROM_BANK8(0x8000,V);break; + case 5:ROM_BANK8(0xA000,V);break; + case 6:VROM_BANK1(0x1000,V);break; + case 7:VROM_BANK1(0x1800,V);break; + } + break; + + + } +} + +void blop(void) +{ + if(IRQa) + { + if(IRQCount) + { + IRQCount--; + if(!IRQCount) + { + IRQa=0; + TriggerIRQ(); + } + } + } +} +void Mapper182_init(void) +{ + SetWriteHandler(0x8000,0xFFFF,Mapper182_write); + GameHBIRQHook=blop; +} + diff --git a/mappers/184.c b/mappers/184.c new file mode 100644 index 0000000..70fd6b1 --- /dev/null +++ b/mappers/184.c @@ -0,0 +1,15 @@ +#include "mapinc.h" + + + +DECLFW(Mapper184_write) +{ +VROM_BANK4(0x0000,V); +VROM_BANK4(0x1000,(V>>4)); +} + +void Mapper184_init(void) +{ + SetWriteHandler(0x6000,0xffff,Mapper184_write); +} + diff --git a/mappers/189.c b/mappers/189.c new file mode 100644 index 0000000..7d8bcff --- /dev/null +++ b/mappers/189.c @@ -0,0 +1,37 @@ +/* Is this an MMC3 workalike piece of hardware, with the addition of + a register at $4120 or does it have only partial MMC3 functionality? + A good test would be to see if commands 6 and 7 can change PRG banks + and of course test the regs >=$c000, on the real cart. +*/ +#include "mapinc.h" + +#define cmd mapbyte1[0] +static DECLFW(Mapper189_write) +{ + if(A==0x4120) ROM_BANK32(V>>4); + else switch(A&0xE001) + { + case 0xa000:MIRROR_SET(V&1);break; + case 0x8000:cmd=V;break; + case 0x8001:switch(cmd&7) + { + case 0:VROM_BANK2(0x0000,V>>1);break; + case 1:VROM_BANK2(0x0800,V>>1);break; + case 2:VROM_BANK1(0x1000,V);break; + case 3:VROM_BANK1(0x1400,V);break; + case 4:VROM_BANK1(0x1800,V);break; + case 5:VROM_BANK1(0x1C00,V);break; + } + break; + + } +} + +void Mapper189_init(void) +{ + SetWriteHandler(0x4120,0xFFFF,Mapper189_write); + SetReadHandler(0x6000,0x7FFF,0); + ROM_BANK32(0); +} + + diff --git a/mappers/19.c b/mappers/19.c new file mode 100644 index 0000000..53de232 --- /dev/null +++ b/mappers/19.c @@ -0,0 +1,286 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + +#define dopol mapbyte1[0] +#define gorfus mapbyte1[1] +#define gorko mapbyte1[2] + +static void NamcoSound(int Count); +static void NamcoSoundHack(void); + +static int32 inc; +void FP_FASTAPASS(1) NamcoIRQHook(int a) +{ + if(IRQa) + { + IRQCount+=a; + if(IRQCount>=0x7fff) + { + X6502_IRQBegin(FCEU_IQEXT); + IRQa=0; + IRQCount=0x7fff; + } + } +} + +DECLFR(Namco_Read4800) +{ + uint8 ret=MapperExRAM[dopol&0x7f]; + if(dopol&0x80) + dopol=(dopol&0x80)|((dopol+1)&0x7f); + return ret; +} + +DECLFR(Namco_Read5000) +{ + return(IRQCount); +} + +DECLFR(Namco_Read5800) +{ + return(IRQCount>>8); +} + +static void FASTAPASS(2) DoNTARAMROM(int w, uint8 V) +{ + mapbyte2[w]=V; + //if(V>=0xE0) + // setntamem(NTARAM+((V&1)<<10), 1, w); + if((V>=0xE0)) // || ((gorko>>(6+(w>>1)))&1) ) + setntamem(NTARAM+((V&1)<<10), 1, w); + else + { + V&=CHRmask1[0]; + setntamem(VROM+(V<<10), 0, w); + } +// else +// setntamem(NTARAM+((V&1)<<10), 1, w); +} + +static void FixNTAR(void) +{ + int x; + + for(x=0;x<4;x++) + DoNTARAMROM(x,mapbyte2[x]); +} + +static void FASTAPASS(2) DoCHRRAMROM(int x, uint8 V) +{ + mapbyte3[x]=V; + if(!((gorfus>>((x>>2)+6))&1) && (V>=0xE0)) + VRAM_BANK1(x<<10,V&7); + else + VROM_BANK1(x<<10,V); +} + +static void FixCRR(void) +{ + int x; + for(x=0;x<8;x++) + DoCHRRAMROM(x,mapbyte3[x]); +} + +static DECLFW(Mapper19_write) +{ + A&=0xF800; + + if(A>=0x8000 && A<=0xb800) + DoCHRRAMROM((A-0x8000)>>11,V); + else if(A>=0xC000 && A<=0xd800) + DoNTARAMROM((A-0xC000)>>11,V); + else switch(A) + { + case 0x4800: + if(dopol&0x40) + { + if(FSettings.SndRate) + { + NamcoSoundHack(); + GameExpSound.Fill=NamcoSound; + } + } + MapperExRAM[dopol&0x7f]=V; + if(dopol&0x80) + dopol=(dopol&0x80)|((dopol+1)&0x7f); + break; + + case 0xf800: dopol=V;break; + case 0x5000: IRQCount&=0xFF00;IRQCount|=V;X6502_IRQEnd(FCEU_IQEXT);break; + case 0x5800: IRQCount&=0x00ff;IRQCount|=(V&0x7F)<<8; + IRQa=V&0x80; + X6502_IRQEnd(FCEU_IQEXT); + break; + + case 0xE000:gorko=V&0xC0; + //FixNTAR(); + ROM_BANK8(0x8000,V); + break; + case 0xE800:gorfus=V&0xC0; + FixCRR(); + ROM_BANK8(0xA000,V); + break; + case 0xF000: + ROM_BANK8(0xC000,V); + break; + } +} + +static int dwave=0; +static void DoNamcoSound(uint32 *Wave, int Count); + +static void NamcoSoundHack(void) +{ + int32 z,a; + + z=((timestamp<<16)/soundtsinc)>>4; + a=z-dwave; + if(a) + DoNamcoSound(&Wave[dwave], a); + dwave+=a; +} + +static void NamcoSound(int Count) +{ + int32 z,a; + + z=((timestamp<<16)/soundtsinc)>>4; + a=z-dwave; + if(a) + DoNamcoSound(&Wave[dwave], a); + dwave=0; +} + +static uint8 PlayIndex[8]; +static int32 vcount[8]; + +static void DoNamcoSound(uint32 *Wave, int Count) +{ + int P,V; + + + //FCEU_DispMessage("%d",MapperExRAM[0x7F]>>4); + for(P=7;P>=7-((MapperExRAM[0x7F]>>4)&7);P--) + { + if((MapperExRAM[0x44+(P<<3)]&0xE0) && (MapperExRAM[0x47+(P<<3)]&0xF)) + { + uint32 freq; + int32 vco; + uint32 duff,duff2,lengo,envelope; + //uint64 ta; + + vco=vcount[P]; + freq=MapperExRAM[0x40+(P<<3)]; + freq|=MapperExRAM[0x42+(P<<3)]<<8; + freq|=(MapperExRAM[0x44+(P<<3)]&3)<<16; + + if(!freq) continue; + + { + int c=((MapperExRAM[0x7F]>>4)&7)+1; + + inc=(long double)(FSettings.SndRate<<15)/((long double)freq* + 21477272/((long double)0x400000*c*45)); + } + + envelope=((MapperExRAM[0x47+(P<<3)]&0xF)<<18)/15; + duff=MapperExRAM[(((MapperExRAM[0x46+(P<<3)]+PlayIndex[P])>>1)&0xFF)]; + if((MapperExRAM[0x46+(P<<3)]+PlayIndex[P])&1) + duff>>=4; + duff&=0xF; + duff2=(duff*envelope)>>14; + + lengo=((8-((MapperExRAM[0x44+(P<<3)]>>2)&7)))<<2; + for(V=0;V=inc) + { + PlayIndex[P]++; + if(PlayIndex[P]>=lengo) + PlayIndex[P]=0; + vco-=inc; + duff=MapperExRAM[(((MapperExRAM[0x46+(P<<3)]+PlayIndex[P])&0xFF)>>1)]; + if((MapperExRAM[0x46+(P<<3)]+PlayIndex[P])&1) + duff>>=4; + duff&=0xF; + duff2=(duff*envelope)>>14; + } + Wave[V>>4]+=duff2; + vco+=0x8000; + } + vcount[P]=vco; + } + } +} + +static void Mapper19_StateRestore(int version) +{ + FixNTAR(); + if(version>=80) + FixCRR(); +} + +static void M19SC(void) +{ + if(FSettings.SndRate) + Mapper19_ESI(); +} + +void Mapper19_ESI(void) +{ + GameExpSound.RChange=M19SC; + SetWriteHandler(0xf800,0xffff,Mapper19_write); + SetWriteHandler(0x4800,0x4fff,Mapper19_write); + SetReadHandler(0x4800,0x4fff,Namco_Read4800); +} + +void Mapper19_init(void) +{ + if(!Mirroring) + { + DoNTARAMROM(0,0xE0); + DoNTARAMROM(1,0xE0); + DoNTARAMROM(2,0xE1); + DoNTARAMROM(3,0xE1); + } + else + { + DoNTARAMROM(0,0xE0); + DoNTARAMROM(2,0xE0); + DoNTARAMROM(1,0xE1); + DoNTARAMROM(3,0xE1); + } + VROM_BANK8(~0); + SetWriteHandler(0x8000,0xffff,Mapper19_write); + SetWriteHandler(0x4020,0x5fff,Mapper19_write); + SetReadHandler(0x4800,0x4fff,Namco_Read4800); + SetReadHandler(0x5000,0x57ff,Namco_Read5000); + SetReadHandler(0x5800,0x5fff,Namco_Read5800); + + MapIRQHook=NamcoIRQHook; + MapStateRestore=Mapper19_StateRestore; + GameExpSound.RChange=M19SC; + if(FSettings.SndRate) + Mapper19_ESI(); + gorfus=0xFF; +} + diff --git a/mappers/21.c b/mappers/21.c new file mode 100644 index 0000000..bda50a5 --- /dev/null +++ b/mappers/21.c @@ -0,0 +1,102 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + +#define K4buf mapbyte2 +#define K4IRQ mapbyte1[1] +#define K4sel mapbyte1[0] + +static int acount=0; + +DECLFW(Mapper21_write) +{ + A|=((A>>5)&0xF); + + if((A&0xF000)==0xA000) + ROM_BANK8(0xA000,V); + else if((A&0xF000)==0x8000) + { + if(K4sel&2) + ROM_BANK8(0xC000,V); + else + ROM_BANK8(0x8000,V); + } + else if(A>=0xb000 && A<=0xefff) + { + A&=0xF006; + { + int x=((A>>2)&1)|((A-0xB000)>>11); + + K4buf[x]&=(0xF0)>>((A&2)<<1); + K4buf[x]|=(V&0xF)<<((A&2)<<1); + VROM_BANK1(x<<10,K4buf[x]); + } + + } + else switch(A&0xF006) + { + case 0x9000: + switch(V&0x3) + { + case 0:MIRROR_SET(0);break; + case 1:MIRROR_SET(1);break; + case 2:onemir(0);break; + case 3:onemir(1);break; + } + break; + case 0x9006: + case 0x9004: + case 0x9002:if((K4sel&2)!=(V&2)) + { + uint8 swa; + swa=PRGBankList[0]; + ROM_BANK8(0x8000,PRGBankList[2]); + ROM_BANK8(0xc000,swa); + } + K4sel=V; + break; + case 0xf000:IRQLatch&=0xF0;IRQLatch|=V&0xF;break; + case 0xf002:IRQLatch&=0x0F;IRQLatch|=V<<4;break; + case 0xf004:IRQCount=IRQLatch; + IRQa=V&2;K4IRQ=V&1;break; + case 0xf006:IRQa=K4IRQ;break; + } +} +static void FP_FASTAPASS(1) KonamiIRQHook(int a) +{ + #define LCYCS 114 + if(IRQa) + { + acount+=a; + if(acount>=LCYCS) + { + doagainbub:acount-=LCYCS;IRQCount++; + if(IRQCount&0x100) {TriggerIRQ();IRQCount=IRQLatch;} + if(acount>=LCYCS) goto doagainbub; + } + } +} + +void Mapper21_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper21_write); + MapIRQHook=KonamiIRQHook; +} diff --git a/mappers/22.c b/mappers/22.c new file mode 100644 index 0000000..44ebe25 --- /dev/null +++ b/mappers/22.c @@ -0,0 +1,63 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + +#define K4buf mapbyte2 + + + +DECLFW(Mapper22_write) +{ + if(A<=0xAFFF) + { + switch(A&0xF000) + { + case 0x8000:ROM_BANK8(0x8000,V);break; + case 0xa000:ROM_BANK8(0xA000,V);break; + case 0x9000:switch(V&3) + { + case 0x00:MIRROR_SET2(1);break; + case 0x01:MIRROR_SET2(0);break; + case 0x02:onemir(0);break; + case 0x03:onemir(1);break; + } + break; + } + } + else + { + A&=0xF003; + if(A>=0xb000 && A<=0xe003) + { + int x=(A&1)|((A-0xB000)>>11); + + K4buf[x]&=(0xF0)>>((A&2)<<1); + K4buf[x]|=(V&0xF)<<((A&2)<<1); + VROM_BANK1(x<<10,K4buf[x]>>1); + } + } +} + + +void Mapper22_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper22_write); +} diff --git a/mappers/225.c b/mappers/225.c new file mode 100644 index 0000000..6edb486 --- /dev/null +++ b/mappers/225.c @@ -0,0 +1,87 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + + +#define reg1 mapbyte1[0] +#define reg2 mapbyte1[1] +#define reg3 mapbyte1[2] +#define reg4 mapbyte1[3] + +DECLFR(A110in1read) +{ +switch(A&0x3) + { + case 0:return reg1;break; + case 1:return reg2;break; + case 2:return reg3;break; + case 3:return reg4;break; + } +return 0xF; +} +DECLFW(A110in1regwr) +{ +switch(A&0x3) + { + case 0:reg1=V&0xF;break; + case 1:reg2=V&0xF;break; + case 2:reg3=V&0xF;break; + case 3:reg4=V&0xF;break; + } +} + +DECLFW(Mapper225_write) +{ + int banks=0; + + MIRROR_SET((A>>13)&1); + if(A&0x4000) + banks=1; + else + banks=0; + + VROM_BANK8(((A&0x003f)+(banks<<6))); + if(A&0x1000) + { + if(A&0x40) + { + ROM_BANK16(0x8000,((((((A>>7)&0x1F)+(banks<<5)))<<1)+1)); + ROM_BANK16(0xC000,((((((A>>7)&0x1F)+(banks<<5)))<<1)+1)); + } + else + { + ROM_BANK16(0x8000,(((((A>>7)&0x1F)+(banks<<5)))<<1)); + ROM_BANK16(0xC000,(((((A>>7)&0x1F)+(banks<<5)))<<1)); + } + } + else + { + ROM_BANK32(((((A>>7)&0x1F)+(banks<<5)))); + } +} + +void Mapper225_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper225_write); + SetReadHandler(0x5800,0x5fff,A110in1read); + SetWriteHandler(0x5800,0x5fff,A110in1regwr); +} + diff --git a/mappers/226.c b/mappers/226.c new file mode 100644 index 0000000..41fd9a4 --- /dev/null +++ b/mappers/226.c @@ -0,0 +1,105 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + +#define rg mapbyte1 +static void DoPRG(void) +{ + int32 b=((rg[0]>>1)&0xF) | ((rg[0]>>3)&0x10) | ((rg[1]&1)<<5); + if(rg[0]&0x20) // 16 KB + { + ROM_BANK16(0x8000,(b<<1)|(rg[0]&1)); + ROM_BANK16(0xC000,(b<<1)|(rg[0]&1)); + } + else + ROM_BANK32(b); +} + +static DECLFW(Mapper226_write) +{ + rg[A&1]=V; + DoPRG(); + if(A&1) + { + if(rg[1]&2) + PPUCHRRAM=0; // Write protected. + else + PPUCHRRAM=0xFF; // Not write protected. + } + else + MIRROR_SET2((rg[0]>>6)&1); +} + +static void M26Reset(void) +{ + rg[0]=rg[1]=0; + DoPRG(); + PPUCHRRAM=0xFF; + MIRROR_SET2(0); +} + +static void M26Restore(int version) +{ + DoPRG(); + if(rg[1]&2) + PPUCHRRAM=0; // Write protected. + else + PPUCHRRAM=0xFF; // Not write protected. + MIRROR_SET2((rg[0]>>6)&1); +} + +void Mapper226_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper226_write); + MapperReset=M26Reset; + GameStateRestore=M26Restore; + M26Reset(); +} + +#ifdef OLD // What the heck is this?? +DECLFW(Mapper226_write) +{ + MIRROR_SET((A>>13)&1); + VROM_BANK8(A&0x7F); + if(A&0x1000) + { + if(A&0x40) + { + ROM_BANK16(0x8000,(((A>>7))<<1)+1); + ROM_BANK16(0xC000,(((A>>7))<<1)+1); + } + else + { + ROM_BANK16(0x8000,(((A>>7))<<1)); + ROM_BANK16(0xC000,(((A>>7))<<1)); + } + } + else + { + ROM_BANK32(A>>7); + } +} + +void Mapper226_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper226_write); +} +#endif diff --git a/mappers/227.c b/mappers/227.c new file mode 100644 index 0000000..a006ce3 --- /dev/null +++ b/mappers/227.c @@ -0,0 +1,79 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + +#define rg mapbyte1 + +static void DoSync(uint32 A) +{ + int32 p=((A>>3)&0xF) | ((A>>4)&0x10); + + rg[0]=A; + rg[1]=A>>8; + + MIRROR_SET((A>>1)&1); + if(A&1) //32 KB + { + ROM_BANK32(p); + } + else //16 KB + { + ROM_BANK16(0x8000,(p<<1)|((A&4)>>2)); + ROM_BANK16(0xc000,(p<<1)|((A&4)>>2)); + } + if(A&0x80) + { + PPUCHRRAM=0; + } + else + { + PPUCHRRAM=0xFF; + if(A&0x200) + ROM_BANK16(0xC000,(p<<1)|7); + else + ROM_BANK16(0xC000,(p<<1)&(~7)); + } +} + +static DECLFW(Mapper227_write) +{ + rg[A&1]=V; + DoSync(A); +} + +static void M227Reset(void) +{ + rg[0]=rg[1]=0; + DoSync(0); +} + +static void M227Restore(int version) +{ + DoSync(rg[0]|(rg[1]<<8)); +} + +void Mapper227_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper227_write); + MapperReset=M227Reset; + GameStateRestore=M227Restore; + M227Reset(); +} diff --git a/mappers/228.c b/mappers/228.c new file mode 100644 index 0000000..741bab0 --- /dev/null +++ b/mappers/228.c @@ -0,0 +1,52 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + +static DECLFW(Mapper228_write) +{ + uint32 page,pagel,pageh; + + MIRROR_SET((A>>13)&1); + + page=(A>>7)&0x3F; + if((page&0x30)==0x30) + page-=0x10; + + pagel=pageh=(page<<1) + (((A>>6)&1)&((A>>5)&1)); + pageh+=((A>>5)&1)^1; + + ROM_BANK16(0x8000,pagel); + ROM_BANK16(0xC000,pageh); + VROM_BANK8( (V&0x3) | ((A&0xF)<<2) ); +} + +static void A52Reset(void) +{ + Mapper228_write(0,0); +} + +void Mapper228_init(void) +{ + MapperReset=A52Reset; + A52Reset(); + SetWriteHandler(0x8000,0xffff,Mapper228_write); +} + diff --git a/mappers/229.c b/mappers/229.c new file mode 100644 index 0000000..8ffe911 --- /dev/null +++ b/mappers/229.c @@ -0,0 +1,48 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + + + +DECLFW(Mapper229_write) +{ +if(A>=0x8000) +{ +MIRROR_SET((A>>5)&1); +if(!(A&0x1e)) + { + ROM_BANK32(0); + } +else + { + ROM_BANK16(0x8000,A&0x1f); + ROM_BANK16(0xC000,A&0x1f); + } + VROM_BANK8(A); +} + +} + +void Mapper229_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper229_write); +} + diff --git a/mappers/23.c b/mappers/23.c new file mode 100644 index 0000000..12f30d8 --- /dev/null +++ b/mappers/23.c @@ -0,0 +1,102 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + +#define K4buf mapbyte2 +#define K4IRQ mapbyte1[1] +#define K4sel mapbyte1[0] + +DECLFW(Mapper23_write) +{ + if((A&0xF000)==0x8000) + { + if(K4sel&2) + ROM_BANK8(0xC000,V); + else + ROM_BANK8(0x8000,V); + } + else if((A&0xF000)==0xA000) + ROM_BANK8(0xA000,V); + else + { + A|=((A>>2)&0x3)|((A>>4)&0x3)|((A>>6)&0x3); + A&=0xF003; + if(A>=0xb000 && A<=0xe003) + { + int x=((A>>1)&1)|((A-0xB000)>>11); + + K4buf[x]&=(0xF0)>>((A&1)<<2); + K4buf[x]|=(V&0xF)<<((A&1)<<2); + VROM_BANK1(x<<10,K4buf[x]); + } + else + switch(A) + { + case 0xf000:X6502_IRQEnd(FCEU_IQEXT);IRQLatch&=0xF0;IRQLatch|=V&0xF;break; + case 0xf001:X6502_IRQEnd(FCEU_IQEXT);IRQLatch&=0x0F;IRQLatch|=V<<4;break; + case 0xf002:X6502_IRQEnd(FCEU_IQEXT);IRQCount=IRQLatch;IRQa=V&2;K4IRQ=V&1;break; + case 0xf003:X6502_IRQEnd(FCEU_IQEXT);IRQa=K4IRQ;break; + case 0x9001: + case 0x9002: + case 0x9003: + if((K4sel&2)!=(V&2)) + { + uint8 swa; + swa=PRGBankList[0]; + ROM_BANK8(0x8000,PRGBankList[2]); + ROM_BANK8(0xc000,swa); + } + K4sel=V; + break; + case 0x9000: + switch(V&0x3) + { + case 0:MIRROR_SET(0);break; + case 1:MIRROR_SET(1);break; + case 2:onemir(0);break; + case 3:onemir(1);break; + } + break; + } + } +} + +void FP_FASTAPASS(1) KonamiIRQHook2(int a) +{ + static int acount=0; + if(IRQa) + { + acount+=(a<<1)+a; + if(acount>=342) + { + doagainbub:acount-=342;IRQCount++; + if(IRQCount&0x100) {X6502_IRQBegin(FCEU_IQEXT);IRQCount=IRQLatch;} + if(acount>=342) goto doagainbub; + } + } +} + +void Mapper23_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper23_write); + MapIRQHook=KonamiIRQHook2; +} + diff --git a/mappers/232.c b/mappers/232.c new file mode 100644 index 0000000..9d71b24 --- /dev/null +++ b/mappers/232.c @@ -0,0 +1,50 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + +static void DoIt(void) +{ + ROM_BANK16(0x8000,(mapbyte1[1]&3) | ((mapbyte1[0]&0x18)>>1)); + ROM_BANK16(0xc000,3|(((mapbyte1[0])&0x18)>>1)); +} + +DECLFW(Mapper232_write) +{ + if(A<=0x9FFF) + mapbyte1[0]=V; + else + mapbyte1[1]=V; + DoIt(); +} + +static void QuattroReset(void) +{ + mapbyte1[0]=0x18; + DoIt(); +} + +void Mapper232_init(void) +{ + SetWriteHandler(0x6000,0xffff,Mapper232_write); + MapperReset=QuattroReset; + QuattroReset(); +} + diff --git a/mappers/234.c b/mappers/234.c new file mode 100644 index 0000000..4a8ac45 --- /dev/null +++ b/mappers/234.c @@ -0,0 +1,107 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + +#define r1 mapbyte1[0] +#define r2 mapbyte1[1] + +static void DoBS(void) +{ + if(r1&0x40) + { + ROM_BANK32((r1&0xE)|(r2&1)); + VROM_BANK8( ((r1&0xE)<<2) | ((r2>>4)&7) ); + } + else + { + ROM_BANK32(r1&0xF); + VROM_BANK8( ((r1&0xF)<<2) | ((r2>>4)&3) ); + } +} + +static void R1Set(uint8 V) +{ + if(r1) return; + r1=V; + MIRROR_SET(V>>7); + DoBS(); +} + +static void R2Set(uint8 V) +{ + r2=V; + DoBS(); +} + +DECLFW(R1W) +{ + R1Set(V); +} + +DECLFR(R1R) +{ + uint8 r=CartBR(A); + R1Set(r); + return r; +} + +DECLFW(R2W) +{ + R2Set(V); +} + +DECLFR(R2R) +{ + uint8 r=CartBR(A); + R2Set(r); + return r; +} + +static void M15Restore(int version) +{ + DoBS(); + MIRROR_SET(r1>>7); +} + +static void M15Reset(void) +{ + r1=r2=0; + DoBS(); + MIRROR_SET(0); +} + +void Mapper234_init(void) +{ + SetWriteHandler(0xff80,0xff9f,R1W); + SetReadHandler(0xff80,0xff9f,R1R); + + SetWriteHandler(0xffe8,0xfff7,R2W); + SetReadHandler(0xffe8,0xfff7,R2R); + + SetReadHandler(0x6000,0x7FFF,0); + SetWriteHandler(0x6000,0x7FFF,0); + + M15Reset(); + + GameStateRestore=M15Restore; + MapperReset=M15Reset; +} + diff --git a/mappers/240.c b/mappers/240.c new file mode 100644 index 0000000..4c1fd27 --- /dev/null +++ b/mappers/240.c @@ -0,0 +1,39 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + + + +DECLFW(Mapper240_write) +{ + if(A<0x8000) + { + ROM_BANK32(V>>4); + VROM_BANK8(V&0xF); + } +} + +void Mapper240_init(void) +{ + SetWriteHandler(0x4020,0x5fff,Mapper240_write); + SetWriteHandler(0x8000,0xffff,Mapper240_write); +} + diff --git a/mappers/242.c b/mappers/242.c new file mode 100644 index 0000000..93f654c --- /dev/null +++ b/mappers/242.c @@ -0,0 +1,41 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + + +DECLFW(Mapper242_write) +{ + ROM_BANK32((A>>3)&0xF); + switch(V&3) + { + case 0:MIRROR_SET(0);break; + case 1:MIRROR_SET(1);break; + case 2:onemir(0);break; + case 3:onemir(1);break; + } +} + +void Mapper242_init(void) +{ + ROM_BANK32(0); + SetWriteHandler(0x8000,0xffff,Mapper242_write); +} + diff --git a/mappers/245.c b/mappers/245.c new file mode 100644 index 0000000..2a25182 --- /dev/null +++ b/mappers/245.c @@ -0,0 +1,51 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + +static void Synco(void) +{ + ROM_BANK8(0x8000,mapbyte2[0]); + ROM_BANK8(0xA000,mapbyte2[1]); + ROM_BANK8(0xc000,0x3e); + ROM_BANK8(0xe000,0x3f); +} +static DECLFW(Mapper245_write) +{ + switch(A&0xe001) + { + case 0xa000:mapbyte1[1]=V;Synco();break; + case 0x8000:mapbyte1[0]=V;break; + case 0x8001:switch(mapbyte1[0]&7) + { +// default:printf("ark\n");break; + case 6:mapbyte2[0]=V;Synco();break; + case 7:mapbyte2[1]=V;Synco();break; + }break; + //case 0xa001:MIRROR_SET2(V>>7);break; + } +// printf("$%04x:$%02x\n",A,V); +} + +void Mapper245_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper245_write); +} + diff --git a/mappers/246.c b/mappers/246.c new file mode 100644 index 0000000..42613fc --- /dev/null +++ b/mappers/246.c @@ -0,0 +1,44 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + + +DECLFW(Mapper246_write) +{ + switch(A&0xF007) + { + case 0x6000:ROM_BANK8(0x8000,V);break; + case 0x6001:ROM_BANK8(0xA000,V);break; + case 0x6002:ROM_BANK8(0xC000,V);break; + case 0x6003:ROM_BANK8(0xE000,V);break; + case 0x6004:VROM_BANK2(0x0000,V);break; + case 0x6005:VROM_BANK2(0x0800,V);break; + case 0x6006:VROM_BANK2(0x1000,V);break; + case 0x6007:VROM_BANK2(0x1800,V);break; + } +} + +void Mapper246_init(void) +{ + SetWriteHandler(0x4020,0x67ff,Mapper246_write); + SetWriteHandler(0x8000,0xffff,Mapper246_write); +} + diff --git a/mappers/248.c b/mappers/248.c new file mode 100644 index 0000000..af40264 --- /dev/null +++ b/mappers/248.c @@ -0,0 +1,90 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + +#define cmd mapbyte1[0] +#define lpa mapbyte1[1] +#define prgl mapbyte2 + +static void PRGSynco(void) +{ + if(lpa&0x80) + { + ROM_BANK16(0x8000,lpa&0xF); + } + else + { + ROM_BANK8(0x8000,prgl[0]&0x1F); + ROM_BANK8(0xa000,prgl[1]&0x1F); + } +} + +static DECLFW(Mapper248_writelow) +{ + lpa=V; + PRGSynco(); +} + +static DECLFW(Mapper248_write) +{ + switch(A&0xF001) + { + case 0xa000:MIRROR_SET(V&1);break; // Not sure if this is right. Mirroring may be hard wired... + case 0xc000:IRQLatch=V;break; + case 0xc001:IRQCount=IRQLatch;break; + case 0xe000:IRQa=0;X6502_IRQEnd(FCEU_IQEXT);break; + case 0xe001:IRQa=1;break; + case 0x8000:cmd=V;break; + case 0x8001:switch(cmd&7) + { + case 0:VROM_BANK2(0x000,V>>1);break; + case 1:VROM_BANK2(0x800,V>>1);break; + case 2:VROM_BANK1(0x1000,V);break; + case 3:VROM_BANK1(0x1400,V);break; + case 4:VROM_BANK1(0x1800,V);break; + case 5:VROM_BANK1(0x1c00,V);break; + case 6:prgl[0]=V;PRGSynco();break; + case 7:prgl[1]=V;PRGSynco();break; + } + break; + } +} + +static void Mapper248_hb(void) +{ + if(IRQa) + { + IRQCount--; + if(IRQCount<0) + { + X6502_IRQBegin(FCEU_IQEXT); + IRQCount=IRQLatch; + } + } +} + +void Mapper248_init(void) +{ + SetWriteHandler(0x6000,0x6fff,Mapper248_writelow); + SetWriteHandler(0x8000,0xffff,Mapper248_write); + GameHBIRQHook=Mapper248_hb; +} + diff --git a/mappers/249.c b/mappers/249.c new file mode 100644 index 0000000..6225d49 --- /dev/null +++ b/mappers/249.c @@ -0,0 +1,46 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + +uint8 doh; + +static DECLFW(Mapper249_write) +{ + switch(A&0xe001) + { + case 0x8000:doh=V;break; + case 0x8001:switch(doh&7) + { + case 0:VROM_BANK2(0x0000,V>>1);break; + case 1:VROM_BANK2(0x0800,V>>1);break; + case 2:VROM_BANK1(0x1000,V);break; +// case 6:ROM_BANK8(0xa000,V);break; +// case 2:ROM_BANK8(0x8000,V);break; + } + } +// printf("$%04x:$%02x\n",A,V); +} + +void Mapper249_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper249_write); +} + diff --git a/mappers/24and26.c b/mappers/24and26.c new file mode 100644 index 0000000..575e5e6 --- /dev/null +++ b/mappers/24and26.c @@ -0,0 +1,351 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + +#define vrctemp mapbyte1[0] +#define regb000 mapbyte3[0] +#define regb001 mapbyte3[1] +#define regb002 mapbyte3[2] +#define exchstat mapbyte4[0] +#define VPSG2 mapbyte3 +#define VPSG mapbyte2 + +static void DoSQV1(void); +static void DoSQV2(void); +static void DoSawV(void); + +static int swaparoo; +static int32 inc; +static DECLFW(VRC6PSGW90) +{ + DoSQV1();VPSG[0]=V; +} +static DECLFW(VRC6PSGW91) +{ + DoSQV1();VPSG[2]=V; +} +static DECLFW(VRC6PSGW92) +{ + DoSQV1();VPSG[3]=V; +} +static DECLFW(VRC6PSGWA0) +{ + DoSQV2();VPSG[4]=V; +} +static DECLFW(VRC6PSGWA1) +{ + DoSQV2();VPSG[6]=V; +} +static DECLFW(VRC6PSGWA2) +{ + DoSQV2();VPSG[7]=V; +} +static DECLFW(VRC6PSGWB0) +{ + DoSawV();VPSG2[0]=V; +} +static DECLFW(VRC6PSGWB1) +{ + DoSawV();VPSG2[1]=V; +} +static DECLFW(VRC6PSGWB2) +{ + DoSawV();VPSG2[2]=V; +} + +static int acount=0; + +static void FP_FASTAPASS(1) KonamiIRQHook(int a) +{ + #define LCYCS 114 + if(IRQa) + { + acount+=a; + if(acount>=LCYCS) + { + doagainbub:acount-=LCYCS;IRQCount++; + if(IRQCount==0x100) {TriggerIRQ();IRQCount=IRQLatch;} + if(acount>=LCYCS) goto doagainbub; + } + } +} + +DECLFW(Mapper24_write) +{ + if(swaparoo) + A=(A&0xFFFC)|((A>>1)&1)|((A<<1)&2); + + switch(A&0xF003) + { + case 0x8000:ROM_BANK16(0x8000,V);break; + case 0xB003: + switch(V&0xF) + { + case 0x0:MIRROR_SET2(1);break; + case 0x4:MIRROR_SET2(0);break; + case 0x8:onemir(0);break; + case 0xC:onemir(1);break; + } + break; + case 0xC000:ROM_BANK8(0xC000,V);break; + case 0xD000:VROM_BANK1(0x0000,V);break; + case 0xD001:VROM_BANK1(0x0400,V);break; + case 0xD002:VROM_BANK1(0x0800,V);break; + case 0xD003:VROM_BANK1(0x0c00,V);break; + case 0xE000:VROM_BANK1(0x1000,V);break; + case 0xE001:VROM_BANK1(0x1400,V);break; + case 0xE002:VROM_BANK1(0x1800,V);break; + case 0xE003:VROM_BANK1(0x1c00,V);break; + case 0xF000:IRQLatch=V;break; + case 0xF001:IRQa=V&2; + vrctemp=V&1; + if(V&2) {IRQCount=IRQLatch;} + //acount=0; + break; + case 0xf002:IRQa=vrctemp;break; + case 0xF003:break; + } +} + +static int CVBC[3]={0,0,0}; +static int32 vcount[2]; + +static void DoSQV1(void) +{ + uint8 amp; + int32 freq; + int V; + int32 start,end; + + start=CVBC[0]; + end=(timestamp<<16)/soundtsinc; + if(end<=start) return; + CVBC[0]=end; + + if(VPSG[0x3]&0x80) + { + amp=(VPSG[0]&15)<<4; + if(VPSG[0]&0x80) + { + for(V=start;V>4]+=amp; + } + else + { + unsigned long dcycs; + freq=(((VPSG[0x2]|((VPSG[0x3]&15)<<8))+1)); + inc=(long double)((unsigned long)((FSettings.SndRate OVERSAMPLE)<<12))/((long double)PSG_base/freq); + switch(VPSG[0]&0x70) + { + default: + case 0x00:dcycs=inc>>4;break; + case 0x10:dcycs=inc>>3;break; + case 0x20:dcycs=(inc*3)>>4;break; + case 0x30:dcycs=inc>>2;break; + case 0x40:dcycs=(inc*5)>>4;break; + case 0x50:dcycs=(inc*6)>>4;break; + case 0x60:dcycs=(inc*7)>>4;break; + case 0x70:dcycs=inc>>1;break; + } + for(V=start;V>4]+=amp; + vcount[0]+=0x1000; + if(vcount[0]>=inc) vcount[0]-=inc; + } + } + } +} +static void DoSQV2(void) +{ + uint8 amp; + int32 freq; + int V; + int32 start,end; + + start=CVBC[1]; + end=(timestamp<<16)/soundtsinc; + if(end<=start) return; + CVBC[1]=end; + + if(VPSG[0x7]&0x80) + { + amp=(VPSG[4]&15)<<4; + if(VPSG[4]&0x80) + { + for(V=start;V>4]+=amp; + } + else + { + unsigned long dcycs; + freq=(((VPSG[0x6]|((VPSG[0x7]&15)<<8))+1)); + inc=(long double)((unsigned long)((FSettings.SndRate OVERSAMPLE)<<12))/((long double)PSG_base/freq); + switch(VPSG[4]&0x70) + { + default: + case 0x00:dcycs=inc>>4;break; + case 0x10:dcycs=inc>>3;break; + case 0x20:dcycs=(inc*3)>>4;break; + case 0x30:dcycs=inc>>2;break; + case 0x40:dcycs=(inc*5)>>4;break; + case 0x50:dcycs=(inc*6)>>4;break; + case 0x60:dcycs=(inc*7)>>4;break; + case 0x70:dcycs=inc>>1;break; + } + for(V=start;V>4]+=amp; + vcount[1]+=0x1000; + if(vcount[1]>=inc) vcount[1]-=inc; + } + } + } +} + +static void DoSawV(void) +{ + int V; + int32 start,end; + + start=CVBC[2]; + end=(timestamp<<16)/soundtsinc; + if(end<=start) return; + CVBC[2]=end; + + if(VPSG2[2]&0x80) + { + static int64 saw1phaseacc=0; + uint32 freq3; + static uint8 b3=0; + static int32 phaseacc=0; + static uint32 duff=0; + + freq3=(VPSG2[1]+((VPSG2[2]&15)<<8)+1); + + for(V=start;V>3)&0x1f)<<4); + } + Wave[V>>4]+=duff; + } + } +} + +void VRC6Sound(int Count) +{ + int x; + + DoSQV1(); + DoSQV2(); + DoSawV(); + for(x=0;x<3;x++) + CVBC[x]=Count; +} + +static int satype=0; + +void VRC6SoundC(void) +{ + int x; + + if(FSettings.SndRate) + VRC6_ESI(satype); + else + { + for(x=000;x<0x1000;x+=4) + { + SetWriteHandler(0x9000+x,0x9002+x,0); + SetWriteHandler(0xa000+x,0xa002+x,0); + SetWriteHandler(0xb000+x,0xb002+x,0); + } + } +} + +void VRC6_ESI(int t) +{ + int x; + + satype=t; + + GameExpSound.RChange=VRC6SoundC; + GameExpSound.Fill=VRC6Sound; + if(FSettings.SndRate) + for(x=000;x<0x1000;x+=4) + { + uint32 a; + + a=0x9000+x; + SetWriteHandler(a,a,VRC6PSGW90); + SetWriteHandler(a+(1^t),a+(1^t),VRC6PSGW91); + SetWriteHandler(a+(2^t),a+(2^t),VRC6PSGW92); + + a=0xa000+x; + SetWriteHandler(a,a,VRC6PSGWA0); + SetWriteHandler(a+(1^t),a+(1^t),VRC6PSGWA1); + SetWriteHandler(a+(2^t),a+(2^t),VRC6PSGWA2); + + a=0xb000+x; + SetWriteHandler(a,a,VRC6PSGWB0); + SetWriteHandler(a+(1^t),a+(1^t),VRC6PSGWB1); + SetWriteHandler(a+(2^t),a+(2^t),VRC6PSGWB2); + } +} + +void Mapper24_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper24_write); + if(FSettings.SndRate) + VRC6_ESI(0); + GameExpSound.RChange=VRC6SoundC; + MapIRQHook=KonamiIRQHook; + swaparoo=0; +} + +void Mapper26_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper24_write); + if(FSettings.SndRate) + VRC6_ESI(3); + GameExpSound.RChange=VRC6SoundC; + MapIRQHook=KonamiIRQHook; + swaparoo=1; +} + diff --git a/mappers/25.c b/mappers/25.c new file mode 100644 index 0000000..db5ba33 --- /dev/null +++ b/mappers/25.c @@ -0,0 +1,95 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + +#define K4buf mapbyte2 +#define K4IRQ mapbyte1[1] +#define K4sel mapbyte1[0] + +DECLFW(Mapper25_write) +{ + A=(A&0xF003)|((A&0xC)>>2); + + if((A&0xF000)==0xA000) + ROM_BANK8(0xA000,V); + else if(A>=0xB000 && A<=0xEFFF) + { + int x=(A&1)|((A-0xB000)>>11); + + K4buf[x]&=(0xF0)>>((A&2)<<1); + K4buf[x]|=(V&0xF)<<((A&2)<<1); + VROM_BANK1(x<<10,K4buf[x]); + } + else if((A&0xF000)==0x8000) + { + if(K4sel&2) + ROM_BANK8(0xC000,V); + else + ROM_BANK8(0x8000,V); + } + else switch(A) + { + case 0x9000:switch(V&0x3) + { + case 0:MIRROR_SET(0);break; + case 1:MIRROR_SET(1);break; + case 2:onemir(0);break; + case 3:onemir(1);break; + } + break; + case 0x9001:if((K4sel&2)!=(V&2)) + { + uint8 swa; + swa=PRGBankList[0]; + ROM_BANK8(0x8000,PRGBankList[2]); + ROM_BANK8(0xc000,swa); + } + K4sel=V; + break; + case 0xf000:IRQLatch&=0xF0;IRQLatch|=V&0xF;break; + case 0xf002:IRQLatch&=0x0F;IRQLatch|=V<<4;break; + case 0xf001:IRQCount=IRQLatch;IRQa=V&2;K4IRQ=V&1;break; + case 0xf003:IRQa=K4IRQ;break; + } +} + +static void FP_FASTAPASS(1) KonamiIRQHook(int a) +{ + static int count=0; + #define LCYCS 114 + if(IRQa) + { + count+=a; + if(count>=LCYCS) + { + doagainbub:count-=LCYCS;IRQCount++; + if(IRQCount&0x100) {count=0;TriggerIRQ();IRQCount=IRQLatch;} + if(count>=LCYCS) goto doagainbub; + } + } +} + +void Mapper25_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper25_write); + MapIRQHook=KonamiIRQHook; +} + diff --git a/mappers/32.c b/mappers/32.c new file mode 100644 index 0000000..da856b9 --- /dev/null +++ b/mappers/32.c @@ -0,0 +1,54 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + + +#define IREMCon mapbyte1[0] + +static DECLFW(Mapper32_write) +{ + switch(A>>12) + { + case 0x8: + mapbyte1[1]=V; + if(IREMCon) {ROM_BANK8(0xc000,V);ROM_BANK8(0x8000,~1);} + else {ROM_BANK8(0x8000,V);ROM_BANK8(0xc000,~1);} + break; + case 0x9:MIRROR_SET2(V&1); + IREMCon=(V>>1)&1; + if(IREMCon) {ROM_BANK8(0xc000,mapbyte1[1]);ROM_BANK8(0x8000,~1);} + else {ROM_BANK8(0x8000,mapbyte1[1]); ROM_BANK8(0xc000,~1);} + MIRROR_SET(V&1); + break; + case 0xa:ROM_BANK8(0xA000,V); + break; + } + + if((A&0xF000)==0xb000) + VROM_BANK1((A&0x7)<<10,V); +} + +void Mapper32_init(void) +{ + ROM_BANK16(0x8000,0); + ROM_BANK16(0xc000,~0); + SetWriteHandler(0x8000,0xffff,Mapper32_write); +} diff --git a/mappers/33.c b/mappers/33.c new file mode 100644 index 0000000..baa2bf9 --- /dev/null +++ b/mappers/33.c @@ -0,0 +1,63 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + +static DECLFW(Mapper33_write) +{ + A&=0xF003; + + //printf("$%04x:$%02x, %d\n",A,V,scanline); + if(A>=0xA000 && A<=0xA003) + VROM_BANK1(0x1000+((A&3)<<10),V); + else switch(A){ + case 0x8000:if(!mapbyte1[0]) + MIRROR_SET((V>>6)&1); + ROM_BANK8(0x8000,V); + break; + case 0x8001:ROM_BANK8(0xA000,V); break; + case 0x8002:VROM_BANK2(0x0000,V);break; + case 0x8003:VROM_BANK2(0x0800,V);break; + case 0xc000:IRQLatch=V;break; + case 0xc001:IRQCount=IRQLatch;break; + case 0xc003:IRQa=0;break; + case 0xc002:IRQa=1;break; + case 0xe000:mapbyte1[0]=1;MIRROR_SET((V>>6)&1);break; + } +} + +static void heho(void) +{ + if(IRQa) + { + IRQCount++; + if(IRQCount==0x100) + { + TriggerIRQ(); + IRQa=0; + } + } +} + +void Mapper33_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper33_write); + GameHBIRQHook=heho; +} diff --git a/mappers/40.c b/mappers/40.c new file mode 100644 index 0000000..30f9459 --- /dev/null +++ b/mappers/40.c @@ -0,0 +1,58 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + + +static DECLFW(Mapper40_write) +{ + switch(A&0xe000) + { + case 0x8000:IRQa=0;IRQCount=0;break; + case 0xa000:IRQa=1;break; + case 0xe000:ROM_BANK8(0xc000,V&7);break; + } +} + +static void FP_FASTAPASS(1) Mapper40IRQ(int a) +{ + if(IRQa) + { + if(IRQCount<4096) + IRQCount+=a; + else + { + IRQa=0; + TriggerIRQ(); + } + } +} + +void Mapper40_init(void) +{ + ROM_BANK8(0x6000,(~0)-1); + ROM_BANK8(0x8000,(~0)-3); + ROM_BANK8(0xa000,(~0)-2); + SetWriteHandler(0x8000,0xffff,Mapper40_write); + SetReadHandler(0x6000,0x7fff,CartBR); + MapIRQHook=Mapper40IRQ; +} + + diff --git a/mappers/41.c b/mappers/41.c new file mode 100644 index 0000000..1139db9 --- /dev/null +++ b/mappers/41.c @@ -0,0 +1,51 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + + +#define calreg mapbyte1[0] +#define calchr mapbyte1[1] + +DECLFW(Mapper41_write) +{ + if(A<0x8000) + { + ROM_BANK32(A&7); + MIRROR_SET((A>>5)&1); + calreg=A; + calchr&=0x3; + calchr|=(A>>1)&0xC; + VROM_BANK8(calchr); + } + else if(calreg&0x4) + { + calchr&=0xC; + calchr|=A&3; + VROM_BANK8(calchr); + } +} + +void Mapper41_init(void) +{ + ROM_BANK32(0); + SetWriteHandler(0x8000,0xffff,Mapper41_write); + SetWriteHandler(0x6000,0x67ff,Mapper41_write); +} diff --git a/mappers/42.c b/mappers/42.c new file mode 100644 index 0000000..2700227 --- /dev/null +++ b/mappers/42.c @@ -0,0 +1,63 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + + +static DECLFW(Mapper42_write) +{ + switch(A&0xe003) + { + case 0xe000:mapbyte1[0]=V;ROM_BANK8(0x6000,V&0xF);break; + case 0xe001:MIRROR_SET((V>>3)&1);break; + case 0xe002:IRQa=V&2;if(!IRQa) IRQCount=0;break; + } +} + +static void FP_FASTAPASS(1) Mapper42IRQ(int a) +{ + if(IRQa) + { + if(IRQCount<24576) + IRQCount+=a; + else + { + IRQa=0; + TriggerIRQ(); + } + } +} + +static void Mapper42_StateRestore(int version) +{ + ROM_BANK8(0x6000,mapbyte1[0]&0xF); +} + + +void Mapper42_init(void) +{ + ROM_BANK8(0x6000,0); + ROM_BANK32(~0); + SetWriteHandler(0xe000,0xffff,Mapper42_write); + SetReadHandler(0x6000,0x7fff,CartBR); + MapStateRestore=Mapper42_StateRestore; + MapIRQHook=Mapper42IRQ; +} + diff --git a/mappers/43.c b/mappers/43.c new file mode 100644 index 0000000..2e5f427 --- /dev/null +++ b/mappers/43.c @@ -0,0 +1,63 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + + + +DECLFW(Mapper43_write) +{ + uint32 m; + int z; + + if(A&0x400) + onemir(0); + else + MIRROR_SET((A>>13)&1); + m=A&0x1f; + + z=(A>>8)&3; + + switch(CHRmask8[0]) + { + default: + case 0xFF: + if(z&2) + m|=0x20; + break; + case 0x1FF: + m|=z<<5; + break; + } + + if(A&0x800) + { + ROM_BANK16(0x8000,(m<<1)|((A&0x1000)>>12)); + ROM_BANK16(0xC000,(m<<1)|((A&0x1000)>>12)); + } + else + ROM_BANK32(m); +} + +void Mapper43_init(void) +{ + ROM_BANK32(0); + SetWriteHandler(0x8000,0xffff,Mapper43_write); +} diff --git a/mappers/46.c b/mappers/46.c new file mode 100644 index 0000000..4157f2b --- /dev/null +++ b/mappers/46.c @@ -0,0 +1,48 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + + + +#define A64reg mapbyte1[0] +#define A64wr mapbyte1[1] + +DECLFW(Mapper46_writel) +{ + A64reg=V; + ROM_BANK32((A64wr&1)+((A64reg&0xF)<<1)); + VROM_BANK8(((A64wr>>4)&7)+((A64reg&0xF0)>>1)); +} + +DECLFW(Mapper46_write) +{ + A64wr=V; + ROM_BANK32((V&1)+((A64reg&0xF)<<1)); + VROM_BANK8(((V>>4)&7)+((A64reg&0xF0)>>1)); +} + +void Mapper46_init(void) +{ + MIRROR_SET(0); + ROM_BANK32(0); + SetWriteHandler(0x8000,0xffff,Mapper46_write); + SetWriteHandler(0x6000,0x7fff,Mapper46_writel); +} diff --git a/mappers/51.c b/mappers/51.c new file mode 100644 index 0000000..c6a33f3 --- /dev/null +++ b/mappers/51.c @@ -0,0 +1,66 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + +#define mode mapbyte1[0] +#define page mapbyte1[1] + +static uint32 Get8K(uint32 A) +{ + uint32 bank; + + bank=(page<<2)|((A>>13)&1); + + if(A&0x4000 && !(mode&1)) bank|=0xC; + if(!(A&0x8000)) bank|=0x20; + if(mode==2) bank|=2; + else bank|=(A>>13)&2; + return(bank); +} + +static void Synco(void) +{ + uint32 x; + if(mapbyte1[0]<=2) + MIRROR_SET2(1); + else + MIRROR_SET2(0); + for(x=0x6000;x<0x10000;x+=8192) + ROM_BANK8(x,Get8K(x)); +} + +static DECLFW(Write) +{ + if(A&0x8000) mapbyte1[1]=V&0xF; + else mapbyte1[0]=(mapbyte1[0]&2)|((V>>1)&1); + + if(A&0x4000) mapbyte1[0]=(mapbyte1[0]&1)|((V>>3)&2); + Synco(); +} + +void Mapper51_init(void) +{ + SetWriteHandler(0x6000,0xFFFF,Write); + SetReadHandler(0x6000,0xFFFF,CartBR); + mapbyte1[0]=1; + mapbyte1[1]=0; + Synco(); +} diff --git a/mappers/6.c b/mappers/6.c new file mode 100644 index 0000000..015aa9c --- /dev/null +++ b/mappers/6.c @@ -0,0 +1,76 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + + +#define FVRAM_BANK8(A,V) {VPage[0]=VPage[1]=VPage[2]=VPage[3]=VPage[4]=VPage[5]=VPage[6]=VPage[7]=V?&MapperExRAM[(V)<<13]-(A):&CHRRAM[(V)<<13]-(A);CHRBankList[0]=((V)<<3);CHRBankList[1]=((V)<<3)+1;CHRBankList[2]=((V)<<3)+2;CHRBankList[3]=((V)<<3)+3;CHRBankList[4]=((V)<<3)+4;CHRBankList[5]=((V)<<3)+5;CHRBankList[6]=((V)<<3)+6;CHRBankList[7]=((V)<<3)+7;PPUCHRRAM=0xFF;} + +static void FP_FASTAPASS(1) FFEIRQHook(int a) +{ + if(IRQa) + { + IRQCount+=a; + if(IRQCount>=0x10000) + { + TriggerIRQ(); + IRQa=0; + IRQCount=0; + } + } +} + +DECLFW(Mapper6_write) +{ + if(A<0x8000) + { + switch(A){ + case 0x42FF:MIRROR_SET((V>>4)&1);break; + case 0x42FE:onemir((V>>3)&2);break; + case 0x4501:IRQa=0;break; + case 0x4502:IRQCount&=0xFF00;IRQCount|=V;break; + case 0x4503:IRQCount&=0xFF;IRQCount|=V<<8;IRQa=1;break; + } + } else { + ROM_BANK16(0x8000,V>>2); + FVRAM_BANK8(0x0000,V&3); + } +} +void Mapper6_StateRestore(int version) +{ + int x; + for(x=0;x<8;x++) + if(PPUCHRRAM&(1<7) + VPage[x]=&MapperExRAM[(CHRBankList[x]&31)*0x400]-(x*0x400); + else VPage[x]=&CHRRAM[(CHRBankList[x]&7)*0x400]-(x*0x400); + } +} +void Mapper6_init(void) +{ +MapIRQHook=FFEIRQHook; +ROM_BANK16(0xc000,7); + +SetWriteHandler(0x4020,0x5fff,Mapper6_write); +SetWriteHandler(0x8000,0xffff,Mapper6_write); +MapStateRestore=Mapper6_StateRestore; +} + diff --git a/mappers/64.c b/mappers/64.c new file mode 100644 index 0000000..96dbac2 --- /dev/null +++ b/mappers/64.c @@ -0,0 +1,150 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + +#define cmd mapbyte1[0] +#define mir mapbyte1[1] +#define rmode mapbyte1[2] +#define regsl mapbyte2 +#define regsh mapbyte3 + +static void RAMBO1_hb(void) +{ + rmode=0; + if(IRQCount>=0) + { + IRQCount--; + if(IRQCount<0) + { + if(IRQa) + { +// printf("IRQ: %d\n",scanline); + rmode = 1; + X6502_IRQBegin(FCEU_IQEXT); + } + } + } +} + +static void Synco(void) +{ + int x; + + if(cmd&0x20) + { + setchr1(0x0000,regsl[0]); + setchr1(0x0800,regsl[1]); + setchr1(0x0400,regsh[0]); + setchr1(0x0c00,regsh[1]); + } + else + { + setchr2(0x0000,regsl[0]>>1); + setchr2(0x0800,regsl[1]>>1); + } + + for(x=0;x<4;x++) + setchr1(0x1000+x*0x400,regsl[2+x]); + + setprg8(0x8000,regsl[6]); + setprg8(0xA000,regsl[7]); + + setprg8(0xC000,regsh[7]); +} + + +static DECLFW(RAMBO1_write) +{ + //if(A>=0xC000 && A<=0xFFFF) printf("$%04x:$%02x, %d, %d\n",A,V,scanline,timestamp); + switch(A&0xF001) + { + case 0xa000:mir=V&1; + setmirror(mir^1); + break; + case 0x8000:cmd = V; + break; + case 0x8001: + if((cmd&15)<8) + regsl[cmd&7]=V; + else + regsh[cmd&7]=V; + Synco(); + break; + case 0xc000:IRQLatch=V; + if(rmode==1) + { + IRQCount=IRQLatch; + } + break; + case 0xc001:rmode=1; + IRQCount=IRQLatch; + break; + case 0xE000:IRQa=0;X6502_IRQEnd(FCEU_IQEXT); + if(rmode==1) + {IRQCount=IRQLatch;} + break; + case 0xE001:IRQa=1; + if(rmode==1) + {IRQCount=IRQLatch;} + break; + } +} + +static void RAMBO1_Restore(int version) +{ + if(version<74) + { + int x; + + x=mapbyte1[1]; // was MMC3_cmd + cmd=x; + + regsl[0]=CHRBankList[0]; + regsl[1]=CHRBankList[2]; + regsh[0]=CHRBankList[1]; + regsh[1]=CHRBankList[3]; + + for(x=0;x<4;x++) + regsl[2+x]=CHRBankList[4+x]; + + regsl[6]=PRGBankList[0]; + regsl[7]=PRGBankList[1]; + regsh[7]=PRGBankList[2]; + mir=Mirroring^1; + } + Synco(); + setmirror(mir^1); +} + +void Mapper64_init(void) +{ + int x; + + for(x=0;x<8;x++) + regsl[x]=regsh[x]=~0; + cmd=0; + mir=0; + setmirror(1); + Synco(); + GameHBIRQHook=RAMBO1_hb; + GameStateRestore=RAMBO1_Restore; + SetWriteHandler(0x8000,0xffff,RAMBO1_write); +} diff --git a/mappers/65.c b/mappers/65.c new file mode 100644 index 0000000..b4051e8 --- /dev/null +++ b/mappers/65.c @@ -0,0 +1,68 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + + +void FP_FASTAPASS(1) IREMIRQHook(int a) +{ + if(IRQa) + { + IRQCount-=a; + if(IRQCount<=0-4) + { + TriggerIRQ(); + IRQa=0; + IRQCount=0xFFFF; + } + } +} + +DECLFW(Mapper65_write) +{ +switch(A) +{ +case 0x8000:ROM_BANK8(0x8000,V);break; +case 0x9000:MIRROR_SET2((V>>6)&1);break; +case 0x9003:IRQa=V&0x80;break; +case 0x9004:IRQCount=IRQLatch;break; +case 0x9005: IRQLatch&=0x00FF; + IRQLatch|=V<<8; + break; +case 0x9006: IRQLatch&=0xFF00;IRQLatch|=V; + break; +case 0xB000:VROM_BANK1(0x0000,V);break; +case 0xB001:VROM_BANK1(0x0400,V);break; +case 0xB002:VROM_BANK1(0x0800,V);break; +case 0xB003:VROM_BANK1(0x0C00,V);break; +case 0xB004:VROM_BANK1(0x1000,V);break; +case 0xB005:VROM_BANK1(0x1400,V);break; +case 0xB006:VROM_BANK1(0x1800,V);break; +case 0xB007:VROM_BANK1(0x1C00,V);break; +case 0xa000:ROM_BANK8(0xA000,V);break; +case 0xC000:ROM_BANK8(0xC000,V);break; + } +} + +void Mapper65_init(void) +{ + MapIRQHook=IREMIRQHook; + SetWriteHandler(0x8000,0xffff,Mapper65_write); +} diff --git a/mappers/67.c b/mappers/67.c new file mode 100644 index 0000000..96c695b --- /dev/null +++ b/mappers/67.c @@ -0,0 +1,67 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + + +#define suntoggle mapbyte1[0] + +DECLFW(Mapper67_write) +{ + A&=0xF800; + if((A&0x800) && A<=0xb800) + { + VROM_BANK2((A-0x8800)>>1,V); + } + else switch(A) + { + case 0xc800: + case 0xc000:if(!suntoggle) + {IRQCount&=0xFF;IRQCount|=V<<8;} + else{IRQCount&=0xFF00;IRQCount|=V;} + suntoggle^=1; + break; + case 0xd800:suntoggle=0;IRQa=V&0x10;break; + + case 0xe800:switch(V&3) + { + case 0:MIRROR_SET2(1);break; + case 1:MIRROR_SET2(0);break; + case 2:onemir(0);break; + case 3:onemir(1);break; + } + break; + case 0xf800:ROM_BANK16(0x8000,V);break; + } +} +static void FP_FASTAPASS(1) SunIRQHook(int a) +{ + if(IRQa) + { + IRQCount-=a; + if(IRQCount<=0) + {TriggerIRQ();IRQa=0;IRQCount=0xFFFF;} + } +} +void Mapper67_init(void) +{ +SetWriteHandler(0x8000,0xffff,Mapper67_write); +MapIRQHook=SunIRQHook; +} diff --git a/mappers/68.c b/mappers/68.c new file mode 100644 index 0000000..98cb0d8 --- /dev/null +++ b/mappers/68.c @@ -0,0 +1,104 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + +static void Fixerit(void) +{ + switch(mapbyte2[0]&3) + { + case 0:vnapage[0]=vnapage[2]=VROM+(((mapbyte1[0]|128)&CHRmask1[0])<<10); + vnapage[1]=vnapage[3]=VROM+(((mapbyte1[1]|128)&CHRmask1[0])<<10); + break; + case 1:vnapage[0]=vnapage[1]=VROM+(((mapbyte1[0]|128)&CHRmask1[0])<<10); + vnapage[2]=vnapage[3]=VROM+(((mapbyte1[1]|128)&CHRmask1[0])<<10); + break; + case 2:vnapage[0]=vnapage[1]=vnapage[2]=vnapage[3]=VROM+(((mapbyte1[0]|128)&CHRmask1[0])<<10); + break; + case 3:vnapage[0]=vnapage[1]=vnapage[2]=vnapage[3]=VROM+(((mapbyte1[1]|128)&CHRmask1[0])<<10); + break; + } +} + +DECLFW(Mapper68_write) +{ + A&=0xF000; + + if(A>=0x8000 && A<=0xB000) + { + VROM_BANK2((A-0x8000)>>1,V); + } + else switch(A) + { + case 0xc000:mapbyte1[0]=V; + if(VROM_size && mapbyte2[0]&0x10) + Fixerit(); + break; + + case 0xd000:mapbyte1[1]=V; + if(VROM_size && mapbyte2[0]&0x10) + Fixerit(); + break; + + case 0xe000: mapbyte2[0]=V; + if(!(V&0x10)) + { + switch(V&3) + { + case 0:MIRROR_SET2(1);break; + case 1:MIRROR_SET2(0);break; + case 2:onemir(0);break; + case 3:onemir(1);break; + } + } + else if(VROM_size) + { + Fixerit(); + PPUNTARAM=0; + } + break; + case 0xf000: ROM_BANK16(0x8000,V);break; + } +} + +static void Mapper68_StateRestore(int version) +{ + if(!(mapbyte2[0]&0x10)) + { + switch(mapbyte2[0]&3) + { + case 0:MIRROR_SET(0);break; + case 1:MIRROR_SET(1);break; + case 2:onemir(0);break; + case 3:onemir(1);break; + } + } + else if(VROM_size) + { + Fixerit(); + PPUNTARAM=0; + } +} + +void Mapper68_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper68_write); + MapStateRestore=Mapper68_StateRestore; +} diff --git a/mappers/69.c b/mappers/69.c new file mode 100644 index 0000000..ec6336f --- /dev/null +++ b/mappers/69.c @@ -0,0 +1,268 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" +static void AYSound(int Count); +static void DoAYSQ(int x); +static void DoAYNoise(void); + +#define sunselect mapbyte1[0] +#define sungah mapbyte1[1] +#define sunindex mapbyte1[2] + +static uint16 znreg; +static int32 inc; + +DECLFW(SUN5BWRAM) +{ + if((sungah&0xC0)==0xC0) + (WRAM-0x6000)[A]=V; +} + +DECLFR(SUN5AWRAM) +{ + if((sungah&0xC0)==0x40) + return X.DB; + return CartBR(A); +} + +DECLFW(Mapper69_SWL) +{ + sunindex=V%14; +} +DECLFW(Mapper69_SWH) +{ + GameExpSound.Fill=AYSound; + switch(sunindex) + { + case 0: + case 1: + case 8:DoAYSQ(0);break; + case 2: + case 3: + case 9:DoAYSQ(1);break; + case 4: + case 5: + case 10:DoAYSQ(2);break; + case 6:DoAYNoise();znreg=0xFFFF;break; + case 7:DoAYNoise(); + DoAYSQ(0); + DoAYSQ(1); + DoAYSQ(2);break; + } + MapperExRAM[sunindex]=V; +} + +DECLFW(Mapper69_write) +{ + switch(A&0xE000) + { + case 0x8000:sunselect=V;break; + case 0xa000: + sunselect&=0xF; + if(sunselect<=7) + VROM_BANK1(sunselect<<10,V); + else + switch(sunselect&0x0f) + { + case 8: + sungah=V; + if(V&0x40) + { + if(V&0x80) // Select WRAM + setprg8r(0x10,0x6000,0); + } + else + setprg8(0x6000,V); + break; + case 9:ROM_BANK8(0x8000,V);break; + case 0xa:ROM_BANK8(0xa000,V);break; + case 0xb:ROM_BANK8(0xc000,V);break; + case 0xc: + switch(V&3) + { + case 0:MIRROR_SET2(1);break; + case 1:MIRROR_SET2(0);break; + case 2:onemir(0);break; + case 3:onemir(1);break; + } + break; + case 0xd:IRQa=V;break; + case 0xe:IRQCount&=0xFF00;IRQCount|=V;break; + case 0xf:IRQCount&=0x00FF;IRQCount|=V<<8;break; + } + break; + } +} + +static int32 vcount[4]; +static int CAYBC[4]={0,0,0,0}; +static void DoAYSQ(int x) +{ + int V; + uint32 freq; + unsigned char amp; + int32 start,end; + + start=CAYBC[x]; + end=(timestamp<<16)/soundtsinc; + if(end<=start) return; + CAYBC[x]=end; + + if(!(MapperExRAM[0x7]&(1<>1)) + Wave[V>>4]+=amp; + vcoo+=0x1000; + if(vcoo>=inc) vcoo-=inc; + } + vcount[x]=vcoo; + } +} +static void DoAYNoise(void) +{ + int V; + uint32 freq; + unsigned char amp; + int32 start,end; + + start=CAYBC[3]; + end=(timestamp<<16)/soundtsinc; + if(end<=start) return; + CAYBC[3]=end; + + amp=0; + for(V=0;V<3;V++) + { + if(!(MapperExRAM[0x7]&(8<44100) + inc=((freq<<11)/(FSettings.SndRate OVERSAMPLE))<<4; + else + inc=(freq<<15)/(FSettings.SndRate OVERSAMPLE); + + for(V=start;V=32768) + { + unsigned char feedback; + mixer=0; + if(znreg&1) mixer+=amp; + feedback=((znreg>>13)&1)^((znreg>>14)&1); + znreg=(znreg<<1)+(feedback); + vcount[3]-=32768; + } + Wave[V>>4]+=mixer; + vcount[3]+=inc; + } + } + + +} +static void AYSound(int Count) +{ + int x; + DoAYSQ(0); + DoAYSQ(1); + DoAYSQ(2); + DoAYNoise(); + for(x=0;x<4;x++) + CAYBC[x]=Count; +} + +static void FP_FASTAPASS(1) SunIRQHook(int a) +{ + if(IRQa) + { + IRQCount-=a; + if(IRQCount<=0) + {TriggerIRQ();IRQa=0;IRQCount=0xFFFF;} + } +} + +void Mapper69_StateRestore(int version) +{ + if(version>=19) + { + if(mapbyte1[1]&0x40) + { + if(mapbyte1[1]&0x80) // Select WRAM + setprg8r(0x10,0x6000,0); + } + else + setprg8(0x6000,mapbyte1[1]); + } + else + mapbyte1[1]=0xC0; +} + +static void M69SC(void) +{ + if(FSettings.SndRate) + Mapper69_ESI(); + else + SetWriteHandler(0xc000,0xffff,(writefunc)0); +} + +void Mapper69_ESI(void) +{ + GameExpSound.RChange=M69SC; + if(FSettings.SndRate) + { + SetWriteHandler(0xc000,0xdfff,Mapper69_SWL); + SetWriteHandler(0xe000,0xffff,Mapper69_SWH); + } +} + +void Mapper69_init(void) +{ + SetupCartPRGMapping(0x10,WRAM,8192,1); + + SetWriteHandler(0x8000,0xbfff,Mapper69_write); + SetWriteHandler(0x6000,0x7fff,SUN5BWRAM); + SetReadHandler(0x6000,0x7fff,SUN5AWRAM); + Mapper69_ESI(); + MapIRQHook=SunIRQHook; + MapStateRestore=Mapper69_StateRestore; + znreg=0; +} + diff --git a/mappers/71.c b/mappers/71.c new file mode 100644 index 0000000..7643978 --- /dev/null +++ b/mappers/71.c @@ -0,0 +1,41 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + + + +DECLFW(Mapper71_write) +{ +switch(A&0xF000) + { + case 0xF000: + case 0xE000: + case 0xD000: + case 0xC000:ROM_BANK16(0x8000,V);break; + case 0x9000:onemir((V>>3)&2);break; + } +} + +void Mapper71_init(void) +{ +SetWriteHandler(0x4020,0xffff,Mapper71_write); +} + diff --git a/mappers/72.c b/mappers/72.c new file mode 100644 index 0000000..dcc5828 --- /dev/null +++ b/mappers/72.c @@ -0,0 +1,37 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + + +DECLFW(Mapper72_write) +{ + mapbyte1[0]=V; + if(V&0x80) + ROM_BANK16(0x8000,V&0xF); + if(V&0x40) + VROM_BANK8(V&0xF); +} + +void Mapper72_init(void) +{ + SetWriteHandler(0x6000,0xffff,Mapper72_write); +} + diff --git a/mappers/73.c b/mappers/73.c new file mode 100644 index 0000000..ca230f8 --- /dev/null +++ b/mappers/73.c @@ -0,0 +1,57 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + + + +DECLFW(Mapper73_write) +{ +switch(A&0xF000) + { + case 0x8000:IRQCount&=0xFFF0;IRQCount|=(V&0xF);break; + case 0x9000:IRQCount&=0xFF0F;IRQCount|=(V&0xF)<<4;break; + case 0xa000:IRQCount&=0xF0FF;IRQCount|=(V&0xF)<<8;break; + case 0xb000:IRQCount&=0x0FFF;IRQCount|=(V&0xF)<<12;break; + case 0xc000:IRQa=V&2;break; + case 0xf000:ROM_BANK16(0x8000,V);break; + } +} + +static void FP_FASTAPASS(1) Mapper73IRQHook(int a) +{ + if(IRQa) + { + IRQCount+=a; + if(IRQCount>=0xFFFF) + { + IRQCount&=0xFFFF; + IRQa=0; + TriggerIRQ(); + } + } +} + +void Mapper73_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper73_write); + MapIRQHook=Mapper73IRQHook; +} + diff --git a/mappers/75.c b/mappers/75.c new file mode 100644 index 0000000..0bd2711 --- /dev/null +++ b/mappers/75.c @@ -0,0 +1,47 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + + +#define map75sel mapbyte1[0] +#define map75ar mapbyte2 + +DECLFW(Mapper75_write) +{ +switch(A&0xF000) + { + case 0x8000:ROM_BANK8(0x8000,V);break; + case 0x9000: + VROM_BANK4(0x0000,map75ar[0]|((V&2)<<3)); + VROM_BANK4(0x1000,map75ar[1]|((V&4)<<2)); + map75sel=V;MIRROR_SET(V&1);break; + case 0xa000:ROM_BANK8(0xa000,V);break; + case 0xc000:ROM_BANK8(0xc000,V);break; + case 0xe000:V&=0xF;map75ar[0]=V;V|=(map75sel&2)<<3;VROM_BANK4(0x0000,V);break; + case 0xf000:V&=0xF;map75ar[1]=V;V|=(map75sel&4)<<2;VROM_BANK4(0x1000,V);break; + } +} + +void Mapper75_init(void) +{ +SetWriteHandler(0x8000,0xffff,Mapper75_write); +} + diff --git a/mappers/76.c b/mappers/76.c new file mode 100644 index 0000000..d901bb7 --- /dev/null +++ b/mappers/76.c @@ -0,0 +1,55 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + + + +DECLFW(Mapper76_write) +{ + switch(A&0xE001){ + case 0x8000: + MMC3_cmd = V; + break; + case 0x8001: + switch(MMC3_cmd&0x07){ + case 2: VROM_BANK2(0x000,V);break; + case 3: VROM_BANK2(0x800,V);break; + case 4: VROM_BANK2(0x1000,V);break; + case 5: VROM_BANK2(0x1800,V);break; + case 6: + if (MMC3_cmd&0x40) ROM_BANK8(0xC000,V); + else ROM_BANK8(0x8000,V); + break; + case 7: ROM_BANK8(0xA000,V); + break; + } + break; + case 0xA000: + MIRROR_SET(V&1); + break; + } +} + +void Mapper76_init(void) +{ +SetWriteHandler(0x8000,0xffff,Mapper76_write); +} + diff --git a/mappers/77.c b/mappers/77.c new file mode 100644 index 0000000..e9d0015 --- /dev/null +++ b/mappers/77.c @@ -0,0 +1,54 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + +/* Original code provided by LULU */ + +static DECLFW(Mapper77_write) +{ + mapbyte1[0]=V; + ROM_BANK32(V&0x7); + VROM_BANK2(0x0000, (V&0xf0)>>4); +} + +static void Mapper77_StateRestore(int version) +{ + int x; + + if(version>=72) + { + ROM_BANK32(mapbyte1[0]&0x7); + VROM_BANK2(0x0000, (mapbyte1[0]&0xf0)>>4); + } + for(x=2;x<8;x++) + VRAM_BANK1(x*0x400,x); +} + +void Mapper77_init(void) +{ + int x; + + ROM_BANK32(0); + for(x=2;x<8;x++) + VRAM_BANK1(x*0x400,x); + SetWriteHandler(0x6000,0xffff,Mapper77_write); + MapStateRestore=Mapper77_StateRestore; +} diff --git a/mappers/79.c b/mappers/79.c new file mode 100644 index 0000000..5e79efe --- /dev/null +++ b/mappers/79.c @@ -0,0 +1,40 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + + + +DECLFW(Mapper79_write) +{ + if(A<0x8000 && ((A^0x4100)==0)) + { + ROM_BANK32((V>>3)&1); + } + VROM_BANK8(V); +} + +void Mapper79_init(void) +{ + ROM_BANK32(~0); + SetWriteHandler(0x8000,0xffff,Mapper79_write); + SetWriteHandler(0x4020,0x5fff,Mapper79_write); +} + diff --git a/mappers/8.c b/mappers/8.c new file mode 100644 index 0000000..1a68bf8 --- /dev/null +++ b/mappers/8.c @@ -0,0 +1,38 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 1998 BERO + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "mapinc.h" + + + +DECLFW(Mapper8_write) +{ + ROM_BANK16(0x8000,V>>3); + VROM_BANK8(V&7); +} + +void Mapper8_init(void) +{ + ROM_BANK32(0); + SetWriteHandler(0x8000,0xFFFF,Mapper8_write); +} + diff --git a/mappers/80.c b/mappers/80.c new file mode 100644 index 0000000..c824a12 --- /dev/null +++ b/mappers/80.c @@ -0,0 +1,49 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + + + +DECLFW(Mapper80_write) +{ +switch(A) + { + case 0x7ef0: VROM_BANK1(0x000,V);VROM_BANK1(0x400,(V+1));break; + case 0x7ef1: VROM_BANK1(0x800,V);VROM_BANK1(0xC00,(V+1));break; + + case 0x7ef2: VROM_BANK1(0x1000,V);break; + case 0x7ef3: VROM_BANK1(0x1400,V);break; + case 0x7ef4: VROM_BANK1(0x1800,V);break; + case 0x7ef5: VROM_BANK1(0x1c00,V);break; + case 0x7efa: + case 0x7efb: ROM_BANK8(0x8000,V);break; + case 0x7efd: + case 0x7efc: ROM_BANK8(0xA000,V);break; + case 0x7efe: + case 0x7eff: ROM_BANK8(0xC000,V);break; + } +} + +void Mapper80_init(void) +{ +SetWriteHandler(0x4020,0x7fff,Mapper80_write); +} + diff --git a/mappers/82.c b/mappers/82.c new file mode 100644 index 0000000..b6a1cb2 --- /dev/null +++ b/mappers/82.c @@ -0,0 +1,62 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + +#define ctrl mapbyte1[6] + +static void DoCHR(void) +{ + int x; + + for(x=0;x<2;x++) + VROM_BANK2((x<<11)|((ctrl&2)<<11),mapbyte1[x]>>1); + for(x=0;x<4;x++) + VROM_BANK1((x<<10) | (((ctrl&2)^2)<<11),mapbyte1[2+x]); +} + +static DECLFW(Mapper82_write) +{ + if(A<=0x7EF5) + { + mapbyte1[A&7]=V; + DoCHR(); + } + else + switch(A) + { + case 0x7ef6:ctrl=V&3; + MIRROR_SET2(V&1); + DoCHR(); + break; + case 0x7efa:V>>=2;mapbyte2[0]=V;ROM_BANK8(0x8000,V);break; + case 0x7efb:V>>=2;mapbyte2[1]=V;ROM_BANK8(0xa000,V);break; + case 0x7efc:V>>=2;mapbyte2[2]=V;ROM_BANK8(0xc000,V);break; + } +} + +void Mapper82_init(void) +{ + ROM_BANK8(0xE000,~0); + + /* external WRAM might end at $73FF */ + SetWriteHandler(0x7ef0,0x7efc,Mapper82_write); +} + diff --git a/mappers/83.c b/mappers/83.c new file mode 100644 index 0000000..6bcca19 --- /dev/null +++ b/mappers/83.c @@ -0,0 +1,113 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + +void FP_FASTAPASS(1) m83IRQHook(int a) +{ + if(IRQa) + { + IRQCount-=a; + if(IRQCount<0) + { + TriggerIRQ(); + IRQa=0; + IRQCount=0xFFFF; + } + } +} + +static DECLFW(wrlow) +{ + mapbyte4[A&3]=V; +} + +static DECLFR(rdlow) +{ + return mapbyte4[A&3]; +} + +static void m83prg(void) +{ + ROM_BANK16(0x8000,mapbyte1[0]&0x3F); + ROM_BANK16(0xC000,(mapbyte1[0]&0x30)|0xF); +} + +static void m83chr(void) +{ + int x; + for(x=0;x<8;x++) + VROM_BANK1(x*0x400,mapbyte2[x]|((mapbyte1[0]&0x30)<<4)); +} + +static DECLFW(Mapper83_write) +{ + //printf("$%04x:$%02x\n",A,V); + switch(A) + { + case 0x8000: + case 0xB000: + case 0xB0FF: + case 0xB1FF: + { + mapbyte1[0]=V; + m83prg(); + m83chr(); + } + break; + case 0x8100: + switch(V&0x3) + { + case 0x00:MIRROR_SET2(1);break; + case 0x01:MIRROR_SET2(0);break; + case 0x02:onemir(0);break; + case 0x03:onemir(1);break; + } + break; + case 0x8200:IRQCount&=0xFF00;IRQCount|=V;break; + case 0x8201:IRQa=1;IRQCount&=0xFF;IRQCount|=V<<8;break; + //case 0x8300:ROM_BANK8(0x8000,V);break; + //case 0x8301:ROM_BANK8(0xA000,V);break; + //case 0x8302:ROM_BANK8(0xC000,V);break; + case 0x8310:mapbyte2[0]=V;m83chr();break; + case 0x8311:mapbyte2[1]=V;m83chr();break; + case 0x8312:mapbyte2[2]=V;m83chr();break; + case 0x8313:mapbyte2[3]=V;m83chr();break; + case 0x8314:mapbyte2[4]=V;m83chr();break; + case 0x8315:mapbyte2[5]=V;m83chr();break; + case 0x8316:mapbyte2[6]=V;m83chr();break; + case 0x8317:mapbyte2[7]=V;m83chr();break; + } +// printf("$%04x:$%02x, $%04x\n",A,V,X.PC.W); + +} + +void Mapper83_init(void) +{ + + ROM_BANK8(0xc000,0x1e); + ROM_BANK8(0xe000,0x1f); + + MapIRQHook=m83IRQHook; + + SetReadHandler(0x5100,0x5103,rdlow); + SetWriteHandler(0x5100,0x5103,wrlow); + SetWriteHandler(0x8000,0xffff,Mapper83_write); +} diff --git a/mappers/85.c b/mappers/85.c new file mode 100644 index 0000000..0d45898 --- /dev/null +++ b/mappers/85.c @@ -0,0 +1,140 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + +#define vrctemp mapbyte1[0] +#define indox mapbyte1[1] + +static int acount=0; + +void KillOPL(void); +void UpdateOPL(int Count); +void vrc7translate(uint8 A, uint8 V); +void LoadOPL(void); +extern uint8 VRC7Instrument[16][8]; +extern uint8 VRC7Chan[3][6]; + +static INLINE void DaMirror(int V) +{ + int salpo[4]={MI_V,MI_H,MI_0,MI_1}; + setmirror(salpo[V&3]); +} + +DECLFW(Mapper85_write) +{ + A|=(A&8)<<1; + + if(A>=0xa000 && A<=0xDFFF) + { + A&=0xF010; + { + int x=((A>>4)&1)|((A-0xA000)>>11); + mapbyte3[x]=V; + setchr1(x<<10,V); + } + } + else if(A==0x9030) + { + if(FSettings.SndRate) + vrc7translate(indox,V); + GameExpSound.Fill=UpdateOPL; + } + else switch(A&0xF010) + { + case 0x8000:mapbyte2[0]=V;setprg8(0x8000,V);break; + case 0x8010:mapbyte2[1]=V;setprg8(0xa000,V);break; + case 0x9000:mapbyte2[2]=V;setprg8(0xc000,V);break; + case 0x9010:indox=V;break; + case 0xe000:mapbyte2[3]=V;DaMirror(V);break; + case 0xE010:IRQLatch=V; + break; + case 0xF000:IRQa=V&2; + vrctemp=V&1; + if(V&2) {IRQCount=IRQLatch;acount=0;} + break; + case 0xf010:if(vrctemp) IRQa=1; + else IRQa=0; + break; + } +} + +static void FP_FASTAPASS(1) KonamiIRQHook(int a) +{ + if(IRQa) + { + acount+=(a<<1)+a; + if(acount>=339) + { + doagainbub:acount-=339;IRQCount++; + if(IRQCount&0x100) {TriggerIRQ();IRQCount=IRQLatch;} + if(acount>=339) goto doagainbub; + } + } +} + +void Mapper85_StateRestore(int version) +{ + int x; + + if(version<72) + { + for(x=0;x<8;x++) + mapbyte3[x]=CHRBankList[x]; + for(x=0;x<3;x++) + mapbyte2[x]=PRGBankList[x]; + mapbyte2[3]=(Mirroring<0x10)?Mirroring:Mirroring-0xE; + } + + for(x=0;x<8;x++) + setchr1(x*0x400,mapbyte3[x]); + for(x=0;x<3;x++) + setprg8(0x8000+x*8192,mapbyte2[x]); + DaMirror(mapbyte2[3]); + LoadOPL(); +} + +static void M85SC(void) +{ + KillOPL(); +} + +void VRC7_ESI(void) +{ + if(FSettings.SndRate) + { + SetWriteHandler(0x9010,0x901F,Mapper85_write); + SetWriteHandler(0x9030,0x903F,Mapper85_write); + } + GameExpSound.RChange=M85SC; +} + +void Mapper85_init(void) +{ + MapIRQHook=KonamiIRQHook; + SetWriteHandler(0x8000,0xffff,Mapper85_write); + GameStateRestore=Mapper85_StateRestore; + VRC7_ESI(); + if(!VROM_size) + SetupCartCHRMapping(0, CHRRAM, 8192, 1); + AddExState(VRC7Instrument, 16, 0, "VC7I"); + AddExState(VRC7Chan, sizeof(VRC7Chan), 0, "V7CH"); + +} diff --git a/mappers/86.c b/mappers/86.c new file mode 100644 index 0000000..5bcefcd --- /dev/null +++ b/mappers/86.c @@ -0,0 +1,32 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + +DECLFW(Mapper86_write) +{ + VROM_BANK8((V&3)|((V>>4)&4)); + ROM_BANK32((V>>4)&3); +} + +void Mapper86_init(void) +{ + SetWriteHandler(0x6000,0x6000,Mapper86_write); +} diff --git a/mappers/88.c b/mappers/88.c new file mode 100644 index 0000000..d6a7f86 --- /dev/null +++ b/mappers/88.c @@ -0,0 +1,56 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + +static DECLFW(Mapper88_write) +{ + //if(A>=0x8002 || A<0x8000) + //if(A==0xc000) + // printf("$%04x:$%02x\n",A,V); + switch(A) //&0xc001) + { + //case 0xc000: + //MIRROR_SET((V&0x40)>>6); + //onemir((V&0x40)>>6); + //break; + case 0x8000:mapbyte1[0]=V;break; + case 0x8001: + switch(mapbyte1[0]&7) + { + case 0:VROM_BANK2(0,V>>1);break; + case 1:VROM_BANK2(0x800,V>>1);break; + case 2:VROM_BANK1(0x1000,V|0x40);break; + case 3:VROM_BANK1(0x1400,V|0x40);break; + case 4:VROM_BANK1(0x1800,V|0x40);break; + case 5:VROM_BANK1(0x1c00,V|0x40);break; + case 6:ROM_BANK8(0x8000,V);break; + case 7:ROM_BANK8(0xA000,V);break; + } + break; + + } +} + +void Mapper88_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper88_write); +} + diff --git a/mappers/89.c b/mappers/89.c new file mode 100644 index 0000000..696e698 --- /dev/null +++ b/mappers/89.c @@ -0,0 +1,34 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + +DECLFW(Mapper89_write) +{ + VROM_BANK8((V&7)|((V>>4)&8)); + ROM_BANK16(0x8000,(V>>4)&7); + onemir((V>>3)&1); +} + +void Mapper89_init(void) +{ + Mirroring=0; + SetWriteHandler(0x8000,0xffff,Mapper89_write); +} diff --git a/mappers/90.c b/mappers/90.c new file mode 100644 index 0000000..97405dc --- /dev/null +++ b/mappers/90.c @@ -0,0 +1,154 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + +#define tkcom1 mapbyte1[1] +#define tkcom2 mapbyte1[2] + +#define prgb mapbyte2 +#define unkl (mapbyte2+4) +#define chrlow mapbyte3 +#define chrhigh mapbyte4 + +static uint8 tekker=0x80; + +static DECLFR(tekread) +{ + return tekker; +} + +static void tekprom(void) +{ + switch(tkcom1&3) + { + case 1: // 16 KB + ROM_BANK16(0x8000,prgb[0]); + ROM_BANK16(0xC000,prgb[2]); + break; + + case 2: //2 = 8 KB ?? + case 3: + ROM_BANK8(0x8000,prgb[0]); + ROM_BANK8(0xa000,prgb[1]); + ROM_BANK8(0xc000,prgb[2]); + ROM_BANK8(0xe000,prgb[3]); + break; + } +} + +static void tekvrom(void) +{ + int x; + switch(tkcom1&0x18) + { + case 0x00: // 8KB + VROM_BANK8(chrlow[0]|(chrhigh[0]<<8)); + break; + case 0x08: // 4KB + for(x=0;x<8;x+=4) + VROM_BANK4(x<<10,chrlow[x]|(chrhigh[x]<<8)); + break; + case 0x10: // 2KB + for(x=0;x<8;x+=2) + VROM_BANK2(x<<10,chrlow[x]|(chrhigh[x]<<8)); + break; + case 0x18: // 1KB + for(x=0;x<8;x++) + VROM_BANK1(x<<10,chrlow[x]|(chrhigh[x]<<8)); + break; + } +} + +static DECLFW(Mapper90_write) +{ + A&=0xF007; + + if(A>=0x8000 && A<=0x8003) + { + prgb[A&3]=V; + tekprom(); + } + else if(A>=0x9000 && A<=0x9007) + { + chrlow[A&7]=V; + tekvrom(); + } + else if(A>=0xa000 && A<=0xa007) + { + chrhigh[A&7]=V; + tekvrom(); + } + else if(A>=0xb000 && A<=0xb003) + { + unkl[A&3]=V; + } + else switch(A) + { + case 0xc004: + case 0xc000:IRQLatch=V;break; + + case 0xc005: + case 0xc001:X6502_IRQEnd(FCEU_IQEXT); + IRQCount=V;break; + case 0xc006: + case 0xc002:X6502_IRQEnd(FCEU_IQEXT); + IRQa=0; + IRQCount=IRQLatch; + break; + case 0xc007: + case 0xc003:IRQa=1;break; + + case 0xd000:tkcom1=V;break; + case 0xd001:switch(V&3){ + case 0x00:MIRROR_SET(0);break; + case 0x01:MIRROR_SET(1);break; + case 0x02:onemir(0);break; + case 0x03:onemir(1);break; + } + break; + break; + } +} + +static void Mapper90_hb(void) +{ + if(IRQa) + { + if(IRQCount) + { + IRQCount--; + if(!IRQCount) + { + X6502_IRQBegin(FCEU_IQEXT); + IRQCount=IRQLatch; + } + } + } +} + +void Mapper90_init(void) +{ + tekker^=0x80; + SetWriteHandler(0x8000,0xffff,Mapper90_write); + SetReadHandler(0x5000,0x5000,tekread); + GameHBIRQHook=Mapper90_hb; +} + diff --git a/mappers/92.c b/mappers/92.c new file mode 100644 index 0000000..1494e7f --- /dev/null +++ b/mappers/92.c @@ -0,0 +1,45 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + +/* Original code provided by LULU */ + +static DECLFW(Mapper92_write) +{ + uint8 reg=(A&0xF0)>>4; + uint8 bank=A&0xF; + + if(A>=0x9000) + { + if(reg==0xD) ROM_BANK16(0xc000,bank); + else if(reg==0xE) VROM_BANK8(bank); + } + else + { + if(reg==0xB) ROM_BANK16(0xc000,bank); + else if(reg==0x7) VROM_BANK8(bank); + } +} + +void Mapper92_init(void) +{ + SetWriteHandler(0x8000,0xFFFF,Mapper92_write); +} diff --git a/mappers/95.c b/mappers/95.c new file mode 100644 index 0000000..2d6792b --- /dev/null +++ b/mappers/95.c @@ -0,0 +1,76 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + + +#define dbarray mapbyte1 +static void FP_FASTAPASS(1) dragonbust_ppu(uint32 A) +{ + static int last=-1; + static uint8 z; + + if(A>=0x2000) return; + + A>>=13; + + z=dbarray[A]; + + if(z!=last) + { + onemir(z); + last=z; + } +} + + +DECLFW(Mapper95_write) +{ + switch(A&0xF001){ + + case 0x8000: + MMC3_cmd = V; + break; + + case 0x8001: + switch(MMC3_cmd&7){ + case 0: dbarray[0]=dbarray[1]=(V&0x20)>>4;onemir((V&0x20)>>4);V>>=1;VROM_BANK2(0x0000,V);break; + case 1: dbarray[2]=dbarray[3]=(V&0x20)>>4;onemir((V&0x20)>>4);V>>=1;VROM_BANK2(0x0800,V);break; + case 2: dbarray[4]=(V&0x20)>>4;onemir((V&0x20)>>4);VROM_BANK1(0x1000,V); break; + case 3: dbarray[5]=(V&0x20)>>4;onemir((V&0x20)>>4);VROM_BANK1(0x1400,V); break; + case 4: dbarray[6]=(V&0x20)>>4;onemir((V&0x20)>>4);VROM_BANK1(0x1800,V); break; + case 5: dbarray[7]=(V&0x20)>>4;onemir((V&0x20)>>4);VROM_BANK1(0x1C00,V); break; + case 6: + ROM_BANK8(0x8000,V); + break; + case 7: + ROM_BANK8(0xA000,V); + break; + } + break; +} +} + +void Mapper95_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper95_write); + PPU_hook=dragonbust_ppu; +} + diff --git a/mappers/97.c b/mappers/97.c new file mode 100644 index 0000000..f019fdc --- /dev/null +++ b/mappers/97.c @@ -0,0 +1,42 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + + + +DECLFW(Mapper97_write) +{ +ROM_BANK16(0xC000,V&15); +switch(V>>6) + { + case 0:break; + case 1:MIRROR_SET2(0);break; + case 2:MIRROR_SET2(1);break; + case 3:break; + } +} + +void Mapper97_init(void) +{ + ROM_BANK16(0x8000,~0); + SetWriteHandler(0x8000,0xffff,Mapper97_write); +} + diff --git a/mappers/99.c b/mappers/99.c new file mode 100644 index 0000000..c71611e --- /dev/null +++ b/mappers/99.c @@ -0,0 +1,35 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + +static writefunc oldmorko; + +static DECLFW(morko) +{ + VROM_BANK8((V>>2)&1); + oldmorko(A,V); +} + +void Mapper99_init(void) +{ + oldmorko=GetWriteHandler(0x4016); + SetWriteHandler(0x4016,0x4016,morko); +} diff --git a/mappers/Makefile b/mappers/Makefile new file mode 100644 index 0000000..c95ca8f --- /dev/null +++ b/mappers/Makefile @@ -0,0 +1,74 @@ +MOBJS = mappers/mmc2and4.o mappers/simple.o mappers/112.o mappers/117.o mappers/15.o mappers/151.o mappers/16.o mappers/17.o mappers/18.o mappers/180.o mappers/184.o mappers/19.o mappers/21.o mappers/22.o mappers/225.o mappers/226.o mappers/228.o mappers/229.o mappers/23.o mappers/24and26.o mappers/240.o mappers/246.o mappers/25.o mappers/32.o mappers/33.o mappers/40.o mappers/41.o mappers/6.o mappers/64.o mappers/65.o mappers/67.o mappers/68.o mappers/69.o mappers/71.o mappers/73.o mappers/75.o mappers/76.o mappers/79.o mappers/8.o mappers/80.o mappers/85.o mappers/90.o mappers/95.o mappers/97.o mappers/99.o mappers/182.o mappers/vrc7snd.o mappers/46.o mappers/43.o mappers/42.o mappers/113.o mappers/86.o mappers/89.o mappers/83.o mappers/77.o mappers/92.o mappers/105.o mappers/88.o mappers/248.o mappers/fmopl.o mappers/242.o mappers/232.o mappers/72.o mappers/234.o mappers/227.o mappers/82.o mappers/189.o mappers/245.o mappers/249.o mappers/51.o + +fmopl.o: mappers/fmopl.c mappers/fmopl.h +vrc7snd.o: mappers/vrc7snd.c + +mappers/simple.o: mappers/simple.c +mappers/mmc2and4.o: mappers/mmc2and4.c + +mappers/112.o: mappers/112.c +mappers/117.o: mappers/117.c +mappers/15.o: mappers/15.c +mappers/151.o: mappers/151.c +mappers/16.o: mappers/16.c +mappers/17.o: mappers/17.c +mappers/18.o: mappers/18.c +mappers/19.o: mappers/19.c +mappers/180.o: mappers/180.c +mappers/184.o: mappers/184.c +mappers/21.o: mappers/21.c +mappers/22.o: mappers/22.c +mappers/23.o: mappers/23.c +mappers/24and26.o: mappers/24and26.c +mappers/25.o: mappers/25.c +mappers/225.o: mappers/225.c +mappers/226.o: mappers/226.c +mappers/228.o: mappers/228.c +mappers/229.o: mappers/229.c +mappers/240.o: mappers/240.c +mappers/246.o: mappers/246.c +mappers/32.o: mappers/32.c +mappers/33.o: mappers/33.c +mappers/40.o: mappers/40.c +mappers/41.o: mappers/41.c +mappers/6.o: mappers/6.c +mappers/64.o: mappers/64.c +mappers/65.o: mappers/65.c +mappers/67.o: mappers/67.c +mappers/68.o: mappers/68.c +mappers/69.o: mappers/69.c +mappers/71.o: mappers/71.c +mappers/73.o: mappers/73.c +mappers/75.o: mappers/75.c +mappers/76.o: mappers/76.c +mappers/79.o: mappers/79.c +mappers/8.o: mappers/8.c +mappers/80.o: mappers/80.c +mappers/85.o: mappers/85.c +mappers/90.o: mappers/90.c +mappers/95.o: mappers/95.c +mappers/97.o: mappers/97.c +mappers/99.o: mappers/99.c +mappers/182.o: mappers/182.c +mappers/46.o: mappers/46.c +mappers/43.o: mappers/43.c +mappers/42.o: mappers/42.c +mappers/113.o: mappers/113.c +mappers/86.o: mappers/86.c +mappers/89.o: mappers/89.c +mappers/83.o: mappers/83.c +mappers/77.o: mappers/77.c +mappers/92.o: mappers/92.c +mappers/105.o: mappers/105.c +mappers/88.o: mappers/88.c +mappers/248.o: mappers/248.c +mappers/242.o: mappers/242.c +mappers/232.o: mappers/232.c +mappers/72.o: mappers/72.c +mappers/234.o: mappers/234.c +mappers/227.o: mappers/227.c +mappers/82.o: mappers/82.c +mappers/189.o: mappers/189.c +mappers/245.o: mappers/245.c +mappers/249.o: mappers/249.c +mappers/51.o: mappers/51.c diff --git a/mappers/fmopl.c b/mappers/fmopl.c new file mode 100644 index 0000000..b2fc12c --- /dev/null +++ b/mappers/fmopl.c @@ -0,0 +1,871 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 1999,2000 Tatsuyuki Satoh + * Copyright (C) 2001,2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* This file has been heavily modified from the original(mostly unused + code was removed). If you want to use it for anything other than + VRC7 sound emulation, you should get the original from the AdPlug + source distribution or the MAME(version 0.37b16) source distribution + (but be careful about the different licenses). + - Xodnizel +*/ + +#include +#include +#include +#include +#include +#include "mapinc.h" +#include "fmopl.h" + +#ifndef PI +#define PI 3.14159265358979323846 +#endif + +/* -------------------- preliminary define section --------------------- */ +/* attack/decay rate time rate */ +#define OPL_ARRATE 141280 /* RATE 4 = 2826.24ms @ 3.6MHz */ +#define OPL_DRRATE 1956000 /* RATE 4 = 39280.64ms @ 3.6MHz */ + +#define DELTAT_MIXING_LEVEL (1) /* DELTA-T ADPCM MIXING LEVEL */ + +#define FREQ_BITS 24 /* frequency turn */ + +/* counter bits = 20 , octerve 7 */ +#define FREQ_RATE (1<<(FREQ_BITS-20)) +#define TL_BITS (FREQ_BITS+2) + +/* final output shift , limit minimum and maximum */ +#define OPL_OUTSB (TL_BITS+3-16) /* OPL output final shift 16bit */ +#define OPL_MAXOUT (0x7fff< max ) + val = max; + else if ( val < min ) + val = min; + + return val; +} + +/* ----- key on ----- */ +INLINE void OPL_KEYON(OPL_SLOT *SLOT) +{ + /* sin wave restart */ + SLOT->Cnt = 0; + /* set attack */ + SLOT->evm = ENV_MOD_AR; + SLOT->evs = SLOT->evsa; + SLOT->evc = EG_AST; + SLOT->eve = EG_AED; +} +/* ----- key off ----- */ +INLINE void OPL_KEYOFF(OPL_SLOT *SLOT) +{ + if( SLOT->evm > ENV_MOD_RR) + { + /* set envelope counter from envleope output */ + SLOT->evm = ENV_MOD_RR; + if( !(SLOT->evc&EG_DST) ) + //SLOT->evc = (ENV_CURVE[SLOT->evc>>ENV_BITS]<evc = EG_DST; + SLOT->eve = EG_DED; + SLOT->evs = SLOT->evsr; + } +} + +/* ---------- calcrate Envelope Generator & Phase Generator ---------- */ +/* return : envelope output */ +INLINE UINT32 OPL_CALC_SLOT( OPL_SLOT *SLOT ) +{ + /* calcrate envelope generator */ + if( (SLOT->evc+=SLOT->evs) >= SLOT->eve ) + { + switch( SLOT->evm ){ + case ENV_MOD_AR: /* ATTACK -> DECAY1 */ + /* next DR */ + SLOT->evm = ENV_MOD_DR; + SLOT->evc = EG_DST; + SLOT->eve = SLOT->SL; + SLOT->evs = SLOT->evsd; + break; + case ENV_MOD_DR: /* DECAY -> SL or RR */ + SLOT->evc = SLOT->SL; + SLOT->eve = EG_DED; + if(SLOT->eg_typ) + { + SLOT->evs = 0; + } + else + { + SLOT->evm = ENV_MOD_RR; + SLOT->evs = SLOT->evsr; + } + break; + case ENV_MOD_RR: /* RR -> OFF */ + SLOT->evc = EG_OFF; + SLOT->eve = EG_OFF+1; + SLOT->evs = 0; + break; + } + } + /* calcrate envelope */ + return SLOT->TLL+ENV_CURVE[SLOT->evc>>ENV_BITS]+(SLOT->ams ? ams : 0); +} + +/* set algorythm connection */ +static void set_algorythm( OPL_CH *CH) +{ + INT32 *carrier = &outd[0]; + CH->connect1 = CH->CON ? carrier : &feedback2; + CH->connect2 = carrier; +} + +/* ---------- frequency counter for operater update ---------- */ +INLINE void CALC_FCSLOT(OPL_CH *CH,OPL_SLOT *SLOT) +{ + int ksr; + + /* frequency step counter */ + SLOT->Incr = CH->fc * SLOT->mul; + ksr = CH->kcode >> SLOT->KSR; + + if( SLOT->ksr != ksr ) + { + SLOT->ksr = ksr; + /* attack , decay rate recalcration */ + SLOT->evsa = SLOT->AR[ksr]; + SLOT->evsd = SLOT->DR[ksr]; + SLOT->evsr = SLOT->RR[ksr]; + } + SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); +} + +/* set multi,am,vib,EG-TYP,KSR,mul */ +INLINE void set_mul(FM_OPL *OPL,int slot,int v) +{ + OPL_CH *CH = &OPL->P_CH[slot/2]; + OPL_SLOT *SLOT = &CH->SLOT[slot&1]; + + SLOT->mul = MUL_TABLE[v&0x0f]; + SLOT->KSR = (v&0x10) ? 0 : 2; + SLOT->eg_typ = (v&0x20)>>5; + SLOT->vib = (v&0x40); + SLOT->ams = (v&0x80); + CALC_FCSLOT(CH,SLOT); +} + +/* set ksl & tl */ +INLINE void set_ksl_tl(FM_OPL *OPL,int slot,int v) +{ + OPL_CH *CH = &OPL->P_CH[slot/2]; + OPL_SLOT *SLOT = &CH->SLOT[slot&1]; + int ksl = v>>6; /* 0 / 1.5 / 3 / 6 db/OCT */ + +// if(slot&1) +// if(ksl) {sprintf(errmsg,"doh");howlong=255;ksl=0;} + + SLOT->ksl = ksl ? ksl : 31; +// SLOT->ksl = ksl ? 3-ksl : 31; + SLOT->TL = (v&0x3f)*(0.75/EG_STEP); /* 0.75db step */ + + if( !(OPL->mode&0x80) ) + { /* not CSM latch total level */ + SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); + } +} + +/* set attack rate & decay rate */ +INLINE void set_ar_dr(FM_OPL *OPL,int slot,int v) +{ + OPL_CH *CH = &OPL->P_CH[slot/2]; + OPL_SLOT *SLOT = &CH->SLOT[slot&1]; + int ar = v>>4; + int dr = v&0x0f; + + SLOT->AR = ar ? &OPL->AR_TABLE[ar<<2] : RATE_0; + SLOT->evsa = SLOT->AR[SLOT->ksr]; + if( SLOT->evm == ENV_MOD_AR ) SLOT->evs = SLOT->evsa; + + SLOT->DR = dr ? &OPL->DR_TABLE[dr<<2] : RATE_0; + SLOT->evsd = SLOT->DR[SLOT->ksr]; + if( SLOT->evm == ENV_MOD_DR ) SLOT->evs = SLOT->evsd; +} + +/* set sustain level & release rate */ +INLINE void set_sl_rr(FM_OPL *OPL,int slot,int v) +{ + OPL_CH *CH = &OPL->P_CH[slot/2]; + OPL_SLOT *SLOT = &CH->SLOT[slot&1]; + int sl = v>>4; + int rr = v & 0x0f; + + SLOT->SL = SL_TABLE[sl]; + if( SLOT->evm == ENV_MOD_DR ) SLOT->eve = SLOT->SL; + SLOT->RR = &OPL->DR_TABLE[rr<<2]; + SLOT->evsr = SLOT->RR[SLOT->ksr]; + if( SLOT->evm == ENV_MOD_RR ) SLOT->evs = SLOT->evsr; +} + +/* operator output calcrator */ +#define OP_OUT(slot,env,con) slot->wavetable[((slot->Cnt+con)/(0x1000000/SIN_ENT))&(SIN_ENT-1)][env] +/* ---------- calcrate one of channel ---------- */ +INLINE void OPL_CALC_CH( OPL_CH *CH ) +{ + UINT32 env_out; + OPL_SLOT *SLOT; + + feedback2 = 0; + /* SLOT 1 */ + SLOT = &CH->SLOT[SLOT1]; + env_out=OPL_CALC_SLOT(SLOT); + if( env_out < EG_ENT-1 ) + { + /* PG */ + if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE); + else SLOT->Cnt += SLOT->Incr; + /* connectoion */ + if(CH->FB) + { + int feedback1 = (CH->op1_out[0]+CH->op1_out[1])>>CH->FB; + CH->op1_out[1] = CH->op1_out[0]; + *CH->connect1 += CH->op1_out[0] = OP_OUT(SLOT,env_out,feedback1); + } + else + { + *CH->connect1 += OP_OUT(SLOT,env_out,0); + } + }else + { + CH->op1_out[1] = CH->op1_out[0]; + CH->op1_out[0] = 0; + } + /* SLOT 2 */ + SLOT = &CH->SLOT[SLOT2]; + env_out=OPL_CALC_SLOT(SLOT); + if( env_out < EG_ENT-1 ) + { + /* PG */ + if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE); + else SLOT->Cnt += SLOT->Incr; + /* connectoion */ + outd[0] += OP_OUT(SLOT,env_out, feedback2); + } +} + +/* ----------- initialize time tabls ----------- */ +static void init_timetables( FM_OPL *OPL , int ARRATE , int DRRATE ) +{ + int i; + double rate; + + /* make attack rate & decay rate tables */ + for (i = 0;i < 4;i++) OPL->AR_TABLE[i] = OPL->DR_TABLE[i] = 0; + for (i = 4;i <= 60;i++){ + rate = OPL->freqbase; /* frequency rate */ + if( i < 60 ) rate *= 1.0+(i&3)*0.25; /* b0-1 : x1 , x1.25 , x1.5 , x1.75 */ + rate *= 1<<((i>>2)-1); /* b2-5 : shift bit */ + rate *= (double)(EG_ENT<AR_TABLE[i] = rate / ARRATE; + OPL->DR_TABLE[i] = rate / DRRATE; + } + for (i = 60;i < 76;i++) + { + OPL->AR_TABLE[i] = EG_AED-1; + OPL->DR_TABLE[i] = OPL->DR_TABLE[60]; + } +} + +/* ---------- generic table initialize ---------- */ +static int OPLOpenTable( void ) +{ + int s,t; + double rate; + int i,j; + double pom; + + /* allocate dynamic tables */ + if( (TL_TABLE = malloc(TL_MAX*2*sizeof(INT32))) == NULL) + return 0; + if( (SIN_TABLE = malloc(SIN_ENT*4 *sizeof(INT32 *))) == NULL) + { + free(TL_TABLE); + return 0; + } + if( (AMS_TABLE = malloc(AMS_ENT*2 *sizeof(INT32))) == NULL) + { + free(TL_TABLE); + free(SIN_TABLE); + return 0; + } + if( (VIB_TABLE = malloc(VIB_ENT*2 *sizeof(INT32))) == NULL) + { + free(TL_TABLE); + free(SIN_TABLE); + free(AMS_TABLE); + return 0; + } + /* make total level table */ + for (t = 0;t < EG_ENT-1 ;t++){ + rate = ((1< voltage */ + TL_TABLE[ t] = (int)rate; + TL_TABLE[TL_MAX+t] = -TL_TABLE[t]; + + } + /* fill volume off area */ + for ( t = EG_ENT-1; t < TL_MAX ;t++){ + TL_TABLE[t] = TL_TABLE[TL_MAX+t] = 0; + } + + /* make sinwave table (total level offet) */ + /* degree 0 = degree 180 = off */ + SIN_TABLE[0] = SIN_TABLE[SIN_ENT/2] = &TL_TABLE[EG_ENT-1]; + for (s = 1;s <= SIN_ENT/4;s++){ + pom = sin(2*PI*s/SIN_ENT); /* sin */ + pom = 20*log10(1/pom); /* decibel */ + j = pom / EG_STEP; /* TL_TABLE steps */ + + /* degree 0 - 90 , degree 180 - 90 : plus section */ + SIN_TABLE[ s] = SIN_TABLE[SIN_ENT/2-s] = &TL_TABLE[j]; + /* degree 180 - 270 , degree 360 - 270 : minus section */ + SIN_TABLE[SIN_ENT/2+s] = SIN_TABLE[SIN_ENT -s] = &TL_TABLE[TL_MAX+j]; + + } + for (s = 0;s < SIN_ENT;s++) + { + SIN_TABLE[SIN_ENT*1+s] = s<(SIN_ENT/2) ? SIN_TABLE[s] : &TL_TABLE[EG_ENT]; + SIN_TABLE[SIN_ENT*2+s] = SIN_TABLE[s % (SIN_ENT/2)]; + SIN_TABLE[SIN_ENT*3+s] = (s/(SIN_ENT/4))&1 ? &TL_TABLE[EG_ENT] : SIN_TABLE[SIN_ENT*2+s]; + } + + /* envelope counter -> envelope output table */ + for (i=0; i= EG_ENT ) pom = EG_ENT-1; */ + ENV_CURVE[i] = (int)pom; + /* DECAY ,RELEASE curve */ + ENV_CURVE[(EG_DST>>ENV_BITS)+i]= i; + } + /* off */ + ENV_CURVE[EG_OFF>>ENV_BITS]= EG_ENT-1; + /* make LFO ams table */ + for (i=0; iSLOT[SLOT1]; + OPL_SLOT *slot2 = &CH->SLOT[SLOT2]; + /* all key off */ + OPL_KEYOFF(slot1); + OPL_KEYOFF(slot2); + /* total level latch */ + slot1->TLL = slot1->TL + (CH->ksl_base>>slot1->ksl); + slot1->TLL = slot1->TL + (CH->ksl_base>>slot1->ksl); + /* key on */ + CH->op1_out[0] = CH->op1_out[1] = 0; + OPL_KEYON(slot1); + OPL_KEYON(slot2); +} + +/* ---------- opl initialize ---------- */ +static void OPL_initalize(FM_OPL *OPL) +{ + int fn; + + /* frequency base */ + OPL->freqbase = (OPL->rate) ? ((double)OPL->clock / OPL->rate) / 72 : 0; + /* make time tables */ + init_timetables( OPL , OPL_ARRATE , OPL_DRRATE ); + /* make fnumber -> increment counter table */ + for( fn=0 ; fn < 1024 ; fn++ ) + { + OPL->FN_TABLE[fn] = OPL->freqbase * fn * FREQ_RATE * (1<<7) / 2; + } + /* LFO freq.table */ + OPL->amsIncr = OPL->rate ? (double)AMS_ENT*(1<rate * 3.7 * ((double)OPL->clock/3600000) : 0; + OPL->vibIncr = OPL->rate ? (double)VIB_ENT*(1<rate * 6.4 * ((double)OPL->clock/3600000) : 0; +} + +/* ---------- write a OPL registers ---------- */ +static void OPLWriteReg(FM_OPL *OPL, int r, int v) +{ + OPL_CH *CH; + int slot; + int block_fnum; + + switch(r&0xe0) + { + case 0x00: /* 00-1f:controll */ + switch(r&0x1f) + { + case 0x01: + /* wave selector enable */ + if(OPL->type&OPL_TYPE_WAVESEL) + { + OPL->wavesel = v&0x20; + if(!OPL->wavesel) + { + /* preset compatible mode */ + int c; + for(c=0;cmax_ch;c++) + { + OPL->P_CH[c].SLOT[SLOT1].wavetable = &SIN_TABLE[0]; + OPL->P_CH[c].SLOT[SLOT2].wavetable = &SIN_TABLE[0]; + } + } + } + return; + } + break; + case 0x20: /* am,vib,ksr,eg type,mul */ + slot = slot_array[r&0x1f]; + if(slot == -1) return; + set_mul(OPL,slot,v); + return; + case 0x40: + slot = slot_array[r&0x1f]; + if(slot == -1) return; + set_ksl_tl(OPL,slot,v); + return; + case 0x60: + slot = slot_array[r&0x1f]; + if(slot == -1) return; + set_ar_dr(OPL,slot,v); + return; + case 0x80: + slot = slot_array[r&0x1f]; + if(slot == -1) return; + set_sl_rr(OPL,slot,v); + return; + case 0xa0: + switch(r) + { + case 0xbd: + /* amsep,vibdep,r,bd,sd,tom,tc,hh */ + { + OPL->ams_table = &AMS_TABLE[v&0x80 ? AMS_ENT : 0]; + OPL->vib_table = &VIB_TABLE[v&0x40 ? VIB_ENT : 0]; + } + return; + } + /* keyon,block,fnum */ + if( (r&0x0f) > 8) return; + CH = &OPL->P_CH[r&0x0f]; + if(!(r&0x10)) + { /* a0-a8 */ + block_fnum = (CH->block_fnum&0x1f00) | v; + } + else + { /* b0-b8 */ + int keyon = (v>>5)&1; + block_fnum = ((v&0x1f)<<8) | (CH->block_fnum&0xff); + if(CH->keyon != keyon) + { + if( (CH->keyon=keyon) ) + { + CH->op1_out[0] = CH->op1_out[1] = 0; + OPL_KEYON(&CH->SLOT[SLOT1]); + OPL_KEYON(&CH->SLOT[SLOT2]); + } + else + { + OPL_KEYOFF(&CH->SLOT[SLOT1]); + OPL_KEYOFF(&CH->SLOT[SLOT2]); + } + } + } + /* update */ + if(CH->block_fnum != block_fnum) + { + int blockRv = 7-(block_fnum>>10); + int fnum = block_fnum&0x3ff; + CH->block_fnum = block_fnum; + + CH->ksl_base = KSL_TABLE[block_fnum>>6]; + CH->fc = OPL->FN_TABLE[fnum]>>blockRv; + CH->kcode = CH->block_fnum>>9; + if( (OPL->mode&0x40) && CH->block_fnum&0x100) CH->kcode |=1; + CALC_FCSLOT(CH,&CH->SLOT[SLOT1]); + CALC_FCSLOT(CH,&CH->SLOT[SLOT2]); + } + return; + case 0xc0: + /* FB,C */ + if( (r&0x0f) > 8) return; + CH = &OPL->P_CH[r&0x0f]; + { + int feedback = (v>>1)&7; + CH->FB = feedback ? (8+1) - feedback : 0; + CH->CON = v&1; + set_algorythm(CH); + } + return; + case 0xe0: /* wave type */ + slot = slot_array[r&0x1f]; + if(slot == -1) return; + CH = &OPL->P_CH[slot/2]; + if(OPL->wavesel) + { + CH->SLOT[slot&1].wavetable = &SIN_TABLE[(v&0x03)*SIN_ENT]; + } + return; + } +} + +/* lock/unlock for common table */ +static int OPL_LockTable(void) +{ + num_lock++; + if(num_lock>1) return 0; + /* first time */ + cur_chip = NULL; + /* allocate total level table (128kb space) */ + if( !OPLOpenTable() ) + { + num_lock--; + return -1; + } + return 0; +} + +static void OPL_UnLockTable(void) +{ + if(num_lock) num_lock--; + if(num_lock) return; + /* last time */ + cur_chip = NULL; + OPLCloseTable(); +} + +/*******************************************************************************/ +/* YM3812 local section */ +/*******************************************************************************/ + +/* ---------- update one of chip ----------- */ +void YM3812UpdateOne(FM_OPL *OPL, UINT32 *buffer, int length) +{ + int i; + UINT32 *buf = buffer; + UINT32 amsCnt = OPL->amsCnt; + UINT32 vibCnt = OPL->vibCnt; + OPL_CH *CH,*R_CH; + + if( (void *)OPL != cur_chip ){ + cur_chip = (void *)OPL; + /* channel pointers */ + S_CH = OPL->P_CH; + E_CH = &S_CH[6]; + /* LFO state */ + amsIncr = OPL->amsIncr; + vibIncr = OPL->vibIncr; + ams_table = OPL->ams_table; + vib_table = OPL->vib_table; + } + R_CH = E_CH; + for( i=0; i < length ; i++ ) + { + /* channel A channel B channel C */ + /* LFO */ + ams = ams_table[(amsCnt+=amsIncr)>>AMS_SHIFT]; + vib = vib_table[(vibCnt+=vibIncr)>>VIB_SHIFT]; + outd[0] = 0; + /* FM part */ + for(CH=S_CH ; CH < R_CH ; CH++) + OPL_CALC_CH(CH); + /* limit check */ + //data = Limit( outd[0] , OPL_MAXOUT, OPL_MINOUT ); + /* store to sound buffer */ + { + int32 d=outd[0]>>OPL_OUTSB; + if(d<-32768) d=-32768; + d+=32768; + buf[i] += d; + } + } + + OPL->amsCnt = amsCnt; + OPL->vibCnt = vibCnt; +} + +/* ---------- reset one of chip ---------- */ +void OPLResetChip(FM_OPL *OPL) +{ + int c,s; + int i; + + /* reset chip */ + OPL->mode = 0; /* normal mode */ + + /* reset with register write */ + OPLWriteReg(OPL,0x01,0); /* wabesel disable */ + for(i = 0xff ; i >= 0x20 ; i-- ) OPLWriteReg(OPL,i,0); + /* reset OPerator paramater */ + for( c = 0 ; c < OPL->max_ch ; c++ ) + { + OPL_CH *CH = &OPL->P_CH[c]; + /* OPL->P_CH[c].PAN = OPN_CENTER; */ + for(s = 0 ; s < 2 ; s++ ) + { + /* wave table */ + CH->SLOT[s].wavetable = &SIN_TABLE[0]; + /* CH->SLOT[s].evm = ENV_MOD_RR; */ + CH->SLOT[s].evc = EG_OFF; + CH->SLOT[s].eve = EG_OFF+1; + CH->SLOT[s].evs = 0; + } + } +} + +/* ---------- Create one of vietual YM3812 ---------- */ +/* 'rate' is sampling rate and 'bufsiz' is the size of the */ +FM_OPL *OPLCreate(int type, int clock, int rate) +{ + char *ptr; + FM_OPL *OPL; + int state_size; + int max_ch = 9; /* normaly 9 channels */ + + if( OPL_LockTable() ==-1) return NULL; + /* allocate OPL state space */ + state_size = sizeof(FM_OPL); + state_size += sizeof(OPL_CH)*max_ch; + + /* allocate memory block */ + ptr = malloc(state_size); + if(ptr==NULL) return NULL; + /* clear */ + memset(ptr,0,state_size); + OPL = (FM_OPL *)ptr; ptr+=sizeof(FM_OPL); + OPL->P_CH = (OPL_CH *)ptr; ptr+=sizeof(OPL_CH)*max_ch; + + /* set channel state pointer */ + OPL->type = type; + OPL->clock = clock; + OPL->rate = rate; + OPL->max_ch = max_ch; + /* init grobal tables */ + OPL_initalize(OPL); + /* reset chip */ + OPLResetChip(OPL); + + return OPL; +} + +/* ---------- Destroy one of vietual YM3812 ---------- */ +void OPLDestroy(FM_OPL *OPL) +{ + OPL_UnLockTable(); + free(OPL); +} + +/* ---------- YM3812 I/O interface ---------- */ +void OPLWrite(FM_OPL *OPL,UINT8 a,UINT8 v) +{ + OPLWriteReg(OPL,a,v); +} diff --git a/mappers/fmopl.h b/mappers/fmopl.h new file mode 100644 index 0000000..f11a087 --- /dev/null +++ b/mappers/fmopl.h @@ -0,0 +1,149 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 1999,2000 Tatsuyuki Satoh + * Copyright (C) 2001,2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/* This file has been heavily modified from the original(mostly unused + code was removed). If you want to use it for anything other than + VRC7 sound emulation, you should get the original from the AdPlug + source distribution or the MAME(version 0.37b16) source distribution + (but be careful about the different licenses). + - Xodnizel +*/ + +#ifndef __FMOPL_H_ +#define __FMOPL_H_ + +/* --- system optimize --- */ +/* select bit size of output : 8 or 16 */ +#define OPL_OUTPUT_BIT 16 + +/* compiler dependence */ +#ifndef OSD_CPU_H +#define OSD_CPU_H + +typedef unsigned char UINT8; /* unsigned 8bit */ +typedef unsigned short UINT16; /* unsigned 16bit */ +typedef unsigned long UINT32; /* unsigned 32bit */ +typedef signed char INT8; /* signed 8bit */ +typedef signed short INT16; /* signed 16bit */ +typedef signed long INT32; /* signed 32bit */ +#endif + +#if (OPL_OUTPUT_BIT==16) +typedef INT16 OPLSAMPLE; +#endif +#if (OPL_OUTPUT_BIT==8) +typedef unsigned char OPLSAMPLE; +#endif + + +/* !!!!! here is private section , do not access there member direct !!!!! */ + +#define OPL_TYPE_WAVESEL 0x01 /* waveform select */ + +/* Saving is necessary for member of the 'R' mark for suspend/resume */ +/* ---------- OPL one of slot ---------- */ +typedef struct fm_opl_slot { + INT32 TL; /* total level :TL << 8 */ + INT32 TLL; /* adjusted now TL */ + UINT8 KSR; /* key scale rate :(shift down bit) */ + INT32 *AR; /* attack rate :&AR_TABLE[AR<<2] */ + INT32 *DR; /* decay rate :&DR_TALBE[DR<<2] */ + INT32 SL; /* sustin level :SL_TALBE[SL] */ + INT32 *RR; /* release rate :&DR_TABLE[RR<<2] */ + UINT8 ksl; /* keyscale level :(shift down bits) */ + UINT8 ksr; /* key scale rate :kcode>>KSR */ + UINT32 mul; /* multiple :ML_TABLE[ML] */ + UINT32 Cnt; /* frequency count : */ + UINT32 Incr; /* frequency step : */ + /* envelope generator state */ + UINT8 eg_typ; /* envelope type flag */ + UINT8 evm; /* envelope phase */ + INT32 evc; /* envelope counter */ + INT32 eve; /* envelope counter end point */ + INT32 evs; /* envelope counter step */ + INT32 evsa; /* envelope step for AR :AR[ksr] */ + INT32 evsd; /* envelope step for DR :DR[ksr] */ + INT32 evsr; /* envelope step for RR :RR[ksr] */ + /* LFO */ + UINT8 ams; /* ams flag */ + UINT8 vib; /* vibrate flag */ + /* wave selector */ + INT32 **wavetable; +}OPL_SLOT; + +/* ---------- OPL one of channel ---------- */ +typedef struct fm_opl_channel { + OPL_SLOT SLOT[2]; + UINT8 CON; /* connection type */ + UINT8 FB; /* feed back :(shift down bit) */ + INT32 *connect1; /* slot1 output pointer */ + INT32 *connect2; /* slot2 output pointer */ + INT32 op1_out[2]; /* slot1 output for selfeedback */ + /* phase generator state */ + UINT32 block_fnum; /* block+fnum : */ + UINT8 kcode; /* key code : KeyScaleCode */ + UINT32 fc; /* Freq. Increment base */ + UINT32 ksl_base; /* KeyScaleLevel Base step */ + UINT8 keyon; /* key on/off flag */ +} OPL_CH; + +/* OPL state */ +typedef struct fm_opl_f { + UINT8 type; /* chip type */ + int clock; /* master clock (Hz) */ + int rate; /* sampling rate (Hz) */ + double freqbase; /* frequency base */ + double TimerBase; /* Timer base time (==sampling time) */ + UINT8 address; /* address register */ + UINT32 mode; /* Reg.08 : CSM , notesel,etc. */ + + /* FM channel slots */ + OPL_CH *P_CH; /* pointer of CH */ + int max_ch; /* maximum channel */ + + /* time tables */ + INT32 AR_TABLE[75]; /* atttack rate tables */ + INT32 DR_TABLE[75]; /* decay rate tables */ + UINT32 FN_TABLE[1024]; /* fnumber -> increment counter */ + /* LFO */ + INT32 *ams_table; + INT32 *vib_table; + INT32 amsCnt; + INT32 amsIncr; + INT32 vibCnt; + INT32 vibIncr; + /* wave selector enable flag */ + UINT8 wavesel; + +} FM_OPL; + +/* ---------- Generic interface section ---------- */ +#define OPL_TYPE_YM3812 (OPL_TYPE_WAVESEL) + +FM_OPL *OPLCreate(int type, int clock, int rate); +void OPLDestroy(FM_OPL *OPL); + +void OPLResetChip(FM_OPL *OPL); +void OPLWrite(FM_OPL *OPL,UINT8 a,UINT8 v); + +/* YM3626/YM3812 local section */ +void YM3812UpdateOne(FM_OPL *OPL, UINT32 *buffer, int length); + +#endif diff --git a/mappers/mapinc.h b/mappers/mapinc.h new file mode 100644 index 0000000..ebe446c --- /dev/null +++ b/mappers/mapinc.h @@ -0,0 +1,12 @@ +#include "../types.h" +#include "../x6502.h" +#include "../fce.h" +#define INESPRIV +#include "../ines.h" +#include "../version.h" +#include "../memory.h" +#include "../sound.h" +#include "../svga.h" +#include "../state.h" +#include "../cart.h" +#include "mapshare.h" diff --git a/mappers/mapshare.h b/mappers/mapshare.h new file mode 100644 index 0000000..4853050 --- /dev/null +++ b/mappers/mapshare.h @@ -0,0 +1,5 @@ +void MMC3_hb(void); + +#define resetmode mapbyte1[0] +#define MMC3_cmd mapbyte1[1] + diff --git a/mappers/mmc2and4.c b/mappers/mmc2and4.c new file mode 100644 index 0000000..1688d57 --- /dev/null +++ b/mappers/mmc2and4.c @@ -0,0 +1,121 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + +#define MMC4reg mapbyte1 +#define latcha1 mapbyte2[0] +#define latcha2 mapbyte2[1] + + +static void FP_FASTAPASS(1) latchcheck(uint32 VAddr) +{ + uint8 l,h; + + h=VAddr>>8; + + if(h>=0x20 || ((h&0xF)!=0xF)) + return; + + l=VAddr&0xF0; + + if(h<0x10) + { + if(l==0xD0) + { + VROM_BANK4(0x0000,MMC4reg[0]); + latcha1=0xFD; + } + else if(l==0xE0) + { + VROM_BANK4(0x0000,MMC4reg[1]); + latcha1=0xFE; + } + } + else + { + if(l==0xD0) + { + VROM_BANK4(0x1000,MMC4reg[2]); + latcha2=0xFD; + } + else if(l==0xE0) + { + VROM_BANK4(0x1000,MMC4reg[3]); + latcha2=0xFE; + } + } +} + +DECLFW(Mapper9_write) // $Axxx +{ + ROM_BANK8(0x8000,V); +} + +DECLFW(Mapper10_write) +{ + ROM_BANK16(0x8000,V); +} + +DECLFW(Mapper9and10_write) +{ + switch(A&0xF000) + { + case 0xB000: + if (latcha1==0xFD) { VROM_BANK4(0x0000,V);} + MMC4reg[0]=V; + break; + case 0xC000: + if (latcha1==0xFE) {VROM_BANK4(0x0000,V);} + MMC4reg[1]=V; + break; + case 0xD000: + if (latcha2==0xFD) {VROM_BANK4(0x1000,V);} + MMC4reg[2]=V; + break; + case 0xE000: + if (latcha2==0xFE) {VROM_BANK4(0x1000,V);} + MMC4reg[3]=V; + break; + case 0xF000: + MIRROR_SET(V&1); + break; + } +} + +void Mapper9_init(void) +{ + latcha1=0xFE; + latcha2=0xFE; + ROM_BANK8(0xA000,~2); + ROM_BANK8(0x8000,0); + SetWriteHandler(0xA000,0xAFFF,Mapper9_write); + SetWriteHandler(0xB000,0xFFFF,Mapper9and10_write); + PPU_hook=latchcheck; +} + +void Mapper10_init(void) +{ + latcha1=latcha2=0xFE; + SetWriteHandler(0xA000,0xAFFF,Mapper10_write); + SetWriteHandler(0xB000,0xFFFF,Mapper9and10_write); + PPU_hook=latchcheck; +} + diff --git a/mappers/simple.c b/mappers/simple.c new file mode 100644 index 0000000..8f1ad74 --- /dev/null +++ b/mappers/simple.c @@ -0,0 +1,299 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 1998 BERO + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + +static uint8 latche; + +static DECLFW(Mapper2_write) +{ + latche=V; + ROM_BANK16(0x8000,V); +} + +void Mapper2_init(void) +{ + SetWriteHandler(0x8000,0xFFFF,Mapper2_write); + AddExState(&latche, 1, 0, "LATC"); +} + +static DECLFW(Mapper3_write) +{ + VROM_BANK8(V); + latche=V; +} + +void Mapper3_init(void) +{ + SetWriteHandler(0x8000,0xFFFF,Mapper3_write); + AddExState(&latche, 1, 0, "LATC"); +} + +DECLFW(Mapper7_write) +{ + ROM_BANK32(V&0xF); + onemir((V>>4)&1); + latche=V; +} + +void Mapper7_init(void) +{ + onemir(0); + SetWriteHandler(0x8000,0xFFFF,Mapper7_write); + AddExState(&latche, 1, 0, "LATC"); +} + +DECLFW(Mapper11_write) +{ + ROM_BANK32(V); + VROM_BANK8(V>>4); + latche=V; +} + +void Mapper11_init(void) +{ + ROM_BANK32(0); + SetWriteHandler(0x8000,0xFFFF,Mapper11_write); + AddExState(&latche, 1, 0, "LATC"); +} + +static DECLFW(Mapper13_write) +{ + setchr4r(0x10,0x1000,V&3); + setprg32(0x8000,(V>>4)&3); + latche=V; +} + +static void Mapper13_StateRestore(int version) +{ + setchr4r(0x10,0x0000,0); + setchr4r(0x10,0x1000,latche&3); + setprg32(0x8000,(latche>>4)&3); +} + +void Mapper13_init(void) +{ + SetWriteHandler(0x8000,0xFFFF,Mapper13_write); + GameStateRestore=Mapper13_StateRestore; + AddExState(&latche, 1, 0, "LATC"); + AddExState(MapperExRAM, 16384, 0, "CHRR"); + SetupCartCHRMapping(0x10, MapperExRAM, 16384, 1); + + latche=0; + Mapper13_StateRestore(VERSION_NUMERIC); +} + +DECLFW(Mapper34_write) +{ +switch(A) + { + case 0x7FFD:ROM_BANK32(V);break; + case 0x7FFE:VROM_BANK4(0x0000,V);break; + case 0x7fff:VROM_BANK4(0x1000,V);break; + } +if(A>=0x8000) + ROM_BANK32(V); +} + +void Mapper34_init(void) +{ + SetWriteHandler(0x7ffd,0xffff,Mapper34_write); +} + +DECLFW(Mapper66_write) +{ + VROM_BANK8(V&0xF); + ROM_BANK32((V>>4)); + latche=V; +} + +void Mapper66_init(void) +{ + ROM_BANK32(0); + SetWriteHandler(0x6000,0xffff,Mapper66_write); + AddExState(&latche, 1, 0, "LATC"); +} + +DECLFW(Mapper152_write) +{ + ROM_BANK16(0x8000,(V>>4)&0x7); + VROM_BANK8(V&0xF); + onemir((V>>7)&1); /* Saint Seiya...hmm. */ + latche=V; +} + +void Mapper152_init(void) +{ + onemir(0); + SetWriteHandler(0x6000,0xffff,Mapper152_write); + AddExState(&latche, 1, 0, "LATC"); +} + +static DECLFW(Mapper70_write) +{ + ROM_BANK16(0x8000,V>>4); + VROM_BANK8(V&0xF); + latche=V; +} + +void Mapper70_init(void) +{ + SetWriteHandler(0x6000,0xffff,Mapper70_write); + AddExState(&latche, 1, 0, "LATC"); +} +/* Should be two separate emulation functions for this "mapper". Sigh. URGE TO KILL RISING. */ +static DECLFW(Mapper78_write) +{ + //printf("$%04x:$%02x\n",A,V); + ROM_BANK16(0x8000,V&0x7); + VROM_BANK8(V>>4); + onemir((V>>3)&1); + latche=V; +} + +void Mapper78_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper78_write); + AddExState(&latche, 1, 0, "LATC"); +} + +DECLFW(Mapper87_write) +{ + VROM_BANK8(V>>1); + latche=V; +} + +void Mapper87_init(void) +{ + SetWriteHandler(0x6000,0xffff,Mapper87_write); + AddExState(&latche, 1, 0, "LATC"); +} + +DECLFW(Mapper93_write) +{ + ROM_BANK16(0x8000,V>>4); + MIRROR_SET(V&1); + latche=V; +} + +void Mapper93_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper93_write); + AddExState(&latche, 1, 0, "LATC"); +} + + +DECLFW(Mapper94_write) +{ + ROM_BANK16(0x8000,V>>2); + latche=V; +} + +void Mapper94_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper94_write); + AddExState(&latche, 1, 0, "LATC"); +} + +/* I might want to add some code to the mapper 96 PPU hook function + to not change CHR banks if the attribute table is being accessed, + if I make emulation a little more accurate in the future. +*/ + +static uint8 M96LA; +static DECLFW(Mapper96_write) +{ + latche=V; + setprg32(0x8000,V&3); + setchr4r(0x10,0x0000,(latche&4)|M96LA); + setchr4r(0x10,0x1000,(latche&4)|3); +} + +static void FP_FASTAPASS(1) M96Hook(uint32 A) +{ + if(A<0x2000) + return; + M96LA=(A>>8)&3; + setchr4r(0x10,0x0000,(latche&4)|M96LA); +} + +static void M96Sync() +{ + setprg32(0x8000,latche&3); + setchr4r(0x10,0x0000,(latche&4)|M96LA); + setchr4r(0x10,0x1000,(latche&4)|3); +} + +void Mapper96_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper96_write); + PPU_hook=M96Hook; + AddExState(&latche, 1, 0, "LATC"); + AddExState(&M96LA, 1, 0, "LAVA"); + SetupCartCHRMapping(0x10, MapperExRAM, 32768, 1); + latche=M96LA=0; + M96Sync(); + GameStateRestore=M96Sync; +} + +static DECLFW(Mapper140_write) +{ + VROM_BANK8(V&0xF); + ROM_BANK32((V>>4)&0xF); +} + +void Mapper140_init(void) +{ + ROM_BANK32(0); + SetWriteHandler(0x6000,0x7FFF,Mapper140_write); +} + +static void M185Sync() +{ + int x; + + if(!(mapbyte1[0]&3)) +// if(!(mapbyte1[0]==0x21)) + { + for(x=0;x<8;x++) + setchr1r(0x10,x<<10,0); + } + else + setchr8(0); +} + +static DECLFW(Mapper185_write) +{ + mapbyte1[0]=V; + M185Sync(); + // printf("Wr: $%04x:$%02x\n",A,V); +} + +void Mapper185_init(void) +{ + memset(MapperExRAM,0xFF,1024); + MapStateRestore=M185Sync; + mapbyte1[0]=0; + M185Sync(); + + SetupCartCHRMapping(0x10,MapperExRAM,1024,0); + SetWriteHandler(0x8000,0xFFFF,Mapper185_write); +} diff --git a/mappers/vrc7snd.c b/mappers/vrc7snd.c new file mode 100644 index 0000000..355143f --- /dev/null +++ b/mappers/vrc7snd.c @@ -0,0 +1,190 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include "mapinc.h" +#include "fmopl.h" + +static void VRC7_LoadInstrument(uint8 Chan); +void vrc7translate(uint8 Reg,uint8 V); + +FM_OPL *fmob=0; +uint8 VRC7Chan[3][6]; +static void InitOPL(void); + +void OPL2_setreg(uint8 A, uint8 V) +{ + if(fmob) + OPLWrite(fmob,A,V); +} + + +void LoadOPL(void) +{ + int x; + int y; + + for(x=y=0;x<0x40;x++) + y|=MapperExRAM[x]; + if(y) + { + InitOPL(); + for(x=0;x<6;x++) + { + VRC7_LoadInstrument(x); + vrc7translate(0x10+x,VRC7Chan[0][x]); + } + } +} + +static int dwave=0; + +void VRC7Update(void) +{ + int32 z,a; + + z=((timestamp<<16)/soundtsinc)>>4; + a=z-dwave; + + if(a && fmob) + YM3812UpdateOne(fmob, &Wave[dwave], a); + dwave+=a; +} + +void UpdateOPL(int Count) +{ + int32 z,a; + + z=((timestamp<<16)/soundtsinc)>>4; + a=z-dwave; + + if(fmob && a) + YM3812UpdateOne(fmob, &Wave[dwave], a); + + dwave=0; +} + +void KillOPL(void) +{ + if(fmob) OPLDestroy(fmob); + fmob=0; +} + +static void InitOPL(void) +{ + int x; + + if(!fmob) + { + if(!( fmob=OPLCreate(OPL_TYPE_WAVESEL,1789772*2,FSettings.SndRate))) + return; + } + GameExpSound.Kill=KillOPL; + OPLResetChip(fmob); + + for(x=0x1;x<0xF6;x++) + OPL2_setreg(x,0); + OPL2_setreg(0xBD,0xC0); + OPL2_setreg(1,0x20); /* Enable waveform type manipulation */ +} + +/* This following code is in the public domain, but the author, Quietust(see */ +/* the "AUTHORS" file, would appreciate credit to go to him if this code */ +/* is used. */ + +uint8 VRC7Instrument[16][8] = { + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, /* Custom instrument. */ + {0x03,0x01,0x14,0x80,0xC2,0x90,0x43,0x14}, /* Currently working on this one */ + {0x13,0x41,0x10,0x0B,0xFF,0xF2,0x32,0xD6}, + {0x01,0x01,0x10,0x08,0xF0,0xF4,0x00,0x04}, /* 90% perfect */ + {0x21,0x41,0x1B,0x08,0x66,0x80,0x30,0x85}, + {0x22,0x21,0x20,0x03,0x75,0x70,0x24,0x14}, + {0x02,0x01,0x06,0x00,0xF0,0xF2,0x03,0x95}, /* Do not touch! 98% perfect! */ + {0x21,0x41,0x18,0x10,0x93,0xE0,0x21,0x15}, + {0x01,0x22,0x13,0x00,0xF0,0x82,0x00,0x15}, + {0x05,0x01,0x22,0x00,0x60,0xE3,0xA0,0xF5}, /* 90% perfect */ + {0x85,0x01,0x20,0x00,0xD7,0xA2,0x22,0xF5}, /* 90% perfect */ + {0x07,0x81,0x2B,0x05,0xF4,0xF2,0x14,0xF4}, /* 95% perfect */ + {0x21,0x41,0x20,0x18,0xF3,0x80,0x13,0x95}, + {0x01,0x02,0x20,0x00,0xF9,0x92,0x41,0x75}, /* Do not touch! 98% perfect! */ + {0x21,0x62,0x0E,0x00,0x84,0x85,0x45,0x15}, /* 90% perfect */ + {0x21,0x62,0x0E,0x00,0xA1,0xA0,0x34,0x16} /* Do not touch! 98% perfect! */ +}; + +static uint8 InstTrans[6] = {0x00,0x01,0x02,0x08,0x09,0x0A}; + +static void VRC7_LoadInstrument(uint8 Chan) +{ + uint8 *i; + uint8 x = InstTrans[Chan]; + uint8 y = (VRC7Chan[2][Chan] >> 4) & 0xF; + + i=VRC7Instrument[y]; + + OPL2_setreg((0x20+x),i[0]); + OPL2_setreg((0x23+x),i[1]); + OPL2_setreg((0x40+x),i[2]); + OPL2_setreg((0x43+x),((i[3] & 0xC0) + | ((VRC7Chan[2][Chan] << 2) & 0x3C))); // quiet + OPL2_setreg(0xe0+x,(i[3] >> 3) & 0x01); + OPL2_setreg(0xe3+x,(i[3] >> 4) & 0x01); + OPL2_setreg(0xC0+Chan,(i[3] << 1) & 0x0E); + OPL2_setreg(0x60+x,i[4]); + OPL2_setreg(0x63+x,i[5]); + OPL2_setreg(0x80+x,i[6]); + OPL2_setreg(0x83+x,i[7]); +} + +void vrc7translate(uint8 Reg,uint8 V) +{ + uint8 x = Reg & 0x0F, y; + if(!fmob) InitOPL(); + + MapperExRAM[Reg]=V; + + VRC7Update(); + switch ((Reg & 0xF0) >> 4) + { + case 0: + if (x & 0x08) break; + VRC7Instrument[0][x] = V; + for (y = 0; y < 6; y++) + if (!(VRC7Chan[2][y]&0xF0)) + VRC7_LoadInstrument(y); + break; + case 1: + if(x>5) break; + VRC7Chan[0][x] = V; + OPL2_setreg(0xA0 + x,(VRC7Chan[0][x] << 1) & 0xFE); + OPL2_setreg(0xB0 + x,((VRC7Chan[0][x] >> 7) & 0x01) | ((VRC7Chan[1][x] << 1) & 0x3E)); + break; + case 2: + if(x>5) break; + VRC7Chan[1][x] = V; + OPL2_setreg(0xB0 + x,(((VRC7Chan[0][x] >> 7) & 0x01) | ((VRC7Chan[1][x] << 1) & 0x3E))); + break; + case 3: + if(x>5) break; + VRC7Chan[2][x] = V; + VRC7_LoadInstrument(x); + break; + } +} diff --git a/mbshare/Makefile b/mbshare/Makefile new file mode 100644 index 0000000..3508da1 --- /dev/null +++ b/mbshare/Makefile @@ -0,0 +1,5 @@ +MUSOBJS = mbshare/mmc5.o mbshare/mmc3.o mbshare/mmc1.o + +mbshare/mmc1.o: mbshare/mmc1.c +mbshare/mmc3.o: mbshare/mmc3.c +mbshare/mmc5.o: mbshare/mmc5.c diff --git a/mbshare/mapinc.h b/mbshare/mapinc.h new file mode 100644 index 0000000..9702ec2 --- /dev/null +++ b/mbshare/mapinc.h @@ -0,0 +1,14 @@ +#include "../types.h" +#include "../x6502.h" +#include "../fce.h" +#define INESPRIV +#include "../ines.h" +#include "../version.h" +#include "../memory.h" +#include "../sound.h" +#include "../svga.h" +#include "../state.h" +#define UNIFPRIV +#include "../unif.h" +#include "../cart.h" +#include "../cheat.h" diff --git a/mbshare/mmc1.c b/mbshare/mmc1.c new file mode 100644 index 0000000..8b238d7 --- /dev/null +++ b/mbshare/mmc1.c @@ -0,0 +1,353 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 1998 BERO + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mapinc.h" + +#define MMC1_reg mapbyte1 +#define MMC1_buf mapbyte2[0] +#define MMC1_sft mapbyte3[0] + +static int mmc1opts; + + +uint8 MMC1WRAMsize; /* For use in iNES.c */ + +static DECLFW(MBWRAM) +{ + if(!(MMC1_reg[3]&0x10)) + Page[A>>11][A]=V; // WRAM is enabled. +} + +static DECLFR(MAWRAM) +{ + if(MMC1_reg[3]&0x10) + return X.DB; // WRAM is disabled + return(Page[A>>11][A]); +} + +static void MMC1CHR(void) +{ + if(mmc1opts&4) + { + if(MMC1_reg[0]&0x10) + setprg8r(0x10,0x6000,(MMC1_reg[1]>>4)&1); + else + setprg8r(0x10,0x6000,(MMC1_reg[1]>>3)&1); + } + + if(MMC1_reg[0]&0x10) + { + setchr4(0x0000,MMC1_reg[1]); + setchr4(0x1000,MMC1_reg[2]); + } + else + setchr8(MMC1_reg[1]>>1); +} + +static void MMC1PRG(void) +{ + uint8 offs; + + offs=MMC1_reg[1]&0x10; + switch(MMC1_reg[0]&0xC) + { + case 0xC: setprg16(0x8000,(MMC1_reg[3]+offs)); + setprg16(0xC000,0xF+offs); + break; + case 0x8: setprg16(0xC000,(MMC1_reg[3]+offs)); + setprg16(0x8000,offs); + break; + case 0x0: + case 0x4: + setprg16(0x8000,((MMC1_reg[3]&~1)+offs)); + setprg16(0xc000,((MMC1_reg[3]&~1)+offs+1)); + break; + } +} +static void MMC1MIRROR(void) +{ + switch(MMC1_reg[0]&3) + { + case 2: setmirror(MI_V);break; + case 3: setmirror(MI_H);break; + case 0: setmirror(MI_0);break; + case 1: setmirror(MI_1);break; + } +} + +static uint64 lreset; + +static DECLFW(MMC1_write) +{ + int n=(A>>13)-4; + //FCEU_DispMessage("%016x",timestampbase+timestamp); + //printf("$%04x:$%02x, $%04x\n",A,V,X.PC); + //DumpMem("out",0xe000,0xffff); + + /* The MMC1 is busy so ignore the write. */ + /* As of version FCE Ultra 0.81, the timestamp is only + increased before each instruction is executed(in other words + precision isn't that great), but this should still work to + deal with 2 writes in a row from a single RMW instruction. + */ + if( (timestampbase+timestamp)<(lreset+2)) + return; + if (V&0x80) + { + MMC1_reg[0]|=0xC; + MMC1_sft=MMC1_buf=0; + MMC1PRG(); + lreset=timestampbase+timestamp; + return; + } + + MMC1_buf|=(V&1)<<(MMC1_sft++); + + if (MMC1_sft==5) { + MMC1_reg[n]=MMC1_buf; + MMC1_sft = MMC1_buf=0; + + switch(n){ + case 0: + MMC1MIRROR(); + MMC1CHR(); + MMC1PRG(); + break; + case 1: + MMC1CHR(); + MMC1PRG(); + break; + case 2: + MMC1CHR(); + break; + case 3: + MMC1PRG(); + break; + } + } +} + +static void MMC1_Restore(int version) +{ + MMC1MIRROR(); + MMC1CHR(); + MMC1PRG(); +} + +static void MMC1CMReset(void) +{ + int i; + + for(i=0;i<4;i++) + MMC1_reg[i]=0; + MMC1_sft = MMC1_buf =0; + MMC1_reg[0]=0x1F; + + MMC1_reg[1]=0; + MMC1_reg[2]=0; // Should this be something other than 0? + MMC1_reg[3]=0; + + MMC1MIRROR(); + MMC1CHR(); + MMC1PRG(); +} + +void DetectMMC1WRAMSize(void) +{ + switch(iNESGameCRC32) + { + default:MMC1WRAMsize=1;break; + case 0xc6182024: /* Romance of the 3 Kingdoms */ + case 0x2225c20f: /* Genghis Khan */ + case 0x4642dda6: /* Nobunaga's Ambition */ + case 0x29449ba9: /* "" "" (J) */ + case 0x2b11e0b0: /* "" "" (J) */ + MMC1WRAMsize=2; + break; + } +} + +void Mapper1_init(void) +{ + lreset=0; + mmc1opts=0; + MMC1CMReset(); + SetWriteHandler(0x8000,0xFFFF,MMC1_write); + MapStateRestore=MMC1_Restore; + AddExState(&lreset, 8, 1, "LRST"); + + if(!VROM_size) + SetupCartCHRMapping(0, CHRRAM, 8192, 1); + + if(MMC1WRAMsize==2) + mmc1opts|=4; + + SetupCartPRGMapping(0x10,WRAM,MMC1WRAMsize*8192,1); + SetReadHandler(0x6000,0x7FFF,MAWRAM); + SetWriteHandler(0x6000,0x7FFF,MBWRAM); + setprg8r(0x10,0x6000,0); +} + +static void GenMMC1Close(void) +{ + UNIFOpenWRAM(UOW_WR,0,0); + UNIFWriteWRAM(WRAM+((mmc1opts&4)?8192:0),8192); + UNIFCloseWRAM(); +} + + +static void GenMMC1Power(void) +{ + lreset=0; + if(mmc1opts&1) + { + FCEU_CheatAddRAM(8,0x6000,WRAM); + if(mmc1opts&4) + FCEU_dwmemset(WRAM,0,8192) + else if(!(mmc1opts&2)) + FCEU_dwmemset(WRAM,0,8192); + } + SetWriteHandler(0x8000,0xFFFF,MMC1_write); + SetReadHandler(0x8000,0xFFFF,CartBR); + + if(mmc1opts&1) + { + SetReadHandler(0x6000,0x7FFF,MAWRAM); + SetWriteHandler(0x6000,0x7FFF,MBWRAM); + setprg8r(0x10,0x6000,0); + } + + MMC1CMReset(); +} + +static void GenMMC1Init(int prg, int chr, int wram, int battery) +{ + mmc1opts=0; + PRGmask16[0]&=(prg>>14)-1; + CHRmask4[0]&=(chr>>12)-1; + CHRmask8[0]&=(chr>>13)-1; + + if(wram) + { + mmc1opts|=1; + if(wram>8) mmc1opts|=4; + SetupCartPRGMapping(0x10,WRAM,wram*1024,1); + AddExState(WRAM, wram*1024, 0, "WRAM"); + } + + if(battery && UNIFbattery) + { + mmc1opts|=2; + BoardClose=GenMMC1Close; + + UNIFOpenWRAM(UOW_RD,0,0); + UNIFReadWRAM(WRAM+((mmc1opts&4)?8192:0),8192); + UNIFCloseWRAM(); + } + + if(!chr) + { + CHRmask4[0]=1; + SetupCartCHRMapping(0, CHRRAM, 8192, 1); + AddExState(CHRRAM, 8192, 0, "CHRR"); + } + AddExState(mapbyte1, 32, 0, "MPBY"); + BoardPower=GenMMC1Power; + + GameStateRestore=MMC1_Restore; + AddExState(&lreset, 8, 1, "LRST"); +} + +//static void GenMMC1Init(int prg, int chr, int wram, int battery) +void SAROM_Init(void) +{ + GenMMC1Init(128, 64, 8, 1); +} + +void SBROM_Init(void) +{ + GenMMC1Init(128, 64, 0, 0); +} + +void SCROM_Init(void) +{ + GenMMC1Init(128, 128, 0, 0); +} + +void SEROM_Init(void) +{ + GenMMC1Init(32, 64, 0, 0); +} + +void SGROM_Init(void) +{ + GenMMC1Init(256, 0, 0, 0); +} + +void SKROM_Init(void) +{ + GenMMC1Init(256, 64, 8, 1); +} + +void SLROM_Init(void) +{ + GenMMC1Init(256, 128, 0, 0); +} + +void SL1ROM_Init(void) +{ + GenMMC1Init(128, 128, 0, 0); +} + +/* Begin unknown - may be wrong - perhaps they use different MMC1s from the + similarly functioning boards? +*/ + +void SL2ROM_Init(void) +{ + GenMMC1Init(256, 256, 0, 0); +} + +void SFROM_Init(void) +{ + GenMMC1Init(256, 256, 0, 0); +} + +void SHROM_Init(void) +{ + GenMMC1Init(256, 256, 0, 0); +} + +/* End unknown */ +/* */ +/* */ + +void SNROM_Init(void) +{ + GenMMC1Init(256, 0, 8, 1); +} + +void SOROM_Init(void) +{ + GenMMC1Init(256, 0, 16, 1); +} + + diff --git a/mbshare/mmc3.c b/mbshare/mmc3.c new file mode 100644 index 0000000..e1e55c8 --- /dev/null +++ b/mbshare/mmc3.c @@ -0,0 +1,649 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 1998 BERO + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Code for emulating iNES mappers 4, 118,119 */ + +#include "mapinc.h" + +#define resetmode mapbyte1[0] +#define MMC3_cmd mapbyte1[1] +#define A000B mapbyte1[2] +#define A001B mapbyte1[3] +#define DRegBuf mapbyte4 + +#define PPUCHRBus mapbyte2[0] +#define TKSMIR mapbyte3 +#define PIRREGS mapbyte2 + +static void (*pwrap)(uint32 A, uint8 V); +static void (*cwrap)(uint32 A, uint8 V); +static void (*mwrap)(uint8 V); + +static int mmc3opts=0; + +static INLINE void FixMMC3PRG(int V); +static INLINE void FixMMC3CHR(int V); + +static int latched; + +static DECLFW(MMC3_IRQWrite) +{ + //printf("$%04x:$%02x, %d\n",A,V,scanline); + switch(A&0xE001) + { + case 0xc000:IRQLatch=V; + latched=1; + if(resetmode) + { + IRQCount=V; + latched=0; + //resetmode=0; + } + break; + case 0xc001:IRQCount=IRQLatch; + break; + case 0xE000:IRQa=0; + X6502_IRQEnd(FCEU_IQEXT); + resetmode=1; + break; + case 0xE001:IRQa=1; + if(latched) + IRQCount=IRQLatch; + break; + } +} + +static INLINE void FixMMC3PRG(int V) +{ + if(V&0x40) + { + pwrap(0xC000,DRegBuf[6]); + pwrap(0x8000,~1); + } + else + { + pwrap(0x8000,DRegBuf[6]); + pwrap(0xC000,~1); + } + pwrap(0xA000,DRegBuf[7]); + pwrap(0xE000,~0); +} + +static INLINE void FixMMC3CHR(int V) +{ + int cbase=(V&0x80)<<5; + cwrap((cbase^0x000),DRegBuf[0]&(~1)); + cwrap((cbase^0x400),DRegBuf[0]|1); + cwrap((cbase^0x800),DRegBuf[1]&(~1)); + cwrap((cbase^0xC00),DRegBuf[1]|1); + + cwrap(cbase^0x1000,DRegBuf[2]); + cwrap(cbase^0x1400,DRegBuf[3]); + cwrap(cbase^0x1800,DRegBuf[4]); + cwrap(cbase^0x1c00,DRegBuf[5]); +} + +static void MMC3RegReset(void) +{ + IRQCount=IRQLatch=IRQa=MMC3_cmd=0; + + DRegBuf[0]=0; + DRegBuf[1]=2; + DRegBuf[2]=4; + DRegBuf[3]=5; + DRegBuf[4]=6; + DRegBuf[5]=7; + DRegBuf[6]=0; + DRegBuf[7]=1; + + FixMMC3PRG(0); + FixMMC3CHR(0); +} + +static DECLFW(Mapper4_write) +{ + switch(A&0xE001) + { + case 0x8000: + if((V&0x40) != (MMC3_cmd&0x40)) + FixMMC3PRG(V); + if((V&0x80) != (MMC3_cmd&0x80)) + FixMMC3CHR(V); + MMC3_cmd = V; + break; + + case 0x8001: + { + int cbase=(MMC3_cmd&0x80)<<5; + DRegBuf[MMC3_cmd&0x7]=V; + switch(MMC3_cmd&0x07) + { + case 0: cwrap((cbase^0x000),V&(~1)); + cwrap((cbase^0x400),V|1); + break; + case 1: cwrap((cbase^0x800),V&(~1)); + cwrap((cbase^0xC00),V|1); + break; + case 2: cwrap(cbase^0x1000,V); break; + case 3: cwrap(cbase^0x1400,V); break; + case 4: cwrap(cbase^0x1800,V); break; + case 5: cwrap(cbase^0x1C00,V); break; + case 6: if (MMC3_cmd&0x40) pwrap(0xC000,V); + else pwrap(0x8000,V); + break; + case 7: pwrap(0xA000,V); + break; + } + } + break; + + case 0xA000: + if(mwrap) mwrap(V&1); + break; + case 0xA001: + A001B=V; + break; + } +} + +static void MMC3_hb(void) +{ + resetmode=0; + if(IRQCount>=0) + { + IRQCount--; + if(IRQCount<0) + { + //printf("IRQ: %d\n",scanline); + if(IRQa) + X6502_IRQBegin(FCEU_IQEXT); + } + } +} +static int isines; + +static void genmmc3restore(int version) +{ + if(version>=56) + { + mwrap(A000B&1); + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); + } + else if(isines) + iNESStateRestore(version); +} + +static void GENCWRAP(uint32 A, uint8 V) +{ + setchr1(A,V); +} + +static void GENPWRAP(uint32 A, uint8 V) +{ + setprg8(A,V&0x3F); +} + +static void GENMWRAP(uint8 V) +{ + A000B=V; + setmirror(V^1); +} + +static void GENNOMWRAP(uint8 V) +{ + A000B=V; +} + +static void genmmc3ii(void (*PW)(uint32 A, uint8 V), + void (*CW)(uint32 A, uint8 V), + void (*MW)(uint8 V)) +{ + pwrap=GENPWRAP; + cwrap=GENCWRAP; + mwrap=GENMWRAP; + if(PW) pwrap=PW; + if(CW) cwrap=CW; + if(MW) mwrap=MW; + A000B=(Mirroring&1)^1; // For hard-wired mirroring on some MMC3 games. + // iNES format needs to die or be extended... + mmc3opts=0; + SetWriteHandler(0x8000,0xBFFF,Mapper4_write); + SetWriteHandler(0xC000,0xFFFF,MMC3_IRQWrite); + + GameHBIRQHook=MMC3_hb; + GameStateRestore=genmmc3restore; + if(!VROM_size) + SetupCartCHRMapping(0, CHRRAM, 8192, 1); + isines=1; + MMC3RegReset(); + MapperReset=MMC3RegReset; +} + +void Mapper4_init(void) +{ + genmmc3ii(0,0,0); +} + +static void M47PW(uint32 A, uint8 V) +{ + V&=0xF; + V|=PIRREGS[0]<<4; + setprg8(A,V); +} + +static void M47CW(uint32 A, uint8 V) +{ + uint32 NV=V; + NV&=0x7F; + NV|=PIRREGS[0]<<7; + setchr1(A,NV); +} + +static DECLFW(M47Write) +{ + PIRREGS[0]=V&1; + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); +} + +void Mapper47_init(void) +{ + genmmc3ii(M47PW,M47CW,0); + SetWriteHandler(0x6000,0x7FFF,M47Write); + SetReadHandler(0x6000,0x7FFF,0); +} + +static void M44PW(uint32 A, uint8 V) +{ + uint32 NV=V; + if(PIRREGS[0]>=6) NV&=0x1F; + else NV&=0x0F; + NV|=PIRREGS[0]<<4; + setprg8(A,NV); +} +static void M44CW(uint32 A, uint8 V) +{ + uint32 NV=V; + if(PIRREGS[0]<6) NV&=0x7F; + NV|=PIRREGS[0]<<7; + setchr1(A,NV); +} + +static DECLFW(Mapper44_write) +{ + if(A&1) + { + PIRREGS[0]=V&7; + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); + } + else + Mapper4_write(A,V); +} + +void Mapper44_init(void) +{ + genmmc3ii(M44PW,M44CW,0); + SetWriteHandler(0xA000,0xBFFF,Mapper44_write); +} + +static void M52PW(uint32 A, uint8 V) +{ + uint32 NV=V; + NV&=0x1F^((PIRREGS[0]&8)<<1); + NV|=((PIRREGS[0]&6)|((PIRREGS[0]>>3)&PIRREGS[0]&1))<<4; + setprg8(A,NV); +} + +static void M52CW(uint32 A, uint8 V) +{ + uint32 NV=V; + NV&=0xFF^((PIRREGS[0]&0x40)<<1); + NV|=(((PIRREGS[0]>>3)&4)|((PIRREGS[0]>>1)&2)|((PIRREGS[0]>>6)&(PIRREGS[0]>>4)&1))<<7; + setchr1(A,NV); +} + +static DECLFW(Mapper52_write) +{ + if(PIRREGS[1]) + { + (WRAM-0x6000)[A]=V; + return; + } + PIRREGS[1]=1; + PIRREGS[0]=V; + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); +} + +static void M52Reset(void) +{ + PIRREGS[0]=PIRREGS[1]=0; + MMC3RegReset(); +} + +void Mapper52_init(void) +{ + genmmc3ii(M52PW,M52CW,0); + SetWriteHandler(0x6000,0x7FFF,Mapper52_write); + MapperReset=M52Reset; +} + +static void M45CW(uint32 A, uint8 V) +{ + uint32 NV=V; + if(PIRREGS[2]&8) + NV&=(1<<( (PIRREGS[2]&7)+1 ))-1; + else + NV&=0; + NV|=PIRREGS[0]|((PIRREGS[2]&0x10)<<4); + setchr1(A,NV); +} + +static void M45PW(uint32 A, uint8 V) +{ + V&=(PIRREGS[3]&0x3F)^0x3F; + V|=PIRREGS[1]; + setprg8(A,V); +} + +static DECLFW(Mapper45_write) +{ + if(PIRREGS[3]&0x40) return; + PIRREGS[PIRREGS[4]]=V; + PIRREGS[4]=(PIRREGS[4]+1)&3; + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); +} + +static void M45Reset(void) +{ + FCEU_dwmemset(PIRREGS,0,5); + MMC3RegReset(); +} + +void Mapper45_init(void) +{ + genmmc3ii(M45PW,M45CW,0); + SetWriteHandler(0x6000,0x7FFF,Mapper45_write); + SetReadHandler(0x6000,0x7FFF,0); + MapperReset=M45Reset; +} +static void M49PW(uint32 A, uint8 V) +{ + if(PIRREGS[0]&1) + { + V&=0xF; + V|=(PIRREGS[0]&0xC0)>>2; + setprg8(A,V); + } + else + setprg32(0x8000,(PIRREGS[0]>>4)&3); +} + +static void M49CW(uint32 A, uint8 V) +{ + uint32 NV=V; + NV&=0x7F; + NV|=(PIRREGS[0]&0xC0)<<1; + setchr1(A,NV); +} + +static DECLFW(M49Write) +{ + //printf("$%04x:$%02x\n",A,V); + if(A001B&0x80) + { + PIRREGS[0]=V; + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); + } +} + +static void M49Reset(void) +{ + PIRREGS[0]=0; + MMC3RegReset(); +} + +void Mapper49_init(void) +{ + genmmc3ii(M49PW,M49CW,0); + SetWriteHandler(0x6000,0x7FFF,M49Write); + SetReadHandler(0x6000,0x7FFF,0); + MapperReset=M49Reset; +} + +static DECLFW(Mapper250_write) +{ + Mapper4_write((A&0xE000)|((A&0x400)>>10),A&0xFF); +} + +static DECLFW(M250_IRQWrite) +{ + MMC3_IRQWrite((A&0xE000)|((A&0x400)>>10),A&0xFF); +} + +void Mapper250_init(void) +{ + genmmc3ii(0,0,0); + SetWriteHandler(0x8000,0xBFFF,Mapper250_write); + SetWriteHandler(0xC000,0xFFFF,M250_IRQWrite); +} + +static void FP_FASTAPASS(1) TKSPPU(uint32 A) +{ + //static uint8 z; + //if(A>=0x2000 || type<0) return; + //if(type<0) return; + A&=0x1FFF; + //if(scanline>=140 && scanline<=200) {setmirror(MI_1);return;} + //if(scanline>=140 && scanline<=200) + // if(scanline>=190 && scanline<=200) {setmirror(MI_1);return;} + // setmirror(MI_1); + //printf("$%04x\n",A); + + A>>=10; + PPUCHRBus=A; + setmirror(MI_0+TKSMIR[A]); +} + +static void TKSWRAP(uint32 A, uint8 V) +{ + TKSMIR[A>>10]=V>>7; + setchr1(A,V&0x7F); + if(PPUCHRBus==(A>>10)) + setmirror(MI_0+(V>>7)); +} + +void Mapper118_init(void) +{ + genmmc3ii(0,TKSWRAP,GENNOMWRAP); + PPU_hook=TKSPPU; +} + +static void TQWRAP(uint32 A, uint8 V) +{ + setchr1r((V&0x40)>>2,A,V&0x3F); +} + +void Mapper119_init(void) +{ + genmmc3ii(0,TQWRAP,0); + SetupCartCHRMapping(0x10, CHRRAM, 8192, 1); +} + +static int wrams; + +static void GenMMC3Close(void) +{ + UNIFOpenWRAM(UOW_WR,0,1); + UNIFWriteWRAM(WRAM,wrams); + UNIFCloseWRAM(); +} + +static DECLFW(MBWRAM) +{ + (WRAM-0x6000)[A]=V; +} + +static DECLFR(MAWRAM) +{ + return((WRAM-0x6000)[A]); +} + +static DECLFW(MBWRAMMMC6) +{ + WRAM[A&0x3ff]=V; +} + +static DECLFR(MAWRAMMMC6) +{ + return(WRAM[A&0x3ff]); +} + +static void GenMMC3Power(void) +{ + SetWriteHandler(0x8000,0xBFFF,Mapper4_write); + SetReadHandler(0x8000,0xFFFF,CartBR); + SetWriteHandler(0xC000,0xFFFF,MMC3_IRQWrite); + + if(mmc3opts&1) + { + if(wrams==1024) + { + FCEU_CheatAddRAM(1,0x7000,WRAM); + SetReadHandler(0x7000,0x7FFF,MAWRAMMMC6); + SetWriteHandler(0x7000,0x7FFF,MBWRAMMMC6); + } + else + { + FCEU_CheatAddRAM(wrams/1024,0x6000,WRAM); + SetReadHandler(0x6000,0x6000+wrams-1,MAWRAM); + SetWriteHandler(0x6000,0x6000+wrams-1,MBWRAM); + } + if(!(mmc3opts&2)) + FCEU_dwmemset(WRAM,0,wrams); + } + MMC3RegReset(); +} + +void GenMMC3_Init(int prg, int chr, int wram, int battery) +{ + pwrap=GENPWRAP; + cwrap=GENCWRAP; + mwrap=GENMWRAP; + + wrams=wram*1024; + + PRGmask8[0]&=(prg>>13)-1; + CHRmask1[0]&=(chr>>10)-1; + CHRmask2[0]&=(chr>>11)-1; + + if(wram) + { + mmc3opts|=1; + AddExState(WRAM, wram*1024, 0, "WRAM"); + } + + if(battery) + { + mmc3opts|=2; + BoardClose=GenMMC3Close; + + UNIFOpenWRAM(UOW_RD,0,1); + UNIFReadWRAM(WRAM,wram*1024); + UNIFCloseWRAM(); + } + + if(!chr) + { + CHRmask1[0]=7; + CHRmask2[0]=3; + SetupCartCHRMapping(0, CHRRAM, 8192, 1); + AddExState(CHRRAM, 8192, 0, "CHRR"); + } + AddExState(mapbyte1, 32, 0, "MPBY"); + AddExState(&IRQa, 1, 0, "IRQA"); + AddExState(&IRQCount, 4, 1, "IRQC"); + AddExState(&IRQLatch, 4, 1, "IQL1"); + + BoardPower=GenMMC3Power; + BoardReset=MMC3RegReset; + + GameHBIRQHook=MMC3_hb; + GameStateRestore=genmmc3restore; + isines=0; +} + +// void GenMMC3_Init(int prg, int chr, int wram, int battery) + +void TFROM_Init(void) +{ + GenMMC3_Init(512, 64, 0, 0); +} + +void TGROM_Init(void) +{ + GenMMC3_Init(512, 0, 0, 0); +} + +void TKROM_Init(void) +{ + GenMMC3_Init(512, 256, 8, 1); +} + +void TLROM_Init(void) +{ + GenMMC3_Init(512, 256, 0, 0); +} + +void TSROM_Init(void) +{ + GenMMC3_Init(512, 256, 8, 0); +} + +void TLSROM_Init(void) +{ + GenMMC3_Init(512, 256, 8, 0); + cwrap=TKSWRAP; + mwrap=GENNOMWRAP; +} + +void TKSROM_Init(void) +{ + GenMMC3_Init(512, 256, 8, 1); + cwrap=TKSWRAP; + mwrap=GENNOMWRAP; +} + +void TQROM_Init(void) +{ + GenMMC3_Init(512, 64, 0, 0); + cwrap=TQWRAP; +} + +/* MMC6 board */ +void HKROM_Init(void) +{ + GenMMC3_Init(512, 512, 1, 1); +} diff --git a/mbshare/mmc5.c b/mbshare/mmc5.c new file mode 100644 index 0000000..bb3e8f1 --- /dev/null +++ b/mbshare/mmc5.c @@ -0,0 +1,758 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* None of this code should use any of the iNES bank switching wrappers. */ + +#include "mapinc.h" + +void MMC5Sound(int Count); +void Do5SQ(int P); + +static INLINE void MMC5SPRVROM_BANK1(uint32 A,uint32 V) +{ + if(CHRptr[0]) + { + V&=CHRmask1[0]; + MMC5SPRVPage[(A)>>10]=&CHRptr[0][(V)<<10]-(A); + } +} + +static INLINE void MMC5BGVROM_BANK1(uint32 A,uint32 V) {if(CHRptr[0]){V&=CHRmask1[0];MMC5BGVPage[(A)>>10]=&CHRptr[0][(V)<<10]-(A);}} + +static INLINE void MMC5SPRVROM_BANK2(uint32 A,uint32 V) {if(CHRptr[0]){V&=CHRmask2[0];MMC5SPRVPage[(A)>>10]=MMC5SPRVPage[((A)>>10)+1]=&CHRptr[0][(V)<<11]-(A);}} +static INLINE void MMC5BGVROM_BANK2(uint32 A,uint32 V) {if(CHRptr[0]){V&=CHRmask2[0];MMC5BGVPage[(A)>>10]=MMC5BGVPage[((A)>>10)+1]=&CHRptr[0][(V)<<11]-(A);}} + +static INLINE void MMC5SPRVROM_BANK4(uint32 A,uint32 V) {if(CHRptr[0]){V&=CHRmask4[0];MMC5SPRVPage[(A)>>10]=MMC5SPRVPage[((A)>>10)+1]= MMC5SPRVPage[((A)>>10)+2]=MMC5SPRVPage[((A)>>10)+3]=&CHRptr[0][(V)<<12]-(A);}} +static INLINE void MMC5BGVROM_BANK4(uint32 A,uint32 V) {if(CHRptr[0]){V&=CHRmask4[0];MMC5BGVPage[(A)>>10]=MMC5BGVPage[((A)>>10)+1]=MMC5BGVPage[((A)>>10)+2]=MMC5BGVPage[((A)>>10)+3]=&CHRptr[0][(V)<<12]-(A);}} + +static INLINE void MMC5SPRVROM_BANK8(uint32 V) {if(CHRptr[0]){V&=CHRmask8[0];MMC5SPRVPage[0]=MMC5SPRVPage[1]=MMC5SPRVPage[2]=MMC5SPRVPage[3]=MMC5SPRVPage[4]=MMC5SPRVPage[5]=MMC5SPRVPage[6]=MMC5SPRVPage[7]=&CHRptr[0][(V)<<13];}} +static INLINE void MMC5BGVROM_BANK8(uint32 V) {if(CHRptr[0]){V&=CHRmask8[0];MMC5BGVPage[0]=MMC5BGVPage[1]=MMC5BGVPage[2]=MMC5BGVPage[3]=MMC5BGVPage[4]=MMC5BGVPage[5]=MMC5BGVPage[6]=MMC5BGVPage[7]=&CHRptr[0][(V)<<13];}} + +static int32 inc; +uint8 MMC5fill[0x400]; + +#define MMC5IRQR mapbyte3[4] +#define MMC5LineCounter mapbyte3[5] +#define mmc5psize mapbyte1[0] +#define mmc5vsize mapbyte1[1] + +uint8 MMC5WRAMsize; +uint8 MMC5WRAMIndex[8]; + +uint8 MMC5ROMWrProtect[4]; +uint8 MMC5MemIn[5]; + +static void MMC5CHRA(void); +static void MMC5CHRB(void); + +typedef struct __cartdata { + unsigned long crc32; + unsigned char size; +} cartdata; + + +// ETROM seems to have 16KB of WRAM, ELROM seems to have 8KB +// EWROM seems to have 32KB of WRAM + +#define MMC5_NOCARTS 14 +cartdata MMC5CartList[MMC5_NOCARTS]= +{ + {0x9c18762b,2}, /* L'Empereur */ + {0x26533405,2}, + {0x6396b988,2}, + + {0xaca15643,2}, /* Uncharted Waters */ + {0xfe3488d1,2}, /* Dai Koukai Jidai */ + + {0x15fe6d0f,2}, /* BKAC */ + {0x39f2ce4b,2}, /* Suikoden */ + + {0x8ce478db,2}, /* Nobunaga's Ambition 2 */ + {0xeee9a682,2}, + + {0x1ced086f,2}, /* Ishin no Arashi */ + + {0xf540677b,4}, /* Nobunaga...Bushou Fuuun Roku */ + + {0x6f4e4312,4}, /* Aoki Ookami..Genchou */ + + {0xf011e490,4}, /* Romance of the 3 Kingdoms 2 */ + {0x184c2124,4}, /* Sangokushi 2 */ +}; + + +// Called by iNESLoad() +void DetectMMC5WRAMSize(void) +{ + int x; + + MMC5WRAMsize=1; + + for(x=0;x3)?255:0;break; + case 2:MMC5WRAMIndex[x]=(x&4)>>2;break; + case 4:MMC5WRAMIndex[x]=(x>3)?255:(x&3);break; + } + } +} + +static void MMC5CHRA(void) +{ + int x; + switch(mapbyte1[1]&3) + { + case 0:MMC5SPRVROM_BANK8(mapbyte2[7]); + setchr8(mapbyte2[7]); + break; + case 1:MMC5SPRVROM_BANK4(0x0000,mapbyte2[3]); + MMC5SPRVROM_BANK4(0x1000,mapbyte2[7]); + setchr4(0x0000,mapbyte2[3]); + setchr4(0x1000,mapbyte2[7]); + break; + case 2:MMC5SPRVROM_BANK2(0x0000,mapbyte2[1]); + MMC5SPRVROM_BANK2(0x0800,mapbyte2[3]); + MMC5SPRVROM_BANK2(0x1000,mapbyte2[5]); + MMC5SPRVROM_BANK2(0x1800,mapbyte2[7]); + setchr2(0x0000,mapbyte2[1]); + setchr2(0x0800,mapbyte2[3]); + setchr2(0x1000,mapbyte2[5]); + setchr2(0x1800,mapbyte2[7]); + break; + case 3: + for(x=0;x<8;x++) + { + setchr1(x<<10,mapbyte2[x]); + MMC5SPRVROM_BANK1(x<<10,mapbyte2[x]); + } + break; + } +} + +static void MMC5CHRB(void) +{ +int x; +switch(mapbyte1[1]&3) + { + case 0:MMC5BGVROM_BANK8(mapbyte3[3]); + setchr8(mapbyte3[3]); + break; + case 1: + MMC5BGVROM_BANK4(0x0000,mapbyte3[3]); + MMC5BGVROM_BANK4(0x1000,mapbyte3[3]); + setchr4(0x0000,mapbyte3[3]); + setchr4(0x1000,mapbyte3[3]); + break; + case 2:MMC5BGVROM_BANK2(0x0000,mapbyte3[1]); + MMC5BGVROM_BANK2(0x0800,mapbyte3[3]); + MMC5BGVROM_BANK2(0x1000,mapbyte3[1]); + MMC5BGVROM_BANK2(0x1800,mapbyte3[3]); + setchr2(0x0000,mapbyte3[1]); + setchr2(0x0800,mapbyte3[3]); + setchr2(0x1000,mapbyte3[1]); + setchr2(0x1800,mapbyte3[3]); + break; + case 3: + for(x=0;x<8;x++) + { + setchr1(x<<10,mapbyte3[x&3]); + MMC5BGVROM_BANK1(x<<10,mapbyte3[x&3]); + } + break; + } +} + +static void FASTAPASS(2) MMC5WRAM(uint32 A, uint32 V) +{ + V=MMC5WRAMIndex[V&7]; + if(V!=255) + { + setprg8r(0x10,A,V); + MMC5MemIn[(A-0x6000)>>13]=1; + } + else + MMC5MemIn[(A-0x6000)>>13]=0; +} + +static void MMC5PRG(void) +{ + int x; + + switch(mapbyte1[0]&3) + { + case 0: + MMC5ROMWrProtect[0]=MMC5ROMWrProtect[1]= + MMC5ROMWrProtect[2]=MMC5ROMWrProtect[3]=1; + setprg32(0x8000,((mapbyte1[5]&0x7F)>>2)); + for(x=0;x<4;x++) + MMC5MemIn[1+x]=1; + break; + case 1: + if(mapbyte1[5]&0x80) + { + MMC5ROMWrProtect[0]=MMC5ROMWrProtect[1]=1; + setprg16(0x8000,(mapbyte1[5]>>1)); + MMC5MemIn[1]=MMC5MemIn[2]=1; + } + else + { + MMC5ROMWrProtect[0]=MMC5ROMWrProtect[1]=0; + MMC5WRAM(0x8000,mapbyte1[5]&7&0xFE); + MMC5WRAM(0xA000,(mapbyte1[5]&7&0xFE)+1); + } + MMC5MemIn[3]=MMC5MemIn[4]=1; + MMC5ROMWrProtect[2]=MMC5ROMWrProtect[3]=1; + setprg16(0xC000,(mapbyte1[7]&0x7F)>>1); + break; + case 2: + if(mapbyte1[5]&0x80) + { + MMC5MemIn[1]=MMC5MemIn[2]=1; + MMC5ROMWrProtect[0]=MMC5ROMWrProtect[1]=1; + setprg16(0x8000,(mapbyte1[5]&0x7F)>>1); + } + else + { + MMC5ROMWrProtect[0]=MMC5ROMWrProtect[1]=0; + MMC5WRAM(0x8000,mapbyte1[5]&7&0xFE); + MMC5WRAM(0xA000,(mapbyte1[5]&7&0xFE)+1); + } + if(mapbyte1[6]&0x80) + {MMC5ROMWrProtect[2]=1;MMC5MemIn[3]=1;setprg8(0xC000,mapbyte1[6]&0x7F);} + else + {MMC5ROMWrProtect[2]=0;MMC5WRAM(0xC000,mapbyte1[6]&7);} + MMC5MemIn[4]=1; + MMC5ROMWrProtect[3]=1; + setprg8(0xE000,mapbyte1[7]&0x7F); + break; + case 3: + for(x=0;x<3;x++) + if(mapbyte1[4+x]&0x80) + { + MMC5ROMWrProtect[0]=1; + setprg8(0x8000+(x<<13),mapbyte1[4+x]&0x7F); + MMC5MemIn[1+x]=1; + } + else + { + MMC5ROMWrProtect[0]=0; + MMC5WRAM(0x8000+(x<<13),mapbyte1[4+x]&7); + } + MMC5MemIn[4]=1; + MMC5ROMWrProtect[3]=1; + setprg8(0xE000,mapbyte1[7]&0x7F); + break; + } +} + +#define mul1 mapbyte3[6] +#define mul2 mapbyte3[7] + +static DECLFW(Mapper5_write) +{ + switch(A) + { + default:break; + case 0x5105: + { + int x; + for(x=0;x<4;x++) + { + switch((V>>(x<<1))&3) + { + case 0:PPUNTARAM|=1<>3)&0x1F;break; + case 0x5202:MMC5HackSPPage=V&0x3F;break; + case 0x5203:X6502_IRQEnd(FCEU_IQEXT);IRQCount=V;break; + case 0x5204:X6502_IRQEnd(FCEU_IQEXT);IRQa=V&0x80;break; + case 0x5205:mul1=V;break; + case 0x5206:mul2=V;break; + } +} + +DECLFR(MMC5_ReadROMRAM) +{ + if(MMC5MemIn[(A-0x6000)>>13]) + return Page[A>>11][A]; + else + return X.DB; +} + +DECLFW(MMC5_WriteROMRAM) +{ + if(A>=0x8000) + if(MMC5ROMWrProtect[(A-0x8000)>>13]) + return; + if(MMC5MemIn[(A-0x6000)>>13]) + if(((mapbyte4[0]&3)|((mapbyte4[1]&3)<<2)) == 6) + Page[A>>11][A]=V; +} + +static DECLFW(MMC5_ExRAMWr) +{ + if(MMC5HackCHRMode!=3) + (MapperExRAM+0x6000)[A&0x3ff]=V; +} + +static DECLFR(MMC5_ExRAMRd) +{ + /* Not sure if this is correct, so I'll comment it out for now. */ + //if(MMC5HackCHRMode>=2) + return (MapperExRAM+0x6000)[A&0x3ff]; + //else + // return(X.DB); +} + +static DECLFR(MMC5_read) +{ + switch(A) + { + //default:printf("$%04x\n",A);break; + case 0x5204:X6502_IRQEnd(FCEU_IQEXT); + { + uint8 x; + x=MMC5IRQR; + MMC5IRQR&=0x40; + return x; + } + case 0x5205:return (mul1*mul2); + case 0x5206:return ((mul1*mul2)>>8); + } + return(X.DB); +} + +uint8 dorko[0x400]; + +void MMC5Synco(void) +{ + int x; + + MMC5PRG(); + for(x=0;x<4;x++) + { + switch((mapbyte4[3]>>(x<<1))&3) + { + case 0:PPUNTARAM|=1<=239) + MMC5IRQR|=0x40; +} + +void MMC5_hb(void) +{ + if(scanline==240) + { + MMC5LineCounter=0; + MMC5IRQR=0x40; + return; + } + + if(MMC5LineCounter<240) + { + MMC5LineCounter++; + if(MMC5LineCounter==IRQCount) + { + MMC5IRQR|=0x80; + if(IRQa&0x80) + X6502_IRQBegin(FCEU_IQEXT); + } + } +// printf("%d:%d\n",MMC5LineCounter,scanline); + if(MMC5LineCounter==240) + MMC5IRQR=0; +} + +void Mapper5_StateRestore(int version) +{ + if(version<=70) + { + uint8 tmp[8192]; + + FCEU_memmove(tmp,MapperExRAM,8192); + FCEU_memmove(MapperExRAM,MapperExRAM+8192,16384+8192); + FCEU_memmove(MapperExRAM+16384+8192,tmp,8192); + } + MMC5Synco(); +} + +#define MMC5PSG (MapperExRAM+0x640B+8) + +static int C5BC[3]={0,0,0}; + +static void Do5PCM(void) +{ + int32 V; + int32 start,end; + + start=C5BC[2]; + end=(timestamp<<16)/soundtsinc; + if(end<=start) return; + C5BC[2]=end; + + if(!(MMC5PSG[0x10]&0x40) && MMC5PSG[0x11]) + for(V=start;V>4]+=MMC5PSG[0x11]<<2; +} + +DECLFW(Mapper5_SW) +{ + GameExpSound.Fill=MMC5Sound; + A&=0x1F; + + switch(A) + { + case 0x10: + case 0x11:Do5PCM();break; + + case 0x0:Do5SQ(0);break; + case 0x2:Do5SQ(0);break; + case 0x3:Do5SQ(0);break; + case 0x4:Do5SQ(1);break; + case 0x6:Do5SQ(1);break; + case 0x7:Do5SQ(1);break; + case 0x15: + { + int t=V^MMC5PSG[0x15]; + if(t&1) + Do5SQ(0); + if(t&2) + Do5SQ(1); + } + break; + } + MMC5PSG[A]=V; +} + +static int32 vcount[2]; +void Do5SQ(int P) +{ + int32 start,end; + int V; + int32 freq; + + start=C5BC[P]; + end=(timestamp<<16)/soundtsinc; + if(end<=start) return; + C5BC[P]=end; + + + + if(MMC5PSG[0x15]&(P+1)) + { + unsigned long dcycs; + unsigned char amplitude; + long vcoo; + + freq=(((MMC5PSG[(P<<2)+0x2]|((MMC5PSG[(P<<2)+0x3]&7)<<8)))); + + if(freq<8) goto mmc5enda; + freq+=1; + inc=(long double)((unsigned long)((FSettings.SndRate OVERSAMPLE)<<12))/((long double)PSG_base/freq); + + switch(MMC5PSG[P<<2]&0xC0) + { + default: + case 0x00:dcycs=inc>>3;break; + case 0x40:dcycs=inc>>2;break; + case 0x80:dcycs=inc>>1;break; + case 0xC0:dcycs=(inc+inc+inc)>>2;break; + } + + amplitude=(MMC5PSG[P<<2]&15)<<4; + + vcoo=vcount[P]; + for(V=start;V>4]+=amplitude; + vcoo+=0x1000; + if(vcoo>=inc) vcoo-=inc; + } + vcount[P]=vcoo; + } + mmc5enda:; // semi-colon must be here for MSVC +} + +void MMC5Sound(int Count) +{ + int x; + Do5SQ(0); + Do5SQ(1); + Do5PCM(); + for(x=0;x<3;x++) + C5BC[x]=Count; +} + +static void MMC5SoundC(void) +{ + if(FSettings.SndRate) + Mapper5_ESI(); + else + SetWriteHandler(0x5000,0x5015,0); +} + +void Mapper5_ESI(void) +{ + GameExpSound.RChange=MMC5SoundC; + + if(FSettings.SndRate) + { + SetWriteHandler(0x5000,0x5015,Mapper5_SW); + SetWriteHandler(0x5205,0x5206,Mapper5_write); + SetReadHandler(0x5205,0x5206,MMC5_read); + } + if(FCEUGameInfo.type==GIT_NSF) + { + SetWriteHandler(0x5c00,0x5fef,MMC5_ExRAMWr); + SetReadHandler(0x5c00,0x5fef,MMC5_ExRAMRd); + MMC5HackCHRMode=2; + } + else + GameHBIRQHook=MMC5_hb; +} + +static void GenMMC5Reset(void) +{ + mapbyte1[4]=mapbyte1[5]=mapbyte1[6]=mapbyte1[7]=~0; + mapbyte1[0]=mapbyte1[1]=3; + mapbyte4[2]=0; + + mapbyte4[3]=mapbyte4[4]=mapbyte4[5]=0xFF; + + MMC5Synco(); + + SetWriteHandler(0x4020,0x5bff,Mapper5_write); + SetReadHandler(0x4020,0x5bff,MMC5_read); + + SetWriteHandler(0x5c00,0x5fff,MMC5_ExRAMWr); + SetReadHandler(0x5c00,0x5fff,MMC5_ExRAMRd); + + SetWriteHandler(0x6000,0xFFFF,MMC5_WriteROMRAM); + SetReadHandler(0x6000,0xFFFF,MMC5_ReadROMRAM); + + Mapper5_ESI(); + + GameHBIRQHook=MMC5_hb; + FCEU_CheatAddRAM(8,0x6000,WRAM); + FCEU_CheatAddRAM(1,0x5c00,MapperExRAM+0x6000); +} + +void Mapper5_init(void) +{ + AddExState(&MMC5HackSPMode, 1, 0, "SPLM"); + AddExState(&MMC5HackSPScroll, 1, 0, "SPLS"); + AddExState(&MMC5HackSPPage, 1, 0, "SPLP"); + SetupCartPRGMapping(0x10,WRAM,32768,1); + GenMMC5Reset(); + BuildWRAMSizeTable(); + GameStateRestore=Mapper5_StateRestore; +} + +static int m5boo; +static void GenMMC5_Close(void) +{ + UNIFOpenWRAM(UOW_WR,0,1); + if(m5boo<=16) + UNIFWriteWRAM(WRAM,8192); + else + UNIFWriteWRAM(WRAM,32768); + UNIFCloseWRAM(); +} + +static void GenMMC5_Init(int wsize, int battery) +{ + SetupCartPRGMapping(0x10,WRAM,32768,1); + AddExState(WRAM, 8192, 0, "WRAM"); + AddExState(MapperExRAM, 32768, 0, "MEXR"); + AddExState(&IRQCount, 4, 1, "IRQC"); + AddExState(&IRQa, 1, 0, "IRQA"); + AddExState(mapbyte1, 32, 0, "MPBY"); + AddExState(&MMC5HackSPMode, 1, 0, "SPLM"); + AddExState(&MMC5HackSPScroll, 1, 0, "SPLS"); + AddExState(&MMC5HackSPPage, 1, 0, "SPLP"); + + MMC5WRAMsize=wsize/8; + BuildWRAMSizeTable(); + GameStateRestore=Mapper5_StateRestore; + BoardPower=GenMMC5Reset; + + if(battery) + { + UNIFOpenWRAM(UOW_RD,0,1); + if(wsize<=16) + UNIFReadWRAM(WRAM,8192); + else + UNIFReadWRAM(WRAM,32768); + UNIFCloseWRAM(); + BoardClose=GenMMC5_Close; + m5boo=wsize; + } + + MMC5HackVROMMask=CHRmask4[0]; + MMC5HackExNTARAMPtr=MapperExRAM+0x6000; + MMC5Hack=1; + MMC5HackVROMPTR=CHRptr[0]; + MMC5HackCHRMode=0; + MMC5HackSPMode=MMC5HackSPScroll=MMC5HackSPPage=0; + +} + +// ETROM seems to have 16KB of WRAM, ELROM seems to have 8KB +// EWROM seems to have 32KB of WRAM + +// ETROM and EWROM are battery-backed, ELROM isn't. + +void ETROM_Init(void) +{ + GenMMC5_Init(16,1); +} + +void ELROM_Init(void) +{ + GenMMC5_Init(8,0); +} + +void EWROM_Init(void) +{ + GenMMC5_Init(32,1); +} + +void EKROM_Init(void) +{ + GenMMC5_Init(8,1); +} diff --git a/memory.c b/memory.c new file mode 100644 index 0000000..bec7270 --- /dev/null +++ b/memory.c @@ -0,0 +1,67 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include "types.h" +#include "version.h" +#include "memory.h" +#include "general.h" +#include "svga.h" + +void *FCEU_malloc(uint32 size) +{ + void *ret; + ret=malloc(size); + if(!ret) + FCEU_PrintError(MSG_ERRAM); + return ret; +} + +void FCEU_free(void *ptr) // Might do something with this and FCEU_malloc later... +{ + free(ptr); +} + +void FASTAPASS(3) FCEU_memmove(void *d, void *s, uint32 l) +{ + uint32 x; + int t; + + /* Type really doesn't matter. */ + t=(int)d; + t|=(int)s; + t|=(int)l; + + if(t&3) // Not 4-byte aligned and/or length is not a multiple of 4. + for(x=l;x;x--) // This could be optimized further, though(more tests could be performed). + { + *(uint8*)d=*(uint8 *)s; + ((uint8 *)d)++; + ((uint8 *)s)++; + } + else + for(x=l>>2;x;x--) + { + *(uint32*)d=*(uint32*)s; + ((uint32 *)d)++; + ((uint32 *)s)++; + } +} diff --git a/memory.h b/memory.h new file mode 100644 index 0000000..122a858 --- /dev/null +++ b/memory.h @@ -0,0 +1,29 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Various macros for faster memory stuff + (at least that's the idea) +*/ + +#define FCEU_dwmemset(d,c,n) {int _x; for(_x=n-4;_x>=0;_x-=4) *(uint32 *)&(d)[_x]=c;} + +void *FCEU_malloc(uint32 size); +void FCEU_free(void *ptr); +void FASTAPASS(3) FCEU_memmove(void *d, void *s, uint32 l); diff --git a/netplay.c b/netplay.c new file mode 100644 index 0000000..fc2a42e --- /dev/null +++ b/netplay.c @@ -0,0 +1,98 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef NETWORK + +#include +#include +#include + +#include "types.h" +#include "svga.h" +#include "netplay.h" + +int netplay=0; /* 1 if we are a server */ + /* 2 if we need a host */ +static uint8 netjoy[4]; // controller stuff. + +static void NetError(void) +{ + FCEU_DispMessage("Network error/connection lost!"); +// FCEUD_PrintError("Network error/connection lost!"); + netplay=0; + FCEUD_NetworkClose(); +} + +void KillNetplay(void) +{ + if(netplay) + { + FCEUD_NetworkClose(); + netplay=0; + } +} + +int InitNetplay(void) +{ + if(!FCEUD_NetworkConnect()) + {NetError();return 0;} + netplay=FSettings.NetworkPlay; + memset(netjoy,0,sizeof(netjoy)); + return 1; +} + +void NetplayUpdate(uint16 *joyp1, uint16 *joyp2) +{ + uint8 buf[5]; + if(netplay==1) // We're the server + { + int t; + + loopo: + + t=FCEUD_NetworkRecvData(&netjoy[2],1,0); + if(!t) {NetError();return;} + if(t!=-1) goto loopo; + + netjoy[0]=*joyp1; + memcpy(buf,netjoy,4); + buf[4]=CommandQueue; + + if(!FCEUD_NetworkSendData(buf,5)) {NetError();return;} + if(CommandQueue) + { + DoCommand(CommandQueue); + CommandQueue=0; + } + } + else if(netplay==2) // We're connected to a host (we're second player) + { + uint8 ja=(*joyp1)|(*joyp1>>8)|(*joyp2)|(*joyp2>>8); + if(!FCEUD_NetworkSendData(&ja,1)) {NetError();return;} + if(!FCEUD_NetworkRecvData(buf,5,1)) {NetError();return;} + + memcpy(netjoy,buf,4); + if(buf[4]) DoCommand(buf[4]); + } + *joyp1=netjoy[0]|(netjoy[1]<<8); + *joyp2=netjoy[2]|(netjoy[3]<<8); + +} +#endif diff --git a/netplay.h b/netplay.h new file mode 100644 index 0000000..d6ddd37 --- /dev/null +++ b/netplay.h @@ -0,0 +1,7 @@ +#ifdef NETWORK +int InitNetplay(void); +void KillNetplay(void); +void NetplayUpdate(uint16 *JS1, uint16 *JS2); + +extern int netplay; +#endif diff --git a/nsf.c b/nsf.c new file mode 100644 index 0000000..cadb726 --- /dev/null +++ b/nsf.c @@ -0,0 +1,410 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include + +#include "types.h" +#include "x6502.h" +#include "fce.h" +#include "svga.h" +#include "video.h" +#include "sound.h" +#include "ines.h" +#include "nsf.h" +#include "nsfbgnew.h" +#include "general.h" +#include "memory.h" +#include "file.h" +#include "fds.h" +#include "cart.h" +#include "input.h" + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +uint8 SongReload; +uint8 CurrentSong; + +static int sinetable[32]; + + +static uint8 NSFROM[0x30+6]= +{ +/* 0x00 */ + +0x08,0x48,0x8A,0x48,0x98,0x48, /* Store regs */ +0xA9,0xFF, +0x8D,0xF2,0x5F, /* NMI has occured */ +0x68,0xA8,0x68,0xAA,0x68,0x28, +0x40, /* Restore regs */ + +/* 0x12 */ + +0xAD,0xF2,0x5F, /* See if an NMI occured */ +0xF0,0xFB, /* If it hasn't, loop */ + +0xA9,0x00, +0x8D,0xF2,0x5F, /* Clear play pending reg*/ + + +0xAD,0xF0,0x5F, /* See if we need to init. */ +0xF0,0x09, /* If 0, go to JMP */ + +0xAD,0xF1,0x5F, /* Confirm and load A */ +0xAE,0xF3,0x5F, /* Load X with PAL/NTSC byte */ + +0x20,0x00,0x00, /* JSR to init routine */ + +0x20,0x00,0x00, /* JSR to play routine */ + +0x4C,0x12,0x38, /* Loop */ + +0xA2,0xFF,0x9A, /* Initialize the stack pointer. */ +0x4C,0x12,0x38 +}; + +static DECLFR(NSFROMRead) +{ + return (NSFROM-0x3800)[A]; +} + + + +static uint8 *NSFDATA=0; +static int NSFMaxBank; + +static int NSFSize; +static uint8 BSon; +static uint16 PlayAddr; +static uint16 InitAddr; +static uint16 LoadAddr; + +NSF_HEADER NSFHeader; + +void NSFGI(int h) +{ + switch(h) + { + case GI_CLOSE: + if(NSFDATA) {free(NSFDATA);NSFDATA=0;} + break; + case GI_POWER: NSF_init();break; + } +} + +// First 32KB is reserved for sound chip emulation in the iNES mapper code. + +#define WRAM (GameMemBlock+32768) +#define FDSMEM (GameMemBlock+32768) + +static INLINE void BANKSET(uint32 A, uint32 bank) +{ + bank&=NSFMaxBank; + if(NSFHeader.SoundChip&4) + memcpy(FDSMEM+(A-0x6000),NSFDATA+(bank<<12),4096); + else + setprg4(A,bank); +} + +int NSFLoad(int fp) +{ + int x; + + FCEU_fseek(fp,0,SEEK_SET); + FCEU_fread(&NSFHeader,1,0x80,fp); + if (memcmp(NSFHeader.ID,"NESM\x1a",5)) + return 0; + NSFHeader.SongName[31]=NSFHeader.Artist[31]=NSFHeader.Copyright[31]=0; + + LoadAddr=NSFHeader.LoadAddressLow; + LoadAddr|=NSFHeader.LoadAddressHigh<<8; + + InitAddr=NSFHeader.InitAddressLow; + InitAddr|=NSFHeader.InitAddressHigh<<8; + + PlayAddr=NSFHeader.PlayAddressLow; + PlayAddr|=NSFHeader.PlayAddressHigh<<8; + + NSFSize=FCEU_fgetsize(fp)-0x80; + + NSFMaxBank=((NSFSize+(LoadAddr&0xfff)+4095)/4096); + NSFMaxBank=uppow2(NSFMaxBank); + + if(!(NSFDATA=(uint8 *)FCEU_malloc(NSFMaxBank*4096))) + return 0; + + FCEU_fseek(fp,0x80,SEEK_SET); + memset(NSFDATA,0x00,NSFMaxBank*4096); + FCEU_fread(NSFDATA+(LoadAddr&0xfff),1,NSFSize,fp); + + NSFMaxBank--; + + BSon=0; + for(x=0;x<8;x++) + BSon|=NSFHeader.BankSwitch[x]; + + FCEUGameInfo.type=GIT_NSF; + FCEUGameInfo.input[0]=FCEUGameInfo.input[1]=SI_NONE; + + for(x=0;;x++) + { + if(NSFROM[x]==0x20) + { + NSFROM[x+1]=InitAddr&0xFF; + NSFROM[x+2]=InitAddr>>8; + NSFROM[x+4]=PlayAddr&0xFF; + NSFROM[x+5]=PlayAddr>>8; + break; + } + } + + if(NSFHeader.VideoSystem==0) + FCEUGameInfo.vidsys=GIV_NTSC; + else if(NSFHeader.VideoSystem==1) + FCEUGameInfo.vidsys=GIV_PAL; + + { + double fruit=0; + for(x=0;x<32;x++) + { + double ta,no; + + ta=sin(fruit)*7; + ta+=modf(ta,&no); + sinetable[x]=ta; + fruit+=(double)M_PI*2/32; + } + } + GameInterface=NSFGI; + + puts("NSF Loaded. File information:\n"); + printf(" Name: %s\n Artist: %s\n Copyright: %s\n\n",NSFHeader.SongName,NSFHeader.Artist,NSFHeader.Copyright); + if(NSFHeader.SoundChip) + { + static char *tab[6]={"Konami VRCVI","Konami VRCVII","Nintendo FDS","Nintendo MMC5","Namco 106","Sunsoft FME-07"}; + for(x=0;x<6;x++) + if(NSFHeader.SoundChip&(1<=6) + BANKSET(0x6000+(x-6)*4096,NSFHeader.BankSwitch[x]); + BANKSET(0x8000+x*4096,NSFHeader.BankSwitch[x]); + } + } + else + { + int32 x; + for(x=(LoadAddr&0x7000);x<0x8000;x+=0x1000) + BANKSET(0x8000+x,((x-(LoadAddr&0x7000))>>12)); + } + + SetWriteHandler(0x2000,0x3fff,0); + SetReadHandler(0x2000,0x37ff,0); + SetReadHandler(0x3836,0x3FFF,0); + SetReadHandler(0x3800,0x3835,NSFROMRead); + + SetWriteHandler(0x4020,0x5fff,NSF_write); + SetReadHandler(0x4020,0x5fff,NSF_read); + + + if(NSFHeader.SoundChip&1) { + VRC6_ESI(0); + } else if (NSFHeader.SoundChip&2) { + VRC7_ESI(); + } else if (NSFHeader.SoundChip&4) { + FDSSoundReset(); + } else if (NSFHeader.SoundChip&8) { + Mapper5_ESI(); + } else if (NSFHeader.SoundChip&0x10) { + Mapper19_ESI(); + } else if (NSFHeader.SoundChip&0x20) { + Mapper69_ESI(); + } + CurrentSong=NSFHeader.StartingSong; + SongReload=1; +} + +static uint8 DoUpdateStuff=0; +DECLFW(NSF_write) +{ +switch(A) +{ + case 0x5FF2:if((X.PC&0xF000)==0x3000) DoUpdateStuff=V;break; + + case 0x5FF6: + case 0x5FF7:if(!(NSFHeader.SoundChip&4)) return; + case 0x5FF8: + case 0x5FF9: + case 0x5FFA: + case 0x5FFB: + case 0x5FFC: + case 0x5FFD: + case 0x5FFE: + case 0x5FFF:if(!BSon) return; + A&=0xF; + BANKSET((A*4096),V); + break; +} +} + +DECLFR(NSF_read) +{ + int x; + + if((X.PC&0xF000)==0x3000) + switch(A) + { + case 0x5ff0:x=SongReload;SongReload=0;return x; + case 0x5ff1: + { + memset(RAM,0x00,0x800); + memset(WRAM,0x00,8192); + BWrite[0x4015](0x4015,0xF); + for(x=0;x<0x14;x++) + {if(x!=0x11) BWrite[0x4015](0x4015,0);} + BWrite[0x4015](0x4015,0x0); + for(x=0;x<0x14;x++) + {if(x!=0x11) BWrite[0x4015](0x4015,0);} + BWrite[0x4011](0x4011,0x40); + BWrite[0x4015](0x4015,0xF); + BWrite[0x4017](0x4017,0x40); + if(NSFHeader.SoundChip&4) + BWrite[0x4089](0x4089,0x80); + if(BSon) + { + for(x=0;x<8;x++) + BANKSET(0x8000+x*4096,NSFHeader.BankSwitch[x]); + } + return (CurrentSong-1); + } + case 0x5FF2:return DoUpdateStuff; + case 0x5FF3:return PAL; + } + return 0; +} +static int32 *Bufpl; +void DrawNSF(uint8 *XBuf) +{ + char snbuf[16]; + static int z=0; + int x,y; + uint8 *XBuf2,*tmpb; + + XBuf+=8; + XBuf2=XBuf; + + tmpb=NSFBG+8; + for(y=120;y;y--) + { + uint8 *offs; + + offs=tmpb+sinetable[((z+y)>>2)&31]; + memcpy(XBuf2,offs,256); + memcpy(XBuf2+(120*272),offs,256); + + XBuf2+=272; + tmpb+=272; + } + tmpb=NSFBG+8; + z=(z+1)&127; + + DrawTextTrans(XBuf+10*272+4+(((31-strlen(NSFHeader.SongName))<<2)), 272, NSFHeader.SongName, 38); + DrawTextTrans(XBuf+30*272+4+(((31-strlen(NSFHeader.Artist))<<2)), 272, NSFHeader.Artist, 38); + DrawTextTrans(XBuf+50*272+4+(((31-strlen(NSFHeader.Copyright))<<2)), 272, NSFHeader.Copyright, 38); + + DrawTextTrans(XBuf+90*272+4+(((31-strlen("Song:"))<<2)), 272, "Song:", 38); + sprintf(snbuf,"<%d/%d>",CurrentSong,NSFHeader.TotalSongs); + DrawTextTrans(XBuf+102*272+4+(((31-strlen(snbuf))<<2)), 272, snbuf, 38); + + GetSoundBuffer(&Bufpl); + for(x=0;x<256;x++) + XBuf[x+(224-((((Bufpl[x]>>(7)^128)&255)*3)>>3))*272]=38; +} + +void NSFControl(int z) +{ + if(z==1) + { + if(CurrentSong1) CurrentSong--; + } + SongReload=0xFF; +} diff --git a/nsf.h b/nsf.h new file mode 100644 index 0000000..3333201 --- /dev/null +++ b/nsf.h @@ -0,0 +1,53 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +typedef struct { + char ID[5]; /*NESM^Z*/ + uint8 Version; + uint8 TotalSongs; + uint8 StartingSong; + uint8 LoadAddressLow; + uint8 LoadAddressHigh; + uint8 InitAddressLow; + uint8 InitAddressHigh; + uint8 PlayAddressLow; + uint8 PlayAddressHigh; + uint8 SongName[32]; + uint8 Artist[32]; + uint8 Copyright[32]; + uint8 NTSCspeed[2]; // Unused + uint8 BankSwitch[8]; + uint8 PALspeed[2]; // Unused + uint8 VideoSystem; + uint8 SoundChip; + uint8 Expansion[4]; + uint8 reserve[8]; + } NSF_HEADER; +int NSFLoad(int fp); +DECLFW(NSF_write); +DECLFR(NSF_read); +void NSF_init(void); +extern uint8 CurrentSong; +extern uint8 SongReload; +void DrawNSF(uint8 *XBuf); +void NSFControl(int z); +extern NSF_HEADER NSFHeader; +void NSFDealloc(void); +void NSFDodo(void); diff --git a/nsfbgnew.h b/nsfbgnew.h new file mode 100644 index 0000000..2c55fe1 --- /dev/null +++ b/nsfbgnew.h @@ -0,0 +1,2062 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +uint8 NSFBG[32640] = { +0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3, +3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2, +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0, +0,0,0,0,4,4,4,4,4,4,4,4,4,4,4,5, +5,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7, +7,7,7,7,7,7,7,7,7,7,7,7,7,7,8,8, +8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, +8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, +8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, +8,8,8,8,8,8,8,8,8,8,8,7,7,7,7,7, +7,7,7,7,7,7,7,7,7,7,7,6,6,6,6,6, +6,6,6,6,6,6,6,6,6,5,4,4,4,4,4,4, +4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0, +4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4, +4,4,4,4,4,4,4,4,4,4,4,4,5,6,6,6, +6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7, +7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, +7,7,8,8,8,8,8,8,8,8,8,8,8,8,8,8, +8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, +8,8,8,8,8,8,8,8,8,8,8,8,8,8,7,7, +7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, +7,7,7,6,6,6,6,6,6,6,6,6,6,6,6,6, +6,6,6,5,4,4,4,4,4,4,4,4,4,4,4,4, +5,5,4,4,4,4,4,4,4,4,4,4,4,4,4,4, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,4,4,4,4, +4,4,4,4,4,4,4,4,4,4,5,5,6,6,6,6, +6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7, +7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, +7,7,7,7,7,7,7,8,8,8,8,8,8,8,8,8, +8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, +8,8,8,8,8,7,7,7,7,7,7,7,7,7,7,7, +7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,6, +6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, +6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,5, +4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,4,4,4,4,4, +4,4,4,4,4,4,4,4,5,5,6,6,6,6,6,6, +6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7, +7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, +7,7,7,8,8,8,8,8,8,8,8,8,8,8,8,8, +8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, +8,8,8,8,8,8,7,7,7,7,7,7,7,7,7,7, +7,7,7,7,7,7,7,7,7,7,7,7,7,7,6,6, +7,7,7,7,7,7,7,7,7,7,7,7,7,7,6,6, +6,6,6,6,6,6,6,6,6,6,6,6,6,5,4,4, +4,4,4,4,4,4,4,4,4,4,4,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2, +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,4,4,4,4,4,4,4,4, +4,4,4,4,4,5,6,6,6,6,6,6,6,6,6,6, +6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7, +7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8, +8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, +8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, +8,8,8,8,8,8,8,8,8,8,8,7,7,7,7,7, +8,8,8,8,8,8,8,8,8,8,8,8,8,7,7,7, +7,7,7,7,7,7,7,7,7,7,7,7,6,6,6,6, +6,6,6,6,6,6,6,6,5,5,4,4,4,4,4,4, +4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2, +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3, +3,3,3,24,24,3,3,3,3,3,3,3,3,3,3,3, +3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0, +0,0,0,4,4,4,4,4,4,4,4,4,4,4,5,6, +6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7, +7,7,7,7,7,7,7,7,7,7,8,8,8,8,8,8, +8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, +8,8,8,8,8,8,9,9,9,9,9,9,9,9,9,9, +9,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, +9,9,9,9,9,9,9,9,9,9,9,9,8,8,8,8, +8,8,8,8,8,8,8,8,8,8,8,7,7,7,7,7, +7,7,7,7,7,7,7,6,6,6,6,6,6,6,6,6, +6,5,4,4,4,4,4,4,4,4,4,0,0,0,0,0, +0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1, +1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3, +3,3,3,3,24,24,24,24,24,24,24,24,24,24,24,24, +24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24, +24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24, +24,24,24,3,3,3,3,3,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1, +1,0,0,0,0,0,0,0,0,0,0,0,4,4,4,4, +4,4,4,4,4,5,6,6,6,6,6,6,6,6,6,6, +6,7,7,7,7,7,7,7,7,7,7,7,8,8,8,8, +8,8,8,8,8,8,8,8,8,8,8,8,9,9,9,9, +9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, +9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, +10,10,10,10,10,10,10,11,11,9,9,9,9,9,9,9, +9,9,9,9,9,9,9,9,9,9,8,8,8,8,8,8, +8,8,8,8,8,8,7,7,7,7,7,7,7,7,7,6, +6,6,6,6,6,6,6,6,5,4,4,4,4,4,4,4, +4,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1, +1,1,1,1,2,2,2,2,2,2,2,2,2,2,3,3, +3,3,24,24,24,24,24,24,24,24,24,24,24,25,25,25, +25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, +25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, +25,25,25,25,25,25,25,24,24,24,24,24,24,24,24,24, +24,24,3,3,3,2,2,2,2,2,2,2,2,2,2,2, +1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, +0,0,4,4,4,4,4,4,4,4,5,6,6,6,6,6, +6,6,6,6,7,7,7,7,7,7,7,7,7,7,8,8, +8,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9, +9,9,9,9,9,9,9,9,9,9,11,11,10,10,10,10, +10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, +12,12,10,10,10,10,10,10,10,10,10,10,10,10,10,10, +10,10,10,10,10,10,10,10,11,9,9,9,9,9,9,9, +9,9,9,9,8,8,8,8,8,8,8,8,8,8,7,7, +7,7,7,7,7,7,6,6,6,6,6,6,6,6,5,4, +4,4,4,4,4,4,0,0,0,0,0,0,0,1,1,1, +1,1,1,1,1,2,2,2,2,2,2,2,2,2,3,3, +3,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25, +25,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26, +26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, +26,26,26,26,25,25,25,25,25,25,25,25,25,25,25,25, +25,25,25,25,24,24,24,24,24,24,24,24,3,3,3,2, +2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1, +0,0,0,0,0,0,0,0,4,4,4,4,4,4,4,6, +6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7, +8,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9, +9,9,9,9,11,10,10,10,10,10,10,10,10,10,10,10, +10,10,10,10,10,10,10,10,10,10,10,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,10,10,10,10,10,10,10,10, +10,10,10,11,9,9,9,9,9,9,9,9,8,8,8,8, +8,8,8,8,7,7,7,7,7,7,7,7,6,6,6,6, +6,6,5,4,4,4,4,4,4,0,0,0,0,0,0,0, +1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,3, +3,24,24,24,24,24,24,25,25,25,25,25,25,25,25,25, +25,25,26,26,26,26,26,26,26,26,26,26,26,26,26,26, +26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, +26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, +26,26,26,25,25,25,25,25,25,25,25,25,25,25,24,24, +24,24,24,24,3,3,3,2,2,2,2,2,2,2,1,1, +1,1,1,1,1,0,0,0,0,0,0,0,4,4,4,4, +4,4,5,6,6,6,6,6,6,6,7,7,7,7,7,7, +7,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9, +9,9,11,10,10,10,10,10,10,10,10,10,10,10,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, +13,13,13,13,13,13,12,12,12,12,12,12,12,12,12,12, +12,12,12,10,10,10,10,10,10,10,10,9,9,9,9,9, +9,9,9,8,8,8,8,8,8,8,7,7,7,7,7,7, +6,6,6,6,6,6,5,4,4,4,4,4,0,0,0,0, +0,0,0,1,1,1,1,1,1,2,2,2,2,2,2,2, +3,3,24,24,24,24,24,25,25,25,25,25,25,25,25,26, +26,26,26,26,26,26,26,26,26,26,27,27,27,27,27,27, +27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27, +27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27, +27,27,26,26,26,26,26,26,26,26,26,26,26,25,25,25, +25,25,25,25,25,24,24,24,24,24,24,3,3,2,2,2, +2,2,2,1,1,1,1,1,1,1,0,0,0,0,0,0, +4,4,4,4,4,5,6,6,6,6,6,6,7,7,7,7, +7,7,7,8,8,8,8,8,8,9,9,9,9,9,9,9, +9,10,10,10,10,10,10,10,10,10,12,12,12,12,12,12, +12,12,12,12,12,12,13,13,13,13,13,13,13,13,13,13, +14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, +14,14,14,13,13,13,13,13,13,13,13,13,13,13,13,13, +13,12,12,12,12,12,12,12,12,12,10,10,10,10,10,10, +10,11,9,9,9,9,9,9,8,8,8,8,8,8,7,7, +7,7,7,7,6,6,6,6,6,6,4,4,4,4,4,0, +0,0,0,0,0,1,1,1,1,1,1,2,2,2,2,2, +2,3,24,24,24,24,24,25,25,25,25,25,25,25,26,26, +26,26,26,26,26,26,26,27,27,27,27,27,27,27,27,27, +27,27,27,29,29,29,29,29,29,29,29,29,29,29,29,29, +29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, +27,27,27,27,27,27,27,27,27,27,27,27,26,26,26,26, +26,26,26,26,25,25,25,25,25,25,25,24,24,24,24,24, +3,3,2,2,2,2,2,2,1,1,1,1,1,1,0,0, +0,0,0,4,4,4,4,4,5,6,6,6,6,6,7,7, +7,7,7,7,8,8,8,8,8,8,9,9,9,9,9,9, +9,10,10,10,10,10,10,10,12,12,12,12,12,12,12,12, +12,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, +14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, +14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, +13,13,13,13,13,13,13,13,13,12,12,12,12,12,12,12, +12,10,10,10,10,10,10,9,9,9,9,9,9,8,8,8, +8,8,7,7,7,7,7,7,6,6,6,6,6,4,4,4, +4,4,0,0,0,0,0,1,1,1,1,1,2,2,2,2, +2,2,3,24,24,24,24,25,25,25,25,25,25,26,26,26, +26,26,26,26,26,27,27,27,27,27,27,27,27,29,29,29, +29,29,28,28,28,28,28,28,28,28,28,28,30,30,30,30, +30,30,30,30,30,30,30,30,30,30,30,30,28,28,28,28, +28,28,28,28,28,29,29,29,29,29,29,27,27,27,27,27, +27,27,27,26,26,26,26,26,26,26,25,25,25,25,25,25, +25,24,24,24,24,3,2,2,2,2,2,2,1,1,1,1, +1,0,0,0,0,0,4,4,4,4,4,6,6,6,6,6, +7,7,7,7,7,8,8,8,8,8,8,9,9,9,9,9, +9,10,10,10,10,10,10,12,12,12,12,12,12,12,12,13, +13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14, +15,15,15,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,15,15,15,15,14,14, +14,14,14,14,14,14,14,14,13,13,13,13,13,13,13,13, +12,12,12,12,12,12,10,10,10,10,10,11,9,9,9,9, +9,8,8,8,8,8,7,7,7,7,7,6,6,6,6,5, +4,4,4,4,0,0,0,0,0,1,1,1,1,1,2,2, +2,2,2,3,24,24,24,24,25,25,25,25,25,26,26,26, +26,26,26,26,27,27,27,27,27,27,27,29,29,29,29,28, +28,28,28,28,30,30,30,30,30,30,30,30,30,30,30,30, +30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30, +30,30,30,30,30,30,30,30,30,28,28,28,28,28,29,29, +29,29,27,27,27,27,27,27,27,26,26,26,26,26,26,25, +25,25,25,25,25,24,24,24,24,3,2,2,2,2,2,1, +1,1,1,1,0,0,0,0,0,4,4,4,4,6,6,6, +6,6,7,7,7,7,7,8,8,8,8,8,9,9,9,9, +9,11,10,10,10,10,10,12,12,12,12,12,12,13,13,13, +13,13,13,13,13,14,14,14,14,14,14,14,14,14,14,15, +16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17, +17,17,17,17,17,17,17,17,17,17,16,16,16,16,16,16, +16,16,16,16,15,15,14,14,14,14,14,14,14,14,13,13, +13,13,13,13,13,12,12,12,12,12,10,10,10,10,10,11, +9,9,9,9,8,8,8,8,8,7,7,7,7,7,6,6, +6,6,4,4,4,4,0,0,0,0,0,1,1,1,1,2, +2,2,2,2,3,24,24,24,24,25,25,25,25,25,26,26, +26,26,26,27,27,27,27,27,27,29,29,29,29,28,28,28, +28,30,30,30,30,30,30,30,30,30,30,31,31,31,31,31, +31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31, +31,31,31,31,31,31,30,30,30,30,30,30,30,30,30,30, +30,28,28,28,28,29,29,29,27,27,27,27,27,27,26,26, +26,26,26,26,25,25,25,25,25,24,24,24,3,3,2,2, +2,2,1,1,1,1,1,0,0,0,0,4,4,4,4,5, +6,6,6,6,7,7,7,7,8,8,8,8,8,9,9,9, +9,9,10,10,10,10,10,12,12,12,12,12,12,13,13,13, +13,13,13,14,14,14,14,14,14,14,14,15,15,16,16,16, +16,16,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +17,17,17,17,16,16,16,16,16,16,16,15,15,14,14,14, +14,14,14,14,13,13,13,13,13,12,12,12,12,12,10,10, +10,10,10,9,9,9,9,9,8,8,8,8,7,7,7,7, +6,6,6,6,5,4,4,4,4,0,0,0,0,1,1,1, +1,2,2,2,2,3,24,24,24,24,25,25,25,25,26,26, +26,26,26,27,27,27,27,27,27,29,29,29,28,28,28,30, +30,30,30,30,30,30,30,31,31,31,31,31,31,31,31,31, +31,31,31,31,31,32,32,32,32,32,32,32,32,32,32,32, +32,31,31,31,31,31,31,31,31,31,31,31,31,31,31,30, +30,30,30,30,30,30,30,28,28,28,29,29,29,27,27,27, +27,27,27,26,26,26,26,26,25,25,25,25,24,24,24,24, +3,2,2,2,2,1,1,1,1,0,0,0,0,4,4,4, +4,5,6,6,6,6,7,7,7,7,8,8,8,8,9,9, +9,9,9,10,10,10,10,10,12,12,12,12,12,13,13,13, +13,13,14,14,14,14,14,14,14,15,15,16,16,16,16,16, +17,17,17,17,17,17,17,17,17,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,17,17,17,17,17,17,17,17,17,17,16,16,16,16,16, +16,15,14,14,14,14,14,14,13,13,13,13,13,12,12,12, +12,12,10,10,10,10,9,9,9,9,9,8,8,8,8,7, +7,7,7,6,6,6,6,4,4,4,4,0,0,0,0,1, +1,1,2,2,2,2,2,3,24,24,24,25,25,25,25,26, +26,26,26,26,27,27,27,27,27,29,29,28,28,28,30,30, +30,30,30,30,30,31,31,31,31,31,31,31,31,31,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,31,31,31,31, +31,31,31,31,31,30,30,30,30,30,30,30,28,28,28,29, +29,27,27,27,27,27,26,26,26,26,26,25,25,25,25,24, +24,24,24,3,2,2,2,2,1,1,1,1,0,0,0,0, +4,4,4,5,6,6,6,6,7,7,7,8,8,8,8,8, +9,9,9,9,10,10,10,10,12,12,12,12,12,13,13,13, +13,13,14,14,14,14,14,14,15,16,16,16,16,16,16,17, +17,17,17,17,17,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,17,17,17,17,17,17,17, +17,16,16,16,16,16,15,14,14,14,14,14,13,13,13,13, +13,12,12,12,12,10,10,10,10,9,9,9,9,8,8,8, +8,7,7,7,7,6,6,6,6,4,4,4,4,0,0,0, +1,1,1,1,2,2,2,2,3,24,24,24,25,25,25,25, +26,26,26,26,27,27,27,27,27,29,29,28,28,28,30,30, +30,30,30,30,31,31,31,31,31,31,31,32,32,32,32,32, +32,32,32,32,32,32,32,33,33,33,33,33,33,33,33,33, +33,33,33,33,33,33,33,33,32,32,32,32,32,32,32,32, +32,32,32,31,31,31,31,31,31,31,30,30,30,30,30,30, +28,28,28,29,29,27,27,27,27,27,26,26,26,26,25,25, +25,25,24,24,24,3,2,2,2,2,1,1,1,1,0,0, +0,0,4,4,4,5,6,6,6,7,7,7,7,8,8,8, +8,9,9,9,9,10,10,10,10,12,12,12,12,13,13,13, +13,13,14,14,14,14,14,15,15,16,16,16,16,16,17,17, +17,17,17,18,18,18,18,18,18,18,18,18,18,19,19,19, +19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, +19,19,19,19,19,19,18,18,18,18,18,18,18,18,18,17, +17,17,17,17,17,16,16,16,16,16,15,14,14,14,14,14, +13,13,13,13,12,12,12,12,10,10,10,10,9,9,9,9, +8,8,8,8,7,7,7,6,6,6,6,4,4,4,4,0, +0,0,1,1,1,1,2,2,2,3,24,24,24,25,25,25, +25,26,26,26,26,27,27,27,27,27,29,29,28,28,30,30, +30,30,30,30,31,31,31,31,31,31,32,32,32,32,32,32, +32,32,33,33,33,33,33,33,33,33,33,35,35,35,35,35, +35,35,35,35,35,35,35,33,33,33,33,33,33,33,33,33, +32,32,32,32,32,32,32,32,32,31,31,31,31,31,31,30, +30,30,30,30,28,28,28,29,29,27,27,27,27,26,26,26, +26,25,25,25,25,24,24,24,3,2,2,2,2,1,1,1, +0,0,0,0,4,4,4,6,6,6,6,7,7,7,7,8, +8,8,9,9,9,9,10,10,10,10,12,12,12,12,13,13, +13,13,13,14,14,14,14,15,15,16,16,16,16,17,17,17, +17,17,17,18,18,18,18,18,18,18,19,19,19,19,19,19, +19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, +19,19,19,19,19,19,19,19,19,19,19,19,18,18,18,18, +18,18,18,18,17,17,17,17,17,17,16,16,16,15,15,14, +14,14,14,13,13,13,13,12,12,12,12,10,10,10,10,9, +9,9,9,8,8,8,7,7,7,7,6,6,6,4,4,4, +0,0,0,0,1,1,1,2,2,2,2,3,24,24,24,25, +25,25,26,26,26,26,27,27,27,27,29,29,28,28,28,30, +30,30,30,30,31,31,31,31,31,32,32,32,32,32,32,32, +33,33,33,33,33,33,35,35,35,35,35,34,34,34,34,34, +34,34,34,34,34,34,34,34,34,34,34,35,35,35,35,35, +35,33,33,33,33,33,32,32,32,32,32,32,32,31,31,31, +31,31,31,30,30,30,30,30,28,28,29,29,27,27,27,27, +26,26,26,26,25,25,25,25,24,24,3,2,2,2,2,1, +1,1,0,0,0,0,4,4,4,6,6,6,6,7,7,7, +8,8,8,8,9,9,9,11,10,10,10,12,12,12,12,13, +13,13,13,14,14,14,14,14,15,16,16,16,16,17,17,17, +17,17,18,18,18,18,18,18,18,19,19,19,19,19,19,19, +19,19,20,20,20,20,20,20,20,20,20,20,20,20,20,20, +20,20,20,20,20,20,20,20,19,19,19,19,19,19,19,19, +19,18,18,18,18,18,18,18,17,17,17,17,17,16,16,16, +16,15,14,14,14,14,13,13,13,13,12,12,12,12,10,10, +10,9,9,9,9,8,8,8,7,7,7,7,6,6,6,4, +4,4,0,0,0,0,1,1,1,2,2,2,2,24,24,24, +25,25,25,25,26,26,26,27,27,27,27,29,29,28,28,30, +30,30,30,30,31,31,31,31,31,32,32,32,32,32,32,33, +33,33,33,33,35,35,35,34,34,34,34,34,34,34,34,34, +34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34, +34,34,34,35,35,35,35,33,33,33,33,32,32,32,32,32, +32,31,31,31,31,31,30,30,30,30,30,28,28,29,29,27, +27,27,27,26,26,26,26,25,25,25,24,24,24,3,2,2, +2,1,1,1,1,0,0,0,4,4,4,6,6,6,6,7, +7,7,8,8,8,9,9,9,9,10,10,10,10,12,12,12, +13,13,13,13,14,14,14,14,14,15,16,16,16,17,17,17, +17,17,17,18,18,18,18,18,19,19,19,19,19,19,19,19, +20,20,20,20,20,21,21,21,21,21,21,21,21,21,21,21, +21,21,21,21,21,21,21,21,21,21,20,20,20,20,19,19, +19,19,19,19,19,19,18,18,18,18,18,18,17,17,17,17, +17,16,16,16,15,14,14,14,14,13,13,13,13,12,12,12, +10,10,10,10,9,9,9,8,8,8,8,7,7,7,6,6, +6,4,4,4,0,0,0,0,1,1,1,2,2,2,3,24, +24,24,25,25,25,26,26,26,26,27,27,27,29,29,28,28, +30,30,30,30,30,31,31,31,31,32,32,32,32,32,32,33, +33,33,33,35,35,35,34,34,34,34,34,34,34,34,34,34, +34,34,34,34,36,36,36,36,36,36,36,34,34,34,34,34, +34,34,34,34,34,34,34,34,35,35,35,33,33,33,33,32, +32,32,32,32,32,31,31,31,31,31,30,30,30,30,28,28, +29,29,27,27,27,27,26,26,26,25,25,25,25,24,24,3, +2,2,2,1,1,1,1,0,0,0,4,4,4,6,6,6, +7,7,7,7,8,8,8,9,9,9,11,10,10,10,12,12, +12,13,13,13,13,14,14,14,14,15,16,16,16,16,17,17, +17,17,17,18,18,18,18,18,18,19,19,19,19,19,19,20, +20,20,20,21,21,21,21,21,21,21,21,22,22,22,22,22, +22,22,22,22,22,22,22,21,21,21,21,21,21,21,21,20, +20,20,20,19,19,19,19,19,19,19,18,18,18,18,18,17, +17,17,17,17,16,16,16,15,14,14,14,14,13,13,13,12, +12,12,12,10,10,10,9,9,9,9,8,8,8,7,7,7, +6,6,6,4,4,4,0,0,0,1,1,1,1,2,2,2, +3,24,24,25,25,25,26,26,26,26,27,27,27,27,29,28, +28,30,30,30,30,30,31,31,31,31,32,32,32,32,32,33, +33,33,33,35,35,34,34,34,34,34,34,34,34,34,34,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,36,34,34,34,34,34,34,34,34,34,35,35,33, +33,33,33,32,32,32,32,32,31,31,31,31,31,30,30,30, +30,28,28,29,29,27,27,27,26,26,26,26,25,25,25,24, +24,24,2,2,2,2,1,1,1,0,0,0,4,4,4,6, +6,6,7,7,7,8,8,8,8,9,9,9,10,10,10,12, +12,12,12,13,13,13,13,14,14,14,15,16,16,16,16,17, +17,17,17,17,18,18,18,18,18,19,19,19,19,19,19,20, +20,20,21,21,21,21,21,21,22,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,22,22,22,22,22,22,22,21,21, +21,21,21,21,20,20,20,19,19,19,19,19,19,18,18,18, +18,18,17,17,17,17,17,16,16,16,15,14,14,14,13,13, +13,13,12,12,12,10,10,10,11,9,9,9,8,8,8,7, +7,7,6,6,6,4,4,4,0,0,0,1,1,1,2,2, +2,2,24,24,24,25,25,25,26,26,26,27,27,27,27,29, +29,28,28,30,30,30,30,31,31,31,31,32,32,32,32,32, +33,33,33,35,35,35,34,34,34,34,34,34,34,36,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,36,36,36,36,36,36,34,34,34,34,34,34,34, +34,35,35,33,33,33,32,32,32,32,32,31,31,31,31,31, +30,30,30,30,28,28,29,27,27,27,27,26,26,26,25,25, +25,24,24,24,3,2,2,2,1,1,1,0,0,0,4,4, +4,6,6,6,7,7,7,8,8,8,9,9,9,9,10,10, +10,12,12,12,13,13,13,13,14,14,14,14,15,16,16,16, +16,17,17,17,17,18,18,18,18,18,19,19,19,19,19,19, +20,20,21,21,21,21,21,22,22,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, +22,22,22,21,21,21,21,21,20,20,20,19,19,19,19,19, +18,18,18,18,18,17,17,17,17,16,16,16,15,14,14,14, +14,13,13,13,12,12,12,12,10,10,10,9,9,9,8,8, +8,7,7,7,6,6,6,5,4,4,0,0,0,1,1,1, +2,2,2,3,24,24,24,25,25,25,26,26,26,27,27,27, +29,29,28,28,30,30,30,30,31,31,31,31,32,32,32,32, +32,33,33,33,35,35,34,34,34,34,34,34,34,36,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,34,34,34, +34,34,34,34,35,35,33,33,33,32,32,32,32,32,31,31, +31,31,30,30,30,30,28,28,29,29,27,27,27,26,26,26, +25,25,25,25,24,24,3,2,2,2,1,1,1,0,0,0, +4,4,4,6,6,6,7,7,7,8,8,8,9,9,9,11, +10,10,10,12,12,12,13,13,13,14,14,14,14,15,16,16, +16,16,17,17,17,17,18,18,18,18,18,19,19,19,19,19, +20,20,20,21,21,21,21,22,22,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,21,21,21,21,20,20,20,19,19, +19,19,19,18,18,18,18,18,17,17,17,17,16,16,16,15, +14,14,14,13,13,13,13,12,12,12,10,10,10,9,9,9, +8,8,8,7,7,7,6,6,6,5,4,4,0,0,0,1, +1,1,2,2,2,3,24,24,25,25,25,26,26,26,26,27, +27,27,29,29,28,28,30,30,30,31,31,31,31,32,32,32, +32,32,33,33,33,35,35,34,34,34,34,34,34,36,36,36, +36,36,36,36,36,36,36,36,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,36,36,36,36,36,36,36,36,36,36, +36,34,34,34,34,34,34,35,35,33,33,33,32,32,32,32, +32,31,31,31,31,30,30,30,30,28,28,29,27,27,27,27, +26,26,26,25,25,25,24,24,3,2,2,2,1,1,1,0, +0,0,4,4,4,6,6,6,7,7,7,8,8,8,9,9, +9,10,10,10,12,12,12,12,13,13,13,14,14,14,14,16, +15,16,16,16,17,17,17,17,18,18,18,18,19,19,19,19, +19,19,20,20,21,21,21,21,22,22,22,22,22,22,22,22, +22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,22, +22,22,22,22,22,22,22,22,22,22,21,21,21,21,20,20, +20,19,19,19,19,19,18,18,18,18,17,17,17,17,16,16, +16,15,14,14,14,14,13,13,13,12,12,12,10,10,10,9, +9,9,8,8,8,7,7,7,6,6,6,5,4,4,0,0, +0,1,1,1,2,2,2,3,24,24,25,25,25,26,26,26, +27,27,27,27,29,28,28,30,30,30,30,31,31,31,31,32, +32,32,32,33,33,33,35,35,34,34,34,34,34,34,36,36, +36,36,36,36,36,36,36,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,36,36,36,36,36, +36,36,36,36,34,34,34,34,34,34,35,35,33,33,33,32, +32,32,32,31,31,31,31,30,30,30,30,28,28,29,29,27, +27,27,26,26,26,25,25,25,24,24,3,2,2,2,1,1, +1,0,0,0,4,4,4,6,6,6,7,7,7,8,8,8, +9,9,9,10,10,10,12,12,12,13,13,13,13,14,14,14, +14,14,15,16,16,16,17,17,17,17,18,18,18,18,19,19, +19,19,19,20,20,21,21,21,21,22,22,22,22,22,22,22, +22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,22,22,22,22,22,22,22,22,22,21,21,21, +21,20,20,19,19,19,19,19,18,18,18,18,18,17,17,17, +17,16,16,16,15,14,14,14,13,13,13,12,12,12,10,10, +10,11,9,9,9,8,8,8,7,7,6,6,6,5,4,4, +0,0,0,1,1,1,2,2,2,3,24,24,25,25,25,26, +26,26,27,27,27,27,29,28,28,30,30,30,30,31,31,31, +31,32,32,32,32,33,33,33,35,35,34,34,34,34,34,36, +36,36,36,36,36,36,36,36,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36, +36,36,36,36,36,36,36,34,34,34,34,34,34,35,35,33, +33,33,32,32,32,32,31,31,31,31,30,30,30,28,28,29, +29,27,27,27,26,26,26,25,25,25,24,24,24,2,2,2, +1,1,1,0,0,0,4,4,4,6,6,6,7,7,7,8, +8,8,9,9,9,10,10,10,12,12,12,13,13,13,14,14, +14,14,14,14,15,16,16,17,17,17,17,18,18,18,18,18, +19,19,19,19,19,20,20,21,21,21,21,22,22,22,22,22, +22,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,23,23,23,22,22,22,22,22,22,22,22,22, +21,21,21,21,20,20,19,19,19,19,19,18,18,18,18,17, +17,17,17,16,16,16,15,14,14,14,13,13,13,13,12,12, +12,10,10,10,9,9,9,8,8,8,7,7,7,6,6,6, +4,4,0,0,0,1,1,1,2,2,2,3,24,24,25,25, +25,26,26,26,27,27,27,27,29,28,28,30,30,30,30,31, +31,31,32,32,32,32,32,33,33,35,35,34,34,34,34,34, +34,36,36,36,36,36,36,36,36,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,36,36,36,36,36,36,36,36,34,34,34,34,34,35, +35,33,33,33,32,32,32,32,31,31,31,31,30,30,30,30, +28,28,29,27,27,27,26,26,26,25,25,25,25,24,24,3, +2,2,1,1,1,0,0,0,4,4,4,6,6,6,7,7, +7,8,8,8,9,9,9,10,10,10,12,12,12,13,13,13, +13,13,14,14,14,14,15,16,16,17,17,17,17,18,18,18, +18,18,19,19,19,19,20,20,20,21,21,21,22,22,22,22, +22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,23,23,23,23,23,23,22,22,22,22,22,22, +22,22,22,21,21,21,20,20,20,19,19,19,19,18,18,18, +18,18,17,17,17,17,16,16,15,14,14,14,14,13,13,13, +12,12,12,10,10,10,9,9,9,8,8,8,7,7,7,6, +6,6,4,4,4,0,0,1,1,1,2,2,2,3,24,24, +25,25,25,26,26,26,27,27,27,27,29,28,28,30,30,30, +30,31,31,31,32,32,32,32,33,33,33,35,35,34,34,34, +34,34,34,36,36,36,36,36,36,36,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,36,36,36,36,36,36,36,34,34,34,34, +34,34,35,35,33,33,33,32,32,32,32,31,31,31,30,30, +30,30,28,28,29,27,27,27,27,26,26,26,25,25,25,24, +24,3,2,2,2,1,1,1,0,0,4,4,4,6,6,6, +7,7,7,8,8,8,9,9,9,10,10,10,12,12,12,13, +12,13,13,13,13,14,14,14,15,16,16,16,17,17,17,18, +18,18,18,18,19,19,19,19,20,20,20,21,21,21,22,22, +22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23, +23,23,23,23,23,23,23,23,23,23,23,23,23,22,22,22, +22,22,22,22,22,21,21,21,21,20,20,19,19,19,19,19, +18,18,18,18,17,17,17,17,16,16,16,14,14,14,14,13, +13,13,12,12,12,10,10,10,9,9,9,8,8,8,7,7, +7,6,6,6,4,4,4,0,0,0,1,1,1,2,2,3, +24,24,25,25,25,26,26,26,26,27,27,27,29,28,28,30, +30,30,30,31,31,31,32,32,32,32,32,33,33,35,35,34, +34,34,34,34,34,36,36,36,36,36,36,36,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,36,36,36,36,36,36,36,36,34, +34,34,34,34,35,35,33,33,33,32,32,32,32,31,31,31, +31,30,30,30,28,28,29,29,27,27,27,26,26,26,25,25, +25,24,24,3,2,2,2,1,1,1,0,0,0,4,4,6, +6,6,7,7,7,8,8,8,9,9,9,10,10,10,12,12, +12,12,12,12,13,13,13,14,14,14,15,16,16,16,17,17, +17,17,18,18,18,18,19,19,19,19,19,20,20,21,21,21, +21,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,22, +22,22,22,22,22,22,22,21,21,21,21,20,20,19,19,19, +19,19,18,18,18,18,17,17,17,17,16,16,16,15,14,14, +14,13,13,13,12,12,12,10,10,10,11,9,9,8,8,8, +7,7,7,6,6,6,4,4,4,0,0,0,1,1,1,2, +2,2,3,24,24,25,25,25,26,26,26,27,27,27,29,28, +28,30,30,30,30,31,31,31,31,32,32,32,32,33,33,33, +35,34,34,34,34,34,34,36,36,36,36,36,36,36,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,36,36,36,36,36,36, +36,34,34,34,34,34,35,35,33,33,33,32,32,32,32,31, +31,31,31,30,30,30,30,28,29,29,27,27,27,26,26,26, +25,25,25,24,24,3,2,2,2,1,1,1,0,0,0,4, +4,5,6,6,6,7,7,7,8,8,9,9,9,11,10,10, +10,10,10,12,12,12,13,13,13,14,14,14,14,16,16,16, +17,17,17,17,18,18,18,18,19,19,19,19,19,20,20,21, +21,21,21,22,22,22,22,22,22,22,22,23,23,23,23,23, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,22,22,22,22,22,22,22,22,22,21,21,21,20,20,19, +19,19,19,19,18,18,18,18,17,17,17,17,16,16,16,15, +14,14,14,13,13,13,13,12,12,12,10,10,10,9,9,9, +8,8,8,7,7,7,6,6,5,4,4,0,0,0,1,1, +1,2,2,2,3,24,24,25,25,25,26,26,26,27,27,27, +29,29,28,28,30,30,30,31,31,31,31,32,32,32,32,33, +33,33,35,35,34,34,34,34,34,36,36,36,36,36,36,36, +36,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,36,36,36,36, +36,36,36,34,34,34,34,34,34,35,35,33,33,32,32,32, +32,31,31,31,31,30,30,30,30,28,28,29,27,27,27,26, +26,26,26,25,25,25,24,24,3,2,2,1,1,1,0,0, +0,4,4,4,6,6,6,7,7,7,8,8,8,9,9,9, +9,9,10,10,10,12,12,12,13,13,13,13,14,14,14,15, +16,16,16,17,17,17,17,18,18,18,18,19,19,19,19,19, +20,20,21,21,21,21,22,22,22,22,22,22,22,22,23,23, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,22,22,22,22,22,22,22,22,22,21,21,21,20, +20,20,19,19,19,19,18,18,18,18,18,17,17,17,17,16, +16,15,14,14,14,14,13,13,13,12,12,12,10,10,10,9, +9,9,8,8,8,7,7,7,6,6,6,4,4,4,0,0, +0,1,1,1,2,2,3,24,24,25,25,25,26,26,26,26, +27,27,27,29,28,28,30,30,30,30,31,31,31,31,32,32, +32,32,33,33,33,35,34,34,34,34,34,34,36,36,36,36, +36,36,36,36,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,36,36, +36,36,36,36,36,34,34,34,34,34,34,35,35,33,33,33, +32,32,32,32,31,31,31,31,30,30,30,28,28,29,27,27, +27,27,26,26,26,25,25,25,24,24,3,2,2,2,1,1, +1,0,0,0,4,4,6,6,6,7,7,7,8,8,8,9, +8,8,9,9,9,10,10,10,12,12,12,13,13,13,14,14, +14,14,16,16,16,17,17,17,17,18,18,18,18,18,19,19, +19,19,20,20,20,21,21,21,22,22,22,22,22,22,22,22, +22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,23,22,22,22,22,22,22,22,22,22,21,21, +21,20,20,20,19,19,19,19,19,18,18,18,18,17,17,17, +17,16,16,16,15,14,14,14,13,13,13,12,12,12,10,10, +10,9,9,9,8,8,8,7,7,7,6,6,6,5,4,4, +0,0,0,1,1,1,2,2,2,3,24,24,25,25,25,26, +26,26,27,27,27,29,29,28,28,30,30,30,31,31,31,31, +32,32,32,32,33,33,33,35,35,34,34,34,34,34,34,36, +36,36,36,36,36,36,36,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36, +36,36,36,36,36,36,36,34,34,34,34,34,34,35,35,33, +33,33,32,32,32,32,31,31,31,31,30,30,30,28,28,29, +29,27,27,27,26,26,26,25,25,25,24,24,3,2,2,2, +1,1,1,0,0,0,4,4,4,6,6,6,7,7,7,8, +7,8,8,8,9,9,9,10,10,10,12,12,12,12,13,13, +13,14,14,14,15,16,16,16,17,17,17,17,18,18,18,18, +19,19,19,19,19,20,20,20,21,21,21,21,22,22,22,22, +22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,23,23,22,22,22,22,22,22,22,22,22,22, +21,21,21,20,20,20,19,19,19,19,19,18,18,18,18,17, +17,17,17,16,16,16,15,14,14,14,13,13,13,13,12,12, +12,10,10,10,9,9,9,8,8,8,7,7,7,6,6,6, +4,4,4,0,0,0,1,1,1,2,2,3,24,24,25,25, +25,25,26,26,26,27,27,27,29,29,28,30,30,30,30,31, +31,31,31,32,32,32,32,33,33,33,35,35,34,34,34,34, +34,34,36,36,36,36,36,36,36,36,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +37,36,36,36,36,36,36,36,36,34,34,34,34,34,34,35, +35,33,33,33,32,32,32,32,31,31,31,31,30,30,30,30, +28,28,29,27,27,27,26,26,26,26,25,25,25,24,24,3, +2,2,2,1,1,1,0,0,4,4,4,6,6,6,7,7, +6,7,7,7,8,8,8,9,9,9,10,10,10,12,12,12, +13,13,13,13,14,14,14,15,16,16,16,17,17,17,17,18, +18,18,18,19,19,19,19,19,20,20,20,21,21,21,21,22, +22,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23, +23,23,23,23,23,23,23,22,22,22,22,22,22,22,22,22, +22,21,21,21,21,20,20,20,19,19,19,19,19,18,18,18, +18,17,17,17,17,16,16,16,15,14,14,14,14,13,13,13, +12,12,12,10,10,10,9,9,9,8,8,8,7,7,7,6, +6,6,5,4,4,0,0,0,1,1,1,2,2,2,3,24, +24,25,25,25,26,26,26,27,27,27,27,29,28,28,30,30, +30,30,31,31,31,31,32,32,32,32,33,33,33,35,35,34, +34,34,34,34,34,36,36,36,36,36,36,36,36,36,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,36,36,36,36,36,36,36,36,34,34,34,34,34, +34,35,35,33,33,33,32,32,32,32,31,31,31,31,30,30, +30,30,28,28,29,29,27,27,27,26,26,26,25,25,25,24, +24,3,2,2,2,1,1,1,0,0,0,4,4,4,6,6, +5,6,6,6,7,7,7,8,8,8,9,9,9,10,10,10, +12,12,12,13,13,13,14,14,14,14,15,16,16,16,17,17, +17,17,18,18,18,18,19,19,19,19,19,19,20,20,21,21, +21,21,22,22,22,22,22,22,22,22,22,22,22,22,22,23, +23,23,23,23,23,23,23,22,22,22,22,22,22,22,22,22, +22,22,22,21,21,21,21,20,20,20,19,19,19,19,19,18, +18,18,18,18,17,17,17,17,16,16,16,15,14,14,14,13, +13,13,12,12,12,12,10,10,10,9,9,9,8,8,8,7, +7,7,6,6,6,4,4,4,0,0,0,1,1,1,2,2, +2,3,24,24,25,25,25,26,26,26,27,27,27,27,29,28, +28,30,30,30,30,31,31,31,31,32,32,32,32,33,33,33, +35,35,34,34,34,34,34,34,36,36,36,36,36,36,36,36, +36,36,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,36,36,36,36,36,36,36,36,36,34,34,34, +34,34,34,35,35,33,33,33,32,32,32,32,32,31,31,31, +31,30,30,30,28,28,29,29,27,27,27,26,26,26,26,25, +25,25,24,24,3,2,2,2,1,1,1,0,0,0,4,4, +4,4,4,6,6,6,7,7,7,8,8,8,9,9,9,11, +10,10,10,12,12,12,13,13,13,14,14,14,14,15,16,16, +16,17,17,17,17,18,18,18,18,19,19,19,19,19,19,20, +20,21,21,21,21,21,22,22,22,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, +22,22,22,22,22,21,21,21,21,20,20,20,19,19,19,19, +19,18,18,18,18,18,17,17,17,17,16,16,16,15,14,14, +14,14,13,13,13,12,12,12,10,10,10,9,9,9,9,8, +8,8,7,7,7,6,6,6,4,4,4,0,0,0,1,1, +1,2,2,2,24,24,24,25,25,25,26,26,26,27,27,27, +29,29,28,28,30,30,30,30,31,31,31,31,32,32,32,32, +33,33,33,35,35,34,34,34,34,34,34,34,36,36,36,36, +36,36,36,36,36,36,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,36,36,36,36,36,36,36,36,36,36,34, +34,34,34,34,34,34,35,35,33,33,33,32,32,32,32,31, +31,31,31,30,30,30,30,28,28,29,27,27,27,27,26,26, +26,25,25,25,24,24,3,2,2,2,1,1,1,0,0,0, +0,0,0,4,4,4,6,6,6,7,7,7,8,8,8,9, +9,9,11,10,10,10,12,12,12,13,13,13,14,14,14,14, +15,16,16,16,17,17,17,17,18,18,18,18,18,19,19,19, +19,19,20,20,20,21,21,21,21,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,21,21,21,21,21,20,20,19,19, +19,19,19,19,18,18,18,18,17,17,17,17,17,16,16,16, +15,14,14,14,13,13,13,13,12,12,12,10,10,10,9,9, +9,8,8,8,7,7,7,6,6,6,5,4,4,0,0,0, +1,1,1,2,2,2,3,24,24,24,25,25,25,26,26,26, +27,27,27,29,29,28,28,30,30,30,30,31,31,31,31,32, +32,32,32,33,33,33,33,35,35,34,34,34,34,34,34,36, +36,36,36,36,36,36,36,36,36,36,36,36,37,37,37,37, +37,37,37,37,37,36,36,36,36,36,36,36,36,36,36,36, +36,34,34,34,34,34,34,34,35,35,33,33,33,32,32,32, +32,32,31,31,31,31,30,30,30,28,28,29,29,27,27,27, +26,26,26,26,25,25,25,24,24,3,2,2,2,1,1,1, +1,1,1,0,0,0,4,4,4,6,6,6,7,7,7,8, +8,8,9,9,9,10,10,10,12,12,12,12,13,13,13,14, +14,14,14,15,16,16,16,17,17,17,17,18,18,18,18,18, +19,19,19,19,19,19,20,20,21,21,21,21,21,22,22,22, +22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,22,22,21,21,21,21,21,20,20, +19,19,19,19,19,19,18,18,18,18,18,17,17,17,17,16, +16,16,15,14,14,14,14,13,13,13,12,12,12,12,10,10, +10,9,9,9,8,8,8,7,7,7,6,6,6,4,4,4, +0,0,0,1,1,1,2,2,2,3,24,24,25,25,25,25, +26,26,26,27,27,27,29,29,28,28,30,30,30,30,31,31, +31,31,32,32,32,32,32,33,33,33,35,35,34,34,34,34, +34,34,34,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,34,34,34,34,34,34,34,35,35,33,33,33,32, +32,32,32,32,31,31,31,31,30,30,30,30,28,28,29,29, +27,27,27,26,26,26,25,25,25,25,24,24,3,2,2,2, +2,2,2,1,1,1,0,0,0,4,4,5,6,6,6,7, +7,7,8,8,8,9,9,9,10,10,10,12,12,12,12,13, +13,13,14,14,14,14,15,16,16,16,17,17,17,17,18,18, +18,18,18,19,19,19,19,19,19,20,20,20,21,21,21,21, +21,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,22,22,22,21,21,21,21,21,21, +20,20,20,19,19,19,19,19,18,18,18,18,18,17,17,17, +17,17,16,16,16,15,14,14,14,13,13,13,13,12,12,12, +10,10,10,11,9,9,9,8,8,8,7,7,7,6,6,6, +4,4,4,0,0,0,1,1,1,2,2,2,3,24,24,25, +25,25,25,26,26,26,27,27,27,29,29,28,28,30,30,30, +30,31,31,31,31,32,32,32,32,32,33,33,33,35,35,35, +34,34,34,34,34,34,34,36,36,36,36,36,36,36,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,36,36,34,34,34,34,34,34,34,35,35,33,33, +33,33,32,32,32,32,31,31,31,31,31,30,30,30,30,28, +28,29,27,27,27,27,26,26,26,25,25,25,24,24,24,3, +24,3,2,2,2,1,1,1,1,0,0,0,4,4,5,6, +6,6,7,7,7,8,8,8,9,9,9,10,10,10,10,12, +12,12,13,13,13,14,14,14,14,15,16,16,16,17,17,17, +17,17,18,18,18,18,18,19,19,19,19,19,19,20,20,20, +21,21,21,21,21,22,22,22,22,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,22,22,22,22,22,21,21,21,21, +21,21,20,20,20,19,19,19,19,19,19,18,18,18,18,18, +17,17,17,17,16,16,16,15,14,14,14,14,13,13,13,13, +12,12,12,10,10,10,9,9,9,9,8,8,8,7,7,7, +6,6,6,4,4,4,0,0,0,1,1,1,2,2,2,3, +24,24,25,25,25,25,26,26,26,27,27,27,27,29,28,28, +30,30,30,30,31,31,31,31,32,32,32,32,32,33,33,33, +33,35,35,34,34,34,34,34,34,34,34,36,36,36,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,36,36,36,36,34,34,34,34,34,34,34,34,35, +35,33,33,33,32,32,32,32,32,31,31,31,31,30,30,30, +30,28,28,29,29,27,27,27,27,26,26,26,25,25,25,24, +25,25,24,24,3,2,2,2,1,1,1,0,0,0,0,4, +4,5,6,6,6,7,7,7,8,8,8,9,9,9,10,10, +10,10,12,12,12,13,13,13,13,14,14,14,15,16,16,16, +17,17,17,17,17,18,18,18,18,18,19,19,19,19,19,19, +20,20,20,21,21,21,21,21,21,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,22,22,22,22,22,22,22,21,21, +21,21,21,21,20,20,20,19,19,19,19,19,19,18,18,18, +18,18,18,17,17,17,17,16,16,16,15,14,14,14,14,13, +13,13,12,12,12,12,10,10,10,9,9,9,8,8,8,8, +7,7,7,6,6,6,4,4,4,0,0,0,1,1,1,2, +2,2,3,24,24,25,25,25,25,26,26,26,27,27,27,27, +29,28,28,30,30,30,30,31,31,31,31,31,32,32,32,32, +32,33,33,33,35,35,34,34,34,34,34,34,34,34,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,36,36,36,36,36,36,34,34,34,34,34,34,34, +34,35,35,33,33,33,33,32,32,32,32,32,31,31,31,31, +30,30,30,30,28,28,29,29,27,27,27,26,26,26,26,25, +26,26,25,25,25,24,24,3,2,2,2,1,1,1,0,0, +0,0,4,4,5,6,6,6,7,7,7,8,8,8,9,9, +9,11,10,10,10,12,12,12,13,13,13,13,14,14,14,14, +16,16,16,16,17,17,17,17,18,18,18,18,18,19,19,19, +19,19,19,19,20,20,20,21,21,21,21,21,21,22,22,22, +22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, +21,21,21,21,21,21,20,20,20,20,19,19,19,19,19,19, +18,18,18,18,18,17,17,17,17,17,16,16,16,15,14,14, +14,14,13,13,13,12,12,12,12,10,10,10,9,9,9,8, +8,8,7,7,7,7,6,6,6,4,4,4,0,0,0,1, +1,1,2,2,2,3,24,24,25,25,25,25,26,26,26,27, +27,27,27,29,28,28,28,30,30,30,30,31,31,31,31,32, +32,32,32,32,33,33,33,35,35,35,34,34,34,34,34,34, +34,34,36,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,36,36,36,36,36,36,36,36,34,34,34,34,34, +34,34,34,35,35,35,33,33,33,32,32,32,32,32,31,31, +31,31,31,30,30,30,30,28,28,29,29,27,27,27,26,26, +27,26,26,26,25,25,25,25,24,24,3,2,2,2,1,1, +1,0,0,0,4,4,4,5,6,6,6,7,7,7,8,8, +8,9,9,9,11,10,10,10,12,12,12,13,13,13,13,14, +14,14,14,15,16,16,16,17,17,17,17,18,18,18,18,18, +18,19,19,19,19,19,19,20,20,20,21,21,21,21,21,21, +21,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, +22,22,21,21,21,21,21,21,21,20,20,20,19,19,19,19, +19,19,19,18,18,18,18,18,17,17,17,17,17,16,16,16, +15,14,14,14,14,13,13,13,12,12,12,12,10,10,10,9, +9,9,8,8,8,7,7,7,7,6,6,6,4,4,4,0, +0,0,1,1,1,2,2,2,3,24,24,24,25,25,25,26, +26,26,27,27,27,27,29,29,28,28,30,30,30,30,31,31, +31,31,32,32,32,32,32,33,33,33,33,35,35,34,34,34, +34,34,34,34,34,34,36,36,36,36,36,36,36,36,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,34,34, +34,34,34,34,34,34,35,35,35,33,33,33,32,32,32,32, +32,31,31,31,31,31,30,30,30,30,28,28,29,27,27,27, +27,27,27,27,26,26,26,25,25,25,25,24,24,3,2,2, +2,1,1,1,0,0,0,4,4,4,5,6,6,6,7,7, +7,8,8,8,9,9,9,11,10,10,10,12,12,12,13,13, +13,13,14,14,14,14,15,16,16,16,17,17,17,17,18,18, +18,18,18,18,19,19,19,19,19,19,20,20,20,20,21,21, +21,21,21,21,21,22,22,22,22,22,22,22,22,22,22,22, +22,22,22,22,22,21,21,21,21,21,21,21,20,20,20,19, +19,19,19,19,19,19,18,18,18,18,18,17,17,17,17,16, +16,16,16,15,14,14,14,13,13,13,13,12,12,12,12,10, +10,10,9,9,9,8,8,8,7,7,7,7,6,6,6,4, +4,4,0,0,0,1,1,1,2,2,2,3,24,24,24,25, +25,25,26,26,26,27,27,27,27,29,29,28,28,30,30,30, +30,31,31,31,31,32,32,32,32,32,33,33,33,33,35,35, +34,34,34,34,34,34,34,34,34,36,36,36,36,36,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, +34,34,34,34,34,34,34,34,34,35,35,33,33,33,33,32, +32,32,32,32,31,31,31,31,30,30,30,30,30,28,28,29, +28,28,29,27,27,27,27,26,26,26,25,25,25,25,24,24, +3,2,2,2,1,1,1,0,0,0,4,4,4,5,6,6, +6,7,7,7,8,8,8,9,9,9,11,10,10,10,12,12, +12,13,13,13,13,14,14,14,14,15,16,16,16,17,17,17, +17,17,18,18,18,18,18,19,19,19,19,19,19,20,20,20, +20,21,21,21,21,21,21,21,22,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,21,21,21,21,21,21,21,20,20, +20,20,19,19,19,19,19,19,18,18,18,18,18,18,17,17, +17,17,16,16,16,15,14,14,14,14,13,13,13,13,12,12, +12,10,10,10,10,9,9,9,8,8,8,7,7,7,7,6, +6,6,4,4,4,0,0,0,1,1,1,2,2,2,3,24, +24,24,25,25,25,26,26,26,27,27,27,27,29,29,28,28, +30,30,30,30,31,31,31,31,32,32,32,32,32,33,33,33, +33,35,35,35,34,34,34,34,34,34,34,34,36,36,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,34,34,34,34,34,34,34,34,34,35,35,33,33, +33,33,32,32,32,32,32,31,31,31,31,30,30,30,30,28, +30,30,28,28,28,29,27,27,27,27,26,26,26,25,25,25, +25,24,24,3,2,2,2,1,1,1,0,0,0,4,4,4, +6,6,6,6,7,7,7,8,8,8,9,9,9,11,10,10, +10,12,12,12,13,13,13,13,14,14,14,14,15,16,16,16, +17,17,17,17,18,18,18,18,18,18,19,19,19,19,19,19, +20,20,20,20,21,21,21,21,21,21,21,22,22,22,22,22, +22,22,22,22,22,22,22,22,22,22,21,21,21,21,21,21, +21,20,20,20,20,19,19,19,19,19,19,18,18,18,18,18, +18,17,17,17,17,16,16,16,15,14,14,14,14,13,13,13, +13,12,12,12,10,10,10,10,9,9,9,8,8,8,7,7, +7,6,6,6,6,4,4,4,0,0,0,1,1,1,2,2, +2,3,24,24,25,25,25,25,26,26,26,27,27,27,27,29, +29,28,28,30,30,30,30,31,31,31,31,32,32,32,32,32, +33,33,33,33,35,35,34,34,34,34,34,34,34,34,34,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,36,36,36,34,34,34,34,34,34,34,34,34,35, +35,33,33,33,33,32,32,32,32,32,31,31,31,31,30,30, +31,30,30,30,30,28,28,29,29,27,27,27,27,26,26,26, +25,25,25,24,24,24,3,2,2,2,1,1,1,0,0,0, +4,4,4,6,6,6,6,7,7,7,8,8,8,9,9,9, +10,10,10,10,12,12,12,13,13,13,13,14,14,14,14,16, +16,16,16,17,17,17,17,18,18,18,18,18,18,19,19,19, +19,19,19,20,20,20,20,21,21,21,21,21,21,21,22,22, +22,22,22,22,22,22,22,22,22,22,22,22,22,21,21,21, +21,21,21,21,20,20,20,20,19,19,19,19,19,19,18,18, +18,18,18,18,17,17,17,17,16,16,16,15,14,14,14,14, +13,13,13,13,12,12,12,10,10,10,11,9,9,9,8,8, +8,7,7,7,6,6,6,5,4,4,4,0,0,0,1,1, +1,2,2,2,3,24,24,25,25,25,25,26,26,26,27,27, +27,27,29,28,28,28,30,30,30,30,31,31,31,31,32,32, +32,32,32,33,33,33,33,35,35,34,34,34,34,34,34,34, +34,34,36,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,36,36,36,36,36,36,34,34,34,34,34,34,34, +34,34,35,35,33,33,33,33,32,32,32,32,32,31,31,31, +31,31,31,31,30,30,30,30,28,28,29,29,27,27,27,27, +26,26,26,25,25,25,24,24,24,3,2,2,2,1,1,1, +0,0,0,4,4,4,6,6,6,7,7,7,8,8,8,8, +9,9,9,10,10,10,12,12,12,12,13,13,13,14,14,14, +14,15,16,16,16,17,17,17,17,17,18,18,18,18,18,19, +19,19,19,19,19,19,20,20,20,21,21,21,21,21,21,21, +22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, +22,21,21,21,21,21,21,20,20,20,20,19,19,19,19,19, +19,18,18,18,18,18,18,17,17,17,17,16,16,16,15,14, +14,14,14,13,13,13,13,12,12,12,10,10,10,11,9,9, +9,8,8,8,7,7,7,6,6,6,5,4,4,0,0,0, +0,1,1,1,2,2,2,3,24,24,25,25,25,26,26,26, +26,27,27,27,29,29,28,28,30,30,30,30,31,31,31,31, +31,32,32,32,32,32,33,33,33,35,35,35,34,34,34,34, +34,34,34,34,36,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,34,34,34,34, +34,34,34,34,34,35,35,33,33,33,33,32,32,32,32,32, +32,32,32,31,31,31,31,30,30,30,30,28,28,29,29,27, +27,27,26,26,26,26,25,25,25,24,24,24,2,2,2,2, +1,1,1,0,0,0,4,4,4,6,6,6,7,7,7,8, +8,8,9,9,9,9,10,10,10,12,12,12,13,13,13,13, +14,14,14,14,15,16,16,16,17,17,17,17,17,18,18,18, +18,18,19,19,19,19,19,19,20,20,20,21,21,21,21,21, +21,21,22,22,22,22,22,22,22,22,22,22,22,22,22,22, +22,22,22,22,21,21,21,21,21,21,21,20,20,20,19,19, +19,19,19,19,18,18,18,18,18,17,17,17,17,17,16,16, +16,15,14,14,14,14,13,13,13,13,12,12,12,10,10,10, +9,9,9,9,8,8,8,7,7,7,6,6,6,4,4,4, +0,0,0,1,1,1,2,2,2,2,24,24,24,25,25,25, +26,26,26,26,27,27,27,29,29,28,28,30,30,30,30,31, +31,31,31,32,32,32,32,32,33,33,33,33,35,35,34,34, +34,34,34,34,34,34,34,36,36,36,36,36,36,36,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,34, +34,34,34,34,34,34,34,34,35,35,33,33,33,33,32,32, +33,32,32,32,32,32,31,31,31,31,30,30,30,30,28,28, +29,29,27,27,27,26,26,26,26,25,25,25,24,24,3,2, +2,2,1,1,1,0,0,0,4,4,4,5,6,6,6,7, +7,7,8,8,8,9,9,9,10,10,10,10,12,12,12,13, +13,13,13,14,14,14,15,16,16,16,17,17,17,17,17,18, +18,18,18,18,19,19,19,19,19,19,20,20,20,20,21,21, +21,21,21,21,22,22,22,22,22,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,22,21,21,21,21,21,21,20,20, +20,19,19,19,19,19,19,18,18,18,18,18,17,17,17,17, +17,16,16,16,15,14,14,14,14,13,13,13,12,12,12,12, +10,10,10,9,9,9,8,8,8,7,7,7,7,6,6,6, +4,4,4,0,0,0,1,1,1,2,2,2,3,24,24,25, +25,25,25,26,26,26,27,27,27,27,29,28,28,30,30,30, +30,31,31,31,31,31,32,32,32,32,32,33,33,33,35,35, +35,34,34,34,34,34,34,34,34,36,36,36,36,36,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,34,34,34,34,34,34,34,34,35,35,33,33,33, +33,33,33,33,32,32,32,32,31,31,31,31,31,30,30,30, +30,28,28,29,27,27,27,27,26,26,26,25,25,25,25,24, +24,3,2,2,2,1,1,1,0,0,0,4,4,4,6,6, +6,7,7,7,8,8,8,8,9,9,9,10,10,10,12,12, +12,13,13,13,13,14,14,14,14,15,16,16,16,17,17,17, +17,18,18,18,18,18,19,19,19,19,19,19,20,20,20,21, +21,21,21,21,21,22,22,22,22,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,22,22,22,22,21,21,21,21,21, +21,20,20,20,19,19,19,19,19,19,18,18,18,18,18,17, +17,17,17,17,16,16,16,15,14,14,14,13,13,13,13,12, +12,12,10,10,10,10,9,9,9,8,8,8,7,7,7,6, +6,6,5,4,4,0,0,0,1,1,1,1,2,2,2,3, +24,24,25,25,25,26,26,26,26,27,27,27,29,29,28,28, +30,30,30,30,31,31,31,31,32,32,32,32,32,33,33,33, +35,35,35,34,34,34,34,34,34,34,36,36,36,36,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,36,36,36,34,34,34,34,34,34,34,34,35,35, +34,35,35,33,33,33,32,32,32,32,32,31,31,31,31,30, +30,30,30,28,28,29,29,27,27,27,26,26,26,26,25,25, +25,24,24,3,2,2,2,1,1,1,0,0,0,0,4,4, +5,6,6,6,7,7,7,8,8,8,9,9,9,10,10,10, +12,12,12,12,13,13,13,14,14,14,14,15,16,16,16,17, +17,17,17,18,18,18,18,18,19,19,19,19,19,19,20,20, +20,21,21,21,21,21,21,22,22,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,21, +21,21,21,21,20,20,20,19,19,19,19,19,19,18,18,18, +18,18,17,17,17,17,16,16,16,15,14,14,14,14,13,13, +13,13,12,12,12,10,10,10,9,9,9,8,8,8,8,7, +7,7,6,6,6,4,4,4,0,0,0,1,1,1,2,2, +2,3,24,24,25,25,25,25,26,26,26,27,27,27,29,29, +28,28,30,30,30,30,31,31,31,31,32,32,32,32,32,33, +33,33,35,35,35,34,34,34,34,34,34,34,36,36,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,36,36,36,36,36,36,34,34,34,34,34,34,34, +34,34,34,34,35,35,33,33,33,32,32,32,32,32,31,31, +31,31,30,30,30,30,28,28,29,29,27,27,27,26,26,26, +25,25,25,25,24,24,3,2,2,2,1,1,1,0,0,0, +4,4,4,6,6,6,7,7,7,8,8,8,9,9,9,11, +10,10,10,12,12,12,13,13,13,14,14,14,14,15,16,16, +16,17,17,17,17,18,18,18,18,18,19,19,19,19,19,19, +20,20,20,21,21,21,21,21,22,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, +22,22,21,21,21,21,21,20,20,20,19,19,19,19,19,19, +18,18,18,18,18,17,17,17,17,16,16,16,15,14,14,14, +14,13,13,13,12,12,12,12,10,10,10,9,9,9,8,8, +8,7,7,7,6,6,6,4,4,4,0,0,0,1,1,1, +2,2,2,3,24,24,24,25,25,25,26,26,26,27,27,27, +27,29,28,28,30,30,30,30,31,31,31,31,32,32,32,32, +32,33,33,33,35,35,35,34,34,34,34,34,34,34,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,34,34,34,34, +34,34,34,34,34,34,35,35,33,33,33,33,32,32,32,32, +31,31,31,31,31,30,30,30,28,28,29,29,27,27,27,26, +26,26,26,25,25,25,24,24,3,2,2,2,1,1,1,0, +0,0,4,4,4,6,6,6,7,7,7,8,8,8,9,9, +9,9,10,10,10,12,12,12,13,13,13,13,14,14,14,15, +16,16,16,17,17,17,17,18,18,18,18,18,19,19,19,19, +19,19,20,20,20,21,21,21,21,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, +22,22,22,22,22,21,21,21,21,21,20,20,20,19,19,19, +19,19,18,18,18,18,18,17,17,17,17,17,16,16,15,14, +14,14,14,13,13,13,13,12,12,12,10,10,10,9,9,9, +8,8,8,7,7,7,6,6,6,5,4,4,0,0,0,1, +1,1,1,2,2,2,24,24,24,25,25,25,26,26,26,27, +27,27,27,29,28,28,30,30,30,30,31,31,31,31,32,32, +32,32,32,33,33,33,35,35,34,34,34,34,34,34,34,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,34, +36,36,34,34,34,34,34,34,34,35,35,33,33,33,32,32, +32,32,32,31,31,31,31,30,30,30,30,28,28,29,27,27, +27,27,26,26,26,25,25,25,24,24,3,2,2,2,1,1, +1,0,0,0,4,4,4,6,6,6,7,7,7,8,8,8, +9,9,9,9,10,10,10,12,12,12,13,13,13,14,14,14, +14,15,16,16,16,17,17,17,17,18,18,18,18,18,19,19, +19,19,19,20,20,20,21,21,21,21,21,22,22,22,22,22, +22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,22,21,21,21,21,21,20,20,19, +19,19,19,19,19,18,18,18,18,18,17,17,17,17,16,16, +16,15,14,14,14,13,13,13,13,12,12,12,10,10,10,9, +9,9,8,8,8,8,7,7,7,6,6,6,4,4,4,0, +0,0,1,1,1,2,2,2,24,24,24,25,25,25,26,26, +26,27,27,27,27,29,28,28,30,30,30,30,31,31,31,31, +32,32,32,32,32,33,33,33,35,35,34,34,34,34,34,34, +34,36,36,36,36,36,36,36,36,36,36,36,36,37,37,37, +37,37,37,37,37,37,36,36,36,36,36,36,36,36,36,36, +36,36,36,36,36,34,34,34,34,34,34,35,35,35,33,33, +33,32,32,32,32,31,31,31,31,30,30,30,30,28,28,29, +27,27,27,27,26,26,26,25,25,25,24,24,3,2,2,2, +1,1,1,0,0,0,4,4,4,6,6,6,7,7,7,8, +8,8,9,9,9,11,10,10,10,12,12,12,13,13,13,14, +14,14,14,15,16,16,16,17,17,17,17,18,18,18,18,19, +19,19,19,19,19,20,20,21,21,21,21,21,22,22,22,22, +22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,22,22,22,22,21,21,21,21,20, +20,20,19,19,19,19,19,18,18,18,18,18,17,17,17,17, +16,16,16,15,14,14,14,14,13,13,13,12,12,12,10,10, +10,9,9,9,9,8,8,8,7,7,7,6,6,6,4,4, +4,0,0,0,1,1,1,2,2,2,24,24,24,25,25,25, +26,26,26,27,27,27,29,29,28,28,30,30,30,30,31,31, +31,31,32,32,32,32,33,33,33,35,35,34,34,34,34,34, +34,34,36,36,36,36,36,36,36,36,36,36,36,37,37,37, +37,37,37,37,37,37,37,37,37,37,36,36,36,36,36,36, +36,36,36,36,36,36,36,36,34,34,34,34,34,34,35,35, +33,33,33,32,32,32,32,31,31,31,31,30,30,30,30,28, +28,29,27,27,27,27,26,26,26,25,25,25,24,24,3,2, +2,2,1,1,1,0,0,0,4,4,4,6,6,6,7,7, +7,8,8,8,9,9,9,10,10,10,12,12,12,12,13,13, +13,14,14,14,14,16,16,16,17,17,17,17,18,18,18,18, +18,19,19,19,19,19,20,20,20,21,21,21,21,22,22,22, +22,22,22,22,22,22,22,22,22,22,22,23,23,23,23,23, +22,22,22,22,22,22,22,22,22,22,22,22,22,22,21,21, +21,21,20,20,19,19,19,19,19,19,18,18,18,18,17,17, +17,17,16,16,16,15,14,14,14,14,13,13,13,12,12,12, +10,10,10,9,9,9,9,8,8,8,7,7,7,6,6,5, +4,4,0,0,0,1,1,1,2,2,2,3,24,24,25,25, +25,25,26,26,26,27,27,27,29,29,28,28,30,30,30,31, +31,31,31,32,32,32,32,32,33,33,33,35,35,34,34,34, +34,34,34,36,36,36,36,36,36,36,36,36,36,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,36,36, +37,36,36,36,36,36,36,36,36,36,34,34,34,34,34,34, +35,35,33,33,33,32,32,32,32,31,31,31,31,30,30,30, +30,28,28,29,27,27,27,27,26,26,26,25,25,25,24,24, +3,2,2,2,1,1,1,0,0,0,4,4,4,6,6,6, +7,7,7,8,8,8,9,9,9,10,10,10,12,12,12,13, +13,13,13,14,14,14,15,16,16,16,17,17,17,17,18,18, +18,18,19,19,19,19,19,19,20,20,21,21,21,21,22,22, +22,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23, +23,23,23,23,23,22,22,22,22,22,22,22,22,22,22,22, +21,21,21,21,20,20,20,19,19,19,19,19,18,18,18,18, +17,17,17,17,16,16,16,15,14,14,14,14,13,13,13,12, +12,12,10,10,10,9,9,9,8,8,8,7,7,7,6,6, +6,5,4,4,0,0,0,1,1,1,2,2,2,3,24,24, +25,25,25,26,26,26,27,27,27,27,29,28,28,30,30,30, +30,31,31,31,31,32,32,32,32,33,33,33,35,35,34,34, +34,34,34,34,36,36,36,36,36,36,36,36,36,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,36,36,36,36,36,36,36,36,36,34,34,34,34, +34,34,35,35,33,33,33,32,32,32,32,31,31,31,31,30, +30,30,30,28,28,29,27,27,27,26,26,26,26,25,25,25, +24,24,3,2,2,2,1,1,1,0,0,0,4,4,5,6, +6,7,7,7,8,8,8,9,9,9,11,10,10,10,12,12, +12,13,13,13,14,14,14,14,16,16,16,17,17,17,17,18, +18,18,18,18,19,19,19,19,19,20,20,21,21,21,21,22, +22,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23, +23,23,23,23,23,23,23,23,22,22,22,22,22,22,22,22, +22,22,21,21,21,21,20,20,20,19,19,19,19,19,18,18, +18,18,17,17,17,17,16,16,16,15,14,14,14,13,13,13, +13,12,12,12,10,10,10,9,9,9,8,8,8,7,7,7, +6,6,6,4,4,4,0,0,0,1,1,1,2,2,2,24, +24,24,25,25,25,26,26,26,27,27,27,29,29,28,28,30, +30,30,31,31,31,31,32,32,32,32,32,33,33,33,35,34, +34,34,34,34,34,36,36,36,36,36,36,36,36,36,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,36,36,36,36,36,36,36,36,34,34, +34,34,34,34,35,35,33,33,33,32,32,32,32,31,31,31, +31,30,30,30,28,28,29,29,27,27,27,26,26,26,25,25, +25,24,24,24,2,2,2,1,1,1,0,0,0,4,4,4, +6,6,6,7,7,7,8,8,8,9,9,9,10,10,10,12, +12,12,13,13,13,14,14,14,14,15,16,16,16,17,17,17, +17,18,18,18,18,19,19,19,19,19,20,20,21,21,21,21, +22,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23, +23,23,23,23,23,23,23,23,23,23,23,22,22,22,22,22, +22,22,22,22,21,21,21,21,20,20,19,19,19,19,19,18, +18,18,18,18,17,17,17,17,16,16,16,15,14,14,14,13, +13,13,12,12,12,10,10,10,11,9,9,9,8,8,8,7, +7,6,6,6,5,4,4,0,0,0,1,1,1,2,2,2, +3,24,24,25,25,25,26,26,26,27,27,27,27,29,28,28, +30,30,30,30,31,31,31,31,32,32,32,32,33,33,33,35, +35,34,34,34,34,34,36,36,36,36,36,36,36,36,36,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,36,36,36,36,36,36,36,36, +34,34,34,34,34,35,35,33,33,33,32,32,32,32,31,31, +31,31,30,30,30,30,28,28,29,27,27,27,27,26,26,26, +25,25,25,24,24,3,2,2,2,1,1,1,0,0,0,4, +4,6,6,6,7,7,7,8,8,8,9,9,9,10,10,10, +12,12,12,13,13,13,13,14,14,14,15,16,16,16,17,17, +17,17,18,18,18,18,19,19,19,19,19,20,20,21,21,21, +21,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23, +23,23,23,23,23,23,23,23,23,23,23,23,23,22,22,22, +22,22,22,22,22,22,21,21,21,21,20,20,19,19,19,19, +19,18,18,18,18,17,17,17,17,16,16,16,15,14,14,14, +13,13,13,13,12,12,12,10,10,10,9,9,9,8,8,8, +7,7,7,6,6,6,4,4,4,0,0,0,1,1,1,2, +2,3,24,24,25,25,25,25,26,26,26,27,27,27,29,28, +28,30,30,30,30,31,31,31,31,32,32,32,32,33,33,33, +35,35,34,34,34,34,34,36,36,36,36,36,36,36,36,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,36,36,36,36,36,36, +36,34,34,34,34,34,34,35,35,33,33,33,32,32,32,32, +31,31,31,31,30,30,30,28,28,29,29,27,27,27,26,26, +26,25,25,25,24,24,3,2,2,2,1,1,1,0,0,0, +4,4,5,6,6,6,7,7,8,8,8,9,9,9,11,10, +10,10,12,12,12,13,13,13,14,14,14,15,16,16,16,17, +17,17,17,18,18,18,18,19,19,19,19,19,20,20,21,21, +21,21,22,22,22,22,22,22,22,22,22,23,23,23,23,23, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,22, +22,22,22,22,22,22,22,22,21,21,21,20,20,20,19,19, +19,19,19,18,18,18,18,17,17,17,17,16,16,16,14,14, +14,14,13,13,13,12,12,12,10,10,10,9,9,9,8,8, +8,7,7,7,6,6,6,4,4,4,0,0,0,1,1,1, +2,2,2,3,24,24,25,25,25,26,26,26,27,27,27,29, +29,28,30,30,30,30,31,31,31,31,32,32,32,32,33,33, +33,35,35,34,34,34,34,34,36,36,36,36,36,36,36,36, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,36,36,36,36,36, +36,36,36,34,34,34,34,34,35,35,33,33,33,32,32,32, +32,31,31,31,31,30,30,30,30,28,29,29,27,27,27,26, +26,26,25,25,25,24,24,3,2,2,2,1,1,1,0,0, +0,4,4,4,6,6,6,7,7,7,8,8,8,9,9,9, +10,10,10,12,12,12,13,13,13,14,14,14,15,16,16,16, +17,17,17,17,18,18,18,18,19,19,19,19,19,20,20,21, +21,21,21,22,22,22,22,22,22,22,22,22,23,23,23,23, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +22,22,22,22,22,22,22,22,22,21,21,21,21,20,20,19, +19,19,19,19,18,18,18,18,17,17,17,17,16,16,16,15, +14,14,14,13,13,13,12,12,12,10,10,10,11,9,9,9, +8,8,7,7,7,6,6,6,5,4,4,0,0,0,1,1, +1,2,2,2,3,24,24,25,25,25,26,26,26,27,27,27, +29,29,28,28,30,30,30,31,31,31,31,32,32,32,32,33, +33,33,35,35,34,34,34,34,34,36,36,36,36,36,36,36, +36,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,36,36,36,36, +36,36,36,36,34,34,34,34,34,35,35,33,33,33,32,32, +32,32,31,31,31,31,30,30,30,30,28,29,29,27,27,27, +26,26,26,25,25,25,24,24,3,2,2,2,1,1,1,0, +0,0,4,4,4,6,6,6,7,7,7,8,8,8,9,9, +9,10,10,10,12,12,12,13,13,13,14,14,14,15,16,16, +16,17,17,17,17,18,18,18,18,19,19,19,19,19,20,20, +21,21,21,21,22,22,22,22,22,22,22,22,23,23,23,23, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,22,22,22,22,22,22,22,22,21,21,21,21,20,20, +19,19,19,19,19,18,18,18,18,17,17,17,17,16,16,16, +15,14,14,14,13,13,13,12,12,12,12,10,10,11,9,9, +9,8,8,7,7,7,6,6,6,5,4,4,0,0,0,1, +1,1,2,2,2,3,24,24,25,25,25,26,26,26,27,27, +27,29,29,28,28,30,30,30,31,31,31,31,32,32,32,32, +33,33,33,35,35,34,34,34,34,34,36,36,36,36,36,36, +36,36,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,36,36, +36,36,36,36,36,34,34,34,34,34,35,35,33,33,33,32, +32,32,32,31,31,31,31,30,30,30,30,28,29,29,27,27, +27,26,26,26,25,25,25,24,24,3,2,2,2,1,1,1, +0,0,0,4,4,5,6,6,6,7,7,7,8,8,9,9, +9,11,10,10,12,12,12,12,13,13,13,14,14,14,15,16, +16,16,17,17,17,17,18,18,18,18,19,19,19,19,19,20, +20,21,21,21,21,22,22,22,22,22,22,22,22,23,23,23, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,22,22,22,22,22,22,22,22,21,21,21,21,20, +20,19,19,19,19,19,18,18,18,18,17,17,17,17,16,16, +16,15,14,14,14,13,13,13,12,12,12,10,10,10,11,9, +9,8,8,8,7,7,7,6,6,6,4,4,4,0,0,0, +1,1,1,2,2,2,3,24,24,25,25,25,26,26,26,27, +27,27,29,29,28,30,30,30,30,31,31,31,31,32,32,32, +32,33,33,33,35,35,34,34,34,34,34,36,36,36,36,36, +36,36,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,36,36, +36,36,36,36,36,36,34,34,34,34,34,35,35,33,33,33, +32,32,32,32,31,31,31,31,30,30,30,28,28,29,29,27, +27,27,26,26,26,25,25,25,24,24,3,2,2,2,1,1, +1,0,0,0,4,4,5,6,6,7,7,7,8,8,8,9, +9,9,10,10,10,12,12,12,13,13,13,13,14,14,14,15, +16,16,16,17,17,17,18,18,18,18,18,19,19,19,19,19, +20,20,21,21,21,22,22,22,22,22,22,22,22,22,23,23, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,22,22,22,22,22,22,22,22,21,21,21,21, +20,20,19,19,19,19,19,18,18,18,18,17,17,17,17,16, +16,16,14,14,14,14,13,13,13,12,12,12,10,10,10,9, +9,9,8,8,8,7,7,7,6,6,6,4,4,4,0,0, +0,1,1,1,2,2,2,24,24,25,25,25,25,26,26,26, +27,27,27,29,28,28,30,30,30,30,31,31,31,32,32,32, +32,32,33,33,35,35,34,34,34,34,34,34,36,36,36,36, +36,36,36,37,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36, +36,36,36,36,36,36,34,34,34,34,34,34,35,35,33,33, +33,32,32,32,32,31,31,31,31,30,30,30,28,28,29,27, +27,27,27,26,26,26,25,25,25,24,24,3,2,2,2,1, +1,1,0,0,4,4,4,6,6,6,7,7,7,8,8,8, +9,9,9,10,10,10,12,12,12,13,13,13,14,14,14,14, +15,16,16,17,17,17,17,18,18,18,18,19,19,19,19,19, +20,20,21,21,21,21,22,22,22,22,22,22,22,22,23,23, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,23,22,22,22,22,22,22,22,22,21,21,21, +20,20,20,19,19,19,19,19,18,18,18,18,17,17,17,17, +16,16,15,14,14,14,14,13,13,13,12,12,12,10,10,10, +9,9,9,8,8,8,7,7,7,6,6,6,4,4,4,0, +0,1,1,1,2,2,2,3,24,24,25,25,25,26,26,26, +27,27,27,27,29,28,28,30,30,30,31,31,31,31,32,32, +32,32,33,33,33,35,35,34,34,34,34,34,36,36,36,36, +36,36,36,36,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +36,36,36,36,36,36,36,34,34,34,34,34,34,35,33,33, +33,32,32,32,32,31,31,31,31,30,30,30,30,28,29,29, +27,27,27,26,26,26,25,25,25,24,24,3,2,2,2,1, +1,1,0,0,0,4,4,4,6,6,6,7,7,7,8,8, +8,9,9,9,10,10,10,12,12,12,13,13,13,14,14,14, +15,16,16,16,17,17,17,17,18,18,18,18,19,19,19,19, +19,20,20,21,21,21,21,22,22,22,22,22,22,22,22,23, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,23,22,22,22,22,22,22,22,22,21,21,21, +21,20,20,19,19,19,19,19,18,18,18,18,17,17,17,17, +16,16,16,15,14,14,14,13,13,13,12,12,12,12,10,10, +11,9,9,9,8,8,7,7,7,6,6,6,5,4,4,0, +0,0,1,1,1,2,2,2,3,24,24,25,25,25,26,26, +26,27,27,27,29,29,28,30,30,30,30,31,31,31,31,32, +32,32,32,33,33,33,35,35,34,34,34,34,34,36,36,36, +36,36,36,36,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +36,36,36,36,36,36,36,34,34,34,34,34,34,35,35,33, +33,33,32,32,32,32,31,31,31,31,30,30,30,28,28,29, +27,27,27,27,26,26,26,25,25,25,24,24,3,2,2,2, +1,1,1,0,0,4,4,4,6,6,6,7,7,7,8,8, +8,9,9,9,10,10,10,12,12,12,13,13,13,14,14,14, +14,15,16,16,17,17,17,17,18,18,18,18,18,19,19,19, +19,20,20,20,21,21,21,22,22,22,22,22,22,22,22,22, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,23,22,22,22,22,22,22,22,22,22,21,21, +21,20,20,20,19,19,19,19,18,18,18,18,18,17,17,17, +17,16,16,15,14,14,14,14,13,13,13,12,12,12,10,10, +10,9,9,9,8,8,8,7,7,7,6,6,6,4,4,4, +0,0,1,1,1,2,2,2,3,24,24,25,25,25,26,26, +26,27,27,27,27,29,28,28,30,30,30,30,31,31,31,32, +32,32,32,33,33,33,35,35,34,34,34,34,34,34,36,36, +36,36,36,36,36,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +36,36,36,36,36,36,36,36,34,34,34,34,34,35,35,33, +33,33,32,32,32,32,31,31,31,31,30,30,30,28,28,29, +29,27,27,27,26,26,26,25,25,25,24,24,3,2,2,2, +1,1,1,0,0,0,4,4,5,6,6,6,7,7,8,8, +8,9,9,9,11,10,10,12,12,12,12,13,13,13,14,14, +14,15,16,16,16,17,17,17,17,18,18,18,18,19,19,19, +19,19,20,20,21,21,21,21,22,22,22,22,22,22,22,22, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,23,23,22,22,22,22,22,22,22,22,21,21, +21,21,20,20,19,19,19,19,19,18,18,18,18,17,17,17, +17,16,16,16,15,14,14,14,13,13,13,12,12,12,10,10, +10,9,9,9,8,8,8,7,7,7,6,6,6,4,4,4, +0,0,0,1,1,1,2,2,2,24,24,24,25,25,25,26, +26,26,27,27,27,29,29,28,30,30,30,30,31,31,31,31, +32,32,32,32,33,33,33,35,35,34,34,34,34,34,36,36, +36,36,36,36,36,36,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +36,36,36,36,36,36,36,36,34,34,34,34,34,35,35,33, +33,33,32,32,32,32,31,31,31,31,30,30,30,30,28,28, +29,27,27,27,26,26,26,25,25,25,24,24,24,2,2,2, +1,1,1,0,0,0,4,4,4,6,6,6,7,7,7,8, +8,8,9,9,9,10,10,10,12,12,12,13,13,13,14,14, +14,14,16,16,16,17,17,17,17,18,18,18,18,19,19,19, +19,19,20,20,21,21,21,21,22,22,22,22,22,22,22,22, +22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,23,23,22,22,22,22,22,22,22,22,21,21, +21,21,20,20,19,19,19,19,19,18,18,18,18,17,17,17, +17,16,16,16,15,14,14,14,13,13,13,12,12,12,12,10, +10,11,9,9,9,8,8,8,7,7,7,6,6,5,4,4, +0,0,0,1,1,1,2,2,2,3,24,24,25,25,25,26, +26,26,27,27,27,29,29,28,28,30,30,30,31,31,31,31, +32,32,32,32,33,33,33,35,35,34,34,34,34,34,34,36, +36,36,36,36,36,36,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +36,36,36,36,36,36,36,36,34,34,34,34,34,34,35,33, +33,33,32,32,32,32,31,31,31,31,30,30,30,30,28,28, +29,27,27,27,26,26,26,26,25,25,25,24,24,3,2,2, +2,1,1,0,0,0,4,4,4,6,6,6,7,7,7,8, +8,8,9,9,9,10,10,10,12,12,12,13,13,13,14,14, +14,14,15,16,16,17,17,17,17,18,18,18,18,18,19,19, +19,19,19,20,20,21,21,21,22,22,22,22,22,22,22,22, +22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,23,23,22,22,22,22,22,22,22,22,21,21, +21,21,20,20,19,19,19,19,19,18,18,18,18,17,17,17, +17,16,16,16,15,14,14,14,13,13,13,13,12,12,12,10, +10,10,9,9,9,8,8,8,7,7,7,6,6,6,4,4, +0,0,0,1,1,1,2,2,2,3,24,24,25,25,25,26, +26,26,27,27,27,27,29,28,28,30,30,30,30,31,31,31, +32,32,32,32,32,33,33,35,35,34,34,34,34,34,34,36, +36,36,36,36,36,36,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +36,36,36,36,36,36,36,36,34,34,34,34,34,34,35,33, +33,33,32,32,32,32,31,31,31,31,30,30,30,30,28,28, +29,27,27,27,27,26,26,26,25,25,25,24,24,3,2,2, +2,1,1,1,0,0,4,4,4,6,6,6,7,7,7,8, +8,8,9,9,9,10,10,10,12,12,12,13,13,13,13,14, +14,14,15,16,16,16,17,17,17,17,18,18,18,18,19,19, +19,19,19,20,20,21,21,21,21,22,22,22,22,22,22,22, +22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,23,22,22,22,22,22,22,22,22,22,21,21, +21,21,20,20,19,19,19,19,19,18,18,18,18,17,17,17, +17,16,16,16,15,14,14,14,13,13,13,13,12,12,12,10, +10,10,9,9,9,8,8,8,7,7,7,6,6,6,4,4, +4,0,0,1,1,1,2,2,2,3,24,24,25,25,25,26, +26,26,27,27,27,27,29,28,28,30,30,30,30,31,31,31, +31,32,32,32,32,33,33,33,35,34,34,34,34,34,34,36, +36,36,36,36,36,36,36,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +36,36,36,36,36,36,36,36,34,34,34,34,34,35,35,33, +33,33,32,32,32,32,31,31,31,31,30,30,30,30,28,28, +29,27,27,27,27,26,26,26,25,25,25,24,24,3,2,2, +2,1,1,1,0,0,4,4,4,6,6,6,7,7,7,8, +8,8,9,9,9,10,10,10,12,12,12,13,13,13,13,14, +14,14,15,16,16,16,17,17,17,17,18,18,18,18,19,19, +19,19,19,20,20,21,21,21,21,22,22,22,22,22,22,22, +22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,23,22,22,22,22,22,22,22,22,22,21,21, +21,21,20,20,19,19,19,19,19,18,18,18,18,17,17,17, +17,16,16,16,15,14,14,14,13,13,13,13,12,12,12,10, +10,10,9,9,9,8,8,8,7,7,7,6,6,6,4,4, +4,0,0,0,1,1,2,2,2,3,24,24,25,25,25,26, +26,26,26,27,27,27,29,28,28,30,30,30,30,31,31,31, +31,32,32,32,32,33,33,33,35,35,34,34,34,34,34,36, +36,36,36,36,36,36,36,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +36,36,36,36,36,36,36,36,34,34,34,34,34,35,35,33, +33,33,32,32,32,32,31,31,31,31,30,30,30,30,28,28, +29,27,27,27,26,26,26,26,25,25,25,24,24,3,2,2, +2,1,1,1,0,0,4,4,4,6,6,6,7,7,7,8, +8,8,9,9,9,10,10,10,12,12,12,13,13,13,13,14, +14,14,15,16,16,16,17,17,17,17,18,18,18,18,19,19, +19,19,19,20,20,21,21,21,21,22,22,22,22,22,22,22, +22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,23,22,22,22,22,22,22,22,22,22,21,21, +21,21,20,20,19,19,19,19,19,18,18,18,18,17,17,17, +17,16,16,16,15,14,14,14,13,13,13,13,12,12,12,10, +10,10,9,9,9,8,8,8,7,7,7,6,6,6,4,4, +4,0,0,1,1,1,2,2,2,3,24,24,25,25,25,26, +26,26,26,27,27,27,29,28,28,30,30,30,30,31,31,31, +31,32,32,32,32,33,33,33,35,35,34,34,34,34,34,36, +36,36,36,36,36,36,36,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36, +36,36,36,36,36,36,36,34,34,34,34,34,34,35,35,33, +33,33,32,32,32,32,31,31,31,31,30,30,30,30,28,28, +29,27,27,27,26,26,26,25,25,25,25,24,24,3,2,2, +2,1,1,0,0,0,4,4,4,6,6,6,7,7,7,8, +8,8,9,9,9,10,10,10,12,12,12,13,13,13,13,14, +14,14,15,16,16,16,17,17,17,17,18,18,18,18,19,19, +19,19,19,20,20,21,21,21,21,22,22,22,22,22,22,22, +22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,22,22,22,22,22,22,22,22,22,22,21,21, +21,21,20,20,19,19,19,19,19,18,18,18,18,17,17,17, +17,16,16,16,15,14,14,14,13,13,13,13,12,12,12,10, +10,10,9,9,9,8,8,8,7,7,7,6,6,6,4,4, +0,0,0,1,1,1,2,2,2,3,24,24,25,25,25,26, +26,26,27,27,27,27,29,28,28,30,30,30,30,31,31,31, +31,32,32,32,32,33,33,33,35,35,34,34,34,34,34,36, +36,36,36,36,36,36,36,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36, +36,36,36,36,36,36,36,34,34,34,34,34,34,35,35,33, +33,33,32,32,32,32,31,31,31,31,30,30,30,30,28,29, +29,27,27,27,26,26,26,25,25,25,24,24,24,2,2,2, +1,1,1,0,0,0,4,4,4,6,6,6,7,7,7,8, +8,8,9,9,9,10,10,10,12,12,12,13,13,13,14,14, +14,14,15,16,16,16,17,17,17,17,18,18,18,18,19,19, +19,19,19,20,20,21,21,21,21,22,22,22,22,22,22,22, +22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,22,22,22,22,22,22,22,22,22,22,21,21, +21,20,20,20,19,19,19,19,19,18,18,18,18,17,17,17, +17,16,16,16,15,14,14,14,13,13,13,12,12,12,12,10, +10,11,9,9,9,8,8,8,7,7,7,6,6,5,4,4, +0,0,0,1,1,1,2,2,2,3,24,24,25,25,25,26, +26,26,27,27,27,27,29,28,28,30,30,30,30,31,31,31, +32,32,32,32,32,33,33,33,35,35,34,34,34,34,34,36, +36,36,36,36,36,36,36,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36, +36,36,36,36,36,36,36,34,34,34,34,34,34,35,35,33, +33,33,32,32,32,32,31,31,31,31,30,30,30,28,28,29, +29,27,27,27,26,26,26,25,25,25,24,24,3,2,2,2, +1,1,1,0,0,0,4,4,4,6,6,6,7,7,7,8, +8,8,9,9,9,10,10,10,12,12,12,13,13,13,14,14, +14,14,15,16,16,17,17,17,17,18,18,18,18,18,19,19, +19,19,19,20,20,21,21,21,21,22,22,22,22,22,22,22, +22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,22,22,22,22,22,22,22,22,22,21,21,21, +21,20,20,20,19,19,19,19,18,18,18,18,18,17,17,17, +17,16,16,16,14,14,14,14,13,13,13,12,12,12,10,10, +10,9,9,9,8,8,8,7,7,7,6,6,6,5,4,4, +0,0,0,1,1,1,2,2,2,3,24,24,25,25,25,26, +26,26,27,27,27,29,29,28,28,30,30,30,31,31,31,31, +32,32,32,32,32,33,33,35,35,34,34,34,34,34,34,36, +36,36,36,36,36,36,36,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36, +36,36,36,36,36,36,36,34,34,34,34,34,34,35,35,33, +33,32,32,32,32,32,31,31,31,31,30,30,30,28,28,29, +29,27,27,27,26,26,26,25,25,25,24,24,3,2,2,2, +1,1,1,0,0,0,4,4,5,6,6,6,7,7,7,8, +8,9,9,9,11,10,10,10,12,12,12,13,13,13,14,14, +14,14,16,16,16,17,17,17,17,18,18,18,18,19,19,19, +19,19,20,20,20,21,21,21,21,22,22,22,22,22,22,22, +22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,22,22,22,22,22,22,22,22,22,21,21,21, +21,20,20,19,19,19,19,19,18,18,18,18,18,17,17,17, +17,16,16,15,14,14,14,14,13,13,13,12,12,12,10,10, +10,9,9,9,8,8,8,7,7,7,6,6,6,4,4,4, +0,0,0,1,1,1,2,2,2,3,24,24,25,25,25,26, +26,26,27,27,27,29,29,28,28,30,30,30,31,31,31,31, +32,32,32,32,33,33,33,35,35,34,34,34,34,34,34,36, +36,36,36,36,36,36,36,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36, +36,36,36,36,36,36,36,34,34,34,34,34,35,35,33,33, +33,32,32,32,32,31,31,31,31,30,30,30,30,28,28,29, +27,27,27,27,26,26,26,25,25,25,24,24,3,2,2,2, +1,1,1,0,0,0,4,4,6,6,6,7,7,7,8,8, +8,9,9,9,10,10,10,12,12,12,12,13,13,13,14,14, +14,15,16,16,16,17,17,17,17,18,18,18,18,19,19,19, +19,19,20,20,20,21,21,21,22,22,22,22,22,22,22,22, +22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,22,22,22,22,22,22,22,22,22,21,21,21, +21,20,20,19,19,19,19,19,18,18,18,18,17,17,17,17, +16,16,16,15,14,14,14,13,13,13,13,12,12,12,10,10, +10,9,9,9,8,8,8,7,7,7,6,6,6,4,4,4, +0,0,0,1,1,1,2,2,2,24,24,25,25,25,25,26, +26,26,27,27,27,29,28,28,30,30,30,30,31,31,31,31, +32,32,32,32,33,33,33,35,35,34,34,34,34,34,34,36, +36,36,36,36,36,36,36,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36, +36,36,36,36,36,36,36,34,34,34,34,34,35,35,33,33, +33,32,32,32,32,31,31,31,31,30,30,30,30,28,28,29, +27,27,27,26,26,26,26,25,25,25,24,24,3,2,2,2, +1,1,0,0,0,4,4,4,6,6,6,7,7,7,8,8, +8,9,9,9,10,10,10,12,12,12,13,13,13,13,14,14, +14,15,16,16,16,17,17,17,17,18,18,18,18,19,19,19, +19,19,20,20,21,21,21,21,22,22,22,22,22,22,22,22, +22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,22,22,22,22,22,22,22,22,22,21,21,21, +21,20,20,19,19,19,19,19,18,18,18,18,17,17,17,17, +16,16,16,15,14,14,14,13,13,13,13,12,12,12,10,10, +10,9,9,9,8,8,8,7,7,7,6,6,6,4,4,0, +0,0,1,1,1,2,2,2,3,24,24,25,25,25,26,26, +26,27,27,27,27,29,28,28,30,30,30,30,31,31,31,31, +32,32,32,32,33,33,33,35,35,34,34,34,34,34,36,36, +36,36,36,36,36,36,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36, +36,36,36,36,36,36,34,34,34,34,34,34,35,35,33,33, +33,32,32,32,32,31,31,31,31,30,30,30,28,28,29,29, +27,27,27,26,26,26,25,25,25,24,24,24,2,2,2,1, +1,1,0,0,0,4,4,4,6,6,6,7,7,7,8,8, +8,9,9,9,10,10,10,12,12,12,13,13,13,14,14,14, +14,15,16,16,17,17,17,17,18,18,18,18,18,19,19,19, +19,19,20,20,21,21,21,21,22,22,22,22,22,22,22,22, +22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,22,22,22,22,22,22,22,22,22,21,21,21, +20,20,20,19,19,19,19,19,18,18,18,18,17,17,17,17, +16,16,16,15,14,14,14,13,13,13,12,12,12,12,10,10, +11,9,9,9,8,8,8,7,7,6,6,6,5,4,4,0, +0,0,1,1,1,2,2,2,3,24,24,25,25,25,26,26, +26,27,27,27,29,29,28,28,30,30,30,31,31,31,31,32, +32,32,32,33,33,33,35,35,34,34,34,34,34,34,36,36, +36,36,36,36,36,36,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36, +36,36,36,36,36,36,34,34,34,34,34,34,35,35,33,33, +33,32,32,32,32,31,31,31,31,30,30,30,28,28,29,29, +27,27,27,26,26,26,25,25,25,24,24,3,2,2,2,1, +1,1,0,0,0,4,4,5,6,6,6,7,7,7,8,8, +9,9,9,11,10,10,10,12,12,12,13,13,13,14,14,14, +15,16,16,16,17,17,17,17,18,18,18,18,19,19,19,19, +19,20,20,21,21,21,21,22,22,22,22,22,22,22,22,22, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,22,22,22,22,22,22,22,22,22,21,21,21, +20,20,20,19,19,19,19,19,18,18,18,18,17,17,17,17, +16,16,16,14,14,14,14,13,13,13,12,12,12,10,10,10, +9,9,9,8,8,8,7,7,7,6,6,6,4,4,4,0, +0,0,1,1,1,2,2,2,3,24,24,25,25,25,26,26, +26,27,27,27,29,29,28,28,30,30,30,31,31,31,31,32, +32,32,32,33,33,33,35,35,34,34,34,34,34,34,36,36, +36,36,36,36,36,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36, +36,36,36,36,36,36,34,34,34,34,34,34,35,35,33,33, +33,32,32,32,32,31,31,31,31,30,30,30,28,28,29,29, +27,27,27,26,26,26,25,25,25,24,24,3,2,2,2,1, +1,1,0,0,0,4,4,5,6,6,7,7,7,8,8,8, +9,9,9,10,10,10,12,12,12,13,13,13,13,14,14,14, +15,16,16,16,17,17,17,17,18,18,18,18,19,19,19,19, +19,20,20,21,21,21,21,22,22,22,22,22,22,22,22,22, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,22,22,22,22,22,22,22,22,22,21,21,21, +20,20,20,19,19,19,19,18,18,18,18,18,17,17,17,17, +16,16,15,14,14,14,14,13,13,13,12,12,12,10,10,10, +9,9,9,8,8,8,7,7,7,6,6,6,4,4,4,0, +0,0,1,1,1,2,2,2,24,24,24,25,25,25,26,26, +26,27,27,27,29,28,28,30,30,30,30,31,31,31,31,32, +32,32,32,33,33,33,35,35,34,34,34,34,34,36,36,36, +36,36,36,36,36,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36, +36,36,36,36,36,36,34,34,34,34,34,34,35,35,33,33, +32,32,32,32,32,31,31,31,30,30,30,30,28,28,29,27, +27,27,27,26,26,26,25,25,25,24,24,3,2,2,2,1, +1,1,0,0,4,4,4,6,6,6,7,7,7,8,8,8, +9,9,9,10,10,10,12,12,12,13,13,13,13,14,14,14, +15,16,16,16,17,17,17,18,18,18,18,18,19,19,19,19, +19,20,20,21,21,21,22,22,22,22,22,22,22,22,22,23, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,22,22,22,22,22,22,22,22,22,21,21,21, +20,20,20,19,19,19,19,18,18,18,18,18,17,17,17,17, +16,16,15,14,14,14,14,13,13,13,12,12,12,10,10,10, +9,9,9,8,8,8,7,7,7,6,6,6,4,4,4,0, +0,1,1,1,2,2,2,3,24,24,25,25,25,26,26,26, +27,27,27,27,29,28,28,30,30,30,30,31,31,31,32,32, +32,32,32,33,33,35,35,34,34,34,34,34,34,36,36,36, +36,36,36,36,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36, +36,36,36,36,36,36,34,34,34,34,34,34,35,35,33,33, +32,32,32,32,32,31,31,31,30,30,30,30,28,28,29,27, +27,27,27,26,26,26,25,25,25,24,24,3,2,2,2,1, +1,0,0,0,4,4,4,6,6,6,7,7,7,8,8,8, +9,9,9,10,10,10,12,12,12,13,13,13,14,14,14,14, +15,16,16,17,17,17,17,18,18,18,18,19,19,19,19,19, +20,20,20,21,21,21,22,22,22,22,22,22,22,22,22,23, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,22,22,22,22,22,22,22,22,22,21,21,21, +20,20,19,19,19,19,19,18,18,18,18,18,17,17,17,16, +16,16,15,14,14,14,13,13,13,13,12,12,12,10,10,10, +9,9,9,8,8,8,7,7,7,6,6,6,4,4,0,0, +0,1,1,1,2,2,2,3,24,24,25,25,25,26,26,26, +27,27,27,29,29,28,28,30,30,30,31,31,31,31,32,32, +32,32,33,33,33,35,35,34,34,34,34,34,34,36,36,36, +36,36,36,36,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36, +36,36,36,36,36,36,34,34,34,34,34,34,35,35,33,33, +32,32,32,32,32,31,31,31,30,30,30,30,28,28,29,27, +27,27,26,26,26,26,25,25,25,24,24,3,2,2,1,1, +1,0,0,0,4,4,4,6,6,6,7,7,7,8,8,8, +9,9,9,10,10,10,12,12,12,13,13,13,14,14,14,14, +16,16,16,17,17,17,17,18,18,18,18,19,19,19,19,19, +20,20,21,21,21,21,22,22,22,22,22,22,22,22,23,23, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,22,22,22,22,22,22,22,22,22,21,21,21, +20,20,19,19,19,19,19,18,18,18,18,18,17,17,17,16, +16,16,15,14,14,14,13,13,13,13,12,12,12,10,10,10, +9,9,9,8,8,8,7,7,7,6,6,5,4,4,0,0, +0,1,1,1,2,2,2,3,24,24,25,25,25,26,26,26, +27,27,27,29,29,28,28,30,30,30,31,31,31,31,32,32, +32,32,33,33,33,35,35,34,34,34,34,34,36,36,36,36, +36,36,36,36,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36, +36,36,36,36,36,36,34,34,34,34,34,34,35,35,33,33, +32,32,32,32,31,31,31,31,30,30,30,30,28,28,29,27, +27,27,26,26,26,25,25,25,25,24,24,2,2,2,1,1, +1,0,0,0,4,4,4,6,6,6,7,7,7,8,8,8, +9,9,9,10,10,10,12,12,12,13,13,13,14,14,14,15, +16,16,16,17,17,17,17,18,18,18,18,19,19,19,19,19, +20,20,21,21,21,21,22,22,22,22,22,22,22,22,23,23, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,22,22,22,22,22,22,22,22,22,21,21,21, +20,20,19,19,19,19,19,18,18,18,18,17,17,17,17,16, +16,16,15,14,14,14,13,13,13,13,12,12,12,10,10,10, +9,9,9,8,8,8,7,7,7,6,6,5,4,4,0,0, +0,1,1,1,2,2,2,3,24,24,25,25,25,26,26,26, +27,27,27,29,29,28,28,30,30,30,31,31,31,31,32,32, +32,32,33,33,33,35,35,34,34,34,34,34,36,36,36,36, +36,36,36,36,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36, +36,36,36,36,36,36,34,34,34,34,34,34,35,35,33,33, +32,32,32,32,31,31,31,31,30,30,30,30,28,28,29,27, +27,27,26,26,26,25,25,25,25,24,24,2,2,2,1,1, +1,0,0,0,4,4,4,6,6,6,7,7,7,8,8,8, +9,9,9,10,10,10,12,12,12,13,13,13,14,14,14,15, +16,16,16,17,17,17,17,18,18,18,18,19,19,19,19,19, +20,20,21,21,21,21,22,22,22,22,22,22,22,22,23,23, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,22,22,22,22,22,22,22,22,22,21,21,21, +20,20,19,19,19,19,19,18,18,18,18,17,17,17,17,16, +16,16,15,14,14,14,13,13,13,13,12,12,12,10,10,10, +9,9,9,8,8,8,7,7,7,6,6,5,4,4,0,0, +0,1,1,1,2,2,2,3,24,24,25,25,25,26,26,26, +27,27,27,29,29,28,28,30,30,30,31,31,31,31,32,32, +32,32,33,33,33,35,35,34,34,34,34,34,36,36,36,36, +36,36,36,36,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36, +36,36,36,36,36,36,34,34,34,34,34,34,35,35,33,33, +32,32,32,32,31,31,31,31,30,30,30,30,28,28,29,27, +27,27,26,26,26,26,25,25,25,24,24,3,2,2,1,1, +1,0,0,0,4,4,4,6,6,6,7,7,7,8,8,8, +9,9,9,10,10,10,12,12,12,13,13,13,14,14,14,14, +16,16,16,17,17,17,17,18,18,18,18,19,19,19,19,19, +20,20,21,21,21,21,22,22,22,22,22,22,22,22,23,23, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,22,22,22,22,22,22,22,22,22,21,21,21, +20,20,19,19,19,19,19,18,18,18,18,18,17,17,17,16, +16,16,15,14,14,14,13,13,13,13,12,12,12,10,10,10, +9,9,9,8,8,8,7,7,7,6,6,5,4,4,0,0, +0,1,1,1,2,2,2,3,24,24,25,25,25,26,26,26, +27,27,27,29,29,28,28,30,30,30,31,31,31,31,32,32, +32,32,33,33,33,35,35,34,34,34,34,34,36,36,36,36, +36,36,36,36,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36, +36,36,36,36,36,36,34,34,34,34,34,34,35,35,33,33, +32,32,32,32,32,31,31,31,30,30,30,30,28,28,29,27, +27,27,26,26,26,26,25,25,25,24,24,3,2,2,2,1, +1,0,0,0,4,4,4,6,6,6,7,7,7,8,8,8, +9,9,9,10,10,10,12,12,12,13,13,13,14,14,14,14, +15,16,16,17,17,17,17,18,18,18,18,19,19,19,19,19, +20,20,20,21,21,21,22,22,22,22,22,22,22,22,22,23, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,22,22,22,22,22,22,22,22,22,21,21,21, +20,20,19,19,19,19,19,18,18,18,18,18,17,17,17,16, +16,16,15,14,14,14,13,13,13,13,12,12,12,10,10,10, +9,9,9,8,8,8,7,7,7,6,6,6,4,4,0,0, +0,1,1,1,2,2,2,3,24,24,25,25,25,26,26,26, +27,27,27,29,29,28,28,30,30,30,31,31,31,31,32,32, +32,32,33,33,33,35,35,34,34,34,34,34,34,36,36,36, +36,36,36,36,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,36,36, +36,36,36,36,36,36,34,34,34,34,34,34,35,35,33,33, +32,32,32,32,32,31,31,31,30,30,30,30,28,28,29,27, +27,27,27,26,26,26,25,25,25,24,24,3,2,2,2,1, +1,1,0,0,4,4,4,6,6,6,7,7,7,8,8,8, +9,9,9,10,10,10,12,12,12,13,13,13,13,14,14,14, +15,16,16,16,17,17,17,18,18,18,18,18,19,19,19,19, +19,20,20,21,21,21,21,22,22,22,22,22,22,22,22,23, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,22,22,22,22,22,22,22,22,21,21,21,21, +20,20,19,19,19,19,19,18,18,18,18,18,17,17,17,16, +16,16,15,14,14,14,14,13,13,13,12,12,12,10,10,10, +9,9,9,8,8,8,7,7,7,6,6,6,4,4,4,0, +0,1,1,1,2,2,2,3,24,24,25,25,25,26,26,26, +27,27,27,27,29,28,28,30,30,30,30,31,31,31,32,32, +32,32,32,33,33,35,35,34,34,34,34,34,34,36,36,36, +36,36,36,36,36,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,36,36, +36,36,36,36,36,36,34,34,34,34,34,34,35,35,33,33, +32,32,32,32,32,31,31,31,30,30,30,30,28,28,29,27, +27,27,27,26,26,26,25,25,25,24,24,3,2,2,2,1, +1,1,0,0,0,4,4,5,6,6,7,7,7,8,8,8, +9,9,9,11,10,10,12,12,12,12,13,13,13,14,14,14, +15,16,16,16,17,17,17,17,18,18,18,18,19,19,19,19, +19,20,20,20,21,21,21,22,22,22,22,22,22,22,22,22, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,22,22,22,22,22,22,22,22,22,21,21,21,21, +20,20,19,19,19,19,19,18,18,18,18,18,17,17,17,17, +16,16,15,14,14,14,14,13,13,13,12,12,12,10,10,10, +9,9,9,8,8,8,7,7,7,6,6,6,4,4,4,0, +0,0,1,1,1,2,2,2,24,24,24,25,25,25,26,26, +26,27,27,27,29,29,28,30,30,30,30,31,31,31,31,32, +32,32,32,33,33,33,35,35,34,34,34,34,34,34,36,36, +36,36,36,36,36,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,36,36,36, +36,36,36,36,36,36,34,34,34,34,34,34,35,33,33,33, +32,32,32,32,32,31,31,31,31,30,30,30,28,28,29,29, +27,27,27,26,26,26,25,25,25,24,24,3,2,2,2,1, +1,1,0,0,0,4,4,4,6,6,6,7,7,7,8,8, +8,9,9,9,10,10,10,12,12,12,13,13,13,14,14,14, +14,15,16,16,17,17,17,17,18,18,18,18,18,19,19,19, +19,19,20,20,21,21,21,21,22,22,22,22,22,22,22,22, +22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,22,22,22,22,22,22,22,22,22,22,21,21,21,21, +20,20,19,19,19,19,19,18,18,18,18,18,17,17,17,17, +16,16,15,14,14,14,14,13,13,13,12,12,12,10,10,10, +9,9,9,8,8,8,7,7,7,6,6,6,5,4,4,0, +0,0,1,1,1,2,2,2,3,24,24,25,25,25,26,26, +26,27,27,27,29,29,28,28,30,30,30,30,31,31,31,32, +32,32,32,32,33,33,33,35,35,34,34,34,34,34,36,36, +36,36,36,36,36,36,36,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,36,36,36,36, +36,36,36,36,36,34,34,34,34,34,34,35,35,33,33,33, +32,32,32,32,32,31,31,31,31,30,30,30,28,28,29,29, +27,27,27,26,26,26,25,25,25,24,24,24,2,2,2,1, +1,1,0,0,0,4,4,4,6,6,6,7,7,7,8,8, +8,9,9,9,10,10,10,12,12,12,12,13,13,13,14,14, +14,15,16,16,16,17,17,17,17,18,18,18,18,18,19,19, +19,19,19,20,20,21,21,21,21,22,22,22,22,22,22,22, +22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23, +22,22,22,22,22,22,22,22,22,22,22,21,21,21,21,20, +20,20,19,19,19,19,19,18,18,18,18,18,17,17,17,17, +16,16,15,14,14,14,14,13,13,13,12,12,12,10,10,10, +11,9,9,9,8,8,8,7,7,7,6,6,6,4,4,0, +0,0,1,1,1,2,2,2,3,24,24,25,25,25,25,26, +26,26,27,27,27,29,29,28,28,30,30,30,31,31,31,31, +32,32,32,32,33,33,33,35,35,34,34,34,34,34,34,34, +36,36,36,36,36,36,36,36,36,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,36,36,36,36,36, +36,36,36,36,36,34,34,34,34,34,34,35,35,33,33,33, +32,32,32,32,32,31,31,31,31,30,30,30,28,28,29,29, +27,27,27,26,26,26,26,25,25,25,24,24,3,2,2,2, +1,1,1,0,0,0,4,4,5,6,6,6,7,7,7,8, +8,8,9,9,9,10,10,10,12,12,12,13,13,13,13,14, +14,14,15,16,16,16,17,17,17,17,18,18,18,18,18,19, +19,19,19,19,20,20,20,21,21,21,21,22,22,22,22,22, +22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,22,22,22,22,21,21,21,21,20, +20,20,19,19,19,19,19,18,18,18,18,18,17,17,17,17, +16,16,16,14,14,14,14,13,13,13,12,12,12,12,10,10, +10,9,9,9,8,8,8,7,7,7,6,6,6,4,4,4, +0,0,0,1,1,1,2,2,2,3,24,24,25,25,25,26, +26,26,27,27,27,27,29,28,28,30,30,30,30,31,31,31, +31,32,32,32,32,33,33,33,33,35,35,34,34,34,34,34, +34,36,36,36,36,36,36,36,36,36,36,37,37,37,37,37, +37,37,37,37,37,37,37,36,36,36,36,36,36,36,36,36, +36,36,36,36,34,34,34,34,34,34,34,35,35,33,33,33, +32,32,32,32,32,31,31,31,31,30,30,30,28,28,29,29, +27,27,27,26,26,26,26,25,25,25,24,24,3,2,2,2, +1,1,1,0,0,0,4,4,4,6,6,6,7,7,7,8, +8,8,9,9,9,11,10,10,10,12,12,12,13,13,13,14, +14,14,14,15,16,16,16,17,17,17,17,18,18,18,18,18, +19,19,19,19,19,20,20,20,21,21,21,21,21,22,22,22, +22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,22,22,22,21,21,21,21,20,20, +20,19,19,19,19,19,19,18,18,18,18,17,17,17,17,17, +16,16,16,15,14,14,14,13,13,13,13,12,12,12,10,10, +10,9,9,9,8,8,8,7,7,7,6,6,6,5,4,4, +0,0,0,1,1,1,2,2,2,2,24,24,24,25,25,25, +26,26,26,27,27,27,27,29,28,28,30,30,30,30,31,31, +31,31,32,32,32,32,32,33,33,33,35,35,34,34,34,34, +34,34,34,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,34,34,34,34,34,34,34,35,35,33,33,33,33, +32,32,32,32,31,31,31,31,31,30,30,30,30,28,28,29, +27,27,27,27,26,26,26,25,25,25,24,24,24,2,2,2, +2,1,1,1,0,0,0,4,4,5,6,6,6,7,7,7, +8,8,8,9,9,9,10,10,10,12,12,12,12,13,13,13, +14,14,14,14,15,16,16,16,17,17,17,17,18,18,18,18, +18,19,19,19,19,19,19,20,20,20,21,21,21,21,21,22, +22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,22,21,21,21,21,21,21,20,20, +20,19,19,19,19,19,18,18,18,18,18,17,17,17,17,17, +16,16,16,15,14,14,14,13,13,13,13,12,12,12,10,10, +10,11,9,9,9,8,8,8,7,7,7,6,6,6,4,4, +4,0,0,0,1,1,1,2,2,2,3,24,24,25,25,25, +25,26,26,26,27,27,27,27,29,28,28,30,30,30,30,31, +31,31,31,32,32,32,32,32,33,33,33,35,35,35,34,34, +34,34,34,34,34,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,34,34,34,34,34,34,34,34,35,35,35,33,33,33,32, +32,32,32,32,31,31,31,31,31,30,30,30,30,28,28,29, +27,27,27,27,26,26,26,25,25,25,25,24,24,3,2,2, +2,1,1,1,0,0,0,4,4,4,6,6,6,6,7,7, +7,8,8,8,9,9,9,10,10,10,12,12,12,12,13,13, +13,13,14,14,14,15,16,16,16,17,17,17,17,17,18,18, +18,18,18,19,19,19,19,19,19,20,20,20,20,21,21,21, +21,21,21,22,22,22,22,22,22,22,22,22,22,22,22,22, +22,22,22,22,22,22,21,21,21,21,21,21,20,20,20,20, +19,19,19,19,19,19,18,18,18,18,18,17,17,17,17,17, +16,16,16,15,14,14,14,13,13,13,13,12,12,12,12,10, +10,10,9,9,9,8,8,8,7,7,7,6,6,6,6,4, +4,4,0,0,0,1,1,1,2,2,2,3,24,24,25,25, +25,25,26,26,26,27,27,27,27,29,28,28,30,30,30,30, +31,31,31,31,31,32,32,32,32,32,33,33,33,35,35,35, +34,34,34,34,34,34,34,34,36,36,36,36,36,36,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,34,34, +34,34,34,34,34,34,34,34,35,35,35,33,33,33,33,32, +32,32,32,32,31,31,31,31,31,30,30,30,30,28,28,29, +29,27,27,27,26,26,26,26,25,25,25,24,24,24,2,2, +2,2,1,1,1,0,0,0,4,4,4,6,6,6,7,7, +7,7,8,8,8,9,9,9,10,10,10,10,12,12,12,13, +13,13,13,14,14,14,14,15,16,16,16,17,17,17,17,17, +18,18,18,18,18,19,19,19,19,19,19,19,20,20,20,20, +21,21,21,21,21,21,21,21,21,21,22,22,22,22,22,22, +22,21,21,21,21,21,21,21,21,21,20,20,20,20,19,19, +19,19,19,19,19,18,18,18,18,18,18,17,17,17,17,16, +16,16,16,15,14,14,14,14,13,13,13,12,12,12,12,10, +10,10,9,9,9,9,8,8,8,7,7,7,6,6,6,5, +4,4,0,0,0,0,1,1,1,2,2,2,3,24,24,25, +25,25,25,26,26,26,27,27,27,27,29,29,28,28,30,30, +30,30,31,31,31,31,31,32,32,32,32,32,33,33,33,33, +35,35,34,34,34,34,34,34,34,34,34,34,36,36,36,36, +34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34, +34,34,34,34,34,34,34,35,35,35,33,33,33,33,32,32, +32,32,32,31,31,31,31,31,30,30,30,30,30,28,28,29, +29,27,27,27,26,26,26,26,25,25,25,25,24,24,3,2, +2,2,1,1,1,1,0,0,0,4,4,4,6,6,6,7, +7,7,7,8,8,8,9,9,9,11,10,10,10,12,12,12, +13,13,13,13,14,14,14,14,15,16,16,16,16,17,17,17, +17,17,18,18,18,18,18,18,19,19,19,19,19,19,19,19, +20,20,20,20,20,21,21,21,21,21,21,21,21,21,21,21, +21,21,21,21,21,21,21,20,20,20,20,20,19,19,19,19, +19,19,19,19,18,18,18,18,18,18,17,17,17,17,17,16, +16,16,16,15,14,14,14,14,13,13,13,13,12,12,12,10, +10,10,11,9,9,9,8,8,8,7,7,7,7,6,6,6, +4,4,4,0,0,0,1,1,1,1,2,2,2,3,24,24, +25,25,25,25,26,26,26,26,27,27,27,29,29,28,28,30, +30,30,30,30,31,31,31,31,31,32,32,32,32,32,33,33, +33,33,35,35,35,34,34,34,34,34,34,34,34,34,34,34, +34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34, +34,34,34,34,35,35,35,35,33,33,33,33,33,32,32,32, +32,32,32,31,31,31,31,31,30,30,30,30,30,28,28,29, +29,27,27,27,27,26,26,26,25,25,25,25,24,24,24,2, +2,2,2,1,1,1,0,0,0,0,4,4,4,6,6,6, +7,7,7,7,8,8,8,9,9,9,9,10,10,10,12,12, +12,12,13,13,13,13,14,14,14,14,15,16,16,16,16,17, +17,17,17,17,18,18,18,18,18,18,18,19,19,19,19,19, +19,19,19,19,19,20,20,20,20,20,20,20,20,20,20,20, +20,20,20,20,20,20,20,20,20,19,19,19,19,19,19,19, +19,19,19,18,18,18,18,18,18,17,17,17,17,17,17,16, +16,16,15,14,14,14,14,14,13,13,13,13,12,12,12,10, +10,10,10,9,9,9,9,8,8,8,7,7,7,6,6,6, +6,4,4,4,0,0,0,1,1,1,1,2,2,2,3,24, +24,24,25,25,25,26,26,26,26,27,27,27,27,29,29,28, +28,30,30,30,30,30,31,31,31,31,31,32,32,32,32,32, +32,33,33,33,33,33,35,35,35,35,34,34,34,34,34,34, +34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34, +35,35,35,35,35,33,33,33,33,33,33,32,32,32,32,32, +32,32,31,31,31,31,31,30,30,30,30,30,28,28,28,29, +29,27,27,27,27,26,26,26,26,25,25,25,24,24,24,3, +2,2,2,2,1,1,1,0,0,0,0,4,4,4,6,6, +6,7,7,7,7,8,8,8,9,9,9,9,10,10,10,10, +12,12,12,12,13,13,13,13,14,14,14,14,15,15,16,16, +16,16,17,17,17,17,17,18,18,18,18,18,18,18,18,19, +19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, +19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, +19,18,18,18,18,18,18,18,17,17,17,17,17,17,16,16, +16,16,15,14,14,14,14,14,13,13,13,13,12,12,12,12, +10,10,10,11,9,9,9,8,8,8,8,7,7,7,6,6, +6,6,4,4,4,0,0,0,0,1,1,1,2,2,2,2, +3,24,24,25,25,25,25,26,26,26,26,27,27,27,27,29, +29,28,28,30,30,30,30,30,31,31,31,31,31,31,32,32, +32,32,32,32,32,33,33,33,33,33,33,35,35,35,35,35, +33,33,35,35,35,35,35,35,35,35,35,35,35,35,35,35, +33,33,33,33,33,33,33,33,32,32,32,32,32,32,32,32, +32,31,31,31,31,31,31,30,30,30,30,30,28,28,28,29, +29,27,27,27,27,26,26,26,26,25,25,25,25,24,24,24, +3,2,2,2,1,1,1,1,0,0,0,4,4,4,4,6, +6,6,6,7,7,7,8,8,8,8,9,9,9,9,10,10, +10,10,12,12,12,12,13,13,13,13,14,14,14,14,14,15, +16,16,16,16,17,17,17,17,17,17,17,18,18,18,18,18, +18,18,18,18,19,19,19,19,19,19,19,19,19,19,19,19, +19,19,19,19,19,19,19,19,19,19,19,19,19,19,18,18, +18,18,18,18,18,18,18,17,17,17,17,17,17,16,16,16, +16,15,15,14,14,14,14,13,13,13,13,13,12,12,12,12, +10,10,10,10,9,9,9,9,8,8,8,7,7,7,7,6, +6,6,6,4,4,4,0,0,0,0,1,1,1,2,2,2, +2,3,24,24,24,25,25,25,25,26,26,26,26,27,27,27, +27,29,29,28,28,28,30,30,30,30,30,31,31,31,31,31, +31,32,32,32,32,32,32,32,32,33,33,33,33,33,33,33, +33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33, +33,33,33,33,32,32,32,32,32,32,32,32,32,32,32,31, +31,31,31,31,31,31,30,30,30,30,30,28,28,28,29,29, +27,27,27,27,27,26,26,26,26,25,25,25,25,24,24,24, +3,2,2,2,2,1,1,1,1,0,0,0,0,4,4,4, +6,6,6,6,7,7,7,7,8,8,8,8,9,9,9,9, +10,10,10,10,12,12,12,12,13,13,13,13,13,14,14,14, +14,14,15,16,16,16,16,16,17,17,17,17,17,17,17,17, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,19,19, +19,19,19,19,19,19,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,17,17,17,17,17,17,17,17,16,16,16,16, +16,15,14,14,14,14,14,13,13,13,13,13,12,12,12,12, +10,10,10,10,9,9,9,9,8,8,8,8,7,7,7,7, +6,6,6,6,4,4,4,0,0,0,0,1,1,1,1,2, +2,2,2,3,24,24,24,25,25,25,25,26,26,26,26,27, +27,27,27,27,29,29,28,28,28,30,30,30,30,30,31,31, +31,31,31,31,31,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,31,31,31,31, +31,31,31,31,30,30,30,30,30,30,28,28,28,28,29,29, +27,27,27,27,27,26,26,26,26,25,25,25,25,25,24,24, +24,3,2,2,2,2,1,1,1,1,0,0,0,0,4,4, +4,5,6,6,6,6,7,7,7,8,8,8,8,8,9,9, +9,9,10,10,10,10,12,12,12,12,12,13,13,13,13,13, +14,14,14,14,14,15,15,16,16,16,16,16,17,17,17,17, +17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,17,17,17,17,17,17,17,17,17,16,16,16,16,16,15, +15,14,14,14,14,14,14,13,13,13,13,13,12,12,12,12, +10,10,10,10,11,9,9,9,9,8,8,8,8,7,7,7, +7,6,6,6,6,4,4,4,4,0,0,0,0,1,1,1, +2,2,2,2,2,3,24,24,24,25,25,25,25,26,26,26, +26,26,27,27,27,27,29,29,29,28,28,28,30,30,30,30, +30,30,31,31,31,31,31,31,31,31,31,32,32,32,32,32, +31,31,31,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,31,31,31,31,31,31,31,31,31,31, +31,31,30,30,30,30,30,30,30,28,28,28,28,29,29,27, +27,27,27,27,27,26,26,26,26,26,25,25,25,25,24,24, +24,3,3,2,2,2,2,1,1,1,1,0,0,0,0,4, +4,4,4,6,6,6,6,7,7,7,7,8,8,8,8,8, +9,9,9,9,10,10,10,10,10,12,12,12,12,12,13,13, +13,13,13,14,14,14,14,14,14,14,15,16,16,16,16,16, +16,16,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +17,17,17,17,17,17,17,16,16,16,16,16,16,16,15,14, +14,14,14,14,14,14,13,13,13,13,13,12,12,12,12,12, +10,10,10,10,10,9,9,9,9,8,8,8,8,8,7,7, +7,7,6,6,6,6,4,4,4,4,0,0,0,0,1,1, +1,1,2,2,2,2,2,3,24,24,24,25,25,25,25,25, +26,26,26,26,27,27,27,27,27,27,29,29,28,28,28,28, +30,30,30,30,30,30,30,31,31,31,31,31,31,31,31,31, +31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31, +31,31,31,31,31,31,31,31,31,31,31,31,31,31,30,30, +30,30,30,30,30,30,30,28,28,28,28,29,29,29,27,27, +27,27,27,27,26,26,26,26,26,26,25,25,25,25,25,24, +24,24,3,2,2,2,2,2,1,1,1,1,0,0,0,0, +4,4,4,4,5,6,6,6,6,7,7,7,7,7,8,8, +8,8,9,9,9,9,9,10,10,10,10,10,12,12,12,12, +12,13,13,13,13,13,13,14,14,14,14,14,14,14,14,15, +15,16,16,16,16,16,16,16,16,16,17,17,17,17,17,17, +17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +17,17,16,16,16,16,16,16,16,16,16,15,15,14,14,14, +14,14,14,14,14,13,13,13,13,13,13,12,12,12,12,12, +10,10,10,10,10,9,9,9,9,9,8,8,8,8,8,7, +7,7,7,6,6,6,6,5,4,4,4,4,0,0,0,0, +1,1,1,1,2,2,2,2,2,3,24,24,24,24,25,25, +25,25,25,26,26,26,26,26,27,27,27,27,27,27,29,29, +29,28,28,28,28,30,30,30,30,30,30,30,30,30,31,31, +30,30,30,30,30,30,30,30,30,30,30,30,31,31,31,31, +31,31,31,31,30,30,30,30,30,30,30,30,30,30,30,30, +30,30,30,30,28,28,28,28,28,29,29,29,29,27,27,27, +27,27,27,27,26,26,26,26,26,25,25,25,25,25,25,24, +24,24,3,3,2,2,2,2,2,1,1,1,1,0,0,0, +0,0,4,4,4,4,5,6,6,6,6,7,7,7,7,7, +8,8,8,8,8,9,9,9,9,9,10,10,10,10,10,12, +12,12,12,12,12,13,13,13,13,13,13,13,13,14,14,14, +14,14,14,14,14,14,15,15,15,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,15,15,15,14,14,14,14,14,14, +14,14,14,13,13,13,13,13,13,13,12,12,12,12,12,12, +10,10,10,10,10,9,9,9,9,9,9,8,8,8,8,7, +7,7,7,7,6,6,6,6,6,4,4,4,4,0,0,0, +0,0,1,1,1,1,1,2,2,2,2,2,3,24,24,24, +24,25,25,25,25,25,26,26,26,26,26,26,27,27,27,27, +27,27,27,29,29,29,28,28,28,28,28,30,30,30,30,30, +28,28,28,28,30,30,30,30,30,30,30,30,30,30,30,30, +30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,28, +28,28,28,28,28,28,29,29,29,29,27,27,27,27,27,27, +27,27,26,26,26,26,26,26,26,25,25,25,25,25,25,24, +24,24,24,3,2,2,2,2,2,2,1,1,1,1,1,0, +0,0,0,0,4,4,4,4,5,6,6,6,6,6,7,7, +7,7,7,8,8,8,8,8,9,9,9,9,9,9,10,10, +10,10,10,10,12,12,12,12,12,12,12,13,13,13,13,13, +13,13,13,14,14,14,14,14,14,14,14,14,14,14,14,14, +14,14,15,15,15,15,15,15,15,15,15,15,15,15,15,15, +15,15,14,14,14,14,14,14,14,14,14,14,14,14,14,14, +13,13,13,13,13,13,13,13,13,12,12,12,12,12,12,10, +10,10,10,10,10,11,9,9,9,9,9,8,8,8,8,8, +7,7,7,7,7,6,6,6,6,6,5,4,4,4,4,0, +0,0,0,0,1,1,1,1,1,2,2,2,2,2,2,3, +24,24,24,24,25,25,25,25,25,25,26,26,26,26,26,26, +26,27,27,27,27,27,27,27,27,29,29,29,29,28,28,28, +27,29,29,29,29,29,29,29,28,28,28,28,28,28,28,28, +28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,29, +29,29,29,29,29,29,27,27,27,27,27,27,27,27,27,27, +26,26,26,26,26,26,26,26,25,25,25,25,25,25,25,24, +24,24,24,3,3,2,2,2,2,2,2,1,1,1,1,1, +0,0,0,0,0,0,4,4,4,4,5,6,6,6,6,6, +7,7,7,7,7,7,8,8,8,8,8,8,9,9,9,9, +9,9,10,10,10,10,10,10,10,12,12,12,12,12,12,12, +12,13,13,13,13,13,13,13,13,13,13,13,13,14,14,14, +14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, +14,14,14,14,14,14,14,14,14,14,14,13,13,13,13,13, +13,13,13,13,13,13,12,12,12,12,12,12,12,12,10,10, +10,10,10,10,10,9,9,9,9,9,9,9,8,8,8,8, +8,7,7,7,7,7,7,6,6,6,6,6,5,4,4,4, +4,4,0,0,0,0,0,1,1,1,1,1,1,2,2,2, +2,2,3,3,24,24,24,24,25,25,25,25,25,25,25,26, +26,26,26,26,26,26,26,27,27,27,27,27,27,27,27,27, +27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27, +27,29,29,29,29,29,29,29,29,29,29,27,27,27,27,27, +27,27,27,27,27,27,27,27,27,27,27,27,27,26,26,26, +26,26,26,26,26,26,26,25,25,25,25,25,25,25,25,24, +24,24,24,24,3,3,2,2,2,2,2,2,1,1,1,1, +1,1,0,0,0,0,0,0,4,4,4,4,4,5,6,6, +6,6,6,6,7,7,7,7,7,7,8,8,8,8,8,8, +9,9,9,9,9,9,9,11,10,10,10,10,10,10,10,12, +12,12,12,12,12,12,12,12,12,12,13,13,13,13,13,13, +13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, +13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, +13,13,12,12,12,12,12,12,12,12,12,12,12,10,10,10, +10,10,10,10,11,9,9,9,9,9,9,9,8,8,8,8, +8,8,7,7,7,7,7,7,6,6,6,6,6,6,5,4, +4,4,4,4,0,0,0,0,0,0,1,1,1,1,1,1, +2,2,2,2,2,2,3,3,24,24,24,24,24,25,25,25, +25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,27, +26,26,26,26,26,26,26,26,26,26,26,27,27,27,27,27, +27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27, +27,27,27,27,27,27,27,26,26,26,26,26,26,26,26,26, +26,26,26,26,26,25,25,25,25,25,25,25,25,25,24,24, +24,24,24,24,3,3,2,2,2,2,2,2,2,1,1,1, +1,1,1,1,0,0,0,0,0,0,4,4,4,4,4,4, +5,6,6,6,6,6,6,7,7,7,7,7,7,7,8,8, +8,8,8,8,8,9,9,9,9,9,9,9,9,9,10,10, +10,10,10,10,10,10,10,10,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13, +13,13,13,13,13,13,13,13,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,10,10,10,10,10,10, +10,10,10,11,9,9,9,9,9,9,9,9,8,8,8,8, +8,8,8,7,7,7,7,7,7,7,6,6,6,6,6,6, +6,4,4,4,4,4,4,0,0,0,0,0,0,0,1,1, +1,1,1,1,2,2,2,2,2,2,2,2,3,3,24,24, +24,24,24,24,25,25,25,25,25,25,25,25,25,26,26,26, +25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26, +26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, +26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, +25,25,25,25,25,25,25,25,25,25,25,25,25,24,24,24, +24,24,24,24,3,3,2,2,2,2,2,2,2,2,2,1, +1,1,1,1,1,1,0,0,0,0,0,0,0,4,4,4, +4,4,4,4,5,6,6,6,6,6,6,6,7,7,7,7, +7,7,7,7,8,8,8,8,8,8,8,8,9,9,9,9, +9,9,9,9,9,9,11,10,10,10,10,10,10,10,10,10, +10,10,10,10,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,10,10,10,10,10,10,10,10,10,10,10,10, +10,11,9,9,9,9,9,9,9,9,9,9,8,8,8,8, +8,8,8,8,8,7,7,7,7,7,7,7,6,6,6,6, +6,6,6,6,4,4,4,4,4,4,4,0,0,0,0,0, +0,0,1,1,1,1,1,1,1,1,2,2,2,2,2,2, +2,2,3,3,24,24,24,24,24,24,24,25,25,25,25,25, +24,24,24,24,24,24,25,25,25,25,25,25,25,25,25,25, +25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, +25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, +25,25,25,25,25,25,25,25,25,25,24,24,24,24,24,24, +24,24,24,3,3,3,2,2,2,2,2,2,2,2,2,2, +1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0, +0,4,4,4,4,4,4,4,5,6,6,6,6,6,6,6, +6,6,7,7,7,7,7,7,7,7,7,8,8,8,8,8, +8,8,8,8,8,9,9,9,9,9,9,9,9,9,9,9, +9,9,11,10,10,10,10,10,10,10,10,10,10,10,10,10, +10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, +10,10,10,10,10,10,10,10,10,10,10,10,10,11,9,9, +9,9,9,9,9,9,9,9,9,9,9,9,8,8,8,8, +8,8,8,8,8,8,7,7,7,7,7,7,7,7,7,6, +6,6,6,6,6,6,6,5,4,4,4,4,4,4,4,0, +0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1, +2,2,2,2,2,2,2,2,2,2,3,3,3,24,24,24, +2,2,2,2,2,3,3,3,3,24,24,24,24,24,24,24, +24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,25, +25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, +25,25,25,25,24,24,24,24,24,24,24,24,24,24,24,24, +24,24,3,3,3,3,2,2,2,2,2,2,2,2,2,2, +2,2,1,1,1,1,1,1,1,1,1,1,0,0,0,0, +0,0,0,0,0,0,4,4,4,4,4,4,4,4,5,6, +6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7, +7,7,7,8,8,8,8,8,8,8,8,8,8,8,8,8, +9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, +9,9,9,9,9,9,9,9,9,9,9,9,9,11,9,9, +9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, +9,9,9,9,9,9,9,9,9,9,8,8,8,8,8,8, +8,8,8,8,8,8,8,7,7,7,7,7,7,7,7,7, +7,7,6,6,6,6,6,6,6,6,6,5,4,4,4,4, +4,4,4,4,4,0,0,0,0,0,0,0,0,0,1,1, +1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2, +1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,3,3,3,3,3,3,24,24,24,24,24,24, +24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24, +24,24,24,24,24,24,24,24,24,24,24,24,24,24,3,3, +3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1, +1,0,0,0,0,0,0,0,0,0,0,0,4,4,4,4, +4,4,4,4,4,4,5,6,6,6,6,6,6,6,6,6, +6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,8, +8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, +8,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, +9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, +9,9,9,9,9,9,8,8,8,8,8,8,8,8,8,8, +8,8,8,8,8,8,8,8,8,7,7,7,7,7,7,7, +7,7,7,7,7,7,6,6,6,6,6,6,6,6,6,6, +6,5,4,4,4,4,4,4,4,4,4,4,0,0,0,0, +0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1 +}; diff --git a/ops.h b/ops.h new file mode 100644 index 0000000..7e7075a --- /dev/null +++ b/ops.h @@ -0,0 +1,479 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +case 0x00: /* BRK */ + _PC++; + PUSH(_PC>>8); + PUSH(_PC); + PUSH(_P|U_FLAG|B_FLAG); + _P|=I_FLAG; + _PC=RdMem(0xFFFE); + _PC|=RdMem(0xFFFF)<<8; + break; + +case 0x40: /* RTI */ + _P=POP(); + _PI=_P; + _PC=POP(); + _PC|=POP()<<8; + break; + +case 0x60: /* RTS */ + _PC=POP(); + _PC|=POP()<<8; + _PC++; + break; + +case 0x48: /* PHA */ + PUSH(_A); + break; +case 0x08: /* PHP */ + PUSH(_P|U_FLAG|B_FLAG); + break; +case 0x68: /* PLA */ + _A=POP(); + X_ZN(_A); + break; +case 0x28: /* PLP */ + _P=POP(); + break; +case 0x4C: + { + uint16 ptmp=_PC; + unsigned int npc; + + npc=RdMem(ptmp); + ptmp++; + npc|=RdMem(ptmp)<<8; + _PC=npc; + } + break; /* JMP ABSOLUTE */ +case 0x6C: + { + uint32 tmp; + GetAB(tmp); + _PC=RdMem(tmp); + _PC|=RdMem( ((tmp+1)&0x00FF) | (tmp&0xFF00))<<8; + } + break; +case 0x20: /* JSR */ + { + uint8 npc; + npc=RdMem(_PC++); + PUSH(_PC>>8); + PUSH(_PC); + _PC=RdMem(_PC)<<8; + _PC|=npc; + } + break; + +case 0xAA: /* TAX */ + _X=_A; + X_ZN(_A); + break; + +case 0x8A: /* TXA */ + _A=_X; + X_ZN(_A); + break; + +case 0xA8: /* TAY */ + _Y=_A; + X_ZN(_A); + break; +case 0x98: /* TYA */ + _A=_Y; + X_ZN(_A); + break; + +case 0xBA: /* TSX */ + _X=_S; + X_ZN(_X); + break; +case 0x9A: /* TXS */ + _S=_X; + break; + +case 0xCA: /* DEX */ + _X--; + X_ZN(_X); + break; +case 0x88: /* DEY */ + _Y--; + X_ZN(_Y); + break; + +case 0xE8: /* INX */ + _X++; + X_ZN(_X); + break; +case 0xC8: /* INY */ + _Y++; + X_ZN(_Y); + break; + +case 0x18: /* CLC */ + _P&=~C_FLAG; + break; +case 0xD8: /* CLD */ + _P&=~D_FLAG; + break; +case 0x58: /* CLI */ + _P&=~I_FLAG; + break; +case 0xB8: /* CLV */ + _P&=~V_FLAG; + break; + +case 0x38: /* SEC */ + _P|=C_FLAG; + break; +case 0xF8: /* SED */ + _P|=D_FLAG; + break; +case 0x78: /* SEI */ + _P|=I_FLAG; + break; + +case 0xEA: /* NOP */ + break; + +case 0x0A: RMW_A(ASL); +case 0x06: RMW_ZP(ASL); +case 0x16: RMW_ZPX(ASL); +case 0x0E: RMW_AB(ASL); +case 0x1E: RMW_ABX(ASL); + +case 0xC6: RMW_ZP(DEC); +case 0xD6: RMW_ZPX(DEC); +case 0xCE: RMW_AB(DEC); +case 0xDE: RMW_ABX(DEC); + +case 0xE6: RMW_ZP(INC); +case 0xF6: RMW_ZPX(INC); +case 0xEE: RMW_AB(INC); +case 0xFE: RMW_ABX(INC); + +case 0x4A: RMW_A(LSR); +case 0x46: RMW_ZP(LSR); +case 0x56: RMW_ZPX(LSR); +case 0x4E: RMW_AB(LSR); +case 0x5E: RMW_ABX(LSR); + +case 0x2A: RMW_A(ROL); +case 0x26: RMW_ZP(ROL); +case 0x36: RMW_ZPX(ROL); +case 0x2E: RMW_AB(ROL); +case 0x3E: RMW_ABX(ROL); + +case 0x6A: RMW_A(ROR); +case 0x66: RMW_ZP(ROR); +case 0x76: RMW_ZPX(ROR); +case 0x6E: RMW_AB(ROR); +case 0x7E: RMW_ABX(ROR); + +case 0x69: LD_IM(ADC); +case 0x65: LD_ZP(ADC); +case 0x75: LD_ZPX(ADC); +case 0x6D: LD_AB(ADC); +case 0x7D: LD_ABX(ADC); +case 0x79: LD_ABY(ADC); +case 0x61: LD_IX(ADC); +case 0x71: LD_IY(ADC); + +case 0x29: LD_IM(AND); +case 0x25: LD_ZP(AND); +case 0x35: LD_ZPX(AND); +case 0x2D: LD_AB(AND); +case 0x3D: LD_ABX(AND); +case 0x39: LD_ABY(AND); +case 0x21: LD_IX(AND); +case 0x31: LD_IY(AND); + +case 0x24: LD_ZP(BIT); +case 0x2C: LD_AB(BIT); + +case 0xC9: LD_IM(CMP); +case 0xC5: LD_ZP(CMP); +case 0xD5: LD_ZPX(CMP); +case 0xCD: LD_AB(CMP); +case 0xDD: LD_ABX(CMP); +case 0xD9: LD_ABY(CMP); +case 0xC1: LD_IX(CMP); +case 0xD1: LD_IY(CMP); + +case 0xE0: LD_IM(CPX); +case 0xE4: LD_ZP(CPX); +case 0xEC: LD_AB(CPX); + +case 0xC0: LD_IM(CPY); +case 0xC4: LD_ZP(CPY); +case 0xCC: LD_AB(CPY); + +case 0x49: LD_IM(EOR); +case 0x45: LD_ZP(EOR); +case 0x55: LD_ZPX(EOR); +case 0x4D: LD_AB(EOR); +case 0x5D: LD_ABX(EOR); +case 0x59: LD_ABY(EOR); +case 0x41: LD_IX(EOR); +case 0x51: LD_IY(EOR); + +case 0xA9: LD_IM(LDA); +case 0xA5: LD_ZP(LDA); +case 0xB5: LD_ZPX(LDA); +case 0xAD: LD_AB(LDA); +case 0xBD: LD_ABX(LDA); +case 0xB9: LD_ABY(LDA); +case 0xA1: LD_IX(LDA); +case 0xB1: LD_IY(LDA); + +case 0xA2: LD_IM(LDX); +case 0xA6: LD_ZP(LDX); +case 0xB6: LD_ZPY(LDX); +case 0xAE: LD_AB(LDX); +case 0xBE: LD_ABY(LDX); + +case 0xA0: LD_IM(LDY); +case 0xA4: LD_ZP(LDY); +case 0xB4: LD_ZPX(LDY); +case 0xAC: LD_AB(LDY); +case 0xBC: LD_ABX(LDY); + +case 0x09: LD_IM(ORA); +case 0x05: LD_ZP(ORA); +case 0x15: LD_ZPX(ORA); +case 0x0D: LD_AB(ORA); +case 0x1D: LD_ABX(ORA); +case 0x19: LD_ABY(ORA); +case 0x01: LD_IX(ORA); +case 0x11: LD_IY(ORA); + +case 0xEB: /* (undocumented) */ +case 0xE9: LD_IM(SBC); +case 0xE5: LD_ZP(SBC); +case 0xF5: LD_ZPX(SBC); +case 0xED: LD_AB(SBC); +case 0xFD: LD_ABX(SBC); +case 0xF9: LD_ABY(SBC); +case 0xE1: LD_IX(SBC); +case 0xF1: LD_IY(SBC); + +case 0x85: ST_ZP(_A); +case 0x95: ST_ZPX(_A); +case 0x8D: ST_AB(_A); +case 0x9D: ST_ABX(_A); +case 0x99: ST_ABY(_A); +case 0x81: ST_IX(_A); +case 0x91: ST_IY(_A); + +case 0x86: ST_ZP(_X); +case 0x96: ST_ZPY(_X); +case 0x8E: ST_AB(_X); + +case 0x84: ST_ZP(_Y); +case 0x94: ST_ZPX(_Y); +case 0x8C: ST_AB(_Y); + +/* BCC */ +case 0x90: if(_P&C_FLAG) _PC++; else {JR();} break; + +/* BCS */ +case 0xB0: if(_P&C_FLAG) {JR();} else _PC++; break; + +/* BEQ */ +case 0xF0: if(_P&Z_FLAG) {JR();} else _PC++; break; + +/* BNE */ +case 0xD0: if(_P&Z_FLAG) _PC++; else {JR();} break; + +/* BMI */ +case 0x30: if(_P&N_FLAG) {JR();} else _PC++; break; + +/* BPL */ +case 0x10: if(_P&N_FLAG) _PC++; else {JR();} break; + +/* BVC */ +case 0x50: if(_P&V_FLAG) _PC++; else {JR();} break; + +/* BVS */ +case 0x70: if(_P&V_FLAG) {JR();} else _PC++; break; + +/* Here comes the undocumented instructions. Note that this implementation + may be "wrong". If so, please tell me. +*/ + +/* AAC */ +case 0x2B: +case 0x0B: LD_IM(AND;_P&=~C_FLAG;_P|=_A>>7); + +/* AAX */ +case 0x87: ST_ZP(_A&_X); +case 0x97: ST_ZPY(_A&_X); +case 0x8F: ST_AB(_A&_X); +case 0x83: ST_IX(_A&_X); + +/* ARR - ARGH, MATEY! */ +case 0x6B: { + uint8 arrtmp; + LD_IM(AND;_P&=~V_FLAG;_P|=(_A^(_A>>1))&0x40;arrtmp=_A>>7;_A>>=1;_A|=(_P&C_FLAG)<<7;_P&=~C_FLAG;_P|=arrtmp;X_ZN(_A)); + } +/* ASR */ +case 0x4B: LD_IM(AND;LSRA); + +/* ATX(OAL) Is this(OR with $EE) correct? */ +case 0xAB: LD_IM(_A|=0xEE;AND;_X=_A); + +/* AXS */ +case 0xCB: LD_IM(AXS); + +/* DCP */ +case 0xC7: LD_ZP(DEC;CMP); +case 0xD7: LD_ZPX(DEC;CMP); +case 0xCF: LD_AB(DEC;CMP); +case 0xDF: LD_ABX(DEC;CMP); +case 0xDB: LD_ABY(DEC;CMP); +case 0xC3: LD_IX(DEC;CMP); +case 0xD3: LD_IY(DEC;CMP); + +/* ISC */ +case 0xE7: LD_ZP(INC;SBC); +case 0xF7: LD_ZPX(INC;SBC); +case 0xEF: LD_AB(INC;SBC); +case 0xFF: LD_ABX(INC;SBC); +case 0xFB: LD_ABY(INC;SBC); +case 0xE3: LD_IX(INC;SBC); +case 0xF3: LD_IY(INC;SBC); + +/* DOP */ + +case 0x04: _PC++;break; +case 0x14: _PC++;break; +case 0x34: _PC++;break; +case 0x44: _PC++;break; +case 0x54: _PC++;break; +case 0x64: _PC++;break; +case 0x74: _PC++;break; + +case 0x80: _PC++;break; +case 0x82: _PC++;break; +case 0x89: _PC++;break; +case 0xC2: _PC++;break; +case 0xD4: _PC++;break; +case 0xE2: _PC++;break; +case 0xF4: _PC++;break; + +/* KIL */ + +case 0x02: +case 0x12: +case 0x22: +case 0x32: +case 0x42: +case 0x52: +case 0x62: +case 0x72: +case 0x92: +case 0xB2: +case 0xD2: +case 0xF2:ADDCYC(0xFF); + _jammed=1; + _PC--; + break; + +/* LAR */ +case 0xBB: RMW_ABY(_S&=x;_A=_X=_S;X_ZN(_X)); + +/* LAX */ +case 0xA7: LD_ZP(LDA;LDX); +case 0xB7: LD_ZPY(LDA;LDX); +case 0xAF: LD_AB(LDA;LDX); +case 0xBF: LD_ABY(LDA;LDX); +case 0xA3: LD_IX(LDA;LDX); +case 0xB3: LD_IY(LDA;LDX); + +/* NOP */ +case 0x1A: +case 0x3A: +case 0x5A: +case 0x7A: +case 0xDA: +case 0xFA: break; + +/* RLA */ +case 0x27: RMW_ZP(ROL;AND); +case 0x37: RMW_ZPX(ROL;AND); +case 0x2F: RMW_AB(ROL;AND); +case 0x3F: RMW_ABX(ROL;AND); +case 0x3B: RMW_ABY(ROL;AND); +case 0x23: RMW_IX(ROL;AND); +case 0x33: RMW_IY(ROL;AND); + +/* RRA */ +case 0x67: RMW_ZP(ROR;ADC); +case 0x77: RMW_ZPX(ROR;ADC); +case 0x6F: RMW_AB(ROR;ADC); +case 0x7F: RMW_ABX(ROR;ADC); +case 0x7B: RMW_ABY(ROR;ADC); +case 0x63: RMW_IX(ROR;ADC); +case 0x73: RMW_IY(ROR;ADC); + +/* SLO */ +case 0x07: RMW_ZP(ASL;ORA); +case 0x17: RMW_ZPX(ASL;ORA); +case 0x0F: RMW_AB(ASL;ORA); +case 0x1F: RMW_ABX(ASL;ORA); +case 0x1B: RMW_ABY(ASL;ORA); +case 0x03: RMW_IX(ASL;ORA); +case 0x13: RMW_IY(ASL;ORA); + +/* SRE */ +case 0x47: RMW_ZP(LSR;EOR); +case 0x57: RMW_ZPX(LSR;EOR); +case 0x4F: RMW_AB(LSR;EOR); +case 0x5F: RMW_ABX(LSR;EOR); +case 0x5B: RMW_ABY(LSR;EOR); +case 0x43: RMW_IX(LSR;EOR); +case 0x53: RMW_IY(LSR;EOR); + +/* AXA - SHA */ +case 0x93: ST_IY(_A&_X&(((A-_Y)>>8)+1)); +case 0x9F: ST_ABY(_A&_X&(((A-_Y)>>8)+1)); + +/* SYA */ +case 0x9C: ST_ABX(_Y&(((A-_X)>>8)+1)); + +/* SXA */ +case 0x9E: ST_ABY(_X&(((A-_Y)>>8)+1)); + +/* XAS */ +case 0x9B: _S=_A&_X;ST_ABY(_S& (((A-_Y)>>8)+1) ); + +/* TOP */ +case 0x0C: LD_AB(;); +case 0x1C: +case 0x3C: +case 0x5C: +case 0x7C: +case 0xDC: +case 0xFC: LD_ABX(;); + +/* XAA - BIG QUESTION MARK HERE */ +case 0x8B: _A|=0xEE; _A&=_X; LD_IM(AND); diff --git a/palette.h b/palette.h new file mode 100644 index 0000000..9a4647c --- /dev/null +++ b/palette.h @@ -0,0 +1,120 @@ +pal rp2c04001[64] = { + #include "palettes/rp2c04001.h" +}; + +pal NSFPalette[39] = { + #include "palettes/nsfnew.h" +}; + +pal palettevseb[64] = { +#include "palettes/vseb.h" +}; + +pal palettevsslalom[64] = { +#include "palettes/vsslalom.h" +}; + +pal palettevsgoon[64] = { +#include "palettes/vsgoonies.h" +}; + +pal palettevsgrad[64] = { +#include "palettes/vsplatoon.h" +}; + +pal palettevscv[64] = { +#include "palettes/vscv.h" +}; + +pal palettevssmb[64] = { +#include "palettes/vssmb.h" +}; + +pal palettevsmar[64] = { +#include "palettes/vsmar.h" +}; + +pal unvpalette[6] = { +{ 0x00<<2,0x00<<2,0x00<<2}, // Black +{ 0x3F<<2,0x3F<<2,0x34<<2}, // White +{ 0x00<<2,0x00<<2,0x00<<2}, // Black +{ 0x1d<<2,0x1d<<2,0x24<<2}, // Greyish +{ 190,0,0 }, // Redish +{ 51,255,51}, // Bright green +}; + + +/* These are dynamically filled/generated palettes: */ +pal palettei[64]; // Custom palette for an individual game. +pal palettec[64]; // Custom "global" palette. +pal paletten[64]; // Mathematically generated palette. + + +/* Default palette */ +pal palette[64] = { + + { 0x1D<<2, 0x1D<<2, 0x1D<<2 }, /* Value 0 */ + { 0x09<<2, 0x06<<2, 0x23<<2 }, /* Value 1 */ + { 0x00<<2, 0x00<<2, 0x2A<<2 }, /* Value 2 */ + { 0x11<<2, 0x00<<2, 0x27<<2 }, /* Value 3 */ + { 0x23<<2, 0x00<<2, 0x1D<<2 }, /* Value 4 */ + { 0x2A<<2, 0x00<<2, 0x04<<2 }, /* Value 5 */ + { 0x29<<2, 0x00<<2, 0x00<<2 }, /* Value 6 */ + { 0x1F<<2, 0x02<<2, 0x00<<2 }, /* Value 7 */ + { 0x10<<2, 0x0B<<2, 0x00<<2 }, /* Value 8 */ + { 0x00<<2, 0x11<<2, 0x00<<2 }, /* Value 9 */ + { 0x00<<2, 0x14<<2, 0x00<<2 }, /* Value 10 */ + { 0x00<<2, 0x0F<<2, 0x05<<2 }, /* Value 11 */ + { 0x06<<2, 0x0F<<2, 0x17<<2 }, /* Value 12 */ + { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 13 */ + { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 14 */ + { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 15 */ + { 0x2F<<2, 0x2F<<2, 0x2F<<2 }, /* Value 16 */ + { 0x00<<2, 0x1C<<2, 0x3B<<2 }, /* Value 17 */ + { 0x08<<2, 0x0E<<2, 0x3B<<2 }, /* Value 18 */ + { 0x20<<2, 0x00<<2, 0x3C<<2 }, /* Value 19 */ + { 0x2F<<2, 0x00<<2, 0x2F<<2 }, /* Value 20 */ + { 0x39<<2, 0x00<<2, 0x16<<2 }, /* Value 21 */ + { 0x36<<2, 0x0A<<2, 0x00<<2 }, /* Value 22 */ + { 0x32<<2, 0x13<<2, 0x03<<2 }, /* Value 23 */ + { 0x22<<2, 0x1C<<2, 0x00<<2 }, /* Value 24 */ + { 0x00<<2, 0x25<<2, 0x00<<2 }, /* Value 25 */ + { 0x00<<2, 0x2A<<2, 0x00<<2 }, /* Value 26 */ + { 0x00<<2, 0x24<<2, 0x0E<<2 }, /* Value 27 */ + { 0x00<<2, 0x20<<2, 0x22<<2 }, /* Value 28 */ + { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 29 */ + { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 30 */ + { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 31 */ + { 0x3F<<2, 0x3F<<2, 0x3F<<2 }, /* Value 32 */ + { 0x0F<<2, 0x2F<<2, 0x3F<<2 }, /* Value 33 */ + { 0x17<<2, 0x25<<2, 0x3F<<2 }, /* Value 34 */ + { 0x10<<2, 0x22<<2, 0x3F<<2 }, /* Value 35 */ + { 0x3D<<2, 0x1E<<2, 0x3F<<2 }, /* Value 36 */ + { 0x3F<<2, 0x1D<<2, 0x2D<<2 }, /* Value 37 */ + { 0x3F<<2, 0x1D<<2, 0x18<<2 }, /* Value 38 */ + { 0x3F<<2, 0x26<<2, 0x0E<<2 }, /* Value 39 */ + { 0x3C<<2, 0x2F<<2, 0x0F<<2 }, /* Value 40 */ + { 0x20<<2, 0x34<<2, 0x04<<2 }, /* Value 41 */ + { 0x13<<2, 0x37<<2, 0x12<<2 }, /* Value 42 */ + { 0x16<<2, 0x3E<<2, 0x26<<2 }, /* Value 43 */ + { 0x00<<2, 0x3A<<2, 0x36<<2 }, /* Value 44 */ + { 0x1E<<2, 0x1E<<2, 0x1E<<2 }, /* Value 45 */ + { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 46 */ + { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 47 */ + { 0x3F<<2, 0x3F<<2, 0x3F<<2 }, /* Value 48 */ + { 0x2A<<2, 0x39<<2, 0x3F<<2 }, /* Value 49 */ + { 0x31<<2, 0x35<<2, 0x3F<<2 }, /* Value 50 */ + { 0x35<<2, 0x32<<2, 0x3F<<2 }, /* Value 51 */ + { 0x3F<<2, 0x31<<2, 0x3F<<2 }, /* Value 52 */ + { 0x3F<<2, 0x31<<2, 0x36<<2 }, /* Value 53 */ + { 0x3F<<2, 0x2F<<2, 0x2C<<2 }, /* Value 54 */ + { 0x3F<<2, 0x36<<2, 0x2A<<2 }, /* Value 55 */ + { 0x3F<<2, 0x39<<2, 0x28<<2 }, /* Value 56 */ + { 0x38<<2, 0x3F<<2, 0x28<<2 }, /* Value 57 */ + { 0x2A<<2, 0x3C<<2, 0x2F<<2 }, /* Value 58 */ + { 0x2C<<2, 0x3F<<2, 0x33<<2 }, /* Value 59 */ + { 0x27<<2, 0x3F<<2, 0x3C<<2 }, /* Value 60 */ + { 0x31<<2, 0x31<<2, 0x31<<2 }, /* Value 61 */ + { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 62 */ + { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 63 */ +}; diff --git a/palettes/nsfnew.h b/palettes/nsfnew.h new file mode 100644 index 0000000..7ee0068 --- /dev/null +++ b/palettes/nsfnew.h @@ -0,0 +1,39 @@ +{0<<2,14<<2,3<<2}, +{0<<2,13<<2,3<<2}, +{0<<2,12<<2,3<<2}, +{0<<2,11<<2,3<<2}, +{0<<2,15<<2,3<<2}, +{0<<2,15<<2,4<<2}, +{0<<2,16<<2,4<<2}, +{0<<2,17<<2,4<<2}, +{0<<2,18<<2,4<<2}, +{0<<2,19<<2,4<<2}, +{0<<2,20<<2,5<<2}, +{0<<2,20<<2,4<<2}, +{0<<2,21<<2,5<<2}, +{0<<2,22<<2,5<<2}, +{0<<2,23<<2,5<<2}, +{0<<2,24<<2,5<<2}, +{0<<2,24<<2,6<<2}, +{0<<2,25<<2,6<<2}, +{0<<2,26<<2,6<<2}, +{0<<2,27<<2,6<<2}, +{0<<2,28<<2,6<<2}, +{0<<2,28<<2,7<<2}, +{0<<2,29<<2,7<<2}, +{0<<2,30<<2,7<<2}, +{0<<2,11<<2,2<<2}, +{0<<2,10<<2,2<<2}, +{0<<2,9<<2,2<<2}, +{0<<2,8<<2,2<<2}, +{0<<2,7<<2,1<<2}, +{0<<2,7<<2,2<<2}, +{0<<2,6<<2,1<<2}, +{0<<2,5<<2,1<<2}, +{0<<2,4<<2,1<<2}, +{0<<2,3<<2,1<<2}, +{0<<2,2<<2,0<<2}, +{0<<2,3<<2,0<<2}, +{0<<2,1<<2,0<<2}, +{0<<2,0<<2,0<<2}, +{3<<2,0<<2,43<<2}, diff --git a/palettes/rp2c04001.h b/palettes/rp2c04001.h new file mode 100644 index 0000000..f2526b9 --- /dev/null +++ b/palettes/rp2c04001.h @@ -0,0 +1,64 @@ +{ 0x3f<<2, 0x31<<2, 0x36<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x36<<2, 0x0a<<2, 0x00<<2, }, +{ 0x17<<2, 0x25<<2, 0x3f<<2, }, +{ 0x00<<2, 0x20<<2, 0x22<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x39<<2, 0x00<<2, 0x16<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x1d<<2, 0x1d<<2, 0x1d<<2, }, +{ 0x3f<<2, 0x26<<2, 0x0e<<2, }, +{ 0x2a<<2, 0x00<<2, 0x04<<2, }, +{ 0x23<<2, 0x00<<2, 0x1d<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x10<<2, 0x0b<<2, 0x00<<2, }, +{ 0x3f<<2, 0x3f<<2, 0x3f<<2, }, +{ 0x0f<<2, 0x2f<<2, 0x3f<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x20<<2, 0x34<<2, 0x04<<2, }, +{ 0x27<<2, 0x3f<<2, 0x3c<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x3f<<2, 0x2f<<2, 0x2c<<2, }, +{ 0x08<<2, 0x0e<<2, 0x3b<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x16<<2, 0x3e<<2, 0x26<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x09<<2, 0x06<<2, 0x23<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x2a<<2, 0x39<<2, 0x3f<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x13<<2, 0x37<<2, 0x12<<2, }, +{ 0x00<<2, 0x3a<<2, 0x36<<2, }, +{ 0x06<<2, 0x0f<<2, 0x17<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x1f<<2, 0x02<<2, 0x00<<2, }, +{ 0x3f<<2, 0x31<<2, 0x3f<<2, }, +{ 0x29<<2, 0x00<<2, 0x00<<2, }, +{ 0x20<<2, 0x00<<2, 0x3c<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x3f<<2, 0x1d<<2, 0x18<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x00<<2, 0x25<<2, 0x00<<2, }, +{ 0x2f<<2, 0x2f<<2, 0x2f<<2, }, +{ 0x00<<2, 0x14<<2, 0x00<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x32<<2, 0x13<<2, 0x03<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x00<<2, 0x1c<<2, 0x3b<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x3f<<2, 0x1d<<2, 0x2d<<2, }, +{ 0x22<<2, 0x1c<<2, 0x00<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, diff --git a/palettes/vscv.h b/palettes/vscv.h new file mode 100644 index 0000000..8220f30 --- /dev/null +++ b/palettes/vscv.h @@ -0,0 +1,64 @@ +{127,127,127}, +{255,163,71}, +{0,0,191}, +{71,43,191}, +{151,0,135}, +{248,88,152}, +{171,19,0}, +{248,184,248}, +{191,0,0}, +{0,120,0}, +{0,107,0}, +{0,91,0}, +{255,255,255}, +{152,120,248}, +{0,0,0}, +{0,0,0}, +{191,191,191}, +{0,120,248}, +{171,19,0}, +{107,71,255}, +{0,174,0}, +{231,0,91}, +{248,56,0}, +{119,119,255}, +{175,127,0}, +{0,184,0}, +{0,171,0}, +{0,171,71}, +{0,139,139}, +{0,0,0}, +{0,0,0}, +{71,43,191}, +{248,248,248}, +{255,227,171}, +{248,120,88}, +{152,120,248}, +{0,120,248}, +{248,88,152}, +{191,191,191}, +{255,163,71}, +{200,0,200}, +{184,248,24}, +{127,127,127}, +{0,120,0}, +{0,235,219}, +{0,0,0}, +{0,0,0}, +{255,255,255}, +{255,255,255}, +{167,231,255}, +{91,219,87}, +{231,95,19}, +{0,67,88}, +{0,0,255}, +{231,0,91}, +{0,184,0}, +{251,219,123}, +{216,248,120}, +{139,23,0}, +{255,227,171}, +{0,255,255}, +{171,0,35}, +{0,0,0}, +{0,0,0}, diff --git a/palettes/vseb.h b/palettes/vseb.h new file mode 100644 index 0000000..234d993 --- /dev/null +++ b/palettes/vseb.h @@ -0,0 +1,64 @@ +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x2a<<2, 0x00<<2}, +{0x3f<<2, 0x3f<<2, 0x3f<<2}, +{0x27<<2, 0x3f<<2, 0x3c<<2}, +{0x00<<2, 0x11<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x2a<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x3f<<2, 0x3f<<2, 0x3f<<2}, +{0x31<<2, 0x35<<2, 0x3f<<2}, +{0x31,0xa6,0xf6}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x08<<2, 0x0e<<2, 0x3b<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x22<<2, 0x1c<<2, 0x00<<2}, +{0x32<<2, 0x13<<2, 0x03<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x06<<2, 0x0f<<2, 0x17<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x2a<<2}, +{0x36<<2, 0x0a<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x3f<<2, 0x36<<2, 0x2a<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0xd8,0xd6,0xb1}, +{0x3f<<2, 0x26<<2, 0x0e<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x20<<2, 0x34<<2, 0x04<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x0f<<2, 0x2f<<2, 0x3f<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x1c<<2, 0x3b<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x2a<<2, 0x39<<2, 0x3f<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0xc6,0x9D,0x62}, +{0x13<<2, 0x37<<2, 0x12<<2}, +{0x3c<<2, 0x2f<<2, 0x0f<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x20<<2, 0x00<<2, 0x3c<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, diff --git a/palettes/vsgoonies.h b/palettes/vsgoonies.h new file mode 100644 index 0000000..9dd272e --- /dev/null +++ b/palettes/vsgoonies.h @@ -0,0 +1,65 @@ + { 0x1D<<2, 0x1D<<2, 0x1D <<2}, /* Value 0 */ + { 0x09<<2, 0x06<<2, 0x23 <<2}, /* Value 1 */ + { 0x00<<2, 0x00<<2, 0x2A <<2}, /* Value 2 */ + { 0x2f<<2, 0x2f<<2, 0x2f <<2}, /* Value 3 */ + { 0x23<<2, 0x00<<2, 0x1D <<2}, /* Value 4 */ + { 0x3f<<2, 0x3f<<2, 0x3f <<2}, /* Value 5 */ + { 0x2a<<2, 0x39<<2, 0x3f <<2}, /* Value 6 */ + { 0x1F<<2, 0x02<<2, 0x00 <<2}, /* Value 7 */ + { 0x09<<2, 0x06<<2, 0x23 <<2}, /* Value 8 */ + { 0x00<<2, 0x00<<2, 0x00 <<2}, /* Value 9 */ + { 0x3f<<2, 0x2f<<2, 0x2c <<2}, /* Value a */ + { 0x00<<2, 0x0F<<2, 0x05 <<2}, /* Value b */ + { 0x06<<2, 0x0F<<2, 0x17 <<2}, /* Value c */ + { 0x00<<2, 0x00<<2, 0x00 <<2}, /* Value d */ + { 0x00<<2, 0x00<<2, 0x00 <<2}, /* Value e */ + { 0x00<<2, 0x00<<2, 0x00 <<2}, /* Value f */ + { 0x2F<<2, 0x2F<<2, 0x2F <<2}, /* Value 10 */ + { 0x00<<2, 0x1C<<2, 0x3B <<2}, /* Value 11 */ + { 0x08<<2, 0x0E<<2, 0x3B <<2}, /* Value 12 */ + { 0x08<<2, 0x0e<<2, 0x3b <<2}, /* Value 13 */ + { 0x00<<2, 0x25<<2, 0x00 <<2}, /* Value 14 */ + { 0x39<<2, 0x00<<2, 0x16 <<2}, /* Value 15 */ + { 0x32<<2, 0x13<<2, 0x03 <<2}, /* Value 16 */ + { 0x32<<2, 0x13<<2, 0x03 <<2}, /* Value 17 */ + { 0x1d<<2, 0x1d<<2, 0x1d <<2}, /* Value 18 */ + { 0x00<<2, 0x25<<2, 0x00 <<2}, /* Value 19 */ + { 0x00<<2, 0x2A<<2, 0x00 <<2}, /* Value 1a */ + { 0x00<<2, 0x00<<2, 0x2a <<2}, /* Value 1b */ + { 0x36<<2, 0x0a<<2, 0x00 <<2}, /* Value 1c */ + { 0x00<<2, 0x00<<2, 0x00 <<2}, /* Value 1d */ + { 0x00<<2, 0x00<<2, 0x00 <<2}, /* Value 1e */ + { 0x00<<2, 0x00<<2, 0x00 <<2}, /* Value 1f */ + { 0x3F<<2, 0x3F<<2, 0x3F <<2}, /* Value 20 */ + { 0x0F<<2, 0x2F<<2, 0x3F <<2}, /* Value 21 */ + { 0x17<<2, 0x25<<2, 0x3F <<2}, /* Value 22 */ + { 0x10<<2, 0x22<<2, 0x3F <<2}, /* Value 23 */ + { 0x3D<<2, 0x1E<<2, 0x3F <<2}, /* Value 24 */ + { 0x3F<<2, 0x26<<2, 0x0e <<2}, /* Value 25 */ + { 0x3F<<2, 0x1D<<2, 0x18 <<2}, /* Value 26 */ + { 0x3F<<2, 0x3f<<2, 0x3f <<2}, /* Value 27 */ + { 0x3C<<2, 0x2F<<2, 0x0F <<2}, /* Value 28 */ + { 0x20<<2, 0x34<<2, 0x04 <<2}, /* Value 29 */ + { 0x17<<2, 0x25<<2, 0x3f <<2}, /* Value 2a */ + { 0x16<<2, 0x3E<<2, 0x26 <<2}, /* Value 2b */ + { 0x00<<2, 0x1c<<2, 0x3b <<2}, /* Value 2c */ + { 0x00<<2, 0x00<<2, 0x00 <<2}, /* Value 2d */ + { 0x00<<2, 0x00<<2, 0x00 <<2}, /* Value 2e */ + { 0x00<<2, 0x00<<2, 0x00 <<2}, /* Value 2f */ + { 0x3F<<2, 0x3F<<2, 0x3F <<2}, /* Value 30 */ + { 0x2A<<2, 0x39<<2, 0x3F <<2}, /* Value 31 */ + { 0x31<<2, 0x35<<2, 0x3F <<2}, /* Value 32 */ + { 0x35<<2, 0x32<<2, 0x3F <<2}, /* Value 33 */ + { 0x1F<<2, 0x02<<2, 0x00 <<2}, /* Value 34 */ + { 0x3F<<2, 0x31<<2, 0x36 <<2}, /* Value 35 */ + { 0x3F<<2, 0x2F<<2, 0x2C <<2}, /* Value 36 */ + { 0x3F<<2, 0x36<<2, 0x2A <<2}, /* Value 37 */ + { 0x3F<<2, 0x39<<2, 0x28 <<2}, /* Value 38 */ + { 0x38<<2, 0x3F<<2, 0x28 <<2}, /* Value 39 */ + { 0x2A<<2, 0x3C<<2, 0x2F <<2}, /* Value 3a */ + { 0x2C<<2, 0x3F<<2, 0x33 <<2}, /* Value 3b */ + { 0x27<<2, 0x3F<<2, 0x3C <<2}, /* Value 3c */ + { 0x00<<2, 0x00<<2, 0x00 <<2}, /* Value 3d */ + { 0x00<<2, 0x00<<2, 0x00 <<2}, /* Value 3e */ + { 0x06<<2, 0x0f<<2, 0x17 <<2}, /* Value 3f */ + diff --git a/palettes/vsgrad.h b/palettes/vsgrad.h new file mode 100644 index 0000000..3f8c6fe --- /dev/null +++ b/palettes/vsgrad.h @@ -0,0 +1,65 @@ + { 0x3f<<2, 0x31<<2, 0x36<<2 }, /* Value 0 */ // Done + { 0x09<<2, 0x06<<2, 0x23<<2 }, /* Value 1 */ + { 0x36<<2, 0x0a<<2, 0x00<<2 }, /* Value 2 */ // Done + { 0x11<<2, 0x00<<2, 0x27<<2 }, /* Value 3 */ + { 0x00<<2, 0x20<<2, 0x22<<2 }, /* Value 4 */ // Done + { 0x2A<<2, 0x00<<2, 0x04<<2 }, /* Value 5 */ + { 0x29<<2, 0x00<<2, 0x00<<2 }, /* Value 6 */ + { 0x1F<<2, 0x02<<2, 0x00<<2 }, /* Value 7 */ + { 0x10<<2, 0x0B<<2, 0x00<<2 }, /* Value 8 */ + { 0x00<<2, 0x11<<2, 0x00<<2 }, /* Value 9 */ + { 0x3f<<2, 0x26<<2, 0x0e<<2 }, /* Value a */ // Done + { 0x00<<2, 0x0F<<2, 0x05<<2 }, /* Value b */ + { 0x06<<2, 0x0F<<2, 0x17<<2 }, /* Value c */ + { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value d */ + { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value e */ + { 0x3f<<2, 0x3f<<2, 0x3f<<2 }, /* Value f */ // Done + { 0x0F<<2, 0x2F<<2, 0x3F<<2 }, /* Value 10 */ //Done + { 0x00<<2, 0x1C<<2, 0x3B<<2 }, /* Value 11 */ + { 0x08<<2, 0x0E<<2, 0x3B<<2 }, /* Value 12 */ + { 0x20<<2, 0x00<<2, 0x3C<<2 }, /* Value 13 */ + { 0x2F<<2, 0x00<<2, 0x2F<<2 }, /* Value 14 */ + { 0x39<<2, 0x00<<2, 0x16<<2 }, /* Value 15 */ + { 0x36<<2, 0x0A<<2, 0x00<<2 }, /* Value 16 */ + { 0x08<<2, 0x0e<<2, 0x3b<<2 }, /* Value 17 */ // Done + { 0x22<<2, 0x1C<<2, 0x00<<2 }, /* Value 18 */ + { 0x00<<2, 0x25<<2, 0x00<<2 }, /* Value 19 */ + { 0x00<<2, 0x2A<<2, 0x00<<2 }, /* Value 1a */ + { 0x00<<2, 0x24<<2, 0x0E<<2 }, /* Value 1b */ + { 0x00<<2, 0x20<<2, 0x22<<2 }, /* Value 1c */ + { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 1d */ + { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 1e */ + { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 1f */ + { 0x3F<<2, 0x3F<<2, 0x3F<<2 }, /* Value 20 */ + { 0x0F<<2, 0x2F<<2, 0x3F<<2 }, /* Value 21 */ + { 0x17<<2, 0x25<<2, 0x3F<<2 }, /* Value 22 */ + { 0x0f<<2, 0x2f<<2, 0x3f<<2 }, /* Value 23 */ // Done + { 0x00<<2, 0x3a<<2, 0x36<<2 }, /* Value 24 */ // Done + { 0x06<<2, 0x0f<<2, 0x17<<2 }, /* Value 25 */ // Done + { 0x3F<<2, 0x1D<<2, 0x18<<2 }, /* Value 26 */ + { 0x3F<<2, 0x26<<2, 0x0E<<2 }, /* Value 27 */ + { 0x3C<<2, 0x2F<<2, 0x0F<<2 }, /* Value 28 */ + { 0x1f<<2, 0x02<<2, 0x00<<2 }, /* Value 29 */ // Done + { 0x13<<2, 0x37<<2, 0x12<<2 }, /* Value 2a */ + { 0x29<<2, 0x00<<2, 0x00<<2 }, /* Value 2b */ // Done + { 0x00<<2, 0x3A<<2, 0x36<<2 }, /* Value 2c */ + { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 2d */ + { 0x3f<<2, 0x1d<<2, 0x18<<2 }, /* Value 2e */ // done + { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 2f */ // done + { 0x3F<<2, 0x3F<<2, 0x3F<<2 }, /* Value 30 */ + { 0x00<<2, 0x25<<2, 0x00<<2 }, /* Value 31 */ // Done + { 0x2f<<2, 0x2f<<2, 0x2F<<2 }, /* Value 32 */ // Done + { 0x35<<2, 0x32<<2, 0x3F<<2 }, /* Value 33 */ + { 0x3F<<2, 0x31<<2, 0x3F<<2 }, /* Value 34 */ + { 0x3F<<2, 0x31<<2, 0x36<<2 }, /* Value 35 */ + { 0x3F<<2, 0x2F<<2, 0x2C<<2 }, /* Value 36 */ + { 0x32<<2, 0x13<<2, 0x03<<2 }, /* Value 37 */ // Done + { 0x3F<<2, 0x39<<2, 0x28<<2 }, /* Value 38 */ + { 0x38<<2, 0x3F<<2, 0x28<<2 }, /* Value 39 */ + { 0x2A<<2, 0x3C<<2, 0x2F<<2 }, /* Value 3a */ + { 0x2C<<2, 0x3F<<2, 0x33<<2 }, /* Value 3b */ + { 0x27<<2, 0x3F<<2, 0x3C<<2 }, /* Value 3c */ + { 0x3f<<2, 0x1d<<2, 0x2d<<2 }, /* Value 3d */ + { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 3e */ + { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 3f */ + diff --git a/palettes/vsmar.h b/palettes/vsmar.h new file mode 100644 index 0000000..5eb7143 --- /dev/null +++ b/palettes/vsmar.h @@ -0,0 +1,64 @@ +{ 0x1d<<2, 0x1d<<2, 0x1d<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x11<<2, 0x00<<2, 0x27<<2 }, +{ 0x00<<2, 0x2a<<2, 0x00<<2 }, +{ 0x3f<<2, 0x3f<<2, 0x3f<<2 }, +{ 0x2a<<2, 0x39<<2, 0x3f<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x39<<2, 0x00<<2, 0x16<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x17<<2, 0x25<<2, 0x3f<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x22<<2, 0x1c<<2, 0x00<<2 }, +{ 0x32<<2, 0x13<<2, 0x03<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x1d<<2, 0x1d<<2, 0x1d<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x3f<<2, 0x3f<<2, 0x3f<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x3f<<2, 0x26<<2, 0x0e<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x20<<2, 0x34<<2, 0x04<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x0f<<2, 0x2f<<2, 0x3f<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x1c<<2, 0x3b<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x3a<<2, 0x36<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x3a<<2, 0x36<<2 }, +{ 0x3c<<2, 0x2f<<2, 0x0f<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x14<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x31<<2, 0x35<<2, 0x3f<<2 }, +{ 0x3f<<2, 0x36<<2, 0x2a<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, diff --git a/palettes/vsplatoon.h b/palettes/vsplatoon.h new file mode 100644 index 0000000..7d7dce4 --- /dev/null +++ b/palettes/vsplatoon.h @@ -0,0 +1,64 @@ +{63<<2,49<<2,54<<2}, +{9<<2,6<<2,35<<2}, +{54<<2,10<<2,0<<2}, +{17<<2,0<<2,39<<2}, +{0<<2,32<<2,34<<2}, +{0<<2,17<<2,0<<2}, +{41<<2,0<<2,0<<2}, +{63<<2,29<<2,45<<2}, +{63<<2,63<<2,63<<2}, +{0<<2,17<<2,0<<2}, +{63<<2,38<<2,14<<2}, +{42<<2,0<<2,4<<2}, +{6<<2,15<<2,23<<2}, +{32<<2,52<<2,4<<2}, +{0<<2,0<<2,0<<2}, +{63<<2,63<<2,63<<2}, +{15<<2,47<<2,63<<2}, +{0<<2,28<<2,59<<2}, +{8<<2,14<<2,59<<2}, +{63<<2,63<<2,63<<2}, +{47<<2,0<<2,47<<2}, +{57<<2,0<<2,22<<2}, +{63<<2,47<<2,44<<2}, +{8<<2,14<<2,59<<2}, +{34<<2,28<<2,0<<2}, +{0<<2,37<<2,0<<2}, +{0<<2,42<<2,0<<2}, +{0<<2,36<<2,14<<2}, +{0<<2,32<<2,34<<2}, +{0<<2,0<<2,0<<2}, +{0<<2,0<<2,0<<2}, +{0<<2,0<<2,0<<2}, +{63<<2,63<<2,63<<2}, +{15<<2,47<<2,63<<2}, +{23<<2,37<<2,63<<2}, +{19<<2,55<<2,18<<2}, +{0<<2,58<<2,54<<2}, +{6<<2,15<<2,23<<2}, +{63<<2,29<<2,24<<2}, +{63<<2,38<<2,14<<2}, +{60<<2,47<<2,15<<2}, +{31<<2,2<<2,0<<2}, +{19<<2,55<<2,18<<2}, +{41<<2,0<<2,0<<2}, +{0<<2,58<<2,54<<2}, +{0<<2,0<<2,0<<2}, +{63<<2,29<<2,24<<2}, +{0<<2,0<<2,0<<2}, +{63<<2,63<<2,63<<2}, +{0<<2,37<<2,0<<2}, +{47<<2,47<<2,47<<2}, +{0<<2,20<<2,0<<2}, +{63<<2,49<<2,63<<2}, +{63<<2,49<<2,54<<2}, +{63<<2,38<<2,14<<2}, +{54<<2,10<<2,0<<2}, +{63<<2,57<<2,40<<2}, +{15<<2,47<<2,63<<2}, +{42<<2,60<<2,47<<2}, +{44<<2,63<<2,51<<2}, +{39<<2,63<<2,60<<2}, +{63<<2,29<<2,45<<2}, +{34<<2,28<<2,0<<2}, +{0<<2,0<<2,0<<2} diff --git a/palettes/vsslalom.h b/palettes/vsslalom.h new file mode 100644 index 0000000..4328b1c --- /dev/null +++ b/palettes/vsslalom.h @@ -0,0 +1,64 @@ +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x3f<<2, 0x26<<2, 0x0e<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x3f<<2, 0x1d<<2, 0x18<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x3f<<2, 0x1d<<2, 0x2d<<2 }, +{ 0x3f<<2, 0x3f<<2, 0x3f<<2 }, +{ 0x2f<<2, 0x00<<2, 0x2f<<2 }, +{ 0x27<<2, 0x3f<<2, 0x3c<<2 }, +{ 0x00<<2, 0x25<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x0f<<2, 0x2f<<2, 0x3f<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x16<<2, 0x3e<<2, 0x26<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x3d<<2, 0x1e<<2, 0x3f<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x2c<<2, 0x3f<<2, 0x33<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x09<<2, 0x06<<2, 0x23<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x35<<2, 0x32<<2, 0x3f<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x2f<<2, 0x2f<<2, 0x2f<<2 }, +{ 0x17<<2, 0x25<<2, 0x3f<<2 }, +{ 0x2f<<2, 0x00<<2, 0x2f<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x17<<2, 0x25<<2, 0x3f<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x3f<<2, 0x3f<<2, 0x3f<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x13<<2, 0x37<<2, 0x12<<2 }, +{ 0x3f<<2, 0x26<<2, 0x0e<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x27<<2, 0x3f<<2, 0x3c<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x3f<<2, 0x39<<2, 0x28<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, diff --git a/palettes/vssmb.h b/palettes/vssmb.h new file mode 100644 index 0000000..f942610 --- /dev/null +++ b/palettes/vssmb.h @@ -0,0 +1,65 @@ +{98,106,0}, // 0 +{0,0,255}, // 1 +{0,106,119}, // 2 +{71,43,191}, // 3 +{151,0,135}, // 4 +{171,0,35}, // 5 +{0x24,0x18,0x8c}, // 6 +{0xc8,0x4c,0x0c}, // 7 +{162,162,162},// 8 +{0,120,0}, // 9 +{0x4c,0xDC,0x48}, // a +{0,91,0}, // b +{255,213,153},// c +{255,255,0}, // d +{0,153,0}, // e +{0,0,0}, // f +{255,102,255},// 10 +{0,120,248}, // 11 +{0x20,0x38,0xec}, // 12 +{107,71,255}, // 13 +{0,0,0}, // 14 +{231,0,91}, // 15 +{248,56,0}, // 16 +{251,0x74,0x60}, // 17 +{175,127,0}, // 18 +{0,184,0}, // 19 +{81,115,255}, // 1a +{0,171,71}, // 1b +{0,139,139}, // 1c +{0,0,0}, // 1d +{145,255,136},// 1e +{0x3F,0xbF,0xFF}, // 1f +{248,248,248},// 20 +{0,0x50,0}, // 21 +{107,0,0}, // 22 +{72,85,248}, // 23 +{248,120,248},// 24 +{248,88,152}, // 25 +{89,89,88}, // 26 +{249,0,0x58}, // 27 +{0,47,47}, // 28 +{184,248,24}, // 29 + +{0x3f,0xbf,0xff}, // 2a +{88,248,152}, // 2b +{0,235,219}, // 2c +{120,120,120},// 2d +{0,0,0}, // 2e +{0,0,0}, // 2f +{255,255,255},// 30 +{167,231,255},// 31 +{89,4,0}, // 32 +{187,0,0}, // 33 +{248,184,248},// 34 +{251,167,195},// 35 +{255,255,255},// 36 +{0,227,225}, // 37 +{251,219,123},// 38 +{255,174,15}, // 39 +{184,248,184},// 3a +{184,248,216},// 3b +{0x80,0xd0,0x10}, // 3c +{248,216,248},// 3d +{255,170,170},// 3e +{0,64,0}, // 3f diff --git a/sound.c b/sound.c new file mode 100644 index 0000000..76f85a0 --- /dev/null +++ b/sound.c @@ -0,0 +1,1029 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/********************************************************/ +/******* sound.c */ +/******* */ +/******* Sound emulation code and waveform synthesis */ +/******* routines. A few ideas were inspired */ +/******* by code from Marat Fayzullin's EMUlib */ +/******* */ +/********************************************************/ + +#include +#include + +#include + +#include "types.h" +#include "x6502.h" + +#include "fce.h" +#include "svga.h" +#include "sound.h" + +uint32 soundtsinc; +uint32 soundtsi; + +uint32 Wave[2048]; +int32 WaveFinal[2048]; + +EXPSOUND GameExpSound={0,0,0}; + +uint8 trimode=0; +uint8 tricoop=0; +uint8 PSG[0x18]; + +uint8 decvolume[3]; +uint8 realvolume[3]; + +static int32 count[5]; +static int64 sqacc[2]={0,0}; +uint8 sqnon=0; + +#undef printf +uint16 nreg; + +int32 lengthcount[4]; + +static const uint8 Slengthtable[0x20]= +{ + 0x5,0x7f,0xA,0x1,0x14,0x2,0x28,0x3,0x50,0x4,0x1E,0x5,0x7,0x6,0x0E,0x7, + 0x6,0x08,0xC,0x9,0x18,0xa,0x30,0xb,0x60,0xc,0x24,0xd,0x8,0xe,0x10,0xf +}; + +static uint32 lengthtable[0x20]; + +static const uint32 SNoiseFreqTable[0x10]= +{ + 2,4,8,0x10,0x20,0x30,0x40,0x50,0x65,0x7f,0xbe,0xfe,0x17d,0x1fc,0x3f9,0x7f2 +}; +static uint32 NoiseFreqTable[0x10]; + +int64 nesincsizeLL; + +static const uint8 NTSCPCMTable[0x10]= +{ + 0xd6,0xbe,0xaa,0xa0,0x8f,0x7f,0x71,0x6b, + 0x5f,0x50,0x47,0x40,0x35,0x2a,0x24,0x1b +}; + +static const uint8 PALPCMTable[0x10]= // These values are just guessed. +{ + 0xc6,0xb0,0x9d,0x94,0x84,0x75,0x68,0x63, + 0x58,0x4a,0x41,0x3b,0x31,0x27,0x21,0x19 +}; + +uint32 PSG_base; + +// $4010 - Frequency +// $4011 - Actual data outputted +// $4012 - Address register: $c000 + V*64 +// $4013 - Size register: Size in bytes = (V+1)*64 + + +static int64 PCMacc=0; +static int PCMfreq; +int32 PCMIRQCount; +uint8 PCMBitIndex=0; +uint32 PCMAddressIndex=0; +int32 PCMSizeIndex=0; +uint8 PCMBuffer=0; +int vdis=0; + +static void Dummyfunc(void) {}; + +static void (*DoNoise)(void)=Dummyfunc; +static void (*DoTriangle)(void)=Dummyfunc; +static void (*DoPCM)(void)=Dummyfunc; +static void (*DoSQ1)(void)=Dummyfunc; +static void (*DoSQ2)(void)=Dummyfunc; + +static void CalcDPCMIRQ(void) +{ + uint32 freq; + uint32 honk; + uint32 cycles; + + if(PAL) + freq=(PALPCMTable[PSG[0x10]&0xF]<<4); + else + freq=(NTSCPCMTable[PSG[0x10]&0xF]<<4); + + cycles=(((PSG[0x13]<<4)+1)); + cycles*=freq/14; + honk=((PSG[0x13]<<4)+1)*freq; + honk-=cycles; + //if(PAL) honk/=107; + //else honk/=(double)113.66666666; + PCMIRQCount=honk*48; + //PCMIRQCount=honk*3; //180; + //if(PAL) PCMIRQCount*=.93; + vdis=0; +} + +static void PrepDPCM() +{ + PCMAddressIndex=0x4000+(PSG[0x12]<<6); + PCMSizeIndex=(PSG[0x13]<<4)+1; + PCMBitIndex=0; + //PCMBuffer=ARead[0x8000+PCMAddressIndex](0x8000+PCMAddressIndex); + if(PAL) + PCMfreq=PALPCMTable[PSG[0x10]&0xF]; + else + PCMfreq=NTSCPCMTable[PSG[0x10]&0xF]; + PCMacc=(int64)PCMfreq<<50; +} + +uint8 sweepon[2]={0,0}; +int32 curfreq[2]={0,0}; + + +uint8 SIRQStat=0; + +uint8 SweepCount[2]; +uint8 DecCountTo1[3]; + +uint8 fcnt=0; +int32 fhcnt=0; +int32 fhinc; + +static uint8 laster; + +/* Instantaneous? Maybe the new freq value is being calculated all of the time... */ +static int FASTAPASS(2) CheckFreq(uint32 cf, uint8 sr) +{ + uint32 mod; + if(!(sr&0x8)) + { + mod=cf>>(sr&7); + if((mod+cf)&0x800) + return(0); + } + return(1); +} + +static DECLFW(Write0x11) +{ + DoPCM(); + PSG[0x11]=V&0x7F; +} + +static uint8 DutyCount[2]={0,0}; + +static DECLFW(Write_PSG) +{ + //if((A>=0x4004 && A<=0x4007) || A==0x4015) + //printf("$%04x:$%02x, %d\n",A,V,timestamp); + A&=0x1f; + switch(A) + { + case 0x0: + DoSQ1(); + if(V&0x10) + realvolume[0]=V&0xF; + break; + case 0x1: + sweepon[0]=V&0x80; + break; + case 0x2: + DoSQ1(); + curfreq[0]&=0xFF00; + curfreq[0]|=V; + break; + case 0x3: + if(PSG[0x15]&1) + { + DoSQ1(); + lengthcount[0]=lengthtable[(V>>3)&0x1f]; + sqnon|=1; + } + sweepon[0]=PSG[1]&0x80; + curfreq[0]=PSG[0x2]|((V&7)<<8); + decvolume[0]=0xF; + DecCountTo1[0]=(PSG[0]&0xF)+1; + SweepCount[0]=((PSG[0x1]>>4)&7)+1; + DutyCount[0]=0; + sqacc[0]=((int64)curfreq[0]+1)<<50; + break; + + case 0x4: + DoSQ2(); + if(V&0x10) + realvolume[1]=V&0xF; + break; + case 0x5: + sweepon[1]=V&0x80; + break; + case 0x6: + DoSQ2(); + curfreq[1]&=0xFF00; + curfreq[1]|=V; + break; + case 0x7: + if(PSG[0x15]&2) + { + DoSQ2(); + lengthcount[1]=lengthtable[(V>>3)&0x1f]; + sqnon|=2; + } + sweepon[1]=PSG[0x5]&0x80; + curfreq[1]=PSG[0x6]|((V&7)<<8); + decvolume[1]=0xF; + DecCountTo1[1]=(PSG[0x4]&0xF)+1; + SweepCount[1]=((PSG[0x5]>>4)&7)+1; + DutyCount[1]=0; + sqacc[1]=((int64)curfreq[1]+1)<<50; + break; + case 0x8: + DoTriangle(); + if(laster&0x80) + { + tricoop=V&0x7F; + trimode=V&0x80; + } + if(!(V&0x7F)) + tricoop=0; + laster=V&0x80; + break; + case 0xa:DoTriangle(); + break; + case 0xb: + if(PSG[0x15]&0x4) + { + DoTriangle(); + sqnon|=4; + lengthcount[2]=lengthtable[(V>>3)&0x1f]; + } + laster=0x80; + tricoop=PSG[0x8]&0x7f; + trimode=PSG[0x8]&0x80; + break; + case 0xC:DoNoise(); + if(V&0x10) + realvolume[2]=V&0xF; + break; + case 0xE:DoNoise();break; + case 0xF: + if(PSG[0x15]&8) + { + DoNoise(); + sqnon|=8; + lengthcount[3]=lengthtable[(V>>3)&0x1f]; + } + decvolume[2]=0xF; + DecCountTo1[2]=(PSG[0xC]&0xF)+1; + break; + case 0x10:DoPCM(); + if(!(V&0x80)) + X6502_IRQEnd(FCEU_IQDPCM); + break; + case 0x15: + { + int t=V^PSG[0x15]; + + if(t&1) + DoSQ1(); + if(t&2) + DoSQ2(); + if(t&4) + DoTriangle(); + if(t&8) + DoNoise(); + if(t&0x10) + DoPCM(); + sqnon&=V; + if(V&0x10) + { + if(!(PSG[0x15]&0x10)) + { + PrepDPCM(); + CalcDPCMIRQ(); + } + else if(vdis) + CalcDPCMIRQ(); + } + else + PCMIRQCount=0; + X6502_IRQEnd(FCEU_IQDPCM); + } + break; + case 0x17: + V&=0xC0; + fcnt=0; + if(V&0x80) + FrameSoundUpdate(); + fhcnt=fhinc; + X6502_IRQEnd(FCEU_IQFCOUNT); + SIRQStat&=~0x40; + break; + } + PSG[A]=V; +} + +DECLFR(Read_PSG) +{ + uint8 ret; + if(PSG[0x15]&0x10) + DoPCM(); + ret=(PSG[0x15]&(sqnon|0x10))|SIRQStat; + SIRQStat&=~0x40; + X6502_IRQEnd(/*FCEU_IQDPCM|*/FCEU_IQFCOUNT); + return ret; +} + +DECLFR(Read_PSGDummy) +{ + uint8 ret; + + ret=(PSG[0x15]&sqnon)|SIRQStat; + SIRQStat&=~0x40; + X6502_IRQEnd(/*FCEU_IQDPCM|*/FCEU_IQFCOUNT); + return ret; +} + +static void FASTAPASS(1) FrameSoundStuff(int V) +{ + int P; + + DoSQ1(); + DoSQ2(); + DoNoise(); + + switch((V&1)) + { + case 1: /* Envelope decay, linear counter, length counter, freq sweep */ + if(PSG[0x15]&4 && sqnon&4) + if(!(PSG[8]&0x80)) + { + if(lengthcount[2]>0) + { + lengthcount[2]--; + if(lengthcount[2]<=0) + { + DoTriangle(); + sqnon&=~4; + } + } + } + + for(P=0;P<2;P++) + { + if(PSG[0x15]&(P+1) && sqnon&(P+1)) + { + if(!(PSG[P<<2]&0x20)) + { + if(lengthcount[P]>0) + { + lengthcount[P]--; + if(lengthcount[P]<=0) + { + sqnon&=~(P+1); + } + } + } + } + /* Frequency Sweep Code Here */ + /* xxxx 0000 */ + /* xxxx = hz. 120/(x+1)*/ + if(sweepon[P]) + { + int32 mod=0; + + if(SweepCount[P]>0) SweepCount[P]--; + if(SweepCount[P]<=0) + { + SweepCount[P]=((PSG[(P<<2)+0x1]>>4)&7)+1; //+1; + { + if(PSG[(P<<2)+0x1]&0x8) + { + mod-=(P^1)+((curfreq[P])>>(PSG[(P<<2)+0x1]&7)); + + if(curfreq[P] && (PSG[(P<<2)+0x1]&7)/* && sweepon[P]&0x80*/) + { + curfreq[P]+=mod; + } + } + else + { + mod=curfreq[P]>>(PSG[(P<<2)+0x1]&7); + if((mod+curfreq[P])&0x800) + { + sweepon[P]=0; + curfreq[P]=0; + } + else + { + if(curfreq[P] && (PSG[(P<<2)+0x1]&7)/* && sweepon[P]&0x80*/) + { + curfreq[P]+=mod; + } + } + } + } + } + } + } + + if(PSG[0x15]&0x8 && sqnon&8) + { + if(!(PSG[0xC]&0x20)) + { + if(lengthcount[3]>0) + { + lengthcount[3]--; + if(lengthcount[3]<=0) + { + sqnon&=~8; + } + } + } + } + + case 0: /* Envelope decay + linear counter */ + if(!trimode) + { + laster=0; + if(tricoop) + { + if(tricoop==1) DoTriangle(); + tricoop--; + } + } + + for(P=0;P<2;P++) + { + if(DecCountTo1[P]>0) DecCountTo1[P]--; + if(DecCountTo1[P]<=0) + { + DecCountTo1[P]=(PSG[P<<2]&0xF)+1; + if(decvolume[P] || PSG[P<<2]&0x20) + { + decvolume[P]--; + /* Step from 0 to full volume seems to take twice as long + as the other steps. I don't know if this is the correct + way to double its length, though(or if it even matters). + */ + if((PSG[P<<2]&0x20) && (decvolume[P]==0)) + DecCountTo1[P]<<=1; + decvolume[P]&=15; + } + } + if(!(PSG[P<<2]&0x10)) + realvolume[P]=decvolume[P]; + } + + if(DecCountTo1[2]>0) DecCountTo1[2]--; + if(DecCountTo1[2]<=0) + { + DecCountTo1[2]=(PSG[0xC]&0xF)+1; + if(decvolume[2] || PSG[0xC]&0x20) + { + decvolume[2]--; + /* Step from 0 to full volume seems to take twice as long + as the other steps. I don't know if this is the correct + way to double its length, though(or if it even matters). + */ + if((PSG[0xC]&0x20) && (decvolume[2]==0)) + DecCountTo1[2]<<=1; + decvolume[2]&=15; + } + } + if(!(PSG[0xC]&0x10)) + realvolume[2]=decvolume[2]; + + break; + } + +} + +void FrameSoundUpdate(void) +{ + // Linear counter: Bit 0-6 of $4008 + // Length counter: Bit 4-7 of $4003, $4007, $400b, $400f + + if(fcnt==3) + { + if(PSG[0x17]&0x80) + fhcnt+=fhinc; + if(!(PSG[0x17]&0xC0)) + { + SIRQStat|=0x40; + X6502_IRQBegin(FCEU_IQFCOUNT); + } + } + //if(SIRQStat&0x40) X6502_IRQBegin(FCEU_IQFCOUNT); + FrameSoundStuff(fcnt); + fcnt=(fcnt+1)&3; +} + +static uint32 ChannelBC[5]; + +static uint32 RectAmp[2][8]; + +static void FASTAPASS(1) CalcRectAmp(int P) +{ + static int tal[4]={1,2,4,6}; + int V; + int x; + uint32 *b=RectAmp[P]; + int m; + + //if(PSG[P<<2]&0x10) + V=realvolume[P]<<4; + //V=(PSG[P<<2]&15)<<4; + //else + // V=decvolume[P]<<4; + m=tal[(PSG[P<<2]&0xC0)>>6]; + for(x=0;x>4]+=PSG[0x11]<<3; + goto endopcmo; + } + } + else + { + PCMBuffer=ARead[0x8000+PCMAddressIndex](0x8000+PCMAddressIndex); + PCMAddressIndex=(PCMAddressIndex+1)&0x7fff; + } + } + + { + int t=(((PCMBuffer>>PCMBitIndex)&1)<<2)-2; + uint8 bah=PSG[0x11]; + + PCMacc+=freq; + PSG[0x11]+=t; + if(PSG[0x11]&0x80) + PSG[0x11]=bah; + else + out=PSG[0x11]<<3; + } + PCMBitIndex=(PCMBitIndex+1)&7; + } + Wave[V>>4]+=out; //(PSG[0x11]-64)<<3; + } + } + else + { + if((end-start)>64) + { + for(V=start;V<=(start|15);V++) + Wave[V>>4]+=out; + out<<=4; + for(V=(start>>4)+1;V<(end>>4);V++) + Wave[V]+=out; + out>>=4; + for(V=end&(~15);V>4]+=out; + } + else + for(V=start;V>4]+=out; + } + endopcmo:; +} + +static void RDoSQ1(void) +{ + int32 V; + int32 start,end; + int64 freq; + + CalcRectAmp(0); + start=ChannelBC[0]; + end=(timestamp<<16)/soundtsinc; + if(end<=start) return; + ChannelBC[0]=end; + + if(curfreq[0]<8 || curfreq[0]>0x7ff) + return; + if(!CheckFreq(curfreq[0],PSG[0x1])) + return; + + if(PSG[0x15]&1 && sqnon&1) + { + uint32 out=RectAmp[0][DutyCount[0]]; + freq=curfreq[0]+1; + { + freq<<=50; + for(V=start;V>4]+=out; + sqacc[0]-=nesincsizeLL; + if(sqacc[0]<=0) + { + rea: + sqacc[0]+=freq; + DutyCount[0]++; + if(sqacc[0]<=0) goto rea; + + DutyCount[0]&=7; + out=RectAmp[0][DutyCount[0]]; + } + + } + } + } +} + +static void RDoSQ2(void) +{ + int32 V; + int32 start,end; + int64 freq; + + CalcRectAmp(1); + start=ChannelBC[1]; + end=(timestamp<<16)/soundtsinc; + if(end<=start) return; + ChannelBC[1]=end; + + if(curfreq[1]<8 || curfreq[1]>0x7ff) + return; + if(!CheckFreq(curfreq[1],PSG[0x5])) + return; + + if(PSG[0x15]&2 && sqnon&2) + { + uint32 out=RectAmp[1][DutyCount[1]]; + freq=curfreq[1]+1; + + { + freq<<=50; + for(V=start;V>4]+=out; + sqacc[1]-=nesincsizeLL; + if(sqacc[1]<=0) + { + rea: + sqacc[1]+=freq; + DutyCount[1]++; + if(sqacc[1]<=0) goto rea; + + DutyCount[1]&=7; + out=RectAmp[1][DutyCount[1]]; + } + + } + } + } +} + + +static void RDoTriangle(void) +{ + static uint32 tcout=0; + int32 V; + int32 start,end; //,freq; + int64 freq=(((PSG[0xa]|((PSG[0xb]&7)<<8))+1)); + + start=ChannelBC[2]; + end=(timestamp<<16)/soundtsinc; + if(end<=start) return; + ChannelBC[2]=end; + + if(! (PSG[0x15]&0x4 && sqnon&4 && tricoop) ) + { // Counter is halted, but we still need to output. + for(V=start;V>4]+=tcout; + } + else if(freq<=4) // 55.9Khz - Might be barely audible on a real NES, but + // it's too costly to generate audio at this high of a frequency + // (55.9Khz * 32 for the stepping). + // The same could probably be said for ~27.8Khz, so we'll + // take care of that too. We'll just output the average + // value(15/2 - scaled properly for our output format, of course). + // We'll also take care of ~18Khz and ~14Khz too, since they should be barely audible. + // (Some proof or anything to confirm/disprove this would be nice.). + { + for(V=start;V>4]+=((0xF<<4)+(0xF<<2))>>1; + } + else + { + static int64 triacc=0; + static uint8 tc=0; + + freq<<=49; + for(V=start;V>4]+=tcout; + } + } +} + +static void RDoNoise(void) +{ + int32 inc,V; + int32 start,end; + + start=ChannelBC[3]; + end=(timestamp<<16)/soundtsinc; + if(end<=start) return; + ChannelBC[3]=end; + + if(PSG[0x15]&0x8 && sqnon&8) + { + uint32 outo; + uint32 amptab[2]; + uint8 amplitude; + + amplitude=realvolume[2]; + //if(PSG[0xC]&0x10) + // amplitude=(PSG[0xC]&0xF); + //else + // amplitude=decvolume[2]&0xF; + + inc=NoiseFreqTable[PSG[0xE]&0xF]; + amptab[0]=((amplitude<<2)+amplitude+amplitude)<<1; + amptab[1]=0; + outo=amptab[nreg&1]; + + if(amplitude) + { + if(PSG[0xE]&0x80) // "short" noise + for(V=start;V>4]+=outo; + if(count[3]>=inc) + { + uint8 feedback; + + feedback=((nreg>>8)&1)^((nreg>>14)&1); + nreg=(nreg<<1)+feedback; + nreg&=0x7fff; + outo=amptab[nreg&1]; + count[3]-=inc; + } + count[3]+=0x1000; + } + else + for(V=start;V>4]+=outo; + if(count[3]>=inc) + { + uint8 feedback; + + feedback=((nreg>>13)&1)^((nreg>>14)&1); + nreg=(nreg<<1)+feedback; + nreg&=0x7fff; + outo=amptab[nreg&1]; + count[3]-=inc; + } + count[3]+=0x1000; + } + } + + } +} + +void SetNESSoundMap(void) +{ + SetWriteHandler(0x4000,0x4013,Write_PSG); + SetWriteHandler(0x4011,0x4011,Write0x11); + SetWriteHandler(0x4015,0x4015,Write_PSG); + SetWriteHandler(0x4017,0x4017,Write_PSG); + SetReadHandler(0x4015,0x4015,Read_PSG); +} + +static int32 WaveNSF[256]; + +int64 highp; // 0 through 65536, 0 = no high pass, 65536 = max high pass + +int64 lowp; // 0 through 65536, 65536 = max low pass(total attenuation) + // 65536 = no low pass +static void FilterSound(uint32 *in, int32 *out, int count) +{ + static int64 acc=0, acc2=0; + + for(;count;count--,in++,out++) + { + int64 diff; + + diff=((int64)*in<<24)-acc; + + acc+=(diff*highp)>>16; + acc2+=((diff-acc2)*lowp)>>16; + *in=0; + *out=(acc2*(int64)FSettings.SoundVolume)>>(24+16); + if(*out<-32767) *out=-32767; + if(*out>32767) *out=32767; + //*out=((int64)(acc2>>24)*(int64)FSettings.SoundVolume)>>16; //acc2>>24; + } +} + +int FlushEmulateSound(void) +{ + uint32 end; + int x; + + if(!timestamp) return(0); + + if(!FSettings.SndRate) + { + end=0; + goto nosoundo; + } + + end=(timestamp<<16)/soundtsinc; + DoSQ1(); + DoSQ2(); + DoTriangle(); + DoNoise(); + DoPCM(); + + if(GameExpSound.Fill) + GameExpSound.Fill(end&0xF); + + FilterSound(Wave,WaveFinal,end>>4); + + if(FCEUGameInfo.type==GIT_NSF) + { + int x,s=0,si=end/1024; // Only want 1/4 of the output buffer to be displayed + for(x=0;x<256;x++) + { + WaveNSF[x]=WaveFinal[s>>4]; + s+=si; + } + } + + if(end&0xF) + Wave[0]=Wave[(end>>4)]; + Wave[(end>>4)]=0; + + nosoundo: + for(x=0;x<5;x++) + ChannelBC[x]=end&0xF; + timestampbase+=timestamp; + timestamp=(soundtsinc*(end&0xF))>>16; + timestampbase-=timestamp; + return(end>>4); +} + +void GetSoundBuffer(int32 **W) +{ + *W=WaveNSF; +} + +void PowerSound(void) +{ + int x; + + SetNESSoundMap(); + + for(x=0;x<0x16;x++) + if(x!=0x14) + BWrite[0x4000+x](0x4000+x,0); + PSG[0x17]=0; //x40; + fhcnt=fhinc; + fcnt=0; + nreg=1; +} + +void ResetSound(void) +{ + int x; + for(x=0;x<0x16;x++) + if(x!=1 && x!=5 && x!=0x14) BWrite[0x4000+x](0x4000+x,0); + PSG[0x17]=0; + fhcnt=fhinc; + fcnt=0; + nreg=1; +} + +void SetSoundVariables(void) +{ + int x; + + fhinc=PAL?16626:14915; // *2 CPU clock rate + fhinc*=24; + for(x=0;x<0x20;x++) + lengthtable[x]=Slengthtable[x]<<1; + + if(FSettings.SndRate) + { + DoNoise=RDoNoise; + DoTriangle=RDoTriangle; + DoPCM=RDoPCM; + DoSQ1=RDoSQ1; + DoSQ2=RDoSQ2; + } + else + { + DoNoise=DoTriangle=DoPCM=DoSQ1=DoSQ2=Dummyfunc; + } + + if(!FSettings.SndRate) return; + if(GameExpSound.RChange) + GameExpSound.RChange(); + + nesincsizeLL=(int64)((int64)562949953421312*(long double)(PAL?PAL_CPU:NTSC_CPU)/(FSettings.SndRate OVERSAMPLE)); + PSG_base=(uint32)(PAL?(long double)PAL_CPU/16:(long double)NTSC_CPU/16); + + for(x=0;x<0x10;x++) + { + long double z; + z=SNoiseFreqTable[x]<<1; + z=(PAL?PAL_CPU:NTSC_CPU)/z; + z=(long double)((uint32)((FSettings.SndRate OVERSAMPLE)<<12))/z; + NoiseFreqTable[x]=z; + } + soundtsinc=(uint32)((uint64)(PAL?(long double)PAL_CPU*65536:(long double)NTSC_CPU*65536)/(FSettings.SndRate OVERSAMPLE)); + memset(Wave,0,2048*4); + for(x=0;x<5;x++) + ChannelBC[x]=0; + highp=(250<<16)/FSettings.SndRate; // Arbitrary + lowp=((int64)25000<<16)/FSettings.SndRate; // Arbitrary + + if(highp>(1<<16)) highp=1<<16; + if(lowp>(1<<16)) lowp=1<<16; +} + +void FixOldSaveStateSFreq(void) +{ + int x; + for(x=0;x<2;x++) + { + curfreq[x]=PSG[0x2+(x<<2)]|((PSG[0x3+(x<<2)]&7)<<8); + } +} + +void FCEUI_Sound(int Rate) +{ + FSettings.SndRate=Rate; + SetSoundVariables(); +} + +void FCEUI_SetSoundVolume(uint32 volume) +{ + FSettings.SoundVolume=(volume<<16)/100; +} diff --git a/sound.h b/sound.h new file mode 100644 index 0000000..53c397b --- /dev/null +++ b/sound.h @@ -0,0 +1,73 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define OVERSAMPLESHIFT 4 +#define OVERSAMPLE *16 +#define SND_BUFSIZE 256 + + +typedef struct { + void (*Fill)(int Count); + void (*RChange)(void); + void (*Kill)(void); +} EXPSOUND; + +extern EXPSOUND GameExpSound; + +extern int64 nesincsizeLL; +extern uint8 PSG[]; +extern uint32 PSG_base; +extern int32 PCMIRQCount; + +void SetSoundVariables(void); +void PowerSound(void); +void ResetSound(void); +extern uint8 decvolume[]; + +extern int vdis; +extern uint8 sqnon; +extern uint16 nreg; + +extern uint8 trimode; +extern uint8 tricoop; +extern uint8 PCMBitIndex; +extern uint32 PCMAddressIndex; +extern int32 PCMSizeIndex; +extern uint8 PCMBuffer; + +extern uint8 sweepon[2]; +extern int32 curfreq[2]; + +extern uint8 SweepCount[2]; +extern uint8 DecCountTo1[3]; + +extern uint8 fcnt; +extern int32 fhcnt; +extern int32 fhinc; + +void GetSoundBuffer(int32 **W); +int FlushEmulateSound(void); +extern uint32 Wave[2048]; +extern int32 WaveFinal[2048]; +extern uint32 soundtsinc; + +void SetNESSoundMap(void); +void FrameSoundUpdate(void); +void FixOldSaveStateSFreq(void); diff --git a/state.c b/state.c new file mode 100644 index 0000000..ac48818 --- /dev/null +++ b/state.c @@ -0,0 +1,607 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* TODO: Add (better) file io error checking */ +/* TODO: Change save state file format. */ + +#include +#include +#include + +#include "types.h" +#include "x6502.h" +#include "version.h" +#include "fce.h" +#include "sound.h" +#define INESPRIV // Take this out when old save state support is removed in a future version. +#include "ines.h" +#include "svga.h" +#include "endian.h" +#include "fds.h" +#include "general.h" +#include "state.h" +#include "memory.h" + +static SFORMAT SFMDATA[64]; +static int SFEXINDEX; +static int stateversion; + +#define RLSB 0x80000000 + +#define SFCPUELEMENTS 7 + +SFORMAT SFCPU[SFCPUELEMENTS]={ + { &X.PC, 2|RLSB, "PC\0"}, + { &X.A, 1, "A\0\0"}, + { &X.P, 1, "P\0\0"}, + { &X.X, 1, "X\0\0"}, + { &X.Y, 1, "Y\0\0"}, + { &X.S, 1, "S\0\0"}, + { RAM, 0x800, "RAM"} +}; + +#define SFCPUCELEMENTS 6 +SFORMAT SFCPUC[SFCPUCELEMENTS]={ + { &X.jammed, 1, "JAMM"}, + { &X.IRQlow, 1, "IRQL"}, + { &X.tcount, 4|RLSB, "ICoa"}, + { &X.count, 4|RLSB, "ICou"}, + { ×tamp, 4|RLSB, "TIME"}, + { ×tampbase, 8|RLSB, "TMEB"} +}; + +static uint16 TempAddrT,RefreshAddrT; + +#define SFPPUELEMENTS 10 +SFORMAT SFPPU[SFPPUELEMENTS]={ + { NTARAM, 0x800, "NTAR"}, + { PALRAM, 0x20, "PRAM"}, + { SPRAM, 0x100, "SPRA"}, + { PPU, 0x4, "PPUR"}, + { &XOffset, 1, "XOFF"}, + { &vtoggle, 1, "VTOG"}, + { &RefreshAddrT, 2|RLSB, "RADD"}, + { &TempAddrT, 2|RLSB, "TADD"}, + { &VRAMBuffer, 1, "VBUF"}, + { &PPUGenLatch, 1, "PGEN"}, +}; + +// Is this chunk necessary? I'll fix it later. +//#define SFCTLRELEMENTS 2 +//SFORMAT SFCTLR[SFCTLRELEMENTS]={ +// { &joy_readbit, 1, "J1RB"}, +// { &joy2_readbit, 1, "J2RB"} +//}; + +#define SFSNDELEMENTS 18 +SFORMAT SFSND[SFSNDELEMENTS]={ + { &fhcnt, 4|RLSB,"FHCN"}, + { &fcnt, 1, "FCNT"}, + { PSG, 14, "PSG"}, + { &PSG[0x15], 1, "P15"}, + { &PSG[0x17], 1, "P17"}, + { decvolume, 3, "DECV"}, + { &sqnon, 1, "SQNO"}, + { &nreg, 2|RLSB, "NREG"}, + { &trimode, 1, "TRIM"}, + { &tricoop, 1, "TRIC"}, + { sweepon, 2, "SWEE"}, + { &curfreq[0], 4|RLSB,"CRF1"}, + { &curfreq[1], 4|RLSB,"CRF2"}, + { SweepCount, 2,"SWCT"}, + { DecCountTo1, 3,"DCT1"}, + { &PCMBitIndex, 1,"PBIN"}, + { &PCMAddressIndex, 4|RLSB, "PAIN"}, + { &PCMSizeIndex, 4|RLSB, "PSIN"} +}; + + +int WriteStateChunk(FILE *st, int type, SFORMAT *sf, int count) +{ + int bsize; + int x; + + fputc(type,st); + + for(x=bsize=0;x=0;z--) + { + fputc(*(uint8*)sf[x].v,st); + } + } + else + fwrite((uint8 *)sf[x].v,1,sf[x].s&(~RLSB),st); + } + #endif + } + return (bsize+5); +} + +int ReadStateChunk(FILE *st, SFORMAT *sf, int count, int size) +{ + uint8 tmpyo[16]; + int bsize; + int x; + + for(x=bsize=0;x=53) + bsize+=count<<3; + else + { + if(bsize!=size) + { + fseek(st,size,SEEK_CUR); + return 0; + } + } + + if(stateversion<56) + memcpy(tmpyo,mapbyte3,16); + + if(stateversion>=53) + { + int temp; + temp=ftell(st); + + while(ftell(st)=0;z--) + *(uint8*)sf[x].v=fgetc(st); + } + else + #endif + { + fread((uint8 *)sf[x].v,1,sf[x].s&(~RLSB),st); + } + goto bloo; + } + } + nkayo: + fseek(st,tsize,SEEK_CUR); + bloo:; + } // while(...) + } // >=53 + else + { + for(x=0;x=0;z--) + { + *(uint8*)sf[x].v=fgetc(st); + } + else + fread((uint8 *)sf[x].v,1,sf[x].s&(~RLSB),st); + #endif + } + } + if(stateversion<56) + { + for(x=0;x<16;x++) + #ifdef LSB_FIRST + mapbyte1[x]=mapbyte1[x<<1]; + #else + mapbyte1[x]=mapbyte1[(x<<1)+1]; + #endif + memcpy(mapbyte3,tmpyo,16); + } + return 1; +} + +int ReadStateChunks(FILE *st) +{ + int t; + uint32 size; + int ret=1; + +for(;;) + { + t=fgetc(st); + if(t==EOF) break; + if(!read32(&size,st)) break; + switch(t) + { + case 1:if(!ReadStateChunk(st,SFCPU,SFCPUELEMENTS,size)) ret=0;break; + case 2:if(!ReadStateChunk(st,SFCPUC,SFCPUCELEMENTS,size)) ret=0; + else + { + X.mooPI=X.P; // Quick and dirty hack. + } + break; + case 3:if(!ReadStateChunk(st,SFPPU,SFPPUELEMENTS,size)) ret=0;break; +// case 4:if(!ReadStateChunk(st,SFCTLR,SFCTLRELEMENTS,size)) ret=0;break; + case 5:if(!ReadStateChunk(st,SFSND,SFSNDELEMENTS,size)) ret=0;break; + case 0x10:if(!ReadStateChunk(st,SFMDATA,SFEXINDEX,size)) ret=0;break; + default: if(fseek(st,size,SEEK_CUR)<0) goto endo;break; + } + } + endo: + return ret; +} + + +int CurrentState=0; +extern int geniestage; +void SaveState(void) +{ + FILE *st=NULL; + + TempAddrT=TempAddr; + RefreshAddrT=RefreshAddr; + + if(geniestage==1) + { + FCEU_DispMessage("Cannot save FCS in GG screen."); + return; + } + + st=fopen(FCEU_MakeFName(FCEUMKF_STATE,CurrentState,0),"wb"); + + if(st!=NULL) + { + static uint32 totalsize; + static uint8 header[16]="FCS"; + memset(header+4,0,13); + header[3]=VERSION_NUMERIC; + fwrite(header,1,16,st); + + totalsize=WriteStateChunk(st,1,SFCPU,SFCPUELEMENTS); + totalsize+=WriteStateChunk(st,2,SFCPUC,SFCPUCELEMENTS); + totalsize+=WriteStateChunk(st,3,SFPPU,SFPPUELEMENTS); + // totalsize+=WriteStateChunk(st,4,SFCTLR,SFCTLRELEMENTS); + totalsize+=WriteStateChunk(st,5,SFSND,SFSNDELEMENTS); + totalsize+=WriteStateChunk(st,0x10,SFMDATA,SFEXINDEX); + + fseek(st,4,SEEK_SET); + write32(totalsize,st); + SaveStateStatus[CurrentState]=1; + fclose(st); + FCEU_DispMessage("State %d saved.",CurrentState); + } + else + FCEU_DispMessage("State %d save error.",CurrentState); +} + +static int LoadStateOld(FILE *st); +void LoadState(void) +{ + int x; + FILE *st=NULL; + + if(geniestage==1) + { + FCEU_DispMessage("Cannot load FCS in GG screen."); + return; + } + + st=fopen(FCEU_MakeFName(FCEUMKF_STATE,CurrentState,0),"rb"); + + if(st!=NULL) + { + uint8 header[16]; + + fread(&header,1,16,st); + if(memcmp(header,"FCS",3)) + { + fseek(st,0,SEEK_SET); + if(!LoadStateOld(st)) + goto lerror; + goto okload; + } + stateversion=header[3]; + if(stateversion<53) + FixOldSaveStateSFreq(); + x=ReadStateChunks(st); + if(GameStateRestore) GameStateRestore(header[3]); + if(x) + { + okload: + TempAddr=TempAddrT; + RefreshAddr=RefreshAddrT; + + SaveStateStatus[CurrentState]=1; + FCEU_DispMessage("State %d loaded.",CurrentState); + SaveStateStatus[CurrentState]=1; + } + else + { + SaveStateStatus[CurrentState]=1; + FCEU_DispMessage("Error(s) reading state %d!",CurrentState); + } + } + else + { + lerror: + FCEU_DispMessage("State %d load error.",CurrentState); + SaveStateStatus[CurrentState]=0; + return; + } + fclose(st); +} + +char SaveStateStatus[10]; +void CheckStates(void) +{ + FILE *st=NULL; + int ssel; + + if(SaveStateStatus[0]==-1) + for(ssel=0;ssel<10;ssel++) + { + st=fopen(FCEU_MakeFName(FCEUMKF_STATE,ssel,0),"rb"); + if(st) + { + SaveStateStatus[ssel]=1; + fclose(st); + } + else + SaveStateStatus[ssel]=0; + } +} + +void SaveStateRefresh(void) +{ + SaveStateStatus[0]=-1; +} + +void ResetExState(void) +{ + int x; + for(x=0;x=31) + PSG[0x17]=MapperExRAM[115]; + else + PSG[0x17]|=0x40; + PSG[0x15]&=0xF; + sqnon=PSG[0x15]; + + X.IRQlow=0; + afread(&nada,1,1); + afread(&nada,1,1); + afread(&nada,1,1); + afread(&nada,1,1); + afread(&nada,1,1); + afread(&nada,1,1); + afread(&XOffset,1,1); + PPUCHRRAM=0; + for(x=0;x<8;x++) + { + nada=0; + afread(&nada,1,1); + PPUCHRRAM|=(nada?1:0)< +#include +#include +#include + +#include + + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#include "types.h" +#include "svga.h" +#include "fce.h" +#include "general.h" +#include "video.h" +#include "sound.h" +#include "version.h" +#include "nsf.h" +#include "palette.h" +#include "fds.h" +#include "netplay.h" +#include "state.h" +#include "cart.h" +#include "input.h" + +FCEUS FSettings; + +static int howlong; +static char errmsg[65]; + +void FCEU_PrintError(char *format, ...) +{ + char temp[2048]; + + va_list ap; + + va_start(ap,format); + vsprintf(temp,format,ap); + FCEUD_PrintError(temp); + + va_end(ap); +} + +void FCEU_DispMessage(char *format, ...) +{ + va_list ap; + + va_start(ap,format); + vsprintf(errmsg,format,ap); + va_end(ap); + + howlong=180; +} + +void FCEUI_SetRenderedLines(int ntscf, int ntscl, int palf, int pall) +{ + FSettings.UsrFirstSLine[0]=ntscf; + FSettings.UsrLastSLine[0]=ntscl; + FSettings.UsrFirstSLine[1]=palf; + FSettings.UsrLastSLine[1]=pall; + if(PAL) + { + FSettings.FirstSLine=FSettings.UsrFirstSLine[1]; + FSettings.LastSLine=FSettings.UsrLastSLine[1]; + } + else + { + FSettings.FirstSLine=FSettings.UsrFirstSLine[0]; + FSettings.LastSLine=FSettings.UsrLastSLine[0]; + } + +} + +void FCEUI_SetVidSystem(int a) +{ + FSettings.PAL=a?1:0; + FCEU_ResetVidSys(); + FCEU_ResetPalette(); +} + +int FCEUI_GetCurrentVidSystem(int *slstart, int *slend) +{ + if(slstart) + *slstart=FSettings.FirstSLine; + if(slend) + *slend=FSettings.LastSLine; + return(PAL); +} + +#ifdef NETWORK +void FCEUI_SetNetworkPlay(int type) +{ + FSettings.NetworkPlay=type; +} +#endif + +void FCEUI_SetGameGenie(int a) +{ + FSettings.GameGenie=a?1:0; +} + +static void CalculatePalette(void); +static void ChoosePalette(void); +static void WritePalette(void); + +#ifndef NETWORK +#define netplay 0 +#endif + +static uint8 StateShow=0; + +uint8 Exit=0; + +uint8 DIPS=0; +uint8 vsdip=0; +int coinon=0; + +uint8 pale=0; +uint8 CommandQueue=0; + +static int controlselect=0; +static int ntsccol=0; +static int ntsctint=46+10; +static int ntschue=72; +static int controllength=0; + +pal *palo; +static pal *palpoint[8]= + { + palette, + palettevscv, + palettevssmb, + palettevsmar, + palettevsgoon, + palettevsslalom, + palettevseb, + rp2c04001 + }; + +void FCEUI_SetSnapName(int a) +{ + FSettings.SnapName=a; +} + +void FCEUI_SaveExtraDataUnderBase(int a) +{ + FSettings.SUnderBase=a; +} + +void FCEUI_SetPaletteArray(uint8 *pal) +{ + if(!pal) + palpoint[0]=palette; + else + { + int x; + palpoint[0]=palettec; + for(x=0;x<64;x++) + { + palpoint[0][x].r=*((uint8 *)pal+x+x+x); + palpoint[0][x].g=*((uint8 *)pal+x+x+x+1); + palpoint[0][x].b=*((uint8 *)pal+x+x+x+2); + } + } + FCEU_ResetPalette(); +} + +void FCEUI_SelectState(int w) +{ + if(netplay!=2 && FCEUGameInfo.type!=GIT_NSF) + CommandQueue=42+w; +} + +void FCEUI_SaveState(void) +{ + if(netplay!=2 && FCEUGameInfo.type!=GIT_NSF) + CommandQueue=40; +} + +void FCEUI_LoadState(void) +{ + if(netplay!=2 && FCEUGameInfo.type!=GIT_NSF) + CommandQueue=41; +} + +int32 FCEUI_GetDesiredFPS(void) +{ + if(PAL) + return(838977920); // ~50.007 + else + return(1008307711); // ~60.1 +} + +static int dosnapsave=0; +void FCEUI_SaveSnapshot(void) +{ + dosnapsave=1; +} + +/* I like the sounds of breaking necks. */ +static void ReallySnap(void) +{ + int x=SaveSnapshot(); + if(!x) + FCEU_DispMessage("Error saving screen snapshot."); + else + FCEU_DispMessage("Screen snapshot %d saved.",x-1); +} + +void DriverInterface(int w, void *d) +{ + switch(w) + { + case DES_NTSCCOL:ntsccol=*(int *)d;FCEU_ResetPalette();break; + case DES_RESET:if(netplay!=2) CommandQueue=30;break; + case DES_POWER:if(netplay!=2) CommandQueue=31;break; + case DES_GETNTSCTINT:*(int*)d=ntsctint;break; + case DES_GETNTSCHUE:*(int*)d=ntschue;break; + case DES_SETNTSCTINT:ntsctint=*(int*)d;if(ntsccol)FCEU_ResetPalette();break; + case DES_SETNTSCHUE:ntschue=*(int*)d;if(ntsccol)FCEU_ResetPalette();break; + + case DES_FDSINSERT:if(netplay!=2) CommandQueue=2;break; + case DES_FDSEJECT:if(netplay!=2) CommandQueue=3;break; + case DES_FDSSELECT:if(netplay!=2) CommandQueue=1;break; + + case DES_NSFINC:NSFControl(1);break; + case DES_NSFDEC:NSFControl(2);break; + case DES_NSFRES:NSFControl(0);break; + + case DES_VSUNIDIPSET:CommandQueue=10+(int)d;break; + case DES_VSUNITOGGLEDIPVIEW:CommandQueue=10;break; + case DES_VSUNICOIN:CommandQueue=19;break; + case DES_NTSCSELHUE:if(ntsccol && FCEUGameInfo.type!=GIT_VSUNI && !PAL && FCEUGameInfo.type!=GIT_NSF){controlselect=1;controllength=360;}break; + case DES_NTSCSELTINT:if(ntsccol && FCEUGameInfo.type!=GIT_VSUNI && !PAL && FCEUGameInfo.type!=GIT_NSF){controlselect=2;controllength=360;}break; + + case DES_NTSCDEC: + if(ntsccol && FCEUGameInfo.type!=GIT_VSUNI && !PAL && FCEUGameInfo.type!=GIT_NSF) + { + char which; + if(controlselect) + { + if(controllength) + { + which=controlselect==1?ntschue:ntsctint; + which--; + if(which<0) which=0; + if(controlselect==1) + ntschue=which; + else ntsctint=which; + CalculatePalette(); + } + controllength=360; + } + } + break; + case DES_NTSCINC: + if(ntsccol && FCEUGameInfo.type!=GIT_VSUNI && !PAL && FCEUGameInfo.type!=GIT_NSF) + if(controlselect) + { + if(controllength) + { + switch(controlselect) + { + case 1:ntschue++; + if(ntschue>128) ntschue=128; + CalculatePalette(); + break; + case 2:ntsctint++; + if(ntsctint>128) ntsctint=128; + CalculatePalette(); + break; + } + } + controllength=360; + } + break; + } +} + +static uint8 lastd=0; +void SetNESDeemph(uint8 d, int force) +{ + static uint16 rtmul[7]={32768*1.239,32768*.794,32768*1.019,32768*.905,32768*1.023,32768*.741,32768*.75}; + static uint16 gtmul[7]={32768*.915,32768*1.086,32768*.98,32768*1.026,32768*.908,32768*.987,32768*.75}; + static uint16 btmul[7]={32768*.743,32768*.882,32768*.653,32768*1.277,32768*.979,32768*.101,32768*.75}; + uint32 r,g,b; + int x; + + /* If it's not forced(only forced when the palette changes), + don't waste cpu time if the same deemphasis bits are set as the last call. + */ + if(!force) + { + if(d==lastd) + return; + } + else /* Only set this when palette has changed. */ + { + r=rtmul[6]; + g=rtmul[6]; + b=rtmul[6]; + + for(x=0;x<0x40;x++) + { + uint32 m,n,o; + m=palo[x].r; + n=palo[x].g; + o=palo[x].b; + m=(m*r)>>15; + n=(n*g)>>15; + o=(o*b)>>15; + if(m>0xff) m=0xff; + if(n>0xff) n=0xff; + if(o>0xff) o=0xff; + FCEUD_SetPalette(x|0x40,m,n,o); + } + } + if(!d) return; /* No deemphasis, so return. */ + + r=rtmul[d-1]; + g=gtmul[d-1]; + b=btmul[d-1]; + + for(x=0;x<0x40;x++) + { + uint32 m,n,o; + + m=palo[x].r; + n=palo[x].g; + o=palo[x].b; + m=(m*r)>>15; + n=(n*g)>>15; + o=(o*b)>>15; + if(m>0xff) m=0xff; + if(n>0xff) n=0xff; + if(o>0xff) o=0xff; + + FCEUD_SetPalette(x|0xC0,m,n,o); + } + + lastd=d; +} + +#define HUEVAL ((double)((double)ntschue/(double)2)+(double)300) +#define TINTVAL ((double)((double)ntsctint/(double)128)) + +static void CalculatePalette(void) +{ + int x,z; + int r,g,b; + double s,y,theta; + static uint8 cols[16]={0,24,21,18,15,12,9,6,3,0,33,30,27,0,0,0}; + static uint8 br1[4]={6,9,12,12}; + static double br2[4]={.29,.45,.73,.9}; + static double br3[4]={0,.24,.47,.77}; + + for(x=0;x<=3;x++) + for(z=0;z<16;z++) + { + s=(double)TINTVAL; + y=(double)br2[x]; + if(z==0) {s=0;y=((double)br1[x])/12;} + + if(z>=13) + { + s=y=0; + if(z==13) + y=(double)br3[x]; + } + + theta=(double)M_PI*(double)(((double)cols[z]*10+HUEVAL)/(double)180); + r=(int)(((double)y+(double)s*(double)sin(theta))*(double)256); + g=(int)(((double)y-(double)((double)27/(double)53)*s*(double)sin(theta)+(double)((double)10/(double)53)*s*cos(theta))*(double)256); + b=(int)(((double)y-(double)s*(double)cos(theta))*(double)256); + + // TODO: Fix RGB to compensate for phosphor changes(add to red??). + + if(r>255) r=255; + if(g>255) g=255; + if(b>255) b=255; + if(r<0) r=0; + if(g<0) g=0; + if(b<0) b=0; + + paletten[(x<<4)+z].r=r; + paletten[(x<<4)+z].g=g; + paletten[(x<<4)+z].b=b; + } + WritePalette(); +} + +#include "drawing.h" +#ifdef FRAMESKIP +void FCEU_PutImageDummy(void) +{ + if(FCEUGameInfo.type!=GIT_NSF) + { + if(controllength) controllength--; + } + if(StateShow) StateShow--; /* DrawState() */ + if(howlong) howlong--; /* DrawMessage() */ + #ifdef FPS + { + extern uint64 frcount; + frcount++; + } + #endif + +} +#endif + +void FCEU_PutImage(void) +{ + if(FCEUGameInfo.type==GIT_NSF) + { + DrawNSF(XBuf); + /* Save snapshot after NSF screen is drawn. Why would we want to + do it before? + */ + if(dosnapsave) + { + ReallySnap(); + dosnapsave=0; + } + } + else + { + /* Save snapshot before overlay stuff is written. */ + if(dosnapsave) + { + ReallySnap(); + dosnapsave=0; + } + if(FCEUGameInfo.type==GIT_VSUNI && DIPS&2) + DrawDips(); + if(StateShow) DrawState(); + if(controllength) {controllength--;DrawBars();} + } + DrawMessage(); + #ifdef FPS + { + extern uint64 frcount; + frcount++; + } + #endif + DrawInput(XBuf+8); +} + +static int ipalette=0; + +void LoadGamePalette(void) +{ + uint8 ptmp[192]; + FILE *fp; + ipalette=0; + if((fp=fopen(FCEU_MakeFName(FCEUMKF_PALETTE,0,0),"rb"))) + { + int x; + fread(ptmp,1,192,fp); + fclose(fp); + for(x=0;x<64;x++) + { + palettei[x].r=ptmp[x+x+x]; + palettei[x].g=ptmp[x+x+x+1]; + palettei[x].b=ptmp[x+x+x+2]; + } + ipalette=1; + } +} + +void FCEU_ResetPalette(void) +{ + ChoosePalette(); + WritePalette(); +} + +static void ChoosePalette(void) +{ + if(FCEUGameInfo.type==GIT_NSF) + palo=NSFPalette; + else if(ipalette) + palo=palettei; + else if(ntsccol && !PAL && FCEUGameInfo.type!=GIT_VSUNI) + { + palo=paletten; + CalculatePalette(); + } + else + palo=palpoint[pale]; +} + +void WritePalette(void) +{ + int x; + + for(x=0;x<6;x++) + FCEUD_SetPalette(x+128,unvpalette[x].r,unvpalette[x].g,unvpalette[x].b); + if(FCEUGameInfo.type==GIT_NSF) + { + for(x=0;x<39;x++) + FCEUD_SetPalette(x,palo[x].r,palo[x].g,palo[x].b); + } + else + { + for(x=0;x<64;x++) + FCEUD_SetPalette(x,palo[x].r,palo[x].g,palo[x].b); + SetNESDeemph(lastd,1); + } +} + +void FlushCommandQueue(void) +{ + if(!netplay && CommandQueue) {DoCommand(CommandQueue);CommandQueue=0;} +} + +void DoCommand(uint8 c) +{ + switch(c) + { + case 1:FDSControl(FDS_SELECT);break; + case 2:FDSControl(FDS_IDISK);break; + case 3:FDSControl(FDS_EJECT);break; + + case 10:DIPS^=2;break; + case 11:vsdip^=1;DIPS|=2;break; + case 12:vsdip^=2;DIPS|=2;break; + case 13:vsdip^=4;DIPS|=2;break; + case 14:vsdip^=8;DIPS|=2;break; + case 15:vsdip^=0x10;DIPS|=2;break; + case 16:vsdip^=0x20;DIPS|=2;break; + case 17:vsdip^=0x40;DIPS|=2;break; + case 18:vsdip^=0x80;DIPS|=2;break; + case 19:coinon=6;break; + case 30:ResetNES();break; + case 31:PowerNES();break; + case 40:CheckStates();StateShow=0;SaveState();break; + case 41:CheckStates();StateShow=0;LoadState();break; + case 42: case 43: case 44: case 45: case 46: case 47: case 48: case 49: + case 50: case 51:StateShow=180;CurrentState=c-42;CheckStates();break; + } +} diff --git a/svga.h b/svga.h new file mode 100644 index 0000000..cebd555 --- /dev/null +++ b/svga.h @@ -0,0 +1,83 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 1998 Bero + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "driver.h" +typedef struct __pal { + uint8 r; + uint8 g; + uint8 b; +} pal; + +typedef struct { + int PAL; + #ifdef NETWORK + int NetworkPlay; + #endif + int SoundVolume; + int GameGenie; + int SUnderBase; + + /* Current first and last rendered scanlines. */ + int FirstSLine; + int LastSLine; + + /* Driver code(user)-specified first and last rendered scanlines. + Usr*SLine[0] is for NTSC, Usr*SLine[1] is for PAL. + */ + int UsrFirstSLine[2]; + int UsrLastSLine[2]; + int SnapName; + unsigned int SndRate; +} FCEUS; + +extern FCEUS FSettings; + +void FCEU_PrintError(char *format, ...); +void FCEU_DispMessage(char *format, ...); + +void SetNESDeemph(uint8 d, int force); +void DrawTextTrans(uint8 *dest, uint32 width, uint8 *textmsg, uint8 fgcolor); +void FCEU_PutImage(void); +#ifdef FRAMESKIP +void FCEU_PutImageDummy(void); +#endif + +extern uint8 Exit; +extern uint8 pale; +extern uint8 vsdip; +void SetNESPalette(void); + +#define JOY_A 1 +#define JOY_B 2 +#define JOY_SELECT 4 +#define JOY_START 8 +#define JOY_UP 0x10 +#define JOY_DOWN 0x20 +#define JOY_LEFT 0x40 +#define JOY_RIGHT 0x80 + +extern pal *palo; + +void DoCommand(uint8 c); +extern uint8 CommandQueue; +void FCEU_ResetPalette(void); +void LoadGamePalette(void); +void FlushCommandQueue(void); diff --git a/types.h b/types.h new file mode 100644 index 0000000..05512f8 --- /dev/null +++ b/types.h @@ -0,0 +1,66 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2001 Aaron Oneal + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __FCEU_TYPES +#define __FCEU_TYPES + +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned long uint32; + +#ifdef __GNUC__ + typedef unsigned long long uint64; + typedef long long int64; + #define INLINE inline + #define GINLINE inline +#elif MSVC + typedef __int64 int64; + typedef unsigned __int64 uint64; + #define INLINE __inline + #define GINLINE /* Can't declare a function INLINE + and global in MSVC. Bummer. + */ + #define PSS_STYLE 2 /* Does MSVC compile for anything + other than Windows/DOS targets? + */ +#endif +typedef signed char int8; +typedef signed short int16; +typedef signed long int32; +#define byte uint8 +#define word uint16 + +#ifdef __GNUC__ + #ifdef C80x86 + #define FASTAPASS(x) __attribute__((regparm(x))) + #define FP_FASTAPASS FASTAPASS + #else + #define FASTAPASS(x) + #define FP_FASTAPASS(x) + #endif +#else + #define FP_FASTAPASS(x) + #define FASTAPASS(x) __fastcall +#endif + +typedef void FP_FASTAPASS(2) (*writefunc)(uint32 A, uint8 V); +typedef uint8 FP_FASTAPASS(1) (*readfunc)(uint32 A); +#endif diff --git a/unif.c b/unif.c new file mode 100644 index 0000000..f54ad39 --- /dev/null +++ b/unif.c @@ -0,0 +1,469 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* TODO: Battery backup file saving, mirror force */ +/* **INCOMPLETE** */ +/* Override stuff: CHR RAM instead of CHR ROM, + mirroring. +*/ + +#include +#include +#include + + +#include "types.h" +#include "fce.h" +#include "unif.h" +#include "version.h" +#include "svga.h" +#include "general.h" +#include "state.h" +#include "endian.h" +#include "file.h" +#include "cart.h" +#include "memory.h" +#include "input.h" + +typedef struct { + char ID[4]; + uint32 info; +} UNIF_HEADER; + +typedef struct { + char *name; + void (*init)(void); + int flags; +} BMAPPING; + +typedef struct { + char *name; + int (*init)(int fp); +} BFMAPPING; + +void (*BoardClose)(void); +void (*BoardPower)(void); +void (*BoardReset)(void); + +static int vramo; +static int mirrortodo; +int UNIFbattery; +static char *boardname; +static char *sboardname; +char *UNIFchrrama; + +static UNIF_HEADER unhead; +static UNIF_HEADER uchead; + + +static uint8 *malloced[32]; + +static int FixRomSize(uint32 size, uint32 minimum) +{ + int x=1; + + if(size15) + return(0); + printf(" PRG ROM %d size: %d",z,(int) uchead.info); + if(malloced[z]) + free(malloced[z]); + t=FixRomSize(uchead.info,2048); + if(!(malloced[z]=FCEU_malloc(t))) + return(0); + memset(malloced[z]+uchead.info,0xFF,t-uchead.info); + if(FCEU_fread(malloced[z],1,uchead.info,fp)!=uchead.info) + { + puts("Read Error!"); + return(0); + } + else + puts(""); + + SetupCartPRGMapping(z,malloced[z],t,0); + return(1); +} + +static int SetBoardName(int fp) +{ + if(!(boardname=FCEU_malloc(uchead.info+1))) + return(0); + FCEU_fread(boardname,1,uchead.info,fp); + boardname[uchead.info]=0; + printf(" Board name: %s\n",boardname); + sboardname=boardname; + if(!memcmp(boardname,"NES-",4) || !memcmp(boardname,"UNL-",4) || !memcmp(boardname,"HVC-",4) || !memcmp(boardname,"BTL-",4) || !memcmp(boardname,"BMC-",4)) + sboardname+=4; + return(1); +} + +static int LoadCHR(int fp) +{ + int z,t; + z=uchead.ID[3]-'0'; + if(z<0 || z>15) + return(0); + printf(" CHR ROM %d size: %d",z,(int) uchead.info); + if(malloced[16+z]) + free(malloced[16+z]); + t=FixRomSize(uchead.info,8192); + if(!(malloced[16+z]=FCEU_malloc(t))) + return(0); + memset(malloced[16+z]+uchead.info,0xFF,t-uchead.info); + if(FCEU_fread(malloced[16+z],1,uchead.info,fp)!=uchead.info) + { + puts("Read Error!"); + return(0); + } + else + puts(""); + + SetupCartCHRMapping(z,malloced[16+z],t,0); + return(1); +} + + +#define BMCFLAG_FORCE4 1 +#define BMCFLAG_CHRROK 2 // Ok for generic UNIF code to make available + // 8KB of CHR RAM if no CHR ROM is present. +#define BMC 48 + +BMAPPING bmap[BMC] = { + +/* Sachen Carts */ + { "TC-U01-1.5M", TCU01_Init,0}, + { "Sachen-8259B", S8259B_Init,BMCFLAG_CHRROK}, + { "Sachen-8259A", S8259A_Init,BMCFLAG_CHRROK}, + { "Sachen-74LS374N", S74LS374N_Init,0}, + { "SA-016-1M", SA0161M_Init,0}, + { "SA-72007", SA72007_Init,0}, + { "SA-72008", SA72008_Init,0}, + { "SA-0036", SA0036_Init,0}, + { "SA-0037", SA0037_Init,0}, + + { "H2288", H2288_Init,0}, +// /* AVE carts. */ +// { "MB-91", MB91_Init,0}, // DeathBots +// { "NINA-06", NINA06_Init,0}, // F-15 City War +// { "NINA-03", NINA03_Init,0}, // Tiles of Fate +// { "NINA-001", NINA001_Init,0}, // Impossible Mission 2 + + { "HKROM", HKROM_Init,0}, + + { "EWROM", EWROM_Init,0}, + { "EKROM", EKROM_Init,0}, + { "ELROM", ELROM_Init,0}, + { "ETROM", ETROM_Init,0}, + + { "SAROM", SAROM_Init,0}, + { "SBROM", SBROM_Init,0}, + { "SCROM", SCROM_Init,0}, + { "SEROM", SEROM_Init,0}, + { "SGROM", SGROM_Init,0}, + { "SKROM", SKROM_Init,0}, + { "SLROM", SLROM_Init,0}, + { "SL1ROM", SL1ROM_Init,0}, + { "SNROM", SNROM_Init,0}, + { "SOROM", SOROM_Init,0}, + + { "TGROM", TGROM_Init,0}, + { "TR1ROM", TFROM_Init,BMCFLAG_FORCE4}, + { "TFROM", TFROM_Init,0}, + { "TLROM", TLROM_Init,0}, + { "TKROM", TKROM_Init,0}, + { "TSROM", TSROM_Init,0}, + + { "TLSROM", TLSROM_Init,0}, + { "TKSROM", TKSROM_Init,0}, + { "TQROM", TQROM_Init,0}, + { "TVROM", TLROM_Init,BMCFLAG_FORCE4}, + + { "CPROM", CPROM_Init,0}, + { "CNROM", CNROM_Init,0}, + { "NROM", NROM256_Init,0 }, + { "RROM", NROM128_Init,0 }, + { "RROM-128", NROM128_Init,0 }, + { "NROM-128", NROM128_Init,0 }, + { "NROM-256", NROM256_Init,0 }, + { "MHROM", MHROM_Init,0}, + { "UNROM", UNROM_Init,0}, + { "MARIO1-MALEE2", MALEE_Init,0}, + { "Supervision16in1", Supervision16_Init,0}, + { "NovelDiamond9999999in1", Novel_Init,0}, + { "Super24in1SC03", Super24_Init,0} +}; + +#define BMF 7 +BFMAPPING bfunc[BMF] = { + { "CTRL", CTRL }, + { "TVCI", TVCI }, + { "BATR", EnableBattery }, + { "MIRR", DoMirroring }, + { "PRG", LoadPRG }, + { "CHR", LoadCHR }, + { "MAPR", SetBoardName } +}; + +int LoadUNIFChunks(int fp) +{ + int x; + int t; + for(;;) + { + t=FCEU_fread(&uchead,1,4,fp); + if(t<4) + { + if(t>0) + return 0; + return 1; + } + if(!(FCEU_read32(&uchead.info,fp))) + return 0; + t=0; + for(x=0;x +#include +#include + +#include "types.h" +#include "video.h" +#include "fce.h" +#include "svga.h" +#include "version.h" +#include "general.h" +#include "memory.h" + +uint8 *XBuf=NULL; + +int InitVirtualVideo(void) +{ + uint32 m; + + if(!XBuf) /* Some driver code may allocate XBuf externally. */ + if(!(XBuf = (uint8*) (FCEU_malloc((256+16) * 240 + 8)))) + return 0; + + if(sizeof(uint8*)==4) + { + m=(uint32) XBuf; + m+=8;m&=0xFFFFFFF8; + (uint32)XBuf=m; + } + + memset(XBuf,128,272*240); + return 1; +} + +#ifndef ZLIB +static uint8 pcxheader[128] = +{ + 10,5,1,8,1,0,1,0,0,1,240,0,2,1,234,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0, +}; + +int SaveSnapshot(void) +{ + char *fn=0; + uint8 *tmp; + int x,u,y; + FILE *pp=NULL; + + for(u=0;u<999;u++) + { + pp=fopen((fn=FCEU_MakeFName(FCEUMKF_SNAP,u,"pcx")),"rb"); + if(pp==NULL) break; + fclose(pp); + } + + if(!(pp=fopen(fn,"wb"))) + return 0; + + { + int totallines=FSettings.LastSLine-FSettings.FirstSLine+1; + + tmp=XBuf+8+FSettings.FirstSLine*272; + + pcxheader[10]=totallines; + fwrite(pcxheader,1,128,pp); + for(y=0;y=0xc0) fputc(0xC1,pp); + fputc(*tmp,pp); + tmp++; + } + tmp+=16; + } + } + + fputc(0xC,pp); + for(x=0;x<256;x++) + { + uint8 r,g,b; + + FCEUD_GetPalette(x,&r,&g,&b); + fputc(r,pp); + fputc(g,pp); + fputc(b,pp); + } + fclose(pp); + + return u+1; +} + +#else + +#include +#include "crc32.h" + +static int WritePNGChunk(FILE *fp, uint32 size, char *type, uint8 *data) +{ + uint32 crc; + + uint8 tempo[4]; + + tempo[0]=size>>24; + tempo[1]=size>>16; + tempo[2]=size>>8; + tempo[3]=size; + + if(fwrite(tempo,4,1,fp)!=1) + return 0; + if(fwrite(type,4,1,fp)!=1) + return 0; + + if(size) + if(fwrite(data,1,size,fp)!=size) + return 0; + + crc=CalcCRC32(0,type,4); + if(size) + crc=CalcCRC32(crc,data,size); + + tempo[0]=crc>>24; + tempo[1]=crc>>16; + tempo[2]=crc>>8; + tempo[3]=crc; + + if(fwrite(tempo,4,1,fp)!=1) + return 0; + return 1; +} + +int SaveSnapshot(void) +{ + char *fn=0; + int totallines=FSettings.LastSLine-FSettings.FirstSLine+1; + int x,u,y; + FILE *pp=NULL; + uint8 *compmem=NULL; + uint32 compmemsize=totallines*263+12; + + if(!(compmem=FCEU_malloc(compmemsize))) + return 0; + + for(u=0;u<999;u++) + { + pp=fopen((fn=FCEU_MakeFName(FCEUMKF_SNAP,u,"png")),"rb"); + if(pp==NULL) break; + fclose(pp); + } + + if(!(pp=fopen(fn,"wb"))) + return 0; + { + static uint8 header[8]={137,80,78,71,13,10,26,10}; + if(fwrite(header,8,1,pp)!=1) + goto PNGerr; + } + + { + uint8 chunko[13]; + + chunko[0]=chunko[1]=chunko[3]=0; + chunko[2]=0x1; // Width of 256 + + chunko[4]=chunko[5]=chunko[6]=0; + chunko[7]=totallines; // Height + + chunko[8]=8; // bit depth + chunko[9]=3; // Color type; indexed 8-bit + chunko[10]=0; // compression: deflate + chunko[11]=0; // Basic adapative filter set(though none are used). + chunko[12]=0; // No interlace. + + if(!WritePNGChunk(pp,13,"IHDR",chunko)) + goto PNGerr; + } + + { + char pdata[256*3]; + for(x=0;x<256;x++) + FCEUD_GetPalette(x,pdata+x*3,pdata+x*3+1,pdata+x*3+2); + if(!WritePNGChunk(pp,256*3,"PLTE",pdata)) + goto PNGerr; + } + + { + uint8 *tmp=XBuf+FSettings.FirstSLine*272+8; + uint8 *dest,*mal,*mork; + + /* If memory couldn't be allocated, just use XBuf(screen contents + will be corrupted for one frame, though. + */ + if(!(mal=mork=dest=malloc((totallines<<8)+totallines))) + mork=dest=XBuf; + + for(y=0;y + +#include "types.h" +#include "x6502.h" +#include "fce.h" +#include "sound.h" + +X6502 X; +uint32 timestamp; +void FP_FASTAPASS(1) (*MapIRQHook)(int a); + +#define _PC X.PC +#define _A X.A +#define _X X.X +#define _Y X.Y +#define _S X.S +#define _P X.P +#define _PI X.mooPI +#define _PZ X.PZ +#define _DB X.DB +#define _count X.count +#define _tcount X.tcount +#define _IRQlow X.IRQlow +#define _jammed X.jammed + + +static INLINE uint8 RdMem(unsigned int A) +{ + return((_DB=ARead[A](A))); +} + +static INLINE void WrMem(unsigned int A, uint8 V) +{ + BWrite[A](A,V); +} + +static INLINE uint8 RdRAM(unsigned int A) +{ + return((_DB=RAM[A])); +} + +static INLINE void WrRAM(unsigned int A, uint8 V) +{ + RAM[A]=V; +} + +static INLINE void ADDCYC(int x) +{ + _tcount+=x; + _count-=x*48; + timestamp+=x; +} + +void FASTAPASS(1) X6502_AddCycles(int x) +{ + ADDCYC(x); +} + +static INLINE void PUSH(uint8 V) +{ + WrRAM(0x100+_S,V); + _S--; +} + +static INLINE uint8 POP(void) +{ + _S++; + return(RdRAM(0x100+_S)); +} + +static uint8 ZNTable[256] = { + Z_FLAG,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG, + N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG, + N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG, + N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG, + N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG, + N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG, + N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG, + N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG +}; +/* Some of these operations will only make sense if you know what the flag + constants are. */ +#define X_ZN(zort) _P&=~(Z_FLAG|N_FLAG);_P|=ZNTable[zort] +#define X_ZNT(zort) _P|=ZNTable[zort] + +/* Care must be taken if you want to turn this into a macro. Use { and }. */ +#define JR(); \ +{ \ + uint32 tmp; \ + int8 disp; \ + disp=RdMem(_PC++); \ + ADDCYC(1); \ + tmp=_PC; \ + _PC+=disp; \ + if((tmp^_PC)&0x100) \ + ADDCYC(1); \ +} + +#define LDA _A=x;X_ZN(_A) +#define LDX _X=x;X_ZN(_X) +#define LDY _Y=x;X_ZN(_Y) + +/* All of the freaky arithmetic operations. */ +#define AND _A&=x;X_ZN(_A) +#define BIT _P&=~(Z_FLAG|V_FLAG|N_FLAG);_P|=ZNTable[x&_A]&Z_FLAG;_P|=x&(V_FLAG|N_FLAG) +#define EOR _A^=x;X_ZN(_A) +#define ORA _A|=x;X_ZN(_A) + +#define ADC { \ + uint32 l=_A+x+(_P&1); \ + _P&=~(Z_FLAG|C_FLAG|N_FLAG|V_FLAG); \ + _P|=((((_A^x)&0x80)^0x80) & ((_A^l)&0x80))>>1; \ + _P|=(l>>8)&C_FLAG; \ + _A=l; \ + X_ZNT(_A); \ + } +#define SBC { \ + uint32 l=_A-x-((_P&1)^1); \ + _P&=~(Z_FLAG|C_FLAG|N_FLAG|V_FLAG); \ + _P|=((_A^l)&(_A^x)&0x80)>>1; \ + _P|=((l>>8)&C_FLAG)^C_FLAG; \ + _A=l; \ + X_ZNT(_A); \ + } + +#define CMPL(a1,a2) { \ + uint32 t=a1-a2; \ + X_ZN(t&0xFF); \ + _P&=~C_FLAG; \ + _P|=((t>>8)&C_FLAG)^C_FLAG; \ + } + +/* Special undocumented operation. Very similar to CMP. */ +#define AXS { \ + uint32 t=(_A&_X)-x; \ + X_ZN(t&0xFF); \ + _P&=~C_FLAG; \ + _P|=((t>>8)&C_FLAG)^C_FLAG; \ + _X=t; \ + } + +#define CMP CMPL(_A,x) +#define CPX CMPL(_X,x) +#define CPY CMPL(_Y,x) + +/* The following operations modify the byte being worked on. */ +#define DEC x--;X_ZN(x) +#define INC x++;X_ZN(x) + +#define ASL _P&=~C_FLAG;_P|=x>>7;x<<=1;X_ZN(x) +#define LSR _P&=~(C_FLAG|N_FLAG|Z_FLAG);_P|=x&1;x>>=1;X_ZNT(x) + +/* For undocumented instructions, maybe for other things later... */ +#define LSRA _P&=~(C_FLAG|N_FLAG|Z_FLAG);_P|=_A&1;_A>>=1;X_ZNT(_A) + +#define ROL { \ + uint8 l=x>>7; \ + x<<=1; \ + x|=_P&C_FLAG; \ + _P&=~(Z_FLAG|N_FLAG|C_FLAG); \ + _P|=l; \ + X_ZNT(x); \ + } +#define ROR { \ + uint8 l=x&1; \ + x>>=1; \ + x|=(_P&C_FLAG)<<7; \ + _P&=~(Z_FLAG|N_FLAG|C_FLAG); \ + _P|=l; \ + X_ZNT(x); \ + } + +/* Icky icky thing for some undocumented instructions. Can easily be + broken if names of local variables are changed. +*/ + +/* Absolute */ +#define GetAB(target) \ +{ \ + target=RdMem(_PC++); \ + target|=RdMem(_PC++)<<8; \ +} + +/* Absolute Indexed(for reads) */ +#define GetABIRD(target, i) \ +{ \ + unsigned int tmp; \ + GetAB(tmp); \ + target=tmp; \ + target+=i; \ + if((target^tmp)&0x100) \ + { \ + target&=0xFFFF; \ + RdMem(target^0x100); \ + ADDCYC(1); \ + } \ +} + +/* Absolute Indexed(for writes and rmws) */ +#define GetABIWR(target, i) \ +{ \ + unsigned int rt; \ + GetAB(rt); \ + target=rt; \ + target+=i; \ + target&=0xFFFF; \ + RdMem((target&0x00FF)|(rt&0xFF00)); \ +} + +/* Zero Page */ +#define GetZP(target) \ +{ \ + target=RdMem(_PC++); \ +} + +/* Zero Page Indexed */ +#define GetZPI(target,i) \ +{ \ + target=i+RdMem(_PC++); \ +} + +/* Indexed Indirect */ +#define GetIX(target) \ +{ \ + uint8 tmp; \ + tmp=RdMem(_PC++); \ + tmp+=_X; \ + target=RdRAM(tmp++); \ + target|=RdRAM(tmp)<<8; \ +} + +/* Indirect Indexed(for reads) */ +#define GetIYRD(target) \ +{ \ + unsigned int rt; \ + uint8 tmp; \ + tmp=RdMem(_PC++); \ + rt=RdRAM(tmp++); \ + rt|=RdRAM(tmp)<<8; \ + target=rt; \ + target+=_Y; \ + if((target^rt)&0x100) \ + { \ + target&=0xFFFF; \ + RdMem(target^0x100); \ + ADDCYC(1); \ + } \ +} + +/* Indirect Indexed(for writes and rmws) */ +#define GetIYWR(target) \ +{ \ + unsigned int rt; \ + uint8 tmp; \ + tmp=RdMem(_PC++); \ + rt=RdRAM(tmp++); \ + rt|=RdRAM(tmp)<<8; \ + target=rt; \ + target+=_Y; \ + RdMem((target&0x00FF)|(rt&0xFF00)); \ +} + +/* Now come the macros to wrap up all of the above stuff addressing mode functions + and operation macros. Note that operation macros will always operate(redundant + redundant) on the variable "x". +*/ + +#define RMW_A(op) {uint8 x=_A; op; _A=x; break; } /* Meh... */ +#define RMW_AB(op) {unsigned int A; uint8 x; GetAB(A); x=RdMem(A); WrMem(A,x); op; WrMem(A,x); break; } +#define RMW_ABI(reg,op) {unsigned int A; uint8 x; GetABIWR(A,reg); x=RdMem(A); WrMem(A,x); op; WrMem(A,x); break; } +#define RMW_ABX(op) RMW_ABI(_X,op) +#define RMW_ABY(op) RMW_ABI(_Y,op) +#define RMW_IX(op) {unsigned int A; uint8 x; GetIX(A); x=RdMem(A); WrMem(A,x); op; WrMem(A,x); break; } +#define RMW_IY(op) {unsigned int A; uint8 x; GetIYWR(A); x=RdMem(A); WrMem(A,x); op; WrMem(A,x); break; } +#define RMW_ZP(op) {uint8 A; uint8 x; GetZP(A); x=RdRAM(A); op; WrRAM(A,x); break; } +#define RMW_ZPX(op) {uint8 A; uint8 x; GetZPI(A,_X); x=RdRAM(A); op; WrRAM(A,x); break;} + +#define LD_IM(op) {uint8 x; x=RdMem(_PC++); op; break;} +#define LD_ZP(op) {uint8 A; uint8 x; GetZP(A); x=RdRAM(A); op; break;} +#define LD_ZPX(op) {uint8 A; uint8 x; GetZPI(A,_X); x=RdRAM(A); op; break;} +#define LD_ZPY(op) {uint8 A; uint8 x; GetZPI(A,_Y); x=RdRAM(A); op; break;} +#define LD_AB(op) {unsigned int A; uint8 x; GetAB(A); x=RdMem(A); op; break; } +#define LD_ABI(reg,op) {unsigned int A; uint8 x; GetABIRD(A,reg); x=RdMem(A); op; break;} +#define LD_ABX(op) LD_ABI(_X,op) +#define LD_ABY(op) LD_ABI(_Y,op) +#define LD_IX(op) {unsigned int A; uint8 x; GetIX(A); x=RdMem(A); op; break;} +#define LD_IY(op) {unsigned int A; uint8 x; GetIYRD(A); x=RdMem(A); op; break;} + +#define ST_ZP(r) {uint8 A; GetZP(A); WrRAM(A,r); break;} +#define ST_ZPX(r) {uint8 A; GetZPI(A,_X); WrRAM(A,r); break;} +#define ST_ZPY(r) {uint8 A; GetZPI(A,_Y); WrRAM(A,r); break;} +#define ST_AB(r) {unsigned int A; GetAB(A); WrMem(A,r); break;} +#define ST_ABI(reg,r) {unsigned int A; GetABIWR(A,reg); WrMem(A,r); break; } +#define ST_ABX(r) ST_ABI(_X,r) +#define ST_ABY(r) ST_ABI(_Y,r) +#define ST_IX(r) {unsigned int A; GetIX(A); WrMem(A,r); break; } +#define ST_IY(r) {unsigned int A; GetIYWR(A); WrMem(A,r); break; } + +static uint8 CycTable[256] = +{ +/*0x00*/ 7,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6, +/*0x10*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7, +/*0x20*/ 6,6,2,8,3,3,5,5,4,2,2,2,4,4,6,6, +/*0x30*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7, +/*0x40*/ 6,6,2,8,3,3,5,5,3,2,2,2,3,4,6,6, +/*0x50*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7, +/*0x60*/ 6,6,2,8,3,3,5,5,4,2,2,2,5,4,6,6, +/*0x70*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7, +/*0x80*/ 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4, +/*0x90*/ 2,6,2,6,4,4,4,4,2,5,2,5,5,5,5,5, +/*0xA0*/ 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4, +/*0xB0*/ 2,5,2,5,4,4,4,4,2,4,2,4,4,4,4,4, +/*0xC0*/ 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6, +/*0xD0*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7, +/*0xE0*/ 2,6,3,8,3,3,5,5,2,2,2,2,4,4,6,6, +/*0xF0*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7, +}; + +void FASTAPASS(1) X6502_IRQBegin(int w) +{ + _IRQlow|=w; +} + +void FASTAPASS(1) X6502_IRQEnd(int w) +{ + _IRQlow&=~w; +} + +void TriggerIRQ(void) /* This function should probably be phased out. */ +{ + _IRQlow|=FCEU_IQTEMP; +} + +void TriggerNMINSF(void) +{ + ADDCYC(7); + PUSH(_PC>>8); + PUSH(_PC); + PUSH((_P&~B_FLAG)|(U_FLAG)); + _PC=0x3800; +} + +void TriggerNMI(void) +{ + _IRQlow|=FCEU_IQNMI; +} + +static void TriggerNMIReal(void) +{ + if(!_jammed) + { + ADDCYC(7); + PUSH(_PC>>8); + PUSH(_PC); + PUSH((_P&~B_FLAG)|(U_FLAG)); + _PC=RdMem(0xFFFA); + _PC|=RdMem(0xFFFB)<<8; + } +} + +void TriggerIRQReal(void) +{ + if(!(_PI&I_FLAG) && !_jammed) + { + ADDCYC(7); + PUSH(_PC>>8); + PUSH(_PC); + PUSH((_P&~B_FLAG)|(U_FLAG)); + _P|=I_FLAG; + _PC=RdMem(0xFFFE); + _PC|=RdMem(0xFFFF)<<8; + } +} + +void X6502_Reset(void) +{ + _PC=RdMem(0xFFFC); + _PC|=RdMem(0xFFFD)<<8; + if(FCEUGameInfo.type==GIT_NSF) _PC=0x3830; + _jammed=0; + _PI=_P=I_FLAG; +} + +void X6502_Power(void) +{ + memset((void *)&X,0,sizeof(X)); + timestamp=0; + X6502_Reset(); +} + +void X6502_Run(int32 cycles) +{ + if(PAL) + cycles*=15; // 15*4=60 + else + cycles*=16; // 16*4=64 + + _count+=cycles; + + while(_count>0) + { + int32 temp; + uint8 b1; + + if(_IRQlow) + { + if(_IRQlow&FCEU_IQNMI) + TriggerNMIReal(); + else + TriggerIRQReal(); + + _IRQlow&=~(FCEU_IQTEMP|FCEU_IQNMI); + if(_count<=0) {_PI=_P;return;} /* Should increase accuracy without a */ + /* major speed hit. */ + } + _PI=_P; + b1=RdMem(_PC); + ADDCYC(CycTable[b1]); + temp=_tcount; + _tcount=0; + if(MapIRQHook) MapIRQHook(temp); + + temp*=48; + + fhcnt-=temp; + if(fhcnt<=0) + { + FrameSoundUpdate(); + fhcnt+=fhinc; + } + + + if(PCMIRQCount>0) + { + PCMIRQCount-=temp; + if(PCMIRQCount<=0) + { + vdis=1; + if((PSG[0x10]&0x80) && !(PSG[0x10]&0x40)) + { + extern uint8 SIRQStat; + SIRQStat|=0x80; + X6502_IRQBegin(FCEU_IQDPCM); + } + } + } + //printf("$%04x:$%02x\n",_PC,b1); + //_PC++; + //printf("$%02x\n",b1); + _PC++; + switch(b1) + { + #include "ops.h" + } + } +} diff --git a/x6502.h b/x6502.h new file mode 100644 index 0000000..414533b --- /dev/null +++ b/x6502.h @@ -0,0 +1,66 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +typedef struct { + int32 count; /* Cycle counter */ + int32 tcount; /* Temporary cycle counter */ + uint16 PC; /* I'll change this to uint32 later... */ + /* I'll need to AND PC after increments to 0xFFFF */ + /* when I do, though. Perhaps an IPC() macro? */ + uint8 A,X,Y,S,P,mooPI,PZ; + uint8 DB; /* Data bus "cache" for reads from certain areas */ + uint8 IRQlow; /* Simulated IRQ pin held low(or is it high?). */ + uint8 jammed; +} X6502; + +extern X6502 X; + +#define N_FLAG 0x80 +#define V_FLAG 0x40 +#define U_FLAG 0x20 +#define B_FLAG 0x10 +#define D_FLAG 0x08 +#define I_FLAG 0x04 +#define Z_FLAG 0x02 +#define C_FLAG 0x01 + +extern uint32 timestamp; +extern void FP_FASTAPASS(1) (*MapIRQHook)(int a); + +#define NTSC_CPU 1789772.7272727272727272 +#define PAL_CPU 1662607.125 + +#define FCEU_IQEXT 0x01 +#define FCEU_IQNMI 0x08 +#define FCEU_IQDPCM 0x10 +#define FCEU_IQFCOUNT 0x20 +#define FCEU_IQTEMP 0x80 + +void X6502_Reset(void); +void X6502_Power(void); +void X6502_Run(int32 cycles); + +void TriggerIRQ(void); +void TriggerNMI(void); +void TriggerNMINSF(void); + +void FASTAPASS(1) X6502_AddCycles(int x); +void FASTAPASS(1) X6502_IRQBegin(int w); +void FASTAPASS(1) X6502_IRQEnd(int w); diff --git a/zlib/ChangeLog b/zlib/ChangeLog new file mode 100644 index 0000000..58d58af --- /dev/null +++ b/zlib/ChangeLog @@ -0,0 +1,481 @@ + + ChangeLog file for zlib + +Changes in 1.1.4 (11 March 2002) +- ZFREE was repeated on same allocation on some error conditions. + This creates a security problem described in + http://www.zlib.org/advisory-2002-03-11.txt +- Returned incorrect error (Z_MEM_ERROR) on some invalid data +- Avoid accesses before window for invalid distances with inflate window + less than 32K. +- force windowBits > 8 to avoid a bug in the encoder for a window size + of 256 bytes. (A complete fix will be available in 1.1.5). + +Changes in 1.1.3 (9 July 1998) +- fix "an inflate input buffer bug that shows up on rare but persistent + occasions" (Mark) +- fix gzread and gztell for concatenated .gz files (Didier Le Botlan) +- fix gzseek(..., SEEK_SET) in write mode +- fix crc check after a gzeek (Frank Faubert) +- fix miniunzip when the last entry in a zip file is itself a zip file + (J Lillge) +- add contrib/asm586 and contrib/asm686 (Brian Raiter) + See http://www.muppetlabs.com/~breadbox/software/assembly.html +- add support for Delphi 3 in contrib/delphi (Bob Dellaca) +- add support for C++Builder 3 and Delphi 3 in contrib/delphi2 (Davide Moretti) +- do not exit prematurely in untgz if 0 at start of block (Magnus Holmgren) +- use macro EXTERN instead of extern to support DLL for BeOS (Sander Stoks) +- added a FAQ file + +- Support gzdopen on Mac with Metrowerks (Jason Linhart) +- Do not redefine Byte on Mac (Brad Pettit & Jason Linhart) +- define SEEK_END too if SEEK_SET is not defined (Albert Chin-A-Young) +- avoid some warnings with Borland C (Tom Tanner) +- fix a problem in contrib/minizip/zip.c for 16-bit MSDOS (Gilles Vollant) +- emulate utime() for WIN32 in contrib/untgz (Gilles Vollant) +- allow several arguments to configure (Tim Mooney, Frodo Looijaard) +- use libdir and includedir in Makefile.in (Tim Mooney) +- support shared libraries on OSF1 V4 (Tim Mooney) +- remove so_locations in "make clean" (Tim Mooney) +- fix maketree.c compilation error (Glenn, Mark) +- Python interface to zlib now in Python 1.5 (Jeremy Hylton) +- new Makefile.riscos (Rich Walker) +- initialize static descriptors in trees.c for embedded targets (Nick Smith) +- use "foo-gz" in example.c for RISCOS and VMS (Nick Smith) +- add the OS/2 files in Makefile.in too (Andrew Zabolotny) +- fix fdopen and halloc macros for Microsoft C 6.0 (Tom Lane) +- fix maketree.c to allow clean compilation of inffixed.h (Mark) +- fix parameter check in deflateCopy (Gunther Nikl) +- cleanup trees.c, use compressed_len only in debug mode (Christian Spieler) +- Many portability patches by Christian Spieler: + . zutil.c, zutil.h: added "const" for zmem* + . Make_vms.com: fixed some typos + . Make_vms.com: msdos/Makefile.*: removed zutil.h from some dependency lists + . msdos/Makefile.msc: remove "default rtl link library" info from obj files + . msdos/Makefile.*: use model-dependent name for the built zlib library + . msdos/Makefile.emx, nt/Makefile.emx, nt/Makefile.gcc: + new makefiles, for emx (DOS/OS2), emx&rsxnt and mingw32 (Windows 9x / NT) +- use define instead of typedef for Bytef also for MSC small/medium (Tom Lane) +- replace __far with _far for better portability (Christian Spieler, Tom Lane) +- fix test for errno.h in configure (Tim Newsham) + +Changes in 1.1.2 (19 March 98) +- added contrib/minzip, mini zip and unzip based on zlib (Gilles Vollant) + See http://www.winimage.com/zLibDll/unzip.html +- preinitialize the inflate tables for fixed codes, to make the code + completely thread safe (Mark) +- some simplifications and slight speed-up to the inflate code (Mark) +- fix gzeof on non-compressed files (Allan Schrum) +- add -std1 option in configure for OSF1 to fix gzprintf (Martin Mokrejs) +- use default value of 4K for Z_BUFSIZE for 16-bit MSDOS (Tim Wegner + Glenn) +- added os2/Makefile.def and os2/zlib.def (Andrew Zabolotny) +- add shared lib support for UNIX_SV4.2MP (MATSUURA Takanori) +- do not wrap extern "C" around system includes (Tom Lane) +- mention zlib binding for TCL in README (Andreas Kupries) +- added amiga/Makefile.pup for Amiga powerUP SAS/C PPC (Andreas Kleinert) +- allow "make install prefix=..." even after configure (Glenn Randers-Pehrson) +- allow "configure --prefix $HOME" (Tim Mooney) +- remove warnings in example.c and gzio.c (Glenn Randers-Pehrson) +- move Makefile.sas to amiga/Makefile.sas + +Changes in 1.1.1 (27 Feb 98) +- fix macros _tr_tally_* in deflate.h for debug mode (Glenn Randers-Pehrson) +- remove block truncation heuristic which had very marginal effect for zlib + (smaller lit_bufsize than in gzip 1.2.4) and degraded a little the + compression ratio on some files. This also allows inlining _tr_tally for + matches in deflate_slow. +- added msdos/Makefile.w32 for WIN32 Microsoft Visual C++ (Bob Frazier) + +Changes in 1.1.0 (24 Feb 98) +- do not return STREAM_END prematurely in inflate (John Bowler) +- revert to the zlib 1.0.8 inflate to avoid the gcc 2.8.0 bug (Jeremy Buhler) +- compile with -DFASTEST to get compression code optimized for speed only +- in minigzip, try mmap'ing the input file first (Miguel Albrecht) +- increase size of I/O buffers in minigzip.c and gzio.c (not a big gain + on Sun but significant on HP) + +- add a pointer to experimental unzip library in README (Gilles Vollant) +- initialize variable gcc in configure (Chris Herborth) + +Changes in 1.0.9 (17 Feb 1998) +- added gzputs and gzgets functions +- do not clear eof flag in gzseek (Mark Diekhans) +- fix gzseek for files in transparent mode (Mark Diekhans) +- do not assume that vsprintf returns the number of bytes written (Jens Krinke) +- replace EXPORT with ZEXPORT to avoid conflict with other programs +- added compress2 in zconf.h, zlib.def, zlib.dnt +- new asm code from Gilles Vollant in contrib/asm386 +- simplify the inflate code (Mark): + . Replace ZALLOC's in huft_build() with single ZALLOC in inflate_blocks_new() + . ZALLOC the length list in inflate_trees_fixed() instead of using stack + . ZALLOC the value area for huft_build() instead of using stack + . Simplify Z_FINISH check in inflate() + +- Avoid gcc 2.8.0 comparison bug a little differently than zlib 1.0.8 +- in inftrees.c, avoid cc -O bug on HP (Farshid Elahi) +- in zconf.h move the ZLIB_DLL stuff earlier to avoid problems with + the declaration of FAR (Gilles VOllant) +- install libz.so* with mode 755 (executable) instead of 644 (Marc Lehmann) +- read_buf buf parameter of type Bytef* instead of charf* +- zmemcpy parameters are of type Bytef*, not charf* (Joseph Strout) +- do not redeclare unlink in minigzip.c for WIN32 (John Bowler) +- fix check for presence of directories in "make install" (Ian Willis) + +Changes in 1.0.8 (27 Jan 1998) +- fixed offsets in contrib/asm386/gvmat32.asm (Gilles Vollant) +- fix gzgetc and gzputc for big endian systems (Markus Oberhumer) +- added compress2() to allow setting the compression level +- include sys/types.h to get off_t on some systems (Marc Lehmann & QingLong) +- use constant arrays for the static trees in trees.c instead of computing + them at run time (thanks to Ken Raeburn for this suggestion). To create + trees.h, compile with GEN_TREES_H and run "make test". +- check return code of example in "make test" and display result +- pass minigzip command line options to file_compress +- simplifying code of inflateSync to avoid gcc 2.8 bug + +- support CC="gcc -Wall" in configure -s (QingLong) +- avoid a flush caused by ftell in gzopen for write mode (Ken Raeburn) +- fix test for shared library support to avoid compiler warnings +- zlib.lib -> zlib.dll in msdos/zlib.rc (Gilles Vollant) +- check for TARGET_OS_MAC in addition to MACOS (Brad Pettit) +- do not use fdopen for Metrowerks on Mac (Brad Pettit)) +- add checks for gzputc and gzputc in example.c +- avoid warnings in gzio.c and deflate.c (Andreas Kleinert) +- use const for the CRC table (Ken Raeburn) +- fixed "make uninstall" for shared libraries +- use Tracev instead of Trace in infblock.c +- in example.c use correct compressed length for test_sync +- suppress +vnocompatwarnings in configure for HPUX (not always supported) + +Changes in 1.0.7 (20 Jan 1998) +- fix gzseek which was broken in write mode +- return error for gzseek to negative absolute position +- fix configure for Linux (Chun-Chung Chen) +- increase stack space for MSC (Tim Wegner) +- get_crc_table and inflateSyncPoint are EXPORTed (Gilles Vollant) +- define EXPORTVA for gzprintf (Gilles Vollant) +- added man page zlib.3 (Rick Rodgers) +- for contrib/untgz, fix makedir() and improve Makefile + +- check gzseek in write mode in example.c +- allocate extra buffer for seeks only if gzseek is actually called +- avoid signed/unsigned comparisons (Tim Wegner, Gilles Vollant) +- add inflateSyncPoint in zconf.h +- fix list of exported functions in nt/zlib.dnt and mdsos/zlib.def + +Changes in 1.0.6 (19 Jan 1998) +- add functions gzprintf, gzputc, gzgetc, gztell, gzeof, gzseek, gzrewind and + gzsetparams (thanks to Roland Giersig and Kevin Ruland for some of this code) +- Fix a deflate bug occuring only with compression level 0 (thanks to + Andy Buckler for finding this one). +- In minigzip, pass transparently also the first byte for .Z files. +- return Z_BUF_ERROR instead of Z_OK if output buffer full in uncompress() +- check Z_FINISH in inflate (thanks to Marc Schluper) +- Implement deflateCopy (thanks to Adam Costello) +- make static libraries by default in configure, add --shared option. +- move MSDOS or Windows specific files to directory msdos +- suppress the notion of partial flush to simplify the interface + (but the symbol Z_PARTIAL_FLUSH is kept for compatibility with 1.0.4) +- suppress history buffer provided by application to simplify the interface + (this feature was not implemented anyway in 1.0.4) +- next_in and avail_in must be initialized before calling inflateInit or + inflateInit2 +- add EXPORT in all exported functions (for Windows DLL) +- added Makefile.nt (thanks to Stephen Williams) +- added the unsupported "contrib" directory: + contrib/asm386/ by Gilles Vollant + 386 asm code replacing longest_match(). + contrib/iostream/ by Kevin Ruland + A C++ I/O streams interface to the zlib gz* functions + contrib/iostream2/ by Tyge Løvset + Another C++ I/O streams interface + contrib/untgz/ by "Pedro A. Aranda Guti\irrez" + A very simple tar.gz file extractor using zlib + contrib/visual-basic.txt by Carlos Rios + How to use compress(), uncompress() and the gz* functions from VB. +- pass params -f (filtered data), -h (huffman only), -1 to -9 (compression + level) in minigzip (thanks to Tom Lane) + +- use const for rommable constants in deflate +- added test for gzseek and gztell in example.c +- add undocumented function inflateSyncPoint() (hack for Paul Mackerras) +- add undocumented function zError to convert error code to string + (for Tim Smithers) +- Allow compilation of gzio with -DNO_DEFLATE to avoid the compression code. +- Use default memcpy for Symantec MSDOS compiler. +- Add EXPORT keyword for check_func (needed for Windows DLL) +- add current directory to LD_LIBRARY_PATH for "make test" +- create also a link for libz.so.1 +- added support for FUJITSU UXP/DS (thanks to Toshiaki Nomura) +- use $(SHAREDLIB) instead of libz.so in Makefile.in (for HPUX) +- added -soname for Linux in configure (Chun-Chung Chen, +- assign numbers to the exported functions in zlib.def (for Windows DLL) +- add advice in zlib.h for best usage of deflateSetDictionary +- work around compiler bug on Atari (cast Z_NULL in call of s->checkfn) +- allow compilation with ANSI keywords only enabled for TurboC in large model +- avoid "versionString"[0] (Borland bug) +- add NEED_DUMMY_RETURN for Borland +- use variable z_verbose for tracing in debug mode (L. Peter Deutsch). +- allow compilation with CC +- defined STDC for OS/2 (David Charlap) +- limit external names to 8 chars for MVS (Thomas Lund) +- in minigzip.c, use static buffers only for 16-bit systems +- fix suffix check for "minigzip -d foo.gz" +- do not return an error for the 2nd of two consecutive gzflush() (Felix Lee) +- use _fdopen instead of fdopen for MSC >= 6.0 (Thomas Fanslau) +- added makelcc.bat for lcc-win32 (Tom St Denis) +- in Makefile.dj2, use copy and del instead of install and rm (Frank Donahoe) +- Avoid expanded $Id: ChangeLog,v 1.2 2002/03/13 17:45:55 xodnizel Exp $. Use "rcs -kb" or "cvs admin -kb" to avoid Id expansion. +- check for unistd.h in configure (for off_t) +- remove useless check parameter in inflate_blocks_free +- avoid useless assignment of s->check to itself in inflate_blocks_new +- do not flush twice in gzclose (thanks to Ken Raeburn) +- rename FOPEN as F_OPEN to avoid clash with /usr/include/sys/file.h +- use NO_ERRNO_H instead of enumeration of operating systems with errno.h +- work around buggy fclose on pipes for HP/UX +- support zlib DLL with BORLAND C++ 5.0 (thanks to Glenn Randers-Pehrson) +- fix configure if CC is already equal to gcc + +Changes in 1.0.5 (3 Jan 98) +- Fix inflate to terminate gracefully when fed corrupted or invalid data +- Use const for rommable constants in inflate +- Eliminate memory leaks on error conditions in inflate +- Removed some vestigial code in inflate +- Update web address in README + +Changes in 1.0.4 (24 Jul 96) +- In very rare conditions, deflate(s, Z_FINISH) could fail to produce an EOF + bit, so the decompressor could decompress all the correct data but went + on to attempt decompressing extra garbage data. This affected minigzip too. +- zlibVersion and gzerror return const char* (needed for DLL) +- port to RISCOS (no fdopen, no multiple dots, no unlink, no fileno) +- use z_error only for DEBUG (avoid problem with DLLs) + +Changes in 1.0.3 (2 Jul 96) +- use z_streamp instead of z_stream *, which is now a far pointer in MSDOS + small and medium models; this makes the library incompatible with previous + versions for these models. (No effect in large model or on other systems.) +- return OK instead of BUF_ERROR if previous deflate call returned with + avail_out as zero but there is nothing to do +- added memcmp for non STDC compilers +- define NO_DUMMY_DECL for more Mac compilers (.h files merged incorrectly) +- define __32BIT__ if __386__ or i386 is defined (pb. with Watcom and SCO) +- better check for 16-bit mode MSC (avoids problem with Symantec) + +Changes in 1.0.2 (23 May 96) +- added Windows DLL support +- added a function zlibVersion (for the DLL support) +- fixed declarations using Bytef in infutil.c (pb with MSDOS medium model) +- Bytef is define's instead of typedef'd only for Borland C +- avoid reading uninitialized memory in example.c +- mention in README that the zlib format is now RFC1950 +- updated Makefile.dj2 +- added algorithm.doc + +Changes in 1.0.1 (20 May 96) [1.0 skipped to avoid confusion] +- fix array overlay in deflate.c which sometimes caused bad compressed data +- fix inflate bug with empty stored block +- fix MSDOS medium model which was broken in 0.99 +- fix deflateParams() which could generated bad compressed data. +- Bytef is define'd instead of typedef'ed (work around Borland bug) +- added an INDEX file +- new makefiles for DJGPP (Makefile.dj2), 32-bit Borland (Makefile.b32), + Watcom (Makefile.wat), Amiga SAS/C (Makefile.sas) +- speed up adler32 for modern machines without auto-increment +- added -ansi for IRIX in configure +- static_init_done in trees.c is an int +- define unlink as delete for VMS +- fix configure for QNX +- add configure branch for SCO and HPUX +- avoid many warnings (unused variables, dead assignments, etc...) +- no fdopen for BeOS +- fix the Watcom fix for 32 bit mode (define FAR as empty) +- removed redefinition of Byte for MKWERKS +- work around an MWKERKS bug (incorrect merge of all .h files) + +Changes in 0.99 (27 Jan 96) +- allow preset dictionary shared between compressor and decompressor +- allow compression level 0 (no compression) +- add deflateParams in zlib.h: allow dynamic change of compression level + and compression strategy. +- test large buffers and deflateParams in example.c +- add optional "configure" to build zlib as a shared library +- suppress Makefile.qnx, use configure instead +- fixed deflate for 64-bit systems (detected on Cray) +- fixed inflate_blocks for 64-bit systems (detected on Alpha) +- declare Z_DEFLATED in zlib.h (possible parameter for deflateInit2) +- always return Z_BUF_ERROR when deflate() has nothing to do +- deflateInit and inflateInit are now macros to allow version checking +- prefix all global functions and types with z_ with -DZ_PREFIX +- make falloc completely reentrant (inftrees.c) +- fixed very unlikely race condition in ct_static_init +- free in reverse order of allocation to help memory manager +- use zlib-1.0/* instead of zlib/* inside the tar.gz +- make zlib warning-free with "gcc -O3 -Wall -Wwrite-strings -Wpointer-arith + -Wconversion -Wstrict-prototypes -Wmissing-prototypes" +- allow gzread on concatenated .gz files +- deflateEnd now returns Z_DATA_ERROR if it was premature +- deflate is finally (?) fully deterministic (no matches beyond end of input) +- Document Z_SYNC_FLUSH +- add uninstall in Makefile +- Check for __cpluplus in zlib.h +- Better test in ct_align for partial flush +- avoid harmless warnings for Borland C++ +- initialize hash_head in deflate.c +- avoid warning on fdopen (gzio.c) for HP cc -Aa +- include stdlib.h for STDC compilers +- include errno.h for Cray +- ignore error if ranlib doesn't exist +- call ranlib twice for NeXTSTEP +- use exec_prefix instead of prefix for libz.a +- renamed ct_* as _tr_* to avoid conflict with applications +- clear z->msg in inflateInit2 before any error return +- initialize opaque in example.c, gzio.c, deflate.c and inflate.c +- fixed typo in zconf.h (_GNUC__ => __GNUC__) +- check for WIN32 in zconf.h and zutil.c (avoid farmalloc in 32-bit mode) +- fix typo in Make_vms.com (f$trnlnm -> f$getsyi) +- in fcalloc, normalize pointer if size > 65520 bytes +- don't use special fcalloc for 32 bit Borland C++ +- use STDC instead of __GO32__ to avoid redeclaring exit, calloc, etc... +- use Z_BINARY instead of BINARY +- document that gzclose after gzdopen will close the file +- allow "a" as mode in gzopen. +- fix error checking in gzread +- allow skipping .gz extra-field on pipes +- added reference to Perl interface in README +- put the crc table in FAR data (I dislike more and more the medium model :) +- added get_crc_table +- added a dimension to all arrays (Borland C can't count). +- workaround Borland C bug in declaration of inflate_codes_new & inflate_fast +- guard against multiple inclusion of *.h (for precompiled header on Mac) +- Watcom C pretends to be Microsoft C small model even in 32 bit mode. +- don't use unsized arrays to avoid silly warnings by Visual C++: + warning C4746: 'inflate_mask' : unsized array treated as '__far' + (what's wrong with far data in far model?). +- define enum out of inflate_blocks_state to allow compilation with C++ + +Changes in 0.95 (16 Aug 95) +- fix MSDOS small and medium model (now easier to adapt to any compiler) +- inlined send_bits +- fix the final (:-) bug for deflate with flush (output was correct but + not completely flushed in rare occasions). +- default window size is same for compression and decompression + (it's now sufficient to set MAX_WBITS in zconf.h). +- voidp -> voidpf and voidnp -> voidp (for consistency with other + typedefs and because voidnp was not near in large model). + +Changes in 0.94 (13 Aug 95) +- support MSDOS medium model +- fix deflate with flush (could sometimes generate bad output) +- fix deflateReset (zlib header was incorrectly suppressed) +- added support for VMS +- allow a compression level in gzopen() +- gzflush now calls fflush +- For deflate with flush, flush even if no more input is provided. +- rename libgz.a as libz.a +- avoid complex expression in infcodes.c triggering Turbo C bug +- work around a problem with gcc on Alpha (in INSERT_STRING) +- don't use inline functions (problem with some gcc versions) +- allow renaming of Byte, uInt, etc... with #define. +- avoid warning about (unused) pointer before start of array in deflate.c +- avoid various warnings in gzio.c, example.c, infblock.c, adler32.c, zutil.c +- avoid reserved word 'new' in trees.c + +Changes in 0.93 (25 June 95) +- temporarily disable inline functions +- make deflate deterministic +- give enough lookahead for PARTIAL_FLUSH +- Set binary mode for stdin/stdout in minigzip.c for OS/2 +- don't even use signed char in inflate (not portable enough) +- fix inflate memory leak for segmented architectures + +Changes in 0.92 (3 May 95) +- don't assume that char is signed (problem on SGI) +- Clear bit buffer when starting a stored block +- no memcpy on Pyramid +- suppressed inftest.c +- optimized fill_window, put longest_match inline for gcc +- optimized inflate on stored blocks. +- untabify all sources to simplify patches + +Changes in 0.91 (2 May 95) +- Default MEM_LEVEL is 8 (not 9 for Unix) as documented in zlib.h +- Document the memory requirements in zconf.h +- added "make install" +- fix sync search logic in inflateSync +- deflate(Z_FULL_FLUSH) now works even if output buffer too short +- after inflateSync, don't scare people with just "lo world" +- added support for DJGPP + +Changes in 0.9 (1 May 95) +- don't assume that zalloc clears the allocated memory (the TurboC bug + was Mark's bug after all :) +- let again gzread copy uncompressed data unchanged (was working in 0.71) +- deflate(Z_FULL_FLUSH), inflateReset and inflateSync are now fully implemented +- added a test of inflateSync in example.c +- moved MAX_WBITS to zconf.h because users might want to change that. +- document explicitly that zalloc(64K) on MSDOS must return a normalized + pointer (zero offset) +- added Makefiles for Microsoft C, Turbo C, Borland C++ +- faster crc32() + +Changes in 0.8 (29 April 95) +- added fast inflate (inffast.c) +- deflate(Z_FINISH) now returns Z_STREAM_END when done. Warning: this + is incompatible with previous versions of zlib which returned Z_OK. +- work around a TurboC compiler bug (bad code for b << 0, see infutil.h) + (actually that was not a compiler bug, see 0.81 above) +- gzread no longer reads one extra byte in certain cases +- In gzio destroy(), don't reference a freed structure +- avoid many warnings for MSDOS +- avoid the ERROR symbol which is used by MS Windows + +Changes in 0.71 (14 April 95) +- Fixed more MSDOS compilation problems :( There is still a bug with + TurboC large model. + +Changes in 0.7 (14 April 95) +- Added full inflate support. +- Simplified the crc32() interface. The pre- and post-conditioning + (one's complement) is now done inside crc32(). WARNING: this is + incompatible with previous versions; see zlib.h for the new usage. + +Changes in 0.61 (12 April 95) +- workaround for a bug in TurboC. example and minigzip now work on MSDOS. + +Changes in 0.6 (11 April 95) +- added minigzip.c +- added gzdopen to reopen a file descriptor as gzFile +- added transparent reading of non-gziped files in gzread. +- fixed bug in gzread (don't read crc as data) +- fixed bug in destroy (gzio.c) (don't return Z_STREAM_END for gzclose). +- don't allocate big arrays in the stack (for MSDOS) +- fix some MSDOS compilation problems + +Changes in 0.5: +- do real compression in deflate.c. Z_PARTIAL_FLUSH is supported but + not yet Z_FULL_FLUSH. +- support decompression but only in a single step (forced Z_FINISH) +- added opaque object for zalloc and zfree. +- added deflateReset and inflateReset +- added a variable zlib_version for consistency checking. +- renamed the 'filter' parameter of deflateInit2 as 'strategy'. + Added Z_FILTERED and Z_HUFFMAN_ONLY constants. + +Changes in 0.4: +- avoid "zip" everywhere, use zlib instead of ziplib. +- suppress Z_BLOCK_FLUSH, interpret Z_PARTIAL_FLUSH as block flush + if compression method == 8. +- added adler32 and crc32 +- renamed deflateOptions as deflateInit2, call one or the other but not both +- added the method parameter for deflateInit2. +- added inflateInit2 +- simplied considerably deflateInit and inflateInit by not supporting + user-provided history buffer. This is supported only in deflateInit2 + and inflateInit2. + +Changes in 0.3: +- prefix all macro names with Z_ +- use Z_FINISH instead of deflateEnd to finish compression. +- added Z_HUFFMAN_ONLY +- added gzerror() diff --git a/zlib/Makefile b/zlib/Makefile new file mode 100644 index 0000000..ac87b2f --- /dev/null +++ b/zlib/Makefile @@ -0,0 +1,25 @@ +UNZIPOBJS = zlib/unzip.o +ZLIBOBJS = zlib/adler32.o zlib/compress.o zlib/crc32.o zlib/gzio.o zlib/uncompr.o zlib/deflate.o zlib/trees.o \ + zlib/zutil.o zlib/inflate.o zlib/infblock.o zlib/inftrees.o zlib/infcodes.o zlib/infutil.o zlib/inffast.o + +# DO NOT DELETE THIS LINE -- make depend depends on it. + +zlib/adler32.o: zlib/zlib.h zlib/zconf.h +zlib/compress.o: zlib/zlib.h zlib/zconf.h +zlib/crc32.o: zlib/zlib.h zlib/zconf.h +zlib/deflate.o: zlib/deflate.h zlib/zutil.h zlib/zlib.h zlib/zconf.h +zlib/example.o: zlib/zlib.h zlib/zconf.h +zlib/gzio.o: zlib/zutil.h zlib/zlib.h zlib/zconf.h +zlib/infblock.o: zlib/infblock.h zlib/inftrees.h zlib/infcodes.h zlib/infutil.h zlib/zutil.h zlib/zlib.h zlib/zconf.h +zlib/infcodes.o: zlib/zutil.h zlib/zlib.h zlib/zconf.h +zlib/infcodes.o: zlib/inftrees.h zlib/infblock.h zlib/infcodes.h zlib/infutil.h zlib/inffast.h +zlib/inffast.o: zlib/zutil.h zlib/zlib.h zlib/zconf.h zlib/inftrees.h +zlib/inffast.o: zlib/infblock.h zlib/infcodes.h zlib/infutil.h zlib/inffast.h +zlib/inflate.o: zlib/zutil.h zlib/zlib.h zlib/zconf.h zlib/infblock.h +zlib/inftrees.o: zlib/zutil.h zlib/zlib.h zlib/zconf.h zlib/inftrees.h +zlib/infutil.o: zlib/zutil.h zlib/zlib.h zlib/zconf.h zlib/infblock.h zlib/inftrees.h zlib/infcodes.h zlib/infutil.h +zlib/trees.o: zlib/deflate.h zlib/zutil.h zlib/zlib.h zlib/zconf.h zlib/trees.h +zlib/uncompr.o: zlib/zlib.h zlib/zconf.h +zlib/zutil.o: zlib/zutil.h zlib/zlib.h zlib/zconf.h + +zlib/unzip.o: zlib/unzip.h diff --git a/zlib/adler32.c b/zlib/adler32.c new file mode 100644 index 0000000..a250f3f --- /dev/null +++ b/zlib/adler32.c @@ -0,0 +1,48 @@ +/* adler32.c -- compute the Adler-32 checksum of a data stream + * Copyright (C) 1995-2002 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id: adler32.c,v 1.2 2002/03/13 17:45:56 xodnizel Exp $ */ + +#include "zlib.h" + +#define BASE 65521L /* largest prime smaller than 65536 */ +#define NMAX 5552 +/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ + +#define DO1(buf,i) {s1 += buf[i]; s2 += s1;} +#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); +#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); +#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); +#define DO16(buf) DO8(buf,0); DO8(buf,8); + +/* ========================================================================= */ +uLong ZEXPORT adler32(adler, buf, len) + uLong adler; + const Bytef *buf; + uInt len; +{ + unsigned long s1 = adler & 0xffff; + unsigned long s2 = (adler >> 16) & 0xffff; + int k; + + if (buf == Z_NULL) return 1L; + + while (len > 0) { + k = len < NMAX ? len : NMAX; + len -= k; + while (k >= 16) { + DO16(buf); + buf += 16; + k -= 16; + } + if (k != 0) do { + s1 += *buf++; + s2 += s1; + } while (--k); + s1 %= BASE; + s2 %= BASE; + } + return (s2 << 16) | s1; +} diff --git a/zlib/algorithm.txt b/zlib/algorithm.txt new file mode 100644 index 0000000..cdc830b --- /dev/null +++ b/zlib/algorithm.txt @@ -0,0 +1,213 @@ +1. Compression algorithm (deflate) + +The deflation algorithm used by gzip (also zip and zlib) is a variation of +LZ77 (Lempel-Ziv 1977, see reference below). It finds duplicated strings in +the input data. The second occurrence of a string is replaced by a +pointer to the previous string, in the form of a pair (distance, +length). Distances are limited to 32K bytes, and lengths are limited +to 258 bytes. When a string does not occur anywhere in the previous +32K bytes, it is emitted as a sequence of literal bytes. (In this +description, `string' must be taken as an arbitrary sequence of bytes, +and is not restricted to printable characters.) + +Literals or match lengths are compressed with one Huffman tree, and +match distances are compressed with another tree. The trees are stored +in a compact form at the start of each block. The blocks can have any +size (except that the compressed data for one block must fit in +available memory). A block is terminated when deflate() determines that +it would be useful to start another block with fresh trees. (This is +somewhat similar to the behavior of LZW-based _compress_.) + +Duplicated strings are found using a hash table. All input strings of +length 3 are inserted in the hash table. A hash index is computed for +the next 3 bytes. If the hash chain for this index is not empty, all +strings in the chain are compared with the current input string, and +the longest match is selected. + +The hash chains are searched starting with the most recent strings, to +favor small distances and thus take advantage of the Huffman encoding. +The hash chains are singly linked. There are no deletions from the +hash chains, the algorithm simply discards matches that are too old. + +To avoid a worst-case situation, very long hash chains are arbitrarily +truncated at a certain length, determined by a runtime option (level +parameter of deflateInit). So deflate() does not always find the longest +possible match but generally finds a match which is long enough. + +deflate() also defers the selection of matches with a lazy evaluation +mechanism. After a match of length N has been found, deflate() searches for +a longer match at the next input byte. If a longer match is found, the +previous match is truncated to a length of one (thus producing a single +literal byte) and the process of lazy evaluation begins again. Otherwise, +the original match is kept, and the next match search is attempted only N +steps later. + +The lazy match evaluation is also subject to a runtime parameter. If +the current match is long enough, deflate() reduces the search for a longer +match, thus speeding up the whole process. If compression ratio is more +important than speed, deflate() attempts a complete second search even if +the first match is already long enough. + +The lazy match evaluation is not performed for the fastest compression +modes (level parameter 1 to 3). For these fast modes, new strings +are inserted in the hash table only when no match was found, or +when the match is not too long. This degrades the compression ratio +but saves time since there are both fewer insertions and fewer searches. + + +2. Decompression algorithm (inflate) + +2.1 Introduction + +The real question is, given a Huffman tree, how to decode fast. The most +important realization is that shorter codes are much more common than +longer codes, so pay attention to decoding the short codes fast, and let +the long codes take longer to decode. + +inflate() sets up a first level table that covers some number of bits of +input less than the length of longest code. It gets that many bits from the +stream, and looks it up in the table. The table will tell if the next +code is that many bits or less and how many, and if it is, it will tell +the value, else it will point to the next level table for which inflate() +grabs more bits and tries to decode a longer code. + +How many bits to make the first lookup is a tradeoff between the time it +takes to decode and the time it takes to build the table. If building the +table took no time (and if you had infinite memory), then there would only +be a first level table to cover all the way to the longest code. However, +building the table ends up taking a lot longer for more bits since short +codes are replicated many times in such a table. What inflate() does is +simply to make the number of bits in the first table a variable, and set it +for the maximum speed. + +inflate() sends new trees relatively often, so it is possibly set for a +smaller first level table than an application that has only one tree for +all the data. For inflate, which has 286 possible codes for the +literal/length tree, the size of the first table is nine bits. Also the +distance trees have 30 possible values, and the size of the first table is +six bits. Note that for each of those cases, the table ended up one bit +longer than the ``average'' code length, i.e. the code length of an +approximately flat code which would be a little more than eight bits for +286 symbols and a little less than five bits for 30 symbols. It would be +interesting to see if optimizing the first level table for other +applications gave values within a bit or two of the flat code size. + + +2.2 More details on the inflate table lookup + +Ok, you want to know what this cleverly obfuscated inflate tree actually +looks like. You are correct that it's not a Huffman tree. It is simply a +lookup table for the first, let's say, nine bits of a Huffman symbol. The +symbol could be as short as one bit or as long as 15 bits. If a particular +symbol is shorter than nine bits, then that symbol's translation is duplicated +in all those entries that start with that symbol's bits. For example, if the +symbol is four bits, then it's duplicated 32 times in a nine-bit table. If a +symbol is nine bits long, it appears in the table once. + +If the symbol is longer than nine bits, then that entry in the table points +to another similar table for the remaining bits. Again, there are duplicated +entries as needed. The idea is that most of the time the symbol will be short +and there will only be one table look up. (That's whole idea behind data +compression in the first place.) For the less frequent long symbols, there +will be two lookups. If you had a compression method with really long +symbols, you could have as many levels of lookups as is efficient. For +inflate, two is enough. + +So a table entry either points to another table (in which case nine bits in +the above example are gobbled), or it contains the translation for the symbol +and the number of bits to gobble. Then you start again with the next +ungobbled bit. + +You may wonder: why not just have one lookup table for how ever many bits the +longest symbol is? The reason is that if you do that, you end up spending +more time filling in duplicate symbol entries than you do actually decoding. +At least for deflate's output that generates new trees every several 10's of +kbytes. You can imagine that filling in a 2^15 entry table for a 15-bit code +would take too long if you're only decoding several thousand symbols. At the +other extreme, you could make a new table for every bit in the code. In fact, +that's essentially a Huffman tree. But then you spend two much time +traversing the tree while decoding, even for short symbols. + +So the number of bits for the first lookup table is a trade of the time to +fill out the table vs. the time spent looking at the second level and above of +the table. + +Here is an example, scaled down: + +The code being decoded, with 10 symbols, from 1 to 6 bits long: + +A: 0 +B: 10 +C: 1100 +D: 11010 +E: 11011 +F: 11100 +G: 11101 +H: 11110 +I: 111110 +J: 111111 + +Let's make the first table three bits long (eight entries): + +000: A,1 +001: A,1 +010: A,1 +011: A,1 +100: B,2 +101: B,2 +110: -> table X (gobble 3 bits) +111: -> table Y (gobble 3 bits) + +Each entry is what the bits decode to and how many bits that is, i.e. how +many bits to gobble. Or the entry points to another table, with the number of +bits to gobble implicit in the size of the table. + +Table X is two bits long since the longest code starting with 110 is five bits +long: + +00: C,1 +01: C,1 +10: D,2 +11: E,2 + +Table Y is three bits long since the longest code starting with 111 is six +bits long: + +000: F,2 +001: F,2 +010: G,2 +011: G,2 +100: H,2 +101: H,2 +110: I,3 +111: J,3 + +So what we have here are three tables with a total of 20 entries that had to +be constructed. That's compared to 64 entries for a single table. Or +compared to 16 entries for a Huffman tree (six two entry tables and one four +entry table). Assuming that the code ideally represents the probability of +the symbols, it takes on the average 1.25 lookups per symbol. That's compared +to one lookup for the single table, or 1.66 lookups per symbol for the +Huffman tree. + +There, I think that gives you a picture of what's going on. For inflate, the +meaning of a particular symbol is often more than just a letter. It can be a +byte (a "literal"), or it can be either a length or a distance which +indicates a base value and a number of bits to fetch after the code that is +added to the base value. Or it might be the special end-of-block code. The +data structures created in inftrees.c try to encode all that information +compactly in the tables. + + +Jean-loup Gailly Mark Adler +jloup@gzip.org madler@alumni.caltech.edu + + +References: + +[LZ77] Ziv J., Lempel A., ``A Universal Algorithm for Sequential Data +Compression,'' IEEE Transactions on Information Theory, Vol. 23, No. 3, +pp. 337-343. + +``DEFLATE Compressed Data Format Specification'' available in +ftp://ds.internic.net/rfc/rfc1951.txt diff --git a/zlib/compress.c b/zlib/compress.c new file mode 100644 index 0000000..c0eecda --- /dev/null +++ b/zlib/compress.c @@ -0,0 +1,68 @@ +/* compress.c -- compress a memory buffer + * Copyright (C) 1995-2002 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id: compress.c,v 1.2 2002/03/13 17:45:56 xodnizel Exp $ */ + +#include "zlib.h" + +/* =========================================================================== + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least 0.1% larger than sourceLen plus + 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ +int ZEXPORT compress2 (dest, destLen, source, sourceLen, level) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; + int level; +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; +#ifdef MAXSEG_64K + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; +#endif + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + stream.opaque = (voidpf)0; + + err = deflateInit(&stream, level); + if (err != Z_OK) return err; + + err = deflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + deflateEnd(&stream); + return err == Z_OK ? Z_BUF_ERROR : err; + } + *destLen = stream.total_out; + + err = deflateEnd(&stream); + return err; +} + +/* =========================================================================== + */ +int ZEXPORT compress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION); +} diff --git a/zlib/crc32.c b/zlib/crc32.c new file mode 100644 index 0000000..558f6f6 --- /dev/null +++ b/zlib/crc32.c @@ -0,0 +1,162 @@ +/* crc32.c -- compute the CRC-32 of a data stream + * Copyright (C) 1995-2002 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id: crc32.c,v 1.2 2002/03/13 17:45:56 xodnizel Exp $ */ + +#include "zlib.h" + +#define local static + +#ifdef DYNAMIC_CRC_TABLE + +local int crc_table_empty = 1; +local uLongf crc_table[256]; +local void make_crc_table OF((void)); + +/* + Generate a table for a byte-wise 32-bit CRC calculation on the polynomial: + x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. + + Polynomials over GF(2) are represented in binary, one bit per coefficient, + with the lowest powers in the most significant bit. Then adding polynomials + is just exclusive-or, and multiplying a polynomial by x is a right shift by + one. If we call the above polynomial p, and represent a byte as the + polynomial q, also with the lowest power in the most significant bit (so the + byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, + where a mod b means the remainder after dividing a by b. + + This calculation is done using the shift-register method of multiplying and + taking the remainder. The register is initialized to zero, and for each + incoming bit, x^32 is added mod p to the register if the bit is a one (where + x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by + x (which is shifting right by one and adding x^32 mod p if the bit shifted + out is a one). We start with the highest power (least significant bit) of + q and repeat for all eight bits of q. + + The table is simply the CRC of all possible eight bit values. This is all + the information needed to generate CRC's on data a byte at a time for all + combinations of CRC register values and incoming bytes. +*/ +local void make_crc_table() +{ + uLong c; + int n, k; + uLong poly; /* polynomial exclusive-or pattern */ + /* terms of polynomial defining this crc (except x^32): */ + static const Byte p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; + + /* make exclusive-or pattern from polynomial (0xedb88320L) */ + poly = 0L; + for (n = 0; n < sizeof(p)/sizeof(Byte); n++) + poly |= 1L << (31 - p[n]); + + for (n = 0; n < 256; n++) + { + c = (uLong)n; + for (k = 0; k < 8; k++) + c = c & 1 ? poly ^ (c >> 1) : c >> 1; + crc_table[n] = c; + } + crc_table_empty = 0; +} +#else +/* ======================================================================== + * Table of CRC-32's of all single-byte values (made by make_crc_table) + */ +local const uLongf crc_table[256] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; +#endif + +/* ========================================================================= + * This function can be used by asm versions of crc32() + */ +const uLongf * ZEXPORT get_crc_table() +{ +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) make_crc_table(); +#endif + return (const uLongf *)crc_table; +} + +/* ========================================================================= */ +#define DO1(buf) crc = crc_table[((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8); +#define DO2(buf) DO1(buf); DO1(buf); +#define DO4(buf) DO2(buf); DO2(buf); +#define DO8(buf) DO4(buf); DO4(buf); + +/* ========================================================================= */ +uLong ZEXPORT crc32(crc, buf, len) + uLong crc; + const Bytef *buf; + uInt len; +{ + if (buf == Z_NULL) return 0L; +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif + crc = crc ^ 0xffffffffL; + while (len >= 8) + { + DO8(buf); + len -= 8; + } + if (len) do { + DO1(buf); + } while (--len); + return crc ^ 0xffffffffL; +} diff --git a/zlib/deflate.c b/zlib/deflate.c new file mode 100644 index 0000000..0b79a14 --- /dev/null +++ b/zlib/deflate.c @@ -0,0 +1,1350 @@ +/* deflate.c -- compress data using the deflation algorithm + * Copyright (C) 1995-2002 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process depends on being able to identify portions + * of the input text which are identical to earlier input (within a + * sliding window trailing behind the input currently being processed). + * + * The most straightforward technique turns out to be the fastest for + * most input files: try all possible matches and select the longest. + * The key feature of this algorithm is that insertions into the string + * dictionary are very simple and thus fast, and deletions are avoided + * completely. Insertions are performed at each input character, whereas + * string matches are performed only when the previous match ends. So it + * is preferable to spend more time in matches to allow very fast string + * insertions and avoid deletions. The matching algorithm for small + * strings is inspired from that of Rabin & Karp. A brute force approach + * is used to find longer strings when a small match has been found. + * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze + * (by Leonid Broukhis). + * A previous version of this file used a more sophisticated algorithm + * (by Fiala and Greene) which is guaranteed to run in linear amortized + * time, but has a larger average cost, uses more memory and is patented. + * However the F&G algorithm may be faster for some highly redundant + * files if the parameter max_chain_length (described below) is too large. + * + * ACKNOWLEDGEMENTS + * + * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and + * I found it in 'freeze' written by Leonid Broukhis. + * Thanks to many people for bug reports and testing. + * + * REFERENCES + * + * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". + * Available in ftp://ds.internic.net/rfc/rfc1951.txt + * + * A description of the Rabin and Karp algorithm is given in the book + * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. + * + * Fiala,E.R., and Greene,D.H. + * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 + * + */ + +/* @(#) $Id: deflate.c,v 1.2 2002/03/13 17:45:56 xodnizel Exp $ */ + +#include "deflate.h" + +const char deflate_copyright[] = + " deflate 1.1.4 Copyright 1995-2002 Jean-loup Gailly "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* =========================================================================== + * Function prototypes. + */ +typedef enum { + need_more, /* block not completed, need more input or more output */ + block_done, /* block flush performed */ + finish_started, /* finish started, need only more output at next deflate */ + finish_done /* finish done, accept no more input or output */ +} block_state; + +typedef block_state (*compress_func) OF((deflate_state *s, int flush)); +/* Compression function. Returns the block state after the call. */ + +local void fill_window OF((deflate_state *s)); +local block_state deflate_stored OF((deflate_state *s, int flush)); +local block_state deflate_fast OF((deflate_state *s, int flush)); +local block_state deflate_slow OF((deflate_state *s, int flush)); +local void lm_init OF((deflate_state *s)); +local void putShortMSB OF((deflate_state *s, uInt b)); +local void flush_pending OF((z_streamp strm)); +local int read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); +#ifdef ASMV + void match_init OF((void)); /* asm code initialization */ + uInt longest_match OF((deflate_state *s, IPos cur_match)); +#else +local uInt longest_match OF((deflate_state *s, IPos cur_match)); +#endif + +#ifdef DEBUG +local void check_match OF((deflate_state *s, IPos start, IPos match, + int length)); +#endif + +/* =========================================================================== + * Local data + */ + +#define NIL 0 +/* Tail of hash chains */ + +#ifndef TOO_FAR +# define TOO_FAR 4096 +#endif +/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +/* Values for max_lazy_match, good_match and max_chain_length, depending on + * the desired pack level (0..9). The values given below have been tuned to + * exclude worst case performance for pathological files. Better values may be + * found for specific files. + */ +typedef struct config_s { + ush good_length; /* reduce lazy search above this match length */ + ush max_lazy; /* do not perform lazy search above this match length */ + ush nice_length; /* quit search above this match length */ + ush max_chain; + compress_func func; +} config; + +local const config configuration_table[10] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}, /* maximum speed, no lazy matches */ +/* 2 */ {4, 5, 16, 8, deflate_fast}, +/* 3 */ {4, 6, 32, 32, deflate_fast}, + +/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */ +/* 5 */ {8, 16, 32, 32, deflate_slow}, +/* 6 */ {8, 16, 128, 128, deflate_slow}, +/* 7 */ {8, 32, 128, 256, deflate_slow}, +/* 8 */ {32, 128, 258, 1024, deflate_slow}, +/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* maximum compression */ + +/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 + * For deflate_fast() (levels <= 3) good is ignored and lazy has a different + * meaning. + */ + +#define EQUAL 0 +/* result of memcmp for equal strings */ + +struct static_tree_desc_s {int dummy;}; /* for buggy compilers */ + +/* =========================================================================== + * Update a hash value with the given input byte + * IN assertion: all calls to to UPDATE_HASH are made with consecutive + * input characters, so that a running hash key can be computed from the + * previous key instead of complete recalculation each time. + */ +#define UPDATE_HASH(s,h,c) (h = (((h)<hash_shift) ^ (c)) & s->hash_mask) + + +/* =========================================================================== + * Insert string str in the dictionary and set match_head to the previous head + * of the hash chain (the most recent string with same hash key). Return + * the previous length of the hash chain. + * If this file is compiled with -DFASTEST, the compression level is forced + * to 1, and no hash chains are maintained. + * IN assertion: all calls to to INSERT_STRING are made with consecutive + * input characters and the first MIN_MATCH bytes of str are valid + * (except for the last MIN_MATCH-1 bytes of the input file). + */ +#ifdef FASTEST +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#else +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + s->prev[(str) & s->w_mask] = match_head = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#endif + +/* =========================================================================== + * Initialize the hash table (avoiding 64K overflow for 16 bit systems). + * prev[] will be initialized on the fly. + */ +#define CLEAR_HASH(s) \ + s->head[s->hash_size-1] = NIL; \ + zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head)); + +/* ========================================================================= */ +int ZEXPORT deflateInit_(strm, level, version, stream_size) + z_streamp strm; + int level; + const char *version; + int stream_size; +{ + return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, version, stream_size); + /* To do: ignore strm->next_in if we use it as window */ +} + +/* ========================================================================= */ +int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, + version, stream_size) + z_streamp strm; + int level; + int method; + int windowBits; + int memLevel; + int strategy; + const char *version; + int stream_size; +{ + deflate_state *s; + int noheader = 0; + static const char* my_version = ZLIB_VERSION; + + ushf *overlay; + /* We overlay pending_buf and d_buf+l_buf. This works since the average + * output size for (length,distance) codes is <= 24 bits. + */ + + if (version == Z_NULL || version[0] != my_version[0] || + stream_size != sizeof(z_stream)) { + return Z_VERSION_ERROR; + } + if (strm == Z_NULL) return Z_STREAM_ERROR; + + strm->msg = Z_NULL; + if (strm->zalloc == Z_NULL) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == Z_NULL) strm->zfree = zcfree; + + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#ifdef FASTEST + level = 1; +#endif + + if (windowBits < 0) { /* undocumented feature: suppress zlib header */ + noheader = 1; + windowBits = -windowBits; + } + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || + windowBits < 9 || windowBits > 15 || level < 0 || level > 9 || + strategy < 0 || strategy > Z_HUFFMAN_ONLY) { + return Z_STREAM_ERROR; + } + s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state)); + if (s == Z_NULL) return Z_MEM_ERROR; + strm->state = (struct internal_state FAR *)s; + s->strm = strm; + + s->noheader = noheader; + s->w_bits = windowBits; + s->w_size = 1 << s->w_bits; + s->w_mask = s->w_size - 1; + + s->hash_bits = memLevel + 7; + s->hash_size = 1 << s->hash_bits; + s->hash_mask = s->hash_size - 1; + s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH); + + s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); + s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); + s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); + + s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ + + overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); + s->pending_buf = (uchf *) overlay; + s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L); + + if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || + s->pending_buf == Z_NULL) { + strm->msg = (char*)ERR_MSG(Z_MEM_ERROR); + deflateEnd (strm); + return Z_MEM_ERROR; + } + s->d_buf = overlay + s->lit_bufsize/sizeof(ush); + s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; + + s->level = level; + s->strategy = strategy; + s->method = (Byte)method; + + return deflateReset(strm); +} + +/* ========================================================================= */ +int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength) + z_streamp strm; + const Bytef *dictionary; + uInt dictLength; +{ + deflate_state *s; + uInt length = dictLength; + uInt n; + IPos hash_head = 0; + + if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL || + strm->state->status != INIT_STATE) return Z_STREAM_ERROR; + + s = strm->state; + strm->adler = adler32(strm->adler, dictionary, dictLength); + + if (length < MIN_MATCH) return Z_OK; + if (length > MAX_DIST(s)) { + length = MAX_DIST(s); +#ifndef USE_DICT_HEAD + dictionary += dictLength - length; /* use the tail of the dictionary */ +#endif + } + zmemcpy(s->window, dictionary, length); + s->strstart = length; + s->block_start = (long)length; + + /* Insert all strings in the hash table (except for the last two bytes). + * s->lookahead stays null, so s->ins_h will be recomputed at the next + * call of fill_window. + */ + s->ins_h = s->window[0]; + UPDATE_HASH(s, s->ins_h, s->window[1]); + for (n = 0; n <= length - MIN_MATCH; n++) { + INSERT_STRING(s, n, hash_head); + } + if (hash_head) hash_head = 0; /* to make compiler happy */ + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateReset (strm) + z_streamp strm; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + strm->zalloc == Z_NULL || strm->zfree == Z_NULL) return Z_STREAM_ERROR; + + strm->total_in = strm->total_out = 0; + strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */ + strm->data_type = Z_UNKNOWN; + + s = (deflate_state *)strm->state; + s->pending = 0; + s->pending_out = s->pending_buf; + + if (s->noheader < 0) { + s->noheader = 0; /* was set to -1 by deflate(..., Z_FINISH); */ + } + s->status = s->noheader ? BUSY_STATE : INIT_STATE; + strm->adler = 1; + s->last_flush = Z_NO_FLUSH; + + _tr_init(s); + lm_init(s); + + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateParams(strm, level, strategy) + z_streamp strm; + int level; + int strategy; +{ + deflate_state *s; + compress_func func; + int err = Z_OK; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + + if (level == Z_DEFAULT_COMPRESSION) { + level = 6; + } + if (level < 0 || level > 9 || strategy < 0 || strategy > Z_HUFFMAN_ONLY) { + return Z_STREAM_ERROR; + } + func = configuration_table[s->level].func; + + if (func != configuration_table[level].func && strm->total_in != 0) { + /* Flush the last buffer: */ + err = deflate(strm, Z_PARTIAL_FLUSH); + } + if (s->level != level) { + s->level = level; + s->max_lazy_match = configuration_table[level].max_lazy; + s->good_match = configuration_table[level].good_length; + s->nice_match = configuration_table[level].nice_length; + s->max_chain_length = configuration_table[level].max_chain; + } + s->strategy = strategy; + return err; +} + +/* ========================================================================= + * Put a short in the pending buffer. The 16-bit value is put in MSB order. + * IN assertion: the stream state is correct and there is enough room in + * pending_buf. + */ +local void putShortMSB (s, b) + deflate_state *s; + uInt b; +{ + put_byte(s, (Byte)(b >> 8)); + put_byte(s, (Byte)(b & 0xff)); +} + +/* ========================================================================= + * Flush as much pending output as possible. All deflate() output goes + * through this function so some applications may wish to modify it + * to avoid allocating a large strm->next_out buffer and copying into it. + * (See also read_buf()). + */ +local void flush_pending(strm) + z_streamp strm; +{ + unsigned len = strm->state->pending; + + if (len > strm->avail_out) len = strm->avail_out; + if (len == 0) return; + + zmemcpy(strm->next_out, strm->state->pending_out, len); + strm->next_out += len; + strm->state->pending_out += len; + strm->total_out += len; + strm->avail_out -= len; + strm->state->pending -= len; + if (strm->state->pending == 0) { + strm->state->pending_out = strm->state->pending_buf; + } +} + +/* ========================================================================= */ +int ZEXPORT deflate (strm, flush) + z_streamp strm; + int flush; +{ + int old_flush; /* value of flush param for previous deflate call */ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + flush > Z_FINISH || flush < 0) { + return Z_STREAM_ERROR; + } + s = strm->state; + + if (strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0) || + (s->status == FINISH_STATE && flush != Z_FINISH)) { + ERR_RETURN(strm, Z_STREAM_ERROR); + } + if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); + + s->strm = strm; /* just in case */ + old_flush = s->last_flush; + s->last_flush = flush; + + /* Write the zlib header */ + if (s->status == INIT_STATE) { + + uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; + uInt level_flags = (s->level-1) >> 1; + + if (level_flags > 3) level_flags = 3; + header |= (level_flags << 6); + if (s->strstart != 0) header |= PRESET_DICT; + header += 31 - (header % 31); + + s->status = BUSY_STATE; + putShortMSB(s, header); + + /* Save the adler32 of the preset dictionary: */ + if (s->strstart != 0) { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + strm->adler = 1L; + } + + /* Flush as much pending output as possible */ + if (s->pending != 0) { + flush_pending(strm); + if (strm->avail_out == 0) { + /* Since avail_out is 0, deflate will be called again with + * more output space, but possibly with both pending and + * avail_in equal to zero. There won't be anything to do, + * but this is not an error situation so make sure we + * return OK instead of BUF_ERROR at next call of deflate: + */ + s->last_flush = -1; + return Z_OK; + } + + /* Make sure there is something to do and avoid duplicate consecutive + * flushes. For repeated and useless calls with Z_FINISH, we keep + * returning Z_STREAM_END instead of Z_BUFF_ERROR. + */ + } else if (strm->avail_in == 0 && flush <= old_flush && + flush != Z_FINISH) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* User must not provide more input after the first FINISH: */ + if (s->status == FINISH_STATE && strm->avail_in != 0) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* Start a new block or continue the current one. + */ + if (strm->avail_in != 0 || s->lookahead != 0 || + (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { + block_state bstate; + + bstate = (*(configuration_table[s->level].func))(s, flush); + + if (bstate == finish_started || bstate == finish_done) { + s->status = FINISH_STATE; + } + if (bstate == need_more || bstate == finish_started) { + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR next call, see above */ + } + return Z_OK; + /* If flush != Z_NO_FLUSH && avail_out == 0, the next call + * of deflate should use the same flush parameter to make sure + * that the flush is complete. So we don't have to output an + * empty block here, this will be done at next call. This also + * ensures that for a very small output buffer, we emit at most + * one empty block. + */ + } + if (bstate == block_done) { + if (flush == Z_PARTIAL_FLUSH) { + _tr_align(s); + } else { /* FULL_FLUSH or SYNC_FLUSH */ + _tr_stored_block(s, (char*)0, 0L, 0); + /* For a full flush, this empty block will be recognized + * as a special marker by inflate_sync(). + */ + if (flush == Z_FULL_FLUSH) { + CLEAR_HASH(s); /* forget history */ + } + } + flush_pending(strm); + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */ + return Z_OK; + } + } + } + Assert(strm->avail_out > 0, "bug2"); + + if (flush != Z_FINISH) return Z_OK; + if (s->noheader) return Z_STREAM_END; + + /* Write the zlib trailer (adler32) */ + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + flush_pending(strm); + /* If avail_out is zero, the application will call deflate again + * to flush the rest. + */ + s->noheader = -1; /* write the trailer only once! */ + return s->pending != 0 ? Z_OK : Z_STREAM_END; +} + +/* ========================================================================= */ +int ZEXPORT deflateEnd (strm) + z_streamp strm; +{ + int status; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + + status = strm->state->status; + if (status != INIT_STATE && status != BUSY_STATE && + status != FINISH_STATE) { + return Z_STREAM_ERROR; + } + + /* Deallocate in reverse order of allocations: */ + TRY_FREE(strm, strm->state->pending_buf); + TRY_FREE(strm, strm->state->head); + TRY_FREE(strm, strm->state->prev); + TRY_FREE(strm, strm->state->window); + + ZFREE(strm, strm->state); + strm->state = Z_NULL; + + return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; +} + +/* ========================================================================= + * Copy the source state to the destination state. + * To simplify the source, this is not supported for 16-bit MSDOS (which + * doesn't have enough memory anyway to duplicate compression states). + */ +int ZEXPORT deflateCopy (dest, source) + z_streamp dest; + z_streamp source; +{ +#ifdef MAXSEG_64K + return Z_STREAM_ERROR; +#else + deflate_state *ds; + deflate_state *ss; + ushf *overlay; + + + if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) { + return Z_STREAM_ERROR; + } + + ss = source->state; + + *dest = *source; + + ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state)); + if (ds == Z_NULL) return Z_MEM_ERROR; + dest->state = (struct internal_state FAR *) ds; + *ds = *ss; + ds->strm = dest; + + ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); + ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos)); + ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos)); + overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2); + ds->pending_buf = (uchf *) overlay; + + if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL || + ds->pending_buf == Z_NULL) { + deflateEnd (dest); + return Z_MEM_ERROR; + } + /* following zmemcpy do not work for 16-bit MSDOS */ + zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); + zmemcpy(ds->prev, ss->prev, ds->w_size * sizeof(Pos)); + zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos)); + zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size); + + ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); + ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush); + ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize; + + ds->l_desc.dyn_tree = ds->dyn_ltree; + ds->d_desc.dyn_tree = ds->dyn_dtree; + ds->bl_desc.dyn_tree = ds->bl_tree; + + return Z_OK; +#endif +} + +/* =========================================================================== + * Read a new buffer from the current input stream, update the adler32 + * and total number of bytes read. All deflate() input goes through + * this function so some applications may wish to modify it to avoid + * allocating a large strm->next_in buffer and copying from it. + * (See also flush_pending()). + */ +local int read_buf(strm, buf, size) + z_streamp strm; + Bytef *buf; + unsigned size; +{ + unsigned len = strm->avail_in; + + if (len > size) len = size; + if (len == 0) return 0; + + strm->avail_in -= len; + + if (!strm->state->noheader) { + strm->adler = adler32(strm->adler, strm->next_in, len); + } + zmemcpy(buf, strm->next_in, len); + strm->next_in += len; + strm->total_in += len; + + return (int)len; +} + +/* =========================================================================== + * Initialize the "longest match" routines for a new zlib stream + */ +local void lm_init (s) + deflate_state *s; +{ + s->window_size = (ulg)2L*s->w_size; + + CLEAR_HASH(s); + + /* Set the default configuration parameters: + */ + s->max_lazy_match = configuration_table[s->level].max_lazy; + s->good_match = configuration_table[s->level].good_length; + s->nice_match = configuration_table[s->level].nice_length; + s->max_chain_length = configuration_table[s->level].max_chain; + + s->strstart = 0; + s->block_start = 0L; + s->lookahead = 0; + s->match_length = s->prev_length = MIN_MATCH-1; + s->match_available = 0; + s->ins_h = 0; +#ifdef ASMV + match_init(); /* initialize the asm code */ +#endif +} + +/* =========================================================================== + * Set match_start to the longest match starting at the given string and + * return its length. Matches shorter or equal to prev_length are discarded, + * in which case the result is equal to prev_length and match_start is + * garbage. + * IN assertions: cur_match is the head of the hash chain for the current + * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + * OUT assertion: the match length is not greater than s->lookahead. + */ +#ifndef ASMV +/* For 80x86 and 680x0, an optimized version will be provided in match.asm or + * match.S. The code will be functionally equivalent. + */ +#ifndef FASTEST +local uInt longest_match(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + unsigned chain_length = s->max_chain_length;/* max hash chain length */ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + int best_len = s->prev_length; /* best match length so far */ + int nice_match = s->nice_match; /* stop if match long enough */ + IPos limit = s->strstart > (IPos)MAX_DIST(s) ? + s->strstart - (IPos)MAX_DIST(s) : NIL; + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + Posf *prev = s->prev; + uInt wmask = s->w_mask; + +#ifdef UNALIGNED_OK + /* Compare two bytes at a time. Note: this is not always beneficial. + * Try with and without -DUNALIGNED_OK to check. + */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1; + register ush scan_start = *(ushf*)scan; + register ush scan_end = *(ushf*)(scan+best_len-1); +#else + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + register Byte scan_end1 = scan[best_len-1]; + register Byte scan_end = scan[best_len]; +#endif + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + /* Do not waste too much time if we already have a good match: */ + if (s->prev_length >= s->good_match) { + chain_length >>= 2; + } + /* Do not look for matches beyond the end of the input. This is necessary + * to make deflate deterministic. + */ + if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + do { + Assert(cur_match < s->strstart, "no future"); + match = s->window + cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2: + */ +#if (defined(UNALIGNED_OK) && MAX_MATCH == 258) + /* This code assumes sizeof(unsigned short) == 2. Do not use + * UNALIGNED_OK if your compiler uses a different size. + */ + if (*(ushf*)(match+best_len-1) != scan_end || + *(ushf*)match != scan_start) continue; + + /* It is not necessary to compare scan[2] and match[2] since they are + * always equal when the other bytes match, given that the hash keys + * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at + * strstart+3, +5, ... up to strstart+257. We check for insufficient + * lookahead only every 4th comparison; the 128th check will be made + * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is + * necessary to put more guard bytes at the end of the window, or + * to check more often for insufficient lookahead. + */ + Assert(scan[2] == match[2], "scan[2]?"); + scan++, match++; + do { + } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + scan < strend); + /* The funny "do {}" generates better code on most compilers */ + + /* Here, scan <= window+strstart+257 */ + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + if (*scan == *match) scan++; + + len = (MAX_MATCH - 1) - (int)(strend-scan); + scan = strend - (MAX_MATCH-1); + +#else /* UNALIGNED_OK */ + + if (match[best_len] != scan_end || + match[best_len-1] != scan_end1 || + *match != *scan || + *++match != scan[1]) continue; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match++; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + +#endif /* UNALIGNED_OK */ + + if (len > best_len) { + s->match_start = cur_match; + best_len = len; + if (len >= nice_match) break; +#ifdef UNALIGNED_OK + scan_end = *(ushf*)(scan+best_len-1); +#else + scan_end1 = scan[best_len-1]; + scan_end = scan[best_len]; +#endif + } + } while ((cur_match = prev[cur_match & wmask]) > limit + && --chain_length != 0); + + if ((uInt)best_len <= s->lookahead) return (uInt)best_len; + return s->lookahead; +} + +#else /* FASTEST */ +/* --------------------------------------------------------------------------- + * Optimized version for level == 1 only + */ +local uInt longest_match(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + Assert(cur_match < s->strstart, "no future"); + + match = s->window + cur_match; + + /* Return failure if the match length is less than 2: + */ + if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match += 2; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + + if (len < MIN_MATCH) return MIN_MATCH - 1; + + s->match_start = cur_match; + return len <= s->lookahead ? len : s->lookahead; +} +#endif /* FASTEST */ +#endif /* ASMV */ + +#ifdef DEBUG +/* =========================================================================== + * Check that the match at match_start is indeed a match. + */ +local void check_match(s, start, match, length) + deflate_state *s; + IPos start, match; + int length; +{ + /* check that the match is indeed a match */ + if (zmemcmp(s->window + match, + s->window + start, length) != EQUAL) { + fprintf(stderr, " start %u, match %u, length %d\n", + start, match, length); + do { + fprintf(stderr, "%c%c", s->window[match++], s->window[start++]); + } while (--length != 0); + z_error("invalid match"); + } + if (z_verbose > 1) { + fprintf(stderr,"\\[%d,%d]", start-match, length); + do { putc(s->window[start++], stderr); } while (--length != 0); + } +} +#else +# define check_match(s, start, match, length) +#endif + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead. + * + * IN assertion: lookahead < MIN_LOOKAHEAD + * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + * At least one byte has been read, or avail_in == 0; reads are + * performed for at least two bytes (required for the zip translate_eol + * option -- not supported here). + */ +local void fill_window(s) + deflate_state *s; +{ + register unsigned n, m; + register Posf *p; + unsigned more; /* Amount of free space at the end of the window. */ + uInt wsize = s->w_size; + + do { + more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); + + /* Deal with !@#$% 64K limit: */ + if (more == 0 && s->strstart == 0 && s->lookahead == 0) { + more = wsize; + + } else if (more == (unsigned)(-1)) { + /* Very unlikely, but possible on 16 bit machine if strstart == 0 + * and lookahead == 1 (input done one byte at time) + */ + more--; + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + } else if (s->strstart >= wsize+MAX_DIST(s)) { + + zmemcpy(s->window, s->window+wsize, (unsigned)wsize); + s->match_start -= wsize; + s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ + s->block_start -= (long) wsize; + + /* Slide the hash table (could be avoided with 32 bit values + at the expense of memory usage). We slide even when level == 0 + to keep the hash table consistent if we switch back to level > 0 + later. (Using level 0 permanently is not an optimal usage of + zlib, so we don't care about this pathological case.) + */ + n = s->hash_size; + p = &s->head[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + } while (--n); + + n = wsize; +#ifndef FASTEST + p = &s->prev[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } while (--n); +#endif + more += wsize; + } + if (s->strm->avail_in == 0) return; + + /* If there was no sliding: + * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + * more == window_size - lookahead - strstart + * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + * => more >= window_size - 2*WSIZE + 2 + * In the BIG_MEM or MMAP case (not yet supported), + * window_size == input_size + MIN_LOOKAHEAD && + * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + * Otherwise, window_size == 2*WSIZE so more >= 2. + * If there was sliding, more >= WSIZE. So in all cases, more >= 2. + */ + Assert(more >= 2, "more < 2"); + + n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); + s->lookahead += n; + + /* Initialize the hash value now that we have some input: */ + if (s->lookahead >= MIN_MATCH) { + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + } + /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + * but this is not important since only literal bytes will be emitted. + */ + + } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); +} + +/* =========================================================================== + * Flush the current block, with given end-of-file flag. + * IN assertion: strstart is set to the end of the current match. + */ +#define FLUSH_BLOCK_ONLY(s, eof) { \ + _tr_flush_block(s, (s->block_start >= 0L ? \ + (charf *)&s->window[(unsigned)s->block_start] : \ + (charf *)Z_NULL), \ + (ulg)((long)s->strstart - s->block_start), \ + (eof)); \ + s->block_start = s->strstart; \ + flush_pending(s->strm); \ + Tracev((stderr,"[FLUSH]")); \ +} + +/* Same but force premature exit if necessary. */ +#define FLUSH_BLOCK(s, eof) { \ + FLUSH_BLOCK_ONLY(s, eof); \ + if (s->strm->avail_out == 0) return (eof) ? finish_started : need_more; \ +} + +/* =========================================================================== + * Copy without compression as much as possible from the input stream, return + * the current block state. + * This function does not insert new strings in the dictionary since + * uncompressible data is probably not useful. This function is used + * only for the level=0 compression option. + * NOTE: this function should be optimized to avoid extra copying from + * window to pending_buf. + */ +local block_state deflate_stored(s, flush) + deflate_state *s; + int flush; +{ + /* Stored blocks are limited to 0xffff bytes, pending_buf is limited + * to pending_buf_size, and each stored block has a 5 byte header: + */ + ulg max_block_size = 0xffff; + ulg max_start; + + if (max_block_size > s->pending_buf_size - 5) { + max_block_size = s->pending_buf_size - 5; + } + + /* Copy as much as possible from input to output: */ + for (;;) { + /* Fill the window as much as possible: */ + if (s->lookahead <= 1) { + + Assert(s->strstart < s->w_size+MAX_DIST(s) || + s->block_start >= (long)s->w_size, "slide too late"); + + fill_window(s); + if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more; + + if (s->lookahead == 0) break; /* flush the current block */ + } + Assert(s->block_start >= 0L, "block gone"); + + s->strstart += s->lookahead; + s->lookahead = 0; + + /* Emit a stored block if pending_buf will be full: */ + max_start = s->block_start + max_block_size; + if (s->strstart == 0 || (ulg)s->strstart >= max_start) { + /* strstart == 0 is possible when wraparound on 16-bit machine */ + s->lookahead = (uInt)(s->strstart - max_start); + s->strstart = (uInt)max_start; + FLUSH_BLOCK(s, 0); + } + /* Flush if we may have to slide, otherwise block_start may become + * negative and the data will be gone: + */ + if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) { + FLUSH_BLOCK(s, 0); + } + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +/* =========================================================================== + * Compress as much as possible from the input stream, return the current + * block state. + * This function does not perform lazy evaluation of matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ +local block_state deflate_fast(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head = NIL; /* head of the hash chain */ + int bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + if (s->strategy != Z_HUFFMAN_ONLY) { + s->match_length = longest_match (s, hash_head); + } + /* longest_match() sets match_start */ + } + if (s->match_length >= MIN_MATCH) { + check_match(s, s->strstart, s->match_start, s->match_length); + + _tr_tally_dist(s, s->strstart - s->match_start, + s->match_length - MIN_MATCH, bflush); + + s->lookahead -= s->match_length; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ +#ifndef FASTEST + if (s->match_length <= s->max_insert_length && + s->lookahead >= MIN_MATCH) { + s->match_length--; /* string at strstart already in hash table */ + do { + s->strstart++; + INSERT_STRING(s, s->strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. + */ + } while (--s->match_length != 0); + s->strstart++; + } else +#endif + { + s->strstart += s->match_length; + s->match_length = 0; + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not + * matter since it will be recomputed at next deflate call. + */ + } + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +/* =========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +local block_state deflate_slow(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head = NIL; /* head of hash chain */ + int bflush; /* set if current block must be flushed */ + + /* Process the input block. */ + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + */ + s->prev_length = s->match_length, s->prev_match = s->match_start; + s->match_length = MIN_MATCH-1; + + if (hash_head != NIL && s->prev_length < s->max_lazy_match && + s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + if (s->strategy != Z_HUFFMAN_ONLY) { + s->match_length = longest_match (s, hash_head); + } + /* longest_match() sets match_start */ + + if (s->match_length <= 5 && (s->strategy == Z_FILTERED || + (s->match_length == MIN_MATCH && + s->strstart - s->match_start > TOO_FAR))) { + + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + s->match_length = MIN_MATCH-1; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) { + uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; + /* Do not insert strings in hash table beyond this. */ + + check_match(s, s->strstart-1, s->prev_match, s->prev_length); + + _tr_tally_dist(s, s->strstart -1 - s->prev_match, + s->prev_length - MIN_MATCH, bflush); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. If there is not + * enough lookahead, the last two strings are not inserted in + * the hash table. + */ + s->lookahead -= s->prev_length-1; + s->prev_length -= 2; + do { + if (++s->strstart <= max_insert) { + INSERT_STRING(s, s->strstart, hash_head); + } + } while (--s->prev_length != 0); + s->match_available = 0; + s->match_length = MIN_MATCH-1; + s->strstart++; + + if (bflush) FLUSH_BLOCK(s, 0); + + } else if (s->match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + if (bflush) { + FLUSH_BLOCK_ONLY(s, 0); + } + s->strstart++; + s->lookahead--; + if (s->strm->avail_out == 0) return need_more; + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + s->match_available = 1; + s->strstart++; + s->lookahead--; + } + } + Assert (flush != Z_NO_FLUSH, "no flush?"); + if (s->match_available) { + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + s->match_available = 0; + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} diff --git a/zlib/deflate.h b/zlib/deflate.h new file mode 100644 index 0000000..9bd8cb0 --- /dev/null +++ b/zlib/deflate.h @@ -0,0 +1,318 @@ +/* deflate.h -- internal compression state + * Copyright (C) 1995-2002 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id: deflate.h,v 1.2 2002/03/13 17:45:56 xodnizel Exp $ */ + +#ifndef _DEFLATE_H +#define _DEFLATE_H + +#include "zutil.h" + +/* =========================================================================== + * Internal compression state. + */ + +#define LENGTH_CODES 29 +/* number of length codes, not counting the special END_BLOCK code */ + +#define LITERALS 256 +/* number of literal bytes 0..255 */ + +#define L_CODES (LITERALS+1+LENGTH_CODES) +/* number of Literal or Length codes, including the END_BLOCK code */ + +#define D_CODES 30 +/* number of distance codes */ + +#define BL_CODES 19 +/* number of codes used to transfer the bit lengths */ + +#define HEAP_SIZE (2*L_CODES+1) +/* maximum heap size */ + +#define MAX_BITS 15 +/* All codes must not exceed MAX_BITS bits */ + +#define INIT_STATE 42 +#define BUSY_STATE 113 +#define FINISH_STATE 666 +/* Stream status */ + + +/* Data structure describing a single value and its code string. */ +typedef struct ct_data_s { + union { + ush freq; /* frequency count */ + ush code; /* bit string */ + } fc; + union { + ush dad; /* father node in Huffman tree */ + ush len; /* length of bit string */ + } dl; +} FAR ct_data; + +#define Freq fc.freq +#define Code fc.code +#define Dad dl.dad +#define Len dl.len + +typedef struct static_tree_desc_s static_tree_desc; + +typedef struct tree_desc_s { + ct_data *dyn_tree; /* the dynamic tree */ + int max_code; /* largest code with non zero frequency */ + static_tree_desc *stat_desc; /* the corresponding static tree */ +} FAR tree_desc; + +typedef ush Pos; +typedef Pos FAR Posf; +typedef unsigned IPos; + +/* A Pos is an index in the character window. We use short instead of int to + * save space in the various tables. IPos is used only for parameter passing. + */ + +typedef struct internal_state { + z_streamp strm; /* pointer back to this zlib stream */ + int status; /* as the name implies */ + Bytef *pending_buf; /* output still pending */ + ulg pending_buf_size; /* size of pending_buf */ + Bytef *pending_out; /* next pending byte to output to the stream */ + int pending; /* nb of bytes in the pending buffer */ + int noheader; /* suppress zlib header and adler32 */ + Byte data_type; /* UNKNOWN, BINARY or ASCII */ + Byte method; /* STORED (for zip only) or DEFLATED */ + int last_flush; /* value of flush param for previous deflate call */ + + /* used by deflate.c: */ + + uInt w_size; /* LZ77 window size (32K by default) */ + uInt w_bits; /* log2(w_size) (8..16) */ + uInt w_mask; /* w_size - 1 */ + + Bytef *window; + /* Sliding window. Input bytes are read into the second half of the window, + * and move to the first half later to keep a dictionary of at least wSize + * bytes. With this organization, matches are limited to a distance of + * wSize-MAX_MATCH bytes, but this ensures that IO is always + * performed with a length multiple of the block size. Also, it limits + * the window size to 64K, which is quite useful on MSDOS. + * To do: use the user input buffer as sliding window. + */ + + ulg window_size; + /* Actual size of window: 2*wSize, except when the user input buffer + * is directly used as sliding window. + */ + + Posf *prev; + /* Link to older string with same hash index. To limit the size of this + * array to 64K, this link is maintained only for the last 32K strings. + * An index in this array is thus a window index modulo 32K. + */ + + Posf *head; /* Heads of the hash chains or NIL. */ + + uInt ins_h; /* hash index of string to be inserted */ + uInt hash_size; /* number of elements in hash table */ + uInt hash_bits; /* log2(hash_size) */ + uInt hash_mask; /* hash_size-1 */ + + uInt hash_shift; + /* Number of bits by which ins_h must be shifted at each input + * step. It must be such that after MIN_MATCH steps, the oldest + * byte no longer takes part in the hash key, that is: + * hash_shift * MIN_MATCH >= hash_bits + */ + + long block_start; + /* Window position at the beginning of the current output block. Gets + * negative when the window is moved backwards. + */ + + uInt match_length; /* length of best match */ + IPos prev_match; /* previous match */ + int match_available; /* set if previous match exists */ + uInt strstart; /* start of string to insert */ + uInt match_start; /* start of matching string */ + uInt lookahead; /* number of valid bytes ahead in window */ + + uInt prev_length; + /* Length of the best match at previous step. Matches not greater than this + * are discarded. This is used in the lazy match evaluation. + */ + + uInt max_chain_length; + /* To speed up deflation, hash chains are never searched beyond this + * length. A higher limit improves compression ratio but degrades the + * speed. + */ + + uInt max_lazy_match; + /* Attempt to find a better match only when the current match is strictly + * smaller than this value. This mechanism is used only for compression + * levels >= 4. + */ +# define max_insert_length max_lazy_match + /* Insert new strings in the hash table only if the match length is not + * greater than this length. This saves time but degrades compression. + * max_insert_length is used only for compression levels <= 3. + */ + + int level; /* compression level (1..9) */ + int strategy; /* favor or force Huffman coding*/ + + uInt good_match; + /* Use a faster search when the previous match is longer than this */ + + int nice_match; /* Stop searching when current match exceeds this */ + + /* used by trees.c: */ + /* Didn't use ct_data typedef below to supress compiler warning */ + struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ + struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ + struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ + + struct tree_desc_s l_desc; /* desc. for literal tree */ + struct tree_desc_s d_desc; /* desc. for distance tree */ + struct tree_desc_s bl_desc; /* desc. for bit length tree */ + + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ + int heap_len; /* number of elements in the heap */ + int heap_max; /* element of largest frequency */ + /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + * The same heap array is used to build all trees. + */ + + uch depth[2*L_CODES+1]; + /* Depth of each subtree used as tie breaker for trees of equal frequency + */ + + uchf *l_buf; /* buffer for literals or lengths */ + + uInt lit_bufsize; + /* Size of match buffer for literals/lengths. There are 4 reasons for + * limiting lit_bufsize to 64K: + * - frequencies can be kept in 16 bit counters + * - if compression is not successful for the first block, all input + * data is still in the window so we can still emit a stored block even + * when input comes from standard input. (This can also be done for + * all blocks if lit_bufsize is not greater than 32K.) + * - if compression is not successful for a file smaller than 64K, we can + * even emit a stored file instead of a stored block (saving 5 bytes). + * This is applicable only for zip (not gzip or zlib). + * - creating new Huffman trees less frequently may not provide fast + * adaptation to changes in the input data statistics. (Take for + * example a binary file with poorly compressible code followed by + * a highly compressible string table.) Smaller buffer sizes give + * fast adaptation but have of course the overhead of transmitting + * trees more frequently. + * - I can't count above 4 + */ + + uInt last_lit; /* running index in l_buf */ + + ushf *d_buf; + /* Buffer for distances. To simplify the code, d_buf and l_buf have + * the same number of elements. To use different lengths, an extra flag + * array would be necessary. + */ + + ulg opt_len; /* bit length of current block with optimal trees */ + ulg static_len; /* bit length of current block with static trees */ + uInt matches; /* number of string matches in current block */ + int last_eob_len; /* bit length of EOB code for last block */ + +#ifdef DEBUG + ulg compressed_len; /* total bit length of compressed file mod 2^32 */ + ulg bits_sent; /* bit length of compressed data sent mod 2^32 */ +#endif + + ush bi_buf; + /* Output buffer. bits are inserted starting at the bottom (least + * significant bits). + */ + int bi_valid; + /* Number of valid bits in bi_buf. All bits above the last valid bit + * are always zero. + */ + +} FAR deflate_state; + +/* Output a byte on the stream. + * IN assertion: there is enough room in pending_buf. + */ +#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);} + + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD) +/* In order to simplify the code, particularly on 16 bit machines, match + * distances are limited to MAX_DIST instead of WSIZE. + */ + + /* in trees.c */ +void _tr_init OF((deflate_state *s)); +int _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); +void _tr_flush_block OF((deflate_state *s, charf *buf, ulg stored_len, + int eof)); +void _tr_align OF((deflate_state *s)); +void _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len, + int eof)); + +#define d_code(dist) \ + ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]) +/* Mapping from a distance to a distance code. dist is the distance - 1 and + * must not have side effects. _dist_code[256] and _dist_code[257] are never + * used. + */ + +#ifndef DEBUG +/* Inline versions of _tr_tally for speed: */ + +#if defined(GEN_TREES_H) || !defined(STDC) + extern uch _length_code[]; + extern uch _dist_code[]; +#else + extern const uch _length_code[]; + extern const uch _dist_code[]; +#endif + +# define _tr_tally_lit(s, c, flush) \ + { uch cc = (c); \ + s->d_buf[s->last_lit] = 0; \ + s->l_buf[s->last_lit++] = cc; \ + s->dyn_ltree[cc].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +# define _tr_tally_dist(s, distance, length, flush) \ + { uch len = (length); \ + ush dist = (distance); \ + s->d_buf[s->last_lit] = dist; \ + s->l_buf[s->last_lit++] = len; \ + dist--; \ + s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ + s->dyn_dtree[d_code(dist)].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +#else +# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c) +# define _tr_tally_dist(s, distance, length, flush) \ + flush = _tr_tally(s, distance, length) +#endif + +#endif diff --git a/zlib/descrip.mms b/zlib/descrip.mms new file mode 100644 index 0000000..9d36459 --- /dev/null +++ b/zlib/descrip.mms @@ -0,0 +1,48 @@ +# descrip.mms: MMS description file for building zlib on VMS +# written by Martin P.J. Zinser + +cc_defs = +c_deb = + +.ifdef __DECC__ +pref = /prefix=all +.endif + +OBJS = adler32.obj, compress.obj, crc32.obj, gzio.obj, uncompr.obj,\ + deflate.obj, trees.obj, zutil.obj, inflate.obj, infblock.obj,\ + inftrees.obj, infcodes.obj, infutil.obj, inffast.obj + +CFLAGS= $(C_DEB) $(CC_DEFS) $(PREF) + +all : example.exe minigzip.exe + @ write sys$output " Example applications available" +libz.olb : libz.olb($(OBJS)) + @ write sys$output " libz available" + +example.exe : example.obj libz.olb + link example,libz.olb/lib + +minigzip.exe : minigzip.obj libz.olb + link minigzip,libz.olb/lib,x11vms:xvmsutils.olb/lib + +clean : + delete *.obj;*,libz.olb;* + + +# Other dependencies. +adler32.obj : zutil.h zlib.h zconf.h +compress.obj : zlib.h zconf.h +crc32.obj : zutil.h zlib.h zconf.h +deflate.obj : deflate.h zutil.h zlib.h zconf.h +example.obj : zlib.h zconf.h +gzio.obj : zutil.h zlib.h zconf.h +infblock.obj : zutil.h zlib.h zconf.h infblock.h inftrees.h infcodes.h infutil.h +infcodes.obj : zutil.h zlib.h zconf.h inftrees.h infutil.h infcodes.h inffast.h +inffast.obj : zutil.h zlib.h zconf.h inftrees.h infutil.h inffast.h +inflate.obj : zutil.h zlib.h zconf.h infblock.h +inftrees.obj : zutil.h zlib.h zconf.h inftrees.h +infutil.obj : zutil.h zlib.h zconf.h inftrees.h infutil.h +minigzip.obj : zlib.h zconf.h +trees.obj : deflate.h zutil.h zlib.h zconf.h +uncompr.obj : zlib.h zconf.h +zutil.obj : zutil.h zlib.h zconf.h diff --git a/zlib/example.c b/zlib/example.c new file mode 100644 index 0000000..af7f43e --- /dev/null +++ b/zlib/example.c @@ -0,0 +1,556 @@ +/* example.c -- usage example of the zlib compression library + * Copyright (C) 1995-2002 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id: example.c,v 1.2 2002/03/13 17:45:56 xodnizel Exp $ */ + +#include +#include "zlib.h" + +#ifdef STDC +# include +# include +#else + extern void exit OF((int)); +#endif + +#if defined(VMS) || defined(RISCOS) +# define TESTFILE "foo-gz" +#else +# define TESTFILE "foo.gz" +#endif + +#define CHECK_ERR(err, msg) { \ + if (err != Z_OK) { \ + fprintf(stderr, "%s error: %d\n", msg, err); \ + exit(1); \ + } \ +} + +const char hello[] = "hello, hello!"; +/* "hello world" would be more standard, but the repeated "hello" + * stresses the compression code better, sorry... + */ + +const char dictionary[] = "hello"; +uLong dictId; /* Adler32 value of the dictionary */ + +void test_compress OF((Byte *compr, uLong comprLen, + Byte *uncompr, uLong uncomprLen)); +void test_gzio OF((const char *out, const char *in, + Byte *uncompr, int uncomprLen)); +void test_deflate OF((Byte *compr, uLong comprLen)); +void test_inflate OF((Byte *compr, uLong comprLen, + Byte *uncompr, uLong uncomprLen)); +void test_large_deflate OF((Byte *compr, uLong comprLen, + Byte *uncompr, uLong uncomprLen)); +void test_large_inflate OF((Byte *compr, uLong comprLen, + Byte *uncompr, uLong uncomprLen)); +void test_flush OF((Byte *compr, uLong *comprLen)); +void test_sync OF((Byte *compr, uLong comprLen, + Byte *uncompr, uLong uncomprLen)); +void test_dict_deflate OF((Byte *compr, uLong comprLen)); +void test_dict_inflate OF((Byte *compr, uLong comprLen, + Byte *uncompr, uLong uncomprLen)); +int main OF((int argc, char *argv[])); + +/* =========================================================================== + * Test compress() and uncompress() + */ +void test_compress(compr, comprLen, uncompr, uncomprLen) + Byte *compr, *uncompr; + uLong comprLen, uncomprLen; +{ + int err; + uLong len = strlen(hello)+1; + + err = compress(compr, &comprLen, (const Bytef*)hello, len); + CHECK_ERR(err, "compress"); + + strcpy((char*)uncompr, "garbage"); + + err = uncompress(uncompr, &uncomprLen, compr, comprLen); + CHECK_ERR(err, "uncompress"); + + if (strcmp((char*)uncompr, hello)) { + fprintf(stderr, "bad uncompress\n"); + exit(1); + } else { + printf("uncompress(): %s\n", (char *)uncompr); + } +} + +/* =========================================================================== + * Test read/write of .gz files + */ +void test_gzio(out, in, uncompr, uncomprLen) + const char *out; /* compressed output file */ + const char *in; /* compressed input file */ + Byte *uncompr; + int uncomprLen; +{ + int err; + int len = strlen(hello)+1; + gzFile file; + z_off_t pos; + + file = gzopen(out, "wb"); + if (file == NULL) { + fprintf(stderr, "gzopen error\n"); + exit(1); + } + gzputc(file, 'h'); + if (gzputs(file, "ello") != 4) { + fprintf(stderr, "gzputs err: %s\n", gzerror(file, &err)); + exit(1); + } + if (gzprintf(file, ", %s!", "hello") != 8) { + fprintf(stderr, "gzprintf err: %s\n", gzerror(file, &err)); + exit(1); + } + gzseek(file, 1L, SEEK_CUR); /* add one zero byte */ + gzclose(file); + + file = gzopen(in, "rb"); + if (file == NULL) { + fprintf(stderr, "gzopen error\n"); + } + strcpy((char*)uncompr, "garbage"); + + uncomprLen = gzread(file, uncompr, (unsigned)uncomprLen); + if (uncomprLen != len) { + fprintf(stderr, "gzread err: %s\n", gzerror(file, &err)); + exit(1); + } + if (strcmp((char*)uncompr, hello)) { + fprintf(stderr, "bad gzread: %s\n", (char*)uncompr); + exit(1); + } else { + printf("gzread(): %s\n", (char *)uncompr); + } + + pos = gzseek(file, -8L, SEEK_CUR); + if (pos != 6 || gztell(file) != pos) { + fprintf(stderr, "gzseek error, pos=%ld, gztell=%ld\n", + (long)pos, (long)gztell(file)); + exit(1); + } + + if (gzgetc(file) != ' ') { + fprintf(stderr, "gzgetc error\n"); + exit(1); + } + + gzgets(file, (char*)uncompr, uncomprLen); + uncomprLen = strlen((char*)uncompr); + if (uncomprLen != 6) { /* "hello!" */ + fprintf(stderr, "gzgets err after gzseek: %s\n", gzerror(file, &err)); + exit(1); + } + if (strcmp((char*)uncompr, hello+7)) { + fprintf(stderr, "bad gzgets after gzseek\n"); + exit(1); + } else { + printf("gzgets() after gzseek: %s\n", (char *)uncompr); + } + + gzclose(file); +} + +/* =========================================================================== + * Test deflate() with small buffers + */ +void test_deflate(compr, comprLen) + Byte *compr; + uLong comprLen; +{ + z_stream c_stream; /* compression stream */ + int err; + int len = strlen(hello)+1; + + c_stream.zalloc = (alloc_func)0; + c_stream.zfree = (free_func)0; + c_stream.opaque = (voidpf)0; + + err = deflateInit(&c_stream, Z_DEFAULT_COMPRESSION); + CHECK_ERR(err, "deflateInit"); + + c_stream.next_in = (Bytef*)hello; + c_stream.next_out = compr; + + while (c_stream.total_in != (uLong)len && c_stream.total_out < comprLen) { + c_stream.avail_in = c_stream.avail_out = 1; /* force small buffers */ + err = deflate(&c_stream, Z_NO_FLUSH); + CHECK_ERR(err, "deflate"); + } + /* Finish the stream, still forcing small buffers: */ + for (;;) { + c_stream.avail_out = 1; + err = deflate(&c_stream, Z_FINISH); + if (err == Z_STREAM_END) break; + CHECK_ERR(err, "deflate"); + } + + err = deflateEnd(&c_stream); + CHECK_ERR(err, "deflateEnd"); +} + +/* =========================================================================== + * Test inflate() with small buffers + */ +void test_inflate(compr, comprLen, uncompr, uncomprLen) + Byte *compr, *uncompr; + uLong comprLen, uncomprLen; +{ + int err; + z_stream d_stream; /* decompression stream */ + + strcpy((char*)uncompr, "garbage"); + + d_stream.zalloc = (alloc_func)0; + d_stream.zfree = (free_func)0; + d_stream.opaque = (voidpf)0; + + d_stream.next_in = compr; + d_stream.avail_in = 0; + d_stream.next_out = uncompr; + + err = inflateInit(&d_stream); + CHECK_ERR(err, "inflateInit"); + + while (d_stream.total_out < uncomprLen && d_stream.total_in < comprLen) { + d_stream.avail_in = d_stream.avail_out = 1; /* force small buffers */ + err = inflate(&d_stream, Z_NO_FLUSH); + if (err == Z_STREAM_END) break; + CHECK_ERR(err, "inflate"); + } + + err = inflateEnd(&d_stream); + CHECK_ERR(err, "inflateEnd"); + + if (strcmp((char*)uncompr, hello)) { + fprintf(stderr, "bad inflate\n"); + exit(1); + } else { + printf("inflate(): %s\n", (char *)uncompr); + } +} + +/* =========================================================================== + * Test deflate() with large buffers and dynamic change of compression level + */ +void test_large_deflate(compr, comprLen, uncompr, uncomprLen) + Byte *compr, *uncompr; + uLong comprLen, uncomprLen; +{ + z_stream c_stream; /* compression stream */ + int err; + + c_stream.zalloc = (alloc_func)0; + c_stream.zfree = (free_func)0; + c_stream.opaque = (voidpf)0; + + err = deflateInit(&c_stream, Z_BEST_SPEED); + CHECK_ERR(err, "deflateInit"); + + c_stream.next_out = compr; + c_stream.avail_out = (uInt)comprLen; + + /* At this point, uncompr is still mostly zeroes, so it should compress + * very well: + */ + c_stream.next_in = uncompr; + c_stream.avail_in = (uInt)uncomprLen; + err = deflate(&c_stream, Z_NO_FLUSH); + CHECK_ERR(err, "deflate"); + if (c_stream.avail_in != 0) { + fprintf(stderr, "deflate not greedy\n"); + exit(1); + } + + /* Feed in already compressed data and switch to no compression: */ + deflateParams(&c_stream, Z_NO_COMPRESSION, Z_DEFAULT_STRATEGY); + c_stream.next_in = compr; + c_stream.avail_in = (uInt)comprLen/2; + err = deflate(&c_stream, Z_NO_FLUSH); + CHECK_ERR(err, "deflate"); + + /* Switch back to compressing mode: */ + deflateParams(&c_stream, Z_BEST_COMPRESSION, Z_FILTERED); + c_stream.next_in = uncompr; + c_stream.avail_in = (uInt)uncomprLen; + err = deflate(&c_stream, Z_NO_FLUSH); + CHECK_ERR(err, "deflate"); + + err = deflate(&c_stream, Z_FINISH); + if (err != Z_STREAM_END) { + fprintf(stderr, "deflate should report Z_STREAM_END\n"); + exit(1); + } + err = deflateEnd(&c_stream); + CHECK_ERR(err, "deflateEnd"); +} + +/* =========================================================================== + * Test inflate() with large buffers + */ +void test_large_inflate(compr, comprLen, uncompr, uncomprLen) + Byte *compr, *uncompr; + uLong comprLen, uncomprLen; +{ + int err; + z_stream d_stream; /* decompression stream */ + + strcpy((char*)uncompr, "garbage"); + + d_stream.zalloc = (alloc_func)0; + d_stream.zfree = (free_func)0; + d_stream.opaque = (voidpf)0; + + d_stream.next_in = compr; + d_stream.avail_in = (uInt)comprLen; + + err = inflateInit(&d_stream); + CHECK_ERR(err, "inflateInit"); + + for (;;) { + d_stream.next_out = uncompr; /* discard the output */ + d_stream.avail_out = (uInt)uncomprLen; + err = inflate(&d_stream, Z_NO_FLUSH); + if (err == Z_STREAM_END) break; + CHECK_ERR(err, "large inflate"); + } + + err = inflateEnd(&d_stream); + CHECK_ERR(err, "inflateEnd"); + + if (d_stream.total_out != 2*uncomprLen + comprLen/2) { + fprintf(stderr, "bad large inflate: %ld\n", d_stream.total_out); + exit(1); + } else { + printf("large_inflate(): OK\n"); + } +} + +/* =========================================================================== + * Test deflate() with full flush + */ +void test_flush(compr, comprLen) + Byte *compr; + uLong *comprLen; +{ + z_stream c_stream; /* compression stream */ + int err; + int len = strlen(hello)+1; + + c_stream.zalloc = (alloc_func)0; + c_stream.zfree = (free_func)0; + c_stream.opaque = (voidpf)0; + + err = deflateInit(&c_stream, Z_DEFAULT_COMPRESSION); + CHECK_ERR(err, "deflateInit"); + + c_stream.next_in = (Bytef*)hello; + c_stream.next_out = compr; + c_stream.avail_in = 3; + c_stream.avail_out = (uInt)*comprLen; + err = deflate(&c_stream, Z_FULL_FLUSH); + CHECK_ERR(err, "deflate"); + + compr[3]++; /* force an error in first compressed block */ + c_stream.avail_in = len - 3; + + err = deflate(&c_stream, Z_FINISH); + if (err != Z_STREAM_END) { + CHECK_ERR(err, "deflate"); + } + err = deflateEnd(&c_stream); + CHECK_ERR(err, "deflateEnd"); + + *comprLen = c_stream.total_out; +} + +/* =========================================================================== + * Test inflateSync() + */ +void test_sync(compr, comprLen, uncompr, uncomprLen) + Byte *compr, *uncompr; + uLong comprLen, uncomprLen; +{ + int err; + z_stream d_stream; /* decompression stream */ + + strcpy((char*)uncompr, "garbage"); + + d_stream.zalloc = (alloc_func)0; + d_stream.zfree = (free_func)0; + d_stream.opaque = (voidpf)0; + + d_stream.next_in = compr; + d_stream.avail_in = 2; /* just read the zlib header */ + + err = inflateInit(&d_stream); + CHECK_ERR(err, "inflateInit"); + + d_stream.next_out = uncompr; + d_stream.avail_out = (uInt)uncomprLen; + + inflate(&d_stream, Z_NO_FLUSH); + CHECK_ERR(err, "inflate"); + + d_stream.avail_in = (uInt)comprLen-2; /* read all compressed data */ + err = inflateSync(&d_stream); /* but skip the damaged part */ + CHECK_ERR(err, "inflateSync"); + + err = inflate(&d_stream, Z_FINISH); + if (err != Z_DATA_ERROR) { + fprintf(stderr, "inflate should report DATA_ERROR\n"); + /* Because of incorrect adler32 */ + exit(1); + } + err = inflateEnd(&d_stream); + CHECK_ERR(err, "inflateEnd"); + + printf("after inflateSync(): hel%s\n", (char *)uncompr); +} + +/* =========================================================================== + * Test deflate() with preset dictionary + */ +void test_dict_deflate(compr, comprLen) + Byte *compr; + uLong comprLen; +{ + z_stream c_stream; /* compression stream */ + int err; + + c_stream.zalloc = (alloc_func)0; + c_stream.zfree = (free_func)0; + c_stream.opaque = (voidpf)0; + + err = deflateInit(&c_stream, Z_BEST_COMPRESSION); + CHECK_ERR(err, "deflateInit"); + + err = deflateSetDictionary(&c_stream, + (const Bytef*)dictionary, sizeof(dictionary)); + CHECK_ERR(err, "deflateSetDictionary"); + + dictId = c_stream.adler; + c_stream.next_out = compr; + c_stream.avail_out = (uInt)comprLen; + + c_stream.next_in = (Bytef*)hello; + c_stream.avail_in = (uInt)strlen(hello)+1; + + err = deflate(&c_stream, Z_FINISH); + if (err != Z_STREAM_END) { + fprintf(stderr, "deflate should report Z_STREAM_END\n"); + exit(1); + } + err = deflateEnd(&c_stream); + CHECK_ERR(err, "deflateEnd"); +} + +/* =========================================================================== + * Test inflate() with a preset dictionary + */ +void test_dict_inflate(compr, comprLen, uncompr, uncomprLen) + Byte *compr, *uncompr; + uLong comprLen, uncomprLen; +{ + int err; + z_stream d_stream; /* decompression stream */ + + strcpy((char*)uncompr, "garbage"); + + d_stream.zalloc = (alloc_func)0; + d_stream.zfree = (free_func)0; + d_stream.opaque = (voidpf)0; + + d_stream.next_in = compr; + d_stream.avail_in = (uInt)comprLen; + + err = inflateInit(&d_stream); + CHECK_ERR(err, "inflateInit"); + + d_stream.next_out = uncompr; + d_stream.avail_out = (uInt)uncomprLen; + + for (;;) { + err = inflate(&d_stream, Z_NO_FLUSH); + if (err == Z_STREAM_END) break; + if (err == Z_NEED_DICT) { + if (d_stream.adler != dictId) { + fprintf(stderr, "unexpected dictionary"); + exit(1); + } + err = inflateSetDictionary(&d_stream, (const Bytef*)dictionary, + sizeof(dictionary)); + } + CHECK_ERR(err, "inflate with dict"); + } + + err = inflateEnd(&d_stream); + CHECK_ERR(err, "inflateEnd"); + + if (strcmp((char*)uncompr, hello)) { + fprintf(stderr, "bad inflate with dict\n"); + exit(1); + } else { + printf("inflate with dictionary: %s\n", (char *)uncompr); + } +} + +/* =========================================================================== + * Usage: example [output.gz [input.gz]] + */ + +int main(argc, argv) + int argc; + char *argv[]; +{ + Byte *compr, *uncompr; + uLong comprLen = 10000*sizeof(int); /* don't overflow on MSDOS */ + uLong uncomprLen = comprLen; + static const char* myVersion = ZLIB_VERSION; + + if (zlibVersion()[0] != myVersion[0]) { + fprintf(stderr, "incompatible zlib version\n"); + exit(1); + + } else if (strcmp(zlibVersion(), ZLIB_VERSION) != 0) { + fprintf(stderr, "warning: different zlib version\n"); + } + + compr = (Byte*)calloc((uInt)comprLen, 1); + uncompr = (Byte*)calloc((uInt)uncomprLen, 1); + /* compr and uncompr are cleared to avoid reading uninitialized + * data and to ensure that uncompr compresses well. + */ + if (compr == Z_NULL || uncompr == Z_NULL) { + printf("out of memory\n"); + exit(1); + } + test_compress(compr, comprLen, uncompr, uncomprLen); + + test_gzio((argc > 1 ? argv[1] : TESTFILE), + (argc > 2 ? argv[2] : TESTFILE), + uncompr, (int)uncomprLen); + + test_deflate(compr, comprLen); + test_inflate(compr, comprLen, uncompr, uncomprLen); + + test_large_deflate(compr, comprLen, uncompr, uncomprLen); + test_large_inflate(compr, comprLen, uncompr, uncomprLen); + + test_flush(compr, &comprLen); + test_sync(compr, comprLen, uncompr, uncomprLen); + comprLen = uncomprLen; + + test_dict_deflate(compr, comprLen); + test_dict_inflate(compr, comprLen, uncompr, uncomprLen); + + exit(0); + return 0; /* to avoid warning */ +} diff --git a/zlib/faq b/zlib/faq new file mode 100644 index 0000000..0feb6d3 --- /dev/null +++ b/zlib/faq @@ -0,0 +1,72 @@ + + Frequently Asked Questions about zlib + + +If your question is not there, please check the zlib home page +http://www.cdrom.com/pub/infozip/zlib/ which may have more recent information. + + +1) I need a Windows DLL +2) I need a Visual Basic interface to zlib +3) compress() returns Z_BUF_ERROR +4) deflate or inflate returns Z_BUF_ERROR +5) Where is the zlib documentation (man pages, etc...)? +6) Why don't you use GNU autoconf, libtool, etc...? +7) There is a bug in zlib. +8) I get "undefined reference to gzputc" + + + +1) I need a Windows DLL + + The zlib sources can be compiled without change to produce a DLL. + If you want a precompiled DLL, see http://www.winimage.com/zLibDll + + +2) I need a Visual Basic interface to zlib + + See http://www.tcfb.com/dowseware/cmp-z-it.zip + http://web2.airmail.net/markn/articles/zlibtool/zlibtool.htm + and contrib/visual-basic.txt + +3) compress() returns Z_BUF_ERROR + + Make sure that before the call of compress, the length of the + compressed buffer is equal to the total size of the compressed buffer + and not zero. For Visual Basic, check that this parameter is passed + by reference ("as any"), not by value ("as long"). + + +4) deflate or inflate returns Z_BUF_ERROR + + Make sure that before the call avail_in and avail_out are not zero. + + +5) Where is the zlib documentation (man pages, etc...)? + + It's in zlib.h for the moment. Volunteers to transform this + to man pages, please contact jloup@gzip.org. Examples of zlib usage + are in the files example.c and minigzip.c. + + +6) Why don't you use GNU autoconf, libtool, etc...? + + Because we would like to keep zlib as a very small and simple package. + zlib is rather portable and doesn't need much configuration. + + +7) There is a bug in zlib. + + Most of the time, such problems are due to an incorrect usage + of zlib. Please try to reproduce the problem with a small + program and send us the corresponding source at zlib@quest.jpl.nasa.gov + Do not send multi-megabyte data files without prior agreement. + + +8) I get "undefined reference to gzputc" + + If "make test" produces something like + example.o(.text+0x174): + check that you don't have old files libz.* in /usr/lib, /usr/local/lib + or /usr/X11R6/lib. Remove old versions then do "make install". + diff --git a/zlib/gzio.c b/zlib/gzio.c new file mode 100644 index 0000000..6142b0a --- /dev/null +++ b/zlib/gzio.c @@ -0,0 +1,875 @@ +/* gzio.c -- IO on .gz files + * Copyright (C) 1995-2002 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Compile this file with -DNO_DEFLATE to avoid the compression code. + */ + +/* @(#) $Id: gzio.c,v 1.2 2002/03/13 17:45:56 xodnizel Exp $ */ + +#include + +#include "zutil.h" + +struct internal_state {int dummy;}; /* for buggy compilers */ + +#ifndef Z_BUFSIZE +# ifdef MAXSEG_64K +# define Z_BUFSIZE 4096 /* minimize memory usage for 16-bit DOS */ +# else +# define Z_BUFSIZE 16384 +# endif +#endif +#ifndef Z_PRINTF_BUFSIZE +# define Z_PRINTF_BUFSIZE 4096 +#endif + +#define ALLOC(size) malloc(size) +#define TRYFREE(p) {if (p) free(p);} + +static int gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define RESERVED 0xE0 /* bits 5..7: reserved */ + +typedef struct gz_stream { + z_stream stream; + int z_err; /* error code for last stream operation */ + int z_eof; /* set if end of input file */ + FILE *file; /* .gz file */ + Byte *inbuf; /* input buffer */ + Byte *outbuf; /* output buffer */ + uLong crc; /* crc32 of uncompressed data */ + char *msg; /* error message */ + char *path; /* path name for debugging only */ + int transparent; /* 1 if input file is not a .gz file */ + char mode; /* 'w' or 'r' */ + long startpos; /* start of compressed data in file (header skipped) */ +} gz_stream; + + +local gzFile gz_open OF((const char *path, const char *mode, int fd)); +local int do_flush OF((gzFile file, int flush)); +local int get_byte OF((gz_stream *s)); +local void check_header OF((gz_stream *s)); +local int destroy OF((gz_stream *s)); +local void putLong OF((FILE *file, uLong x)); +local uLong getLong OF((gz_stream *s)); + +/* =========================================================================== + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb"). The file is given either by file descriptor + or path name (if fd == -1). + gz_open return NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). +*/ +local gzFile gz_open (path, mode, fd) + const char *path; + const char *mode; + int fd; +{ + int err; + int level = Z_DEFAULT_COMPRESSION; /* compression level */ + int strategy = Z_DEFAULT_STRATEGY; /* compression strategy */ + char *p = (char*)mode; + gz_stream *s; + char fmode[80]; /* copy of mode, without the compression level */ + char *m = fmode; + + if (!path || !mode) return Z_NULL; + + s = (gz_stream *)ALLOC(sizeof(gz_stream)); + if (!s) return Z_NULL; + + s->stream.zalloc = (alloc_func)0; + s->stream.zfree = (free_func)0; + s->stream.opaque = (voidpf)0; + s->stream.next_in = s->inbuf = Z_NULL; + s->stream.next_out = s->outbuf = Z_NULL; + s->stream.avail_in = s->stream.avail_out = 0; + s->file = NULL; + s->z_err = Z_OK; + s->z_eof = 0; + s->crc = crc32(0L, Z_NULL, 0); + s->msg = NULL; + s->transparent = 0; + + s->path = (char*)ALLOC(strlen(path)+1); + if (s->path == NULL) { + return destroy(s), (gzFile)Z_NULL; + } + strcpy(s->path, path); /* do this early for debugging */ + + s->mode = '\0'; + do { + if (*p == 'r') s->mode = 'r'; + if (*p == 'w' || *p == 'a') s->mode = 'w'; + if (*p >= '0' && *p <= '9') { + level = *p - '0'; + } else if (*p == 'f') { + strategy = Z_FILTERED; + } else if (*p == 'h') { + strategy = Z_HUFFMAN_ONLY; + } else { + *m++ = *p; /* copy the mode */ + } + } while (*p++ && m != fmode + sizeof(fmode)); + if (s->mode == '\0') return destroy(s), (gzFile)Z_NULL; + + if (s->mode == 'w') { +#ifdef NO_DEFLATE + err = Z_STREAM_ERROR; +#else + err = deflateInit2(&(s->stream), level, + Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, strategy); + /* windowBits is passed < 0 to suppress zlib header */ + + s->stream.next_out = s->outbuf = (Byte*)ALLOC(Z_BUFSIZE); +#endif + if (err != Z_OK || s->outbuf == Z_NULL) { + return destroy(s), (gzFile)Z_NULL; + } + } else { + s->stream.next_in = s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); + + err = inflateInit2(&(s->stream), -MAX_WBITS); + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are + * present after the compressed stream. + */ + if (err != Z_OK || s->inbuf == Z_NULL) { + return destroy(s), (gzFile)Z_NULL; + } + } + s->stream.avail_out = Z_BUFSIZE; + + errno = 0; + s->file = fd < 0 ? F_OPEN(path, fmode) : (FILE*)fdopen(fd, fmode); + + if (s->file == NULL) { + return destroy(s), (gzFile)Z_NULL; + } + if (s->mode == 'w') { + /* Write a very simple .gz header: + */ + fprintf(s->file, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1], + Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/, OS_CODE); + s->startpos = 10L; + /* We use 10L instead of ftell(s->file) to because ftell causes an + * fflush on some systems. This version of the library doesn't use + * startpos anyway in write mode, so this initialization is not + * necessary. + */ + } else { + check_header(s); /* skip the .gz header */ + s->startpos = (ftell(s->file) - s->stream.avail_in); + } + + return (gzFile)s; +} + +/* =========================================================================== + Opens a gzip (.gz) file for reading or writing. +*/ +gzFile ZEXPORT gzopen (path, mode) + const char *path; + const char *mode; +{ + return gz_open (path, mode, -1); +} + +/* =========================================================================== + Associate a gzFile with the file descriptor fd. fd is not dup'ed here + to mimic the behavio(u)r of fdopen. +*/ +gzFile ZEXPORT gzdopen (fd, mode) + int fd; + const char *mode; +{ + char name[20]; + + if (fd < 0) return (gzFile)Z_NULL; + sprintf(name, "", fd); /* for debugging */ + + return gz_open (name, mode, fd); +} + +/* =========================================================================== + * Update the compression level and strategy + */ +int ZEXPORT gzsetparams (file, level, strategy) + gzFile file; + int level; + int strategy; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + /* Make room to allow flushing */ + if (s->stream.avail_out == 0) { + + s->stream.next_out = s->outbuf; + if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) { + s->z_err = Z_ERRNO; + } + s->stream.avail_out = Z_BUFSIZE; + } + + return deflateParams (&(s->stream), level, strategy); +} + +/* =========================================================================== + Read a byte from a gz_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. +*/ +local int get_byte(s) + gz_stream *s; +{ + if (s->z_eof) return EOF; + if (s->stream.avail_in == 0) { + errno = 0; + s->stream.avail_in = fread(s->inbuf, 1, Z_BUFSIZE, s->file); + if (s->stream.avail_in == 0) { + s->z_eof = 1; + if (ferror(s->file)) s->z_err = Z_ERRNO; + return EOF; + } + s->stream.next_in = s->inbuf; + } + s->stream.avail_in--; + return *(s->stream.next_in)++; +} + +/* =========================================================================== + Check the gzip header of a gz_stream opened for reading. Set the stream + mode to transparent if the gzip magic header is not present; set s->err + to Z_DATA_ERROR if the magic header is present but the rest of the header + is incorrect. + IN assertion: the stream s has already been created sucessfully; + s->stream.avail_in is zero for the first time, but may be non-zero + for concatenated .gz files. +*/ +local void check_header(s) + gz_stream *s; +{ + int method; /* method byte */ + int flags; /* flags byte */ + uInt len; + int c; + + /* Check the gzip magic header */ + for (len = 0; len < 2; len++) { + c = get_byte(s); + if (c != gz_magic[len]) { + if (len != 0) s->stream.avail_in++, s->stream.next_in--; + if (c != EOF) { + s->stream.avail_in++, s->stream.next_in--; + s->transparent = 1; + } + s->z_err = s->stream.avail_in != 0 ? Z_OK : Z_STREAM_END; + return; + } + } + method = get_byte(s); + flags = get_byte(s); + if (method != Z_DEFLATED || (flags & RESERVED) != 0) { + s->z_err = Z_DATA_ERROR; + return; + } + + /* Discard time, xflags and OS code: */ + for (len = 0; len < 6; len++) (void)get_byte(s); + + if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */ + len = (uInt)get_byte(s); + len += ((uInt)get_byte(s))<<8; + /* len is garbage if EOF but the loop below will quit anyway */ + while (len-- != 0 && get_byte(s) != EOF) ; + } + if ((flags & ORIG_NAME) != 0) { /* skip the original file name */ + while ((c = get_byte(s)) != 0 && c != EOF) ; + } + if ((flags & COMMENT) != 0) { /* skip the .gz file comment */ + while ((c = get_byte(s)) != 0 && c != EOF) ; + } + if ((flags & HEAD_CRC) != 0) { /* skip the header crc */ + for (len = 0; len < 2; len++) (void)get_byte(s); + } + s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK; +} + + /* =========================================================================== + * Cleanup then free the given gz_stream. Return a zlib error code. + Try freeing in the reverse order of allocations. + */ +local int destroy (s) + gz_stream *s; +{ + int err = Z_OK; + + if (!s) return Z_STREAM_ERROR; + + TRYFREE(s->msg); + + if (s->stream.state != NULL) { + if (s->mode == 'w') { +#ifdef NO_DEFLATE + err = Z_STREAM_ERROR; +#else + err = deflateEnd(&(s->stream)); +#endif + } else if (s->mode == 'r') { + err = inflateEnd(&(s->stream)); + } + } + if (s->file != NULL && fclose(s->file)) { +#ifdef ESPIPE + if (errno != ESPIPE) /* fclose is broken for pipes in HP/UX */ +#endif + err = Z_ERRNO; + } + if (s->z_err < 0) err = s->z_err; + + TRYFREE(s->inbuf); + TRYFREE(s->outbuf); + TRYFREE(s->path); + TRYFREE(s); + return err; +} + +/* =========================================================================== + Reads the given number of uncompressed bytes from the compressed file. + gzread returns the number of bytes actually read (0 for end of file). +*/ +int ZEXPORT gzread (file, buf, len) + gzFile file; + voidp buf; + unsigned len; +{ + gz_stream *s = (gz_stream*)file; + Bytef *start = (Bytef*)buf; /* starting point for crc computation */ + Byte *next_out; /* == stream.next_out but not forced far (for MSDOS) */ + + if (s == NULL || s->mode != 'r') return Z_STREAM_ERROR; + + if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) return -1; + if (s->z_err == Z_STREAM_END) return 0; /* EOF */ + + next_out = (Byte*)buf; + s->stream.next_out = (Bytef*)buf; + s->stream.avail_out = len; + + while (s->stream.avail_out != 0) { + + if (s->transparent) { + /* Copy first the lookahead bytes: */ + uInt n = s->stream.avail_in; + if (n > s->stream.avail_out) n = s->stream.avail_out; + if (n > 0) { + zmemcpy(s->stream.next_out, s->stream.next_in, n); + next_out += n; + s->stream.next_out = next_out; + s->stream.next_in += n; + s->stream.avail_out -= n; + s->stream.avail_in -= n; + } + if (s->stream.avail_out > 0) { + s->stream.avail_out -= fread(next_out, 1, s->stream.avail_out, + s->file); + } + len -= s->stream.avail_out; + s->stream.total_in += (uLong)len; + s->stream.total_out += (uLong)len; + if (len == 0) s->z_eof = 1; + return (int)len; + } + if (s->stream.avail_in == 0 && !s->z_eof) { + + errno = 0; + s->stream.avail_in = fread(s->inbuf, 1, Z_BUFSIZE, s->file); + if (s->stream.avail_in == 0) { + s->z_eof = 1; + if (ferror(s->file)) { + s->z_err = Z_ERRNO; + break; + } + } + s->stream.next_in = s->inbuf; + } + s->z_err = inflate(&(s->stream), Z_NO_FLUSH); + + if (s->z_err == Z_STREAM_END) { + /* Check CRC and original size */ + s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); + start = s->stream.next_out; + + if (getLong(s) != s->crc) { + s->z_err = Z_DATA_ERROR; + } else { + (void)getLong(s); + /* The uncompressed length returned by above getlong() may + * be different from s->stream.total_out) in case of + * concatenated .gz files. Check for such files: + */ + check_header(s); + if (s->z_err == Z_OK) { + uLong total_in = s->stream.total_in; + uLong total_out = s->stream.total_out; + + inflateReset(&(s->stream)); + s->stream.total_in = total_in; + s->stream.total_out = total_out; + s->crc = crc32(0L, Z_NULL, 0); + } + } + } + if (s->z_err != Z_OK || s->z_eof) break; + } + s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); + + return (int)(len - s->stream.avail_out); +} + + +/* =========================================================================== + Reads one byte from the compressed file. gzgetc returns this byte + or -1 in case of end of file or error. +*/ +int ZEXPORT gzgetc(file) + gzFile file; +{ + unsigned char c; + + return gzread(file, &c, 1) == 1 ? c : -1; +} + + +/* =========================================================================== + Reads bytes from the compressed file until len-1 characters are + read, or a newline character is read and transferred to buf, or an + end-of-file condition is encountered. The string is then terminated + with a null character. + gzgets returns buf, or Z_NULL in case of error. + + The current implementation is not optimized at all. +*/ +char * ZEXPORT gzgets(file, buf, len) + gzFile file; + char *buf; + int len; +{ + char *b = buf; + if (buf == Z_NULL || len <= 0) return Z_NULL; + + while (--len > 0 && gzread(file, buf, 1) == 1 && *buf++ != '\n') ; + *buf = '\0'; + return b == buf && len > 0 ? Z_NULL : b; +} + + +#ifndef NO_DEFLATE +/* =========================================================================== + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of bytes actually written (0 in case of error). +*/ +int ZEXPORT gzwrite (file, buf, len) + gzFile file; + const voidp buf; + unsigned len; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + s->stream.next_in = (Bytef*)buf; + s->stream.avail_in = len; + + while (s->stream.avail_in != 0) { + + if (s->stream.avail_out == 0) { + + s->stream.next_out = s->outbuf; + if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) { + s->z_err = Z_ERRNO; + break; + } + s->stream.avail_out = Z_BUFSIZE; + } + s->z_err = deflate(&(s->stream), Z_NO_FLUSH); + if (s->z_err != Z_OK) break; + } + s->crc = crc32(s->crc, (const Bytef *)buf, len); + + return (int)(len - s->stream.avail_in); +} + +/* =========================================================================== + Converts, formats, and writes the args to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written (0 in case of error). +*/ +#ifdef STDC +#include + +int ZEXPORTVA gzprintf (gzFile file, const char *format, /* args */ ...) +{ + char buf[Z_PRINTF_BUFSIZE]; + va_list va; + int len; + + va_start(va, format); +#ifdef HAS_vsnprintf + (void)vsnprintf(buf, sizeof(buf), format, va); +#else + (void)vsprintf(buf, format, va); +#endif + va_end(va); + len = strlen(buf); /* some *sprintf don't return the nb of bytes written */ + if (len <= 0) return 0; + + return gzwrite(file, buf, (unsigned)len); +} +#else /* not ANSI C */ + +int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + gzFile file; + const char *format; + int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20; +{ + char buf[Z_PRINTF_BUFSIZE]; + int len; + +#ifdef HAS_snprintf + snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +#else + sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +#endif + len = strlen(buf); /* old sprintf doesn't return the nb of bytes written */ + if (len <= 0) return 0; + + return gzwrite(file, buf, len); +} +#endif + +/* =========================================================================== + Writes c, converted to an unsigned char, into the compressed file. + gzputc returns the value that was written, or -1 in case of error. +*/ +int ZEXPORT gzputc(file, c) + gzFile file; + int c; +{ + unsigned char cc = (unsigned char) c; /* required for big endian systems */ + + return gzwrite(file, &cc, 1) == 1 ? (int)cc : -1; +} + + +/* =========================================================================== + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + gzputs returns the number of characters written, or -1 in case of error. +*/ +int ZEXPORT gzputs(file, s) + gzFile file; + const char *s; +{ + return gzwrite(file, (char*)s, (unsigned)strlen(s)); +} + + +/* =========================================================================== + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. +*/ +local int do_flush (file, flush) + gzFile file; + int flush; +{ + uInt len; + int done = 0; + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + s->stream.avail_in = 0; /* should be zero already anyway */ + + for (;;) { + len = Z_BUFSIZE - s->stream.avail_out; + + if (len != 0) { + if ((uInt)fwrite(s->outbuf, 1, len, s->file) != len) { + s->z_err = Z_ERRNO; + return Z_ERRNO; + } + s->stream.next_out = s->outbuf; + s->stream.avail_out = Z_BUFSIZE; + } + if (done) break; + s->z_err = deflate(&(s->stream), flush); + + /* Ignore the second of two consecutive flushes: */ + if (len == 0 && s->z_err == Z_BUF_ERROR) s->z_err = Z_OK; + + /* deflate has finished flushing only when it hasn't used up + * all the available space in the output buffer: + */ + done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END); + + if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break; + } + return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; +} + +int ZEXPORT gzflush (file, flush) + gzFile file; + int flush; +{ + gz_stream *s = (gz_stream*)file; + int err = do_flush (file, flush); + + if (err) return err; + fflush(s->file); + return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; +} +#endif /* NO_DEFLATE */ + +/* =========================================================================== + Sets the starting position for the next gzread or gzwrite on the given + compressed file. The offset represents a number of bytes in the + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error. + SEEK_END is not implemented, returns error. + In this version of the library, gzseek can be extremely slow. +*/ +z_off_t ZEXPORT gzseek (file, offset, whence) + gzFile file; + z_off_t offset; + int whence; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || whence == SEEK_END || + s->z_err == Z_ERRNO || s->z_err == Z_DATA_ERROR) { + return -1L; + } + + if (s->mode == 'w') { +#ifdef NO_DEFLATE + return -1L; +#else + if (whence == SEEK_SET) { + offset -= s->stream.total_in; + } + if (offset < 0) return -1L; + + /* At this point, offset is the number of zero bytes to write. */ + if (s->inbuf == Z_NULL) { + s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); /* for seeking */ + zmemzero(s->inbuf, Z_BUFSIZE); + } + while (offset > 0) { + uInt size = Z_BUFSIZE; + if (offset < Z_BUFSIZE) size = (uInt)offset; + + size = gzwrite(file, s->inbuf, size); + if (size == 0) return -1L; + + offset -= size; + } + return (z_off_t)s->stream.total_in; +#endif + } + /* Rest of function is for reading only */ + + /* compute absolute position */ + if (whence == SEEK_CUR) { + offset += s->stream.total_out; + } + if (offset < 0) return -1L; + + if (s->transparent) { + /* map to fseek */ + s->stream.avail_in = 0; + s->stream.next_in = s->inbuf; + if (fseek(s->file, offset, SEEK_SET) < 0) return -1L; + + s->stream.total_in = s->stream.total_out = (uLong)offset; + return offset; + } + + /* For a negative seek, rewind and use positive seek */ + if ((uLong)offset >= s->stream.total_out) { + offset -= s->stream.total_out; + } else if (gzrewind(file) < 0) { + return -1L; + } + /* offset is now the number of bytes to skip. */ + + if (offset != 0 && s->outbuf == Z_NULL) { + s->outbuf = (Byte*)ALLOC(Z_BUFSIZE); + } + while (offset > 0) { + int size = Z_BUFSIZE; + if (offset < Z_BUFSIZE) size = (int)offset; + + size = gzread(file, s->outbuf, (uInt)size); + if (size <= 0) return -1L; + offset -= size; + } + return (z_off_t)s->stream.total_out; +} + +/* =========================================================================== + Rewinds input file. +*/ +int ZEXPORT gzrewind (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r') return -1; + + s->z_err = Z_OK; + s->z_eof = 0; + s->stream.avail_in = 0; + s->stream.next_in = s->inbuf; + s->crc = crc32(0L, Z_NULL, 0); + + if (s->startpos == 0) { /* not a compressed file */ + rewind(s->file); + return 0; + } + + (void) inflateReset(&s->stream); + return fseek(s->file, s->startpos, SEEK_SET); +} + +/* =========================================================================== + Returns the starting position for the next gzread or gzwrite on the + given compressed file. This position represents a number of bytes in the + uncompressed data stream. +*/ +z_off_t ZEXPORT gztell (file) + gzFile file; +{ + return gzseek(file, 0L, SEEK_CUR); +} + +/* =========================================================================== + Returns 1 when EOF has previously been detected reading the given + input stream, otherwise zero. +*/ +int ZEXPORT gzeof (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + return (s == NULL || s->mode != 'r') ? 0 : s->z_eof; +} + +/* =========================================================================== + Outputs a long in LSB order to the given file +*/ +local void putLong (file, x) + FILE *file; + uLong x; +{ + int n; + for (n = 0; n < 4; n++) { + fputc((int)(x & 0xff), file); + x >>= 8; + } +} + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets z_err in case + of error. +*/ +local uLong getLong (s) + gz_stream *s; +{ + uLong x = (uLong)get_byte(s); + int c; + + x += ((uLong)get_byte(s))<<8; + x += ((uLong)get_byte(s))<<16; + c = get_byte(s); + if (c == EOF) s->z_err = Z_DATA_ERROR; + x += ((uLong)c)<<24; + return x; +} + +/* =========================================================================== + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. +*/ +int ZEXPORT gzclose (file) + gzFile file; +{ + int err; + gz_stream *s = (gz_stream*)file; + + if (s == NULL) return Z_STREAM_ERROR; + + if (s->mode == 'w') { +#ifdef NO_DEFLATE + return Z_STREAM_ERROR; +#else + err = do_flush (file, Z_FINISH); + if (err != Z_OK) return destroy((gz_stream*)file); + + putLong (s->file, s->crc); + putLong (s->file, s->stream.total_in); +#endif + } + return destroy((gz_stream*)file); +} + +/* =========================================================================== + Returns the error message for the last error which occured on the + given compressed file. errnum is set to zlib error number. If an + error occured in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ +const char* ZEXPORT gzerror (file, errnum) + gzFile file; + int *errnum; +{ + char *m; + gz_stream *s = (gz_stream*)file; + + if (s == NULL) { + *errnum = Z_STREAM_ERROR; + return (const char*)ERR_MSG(Z_STREAM_ERROR); + } + *errnum = s->z_err; + if (*errnum == Z_OK) return (const char*)""; + + m = (char*)(*errnum == Z_ERRNO ? zstrerror(errno) : s->stream.msg); + + if (m == NULL || *m == '\0') m = (char*)ERR_MSG(s->z_err); + + TRYFREE(s->msg); + s->msg = (char*)ALLOC(strlen(s->path) + strlen(m) + 3); + strcpy(s->msg, s->path); + strcat(s->msg, ": "); + strcat(s->msg, m); + return (const char*)s->msg; +} diff --git a/zlib/infblock.c b/zlib/infblock.c new file mode 100644 index 0000000..dd7a6d4 --- /dev/null +++ b/zlib/infblock.c @@ -0,0 +1,403 @@ +/* infblock.c -- interpret and process block types to last block + * Copyright (C) 1995-2002 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "infblock.h" +#include "inftrees.h" +#include "infcodes.h" +#include "infutil.h" + +struct inflate_codes_state {int dummy;}; /* for buggy compilers */ + +/* simplify the use of the inflate_huft type with some defines */ +#define exop word.what.Exop +#define bits word.what.Bits + +/* Table for deflate from PKZIP's appnote.txt. */ +local const uInt border[] = { /* Order of the bit length code lengths */ + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + +/* + Notes beyond the 1.93a appnote.txt: + + 1. Distance pointers never point before the beginning of the output + stream. + 2. Distance pointers can point back across blocks, up to 32k away. + 3. There is an implied maximum of 7 bits for the bit length table and + 15 bits for the actual data. + 4. If only one code exists, then it is encoded using one bit. (Zero + would be more efficient, but perhaps a little confusing.) If two + codes exist, they are coded using one bit each (0 and 1). + 5. There is no way of sending zero distance codes--a dummy must be + sent if there are none. (History: a pre 2.0 version of PKZIP would + store blocks with no distance codes, but this was discovered to be + too harsh a criterion.) Valid only for 1.93a. 2.04c does allow + zero distance codes, which is sent as one code of zero bits in + length. + 6. There are up to 286 literal/length codes. Code 256 represents the + end-of-block. Note however that the static length tree defines + 288 codes just to fill out the Huffman codes. Codes 286 and 287 + cannot be used though, since there is no length base or extra bits + defined for them. Similarily, there are up to 30 distance codes. + However, static trees define 32 codes (all 5 bits) to fill out the + Huffman codes, but the last two had better not show up in the data. + 7. Unzip can check dynamic Huffman blocks for complete code sets. + The exception is that a single code would not be complete (see #4). + 8. The five bits following the block type is really the number of + literal codes sent minus 257. + 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits + (1+6+6). Therefore, to output three times the length, you output + three codes (1+1+1), whereas to output four times the same length, + you only need two codes (1+3). Hmm. + 10. In the tree reconstruction algorithm, Code = Code + Increment + only if BitLength(i) is not zero. (Pretty obvious.) + 11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19) + 12. Note: length code 284 can represent 227-258, but length code 285 + really is 258. The last length deserves its own, short code + since it gets used a lot in very redundant files. The length + 258 is special since 258 - 3 (the min match length) is 255. + 13. The literal/length and distance code bit lengths are read as a + single stream of lengths. It is possible (and advantageous) for + a repeat code (16, 17, or 18) to go across the boundary between + the two sets of lengths. + */ + + +void inflate_blocks_reset(s, z, c) +inflate_blocks_statef *s; +z_streamp z; +uLongf *c; +{ + if (c != Z_NULL) + *c = s->check; + if (s->mode == BTREE || s->mode == DTREE) + ZFREE(z, s->sub.trees.blens); + if (s->mode == CODES) + inflate_codes_free(s->sub.decode.codes, z); + s->mode = TYPE; + s->bitk = 0; + s->bitb = 0; + s->read = s->write = s->window; + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(0L, (const Bytef *)Z_NULL, 0); + Tracev((stderr, "inflate: blocks reset\n")); +} + + +inflate_blocks_statef *inflate_blocks_new(z, c, w) +z_streamp z; +check_func c; +uInt w; +{ + inflate_blocks_statef *s; + + if ((s = (inflate_blocks_statef *)ZALLOC + (z,1,sizeof(struct inflate_blocks_state))) == Z_NULL) + return s; + if ((s->hufts = + (inflate_huft *)ZALLOC(z, sizeof(inflate_huft), MANY)) == Z_NULL) + { + ZFREE(z, s); + return Z_NULL; + } + if ((s->window = (Bytef *)ZALLOC(z, 1, w)) == Z_NULL) + { + ZFREE(z, s->hufts); + ZFREE(z, s); + return Z_NULL; + } + s->end = s->window + w; + s->checkfn = c; + s->mode = TYPE; + Tracev((stderr, "inflate: blocks allocated\n")); + inflate_blocks_reset(s, z, Z_NULL); + return s; +} + + +int inflate_blocks(s, z, r) +inflate_blocks_statef *s; +z_streamp z; +int r; +{ + uInt t; /* temporary storage */ + uLong b; /* bit buffer */ + uInt k; /* bits in bit buffer */ + Bytef *p; /* input data pointer */ + uInt n; /* bytes available there */ + Bytef *q; /* output window write pointer */ + uInt m; /* bytes to end of window or read pointer */ + + /* copy input/output information to locals (UPDATE macro restores) */ + LOAD + + /* process input based on current state */ + while (1) switch (s->mode) + { + case TYPE: + NEEDBITS(3) + t = (uInt)b & 7; + s->last = t & 1; + switch (t >> 1) + { + case 0: /* stored */ + Tracev((stderr, "inflate: stored block%s\n", + s->last ? " (last)" : "")); + DUMPBITS(3) + t = k & 7; /* go to byte boundary */ + DUMPBITS(t) + s->mode = LENS; /* get length of stored block */ + break; + case 1: /* fixed */ + Tracev((stderr, "inflate: fixed codes block%s\n", + s->last ? " (last)" : "")); + { + uInt bl, bd; + inflate_huft *tl, *td; + + inflate_trees_fixed(&bl, &bd, &tl, &td, z); + s->sub.decode.codes = inflate_codes_new(bl, bd, tl, td, z); + if (s->sub.decode.codes == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + } + DUMPBITS(3) + s->mode = CODES; + break; + case 2: /* dynamic */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + s->last ? " (last)" : "")); + DUMPBITS(3) + s->mode = TABLE; + break; + case 3: /* illegal */ + DUMPBITS(3) + s->mode = BAD; + z->msg = (char*)"invalid block type"; + r = Z_DATA_ERROR; + LEAVE + } + break; + case LENS: + NEEDBITS(32) + if ((((~b) >> 16) & 0xffff) != (b & 0xffff)) + { + s->mode = BAD; + z->msg = (char*)"invalid stored block lengths"; + r = Z_DATA_ERROR; + LEAVE + } + s->sub.left = (uInt)b & 0xffff; + b = k = 0; /* dump bits */ + Tracev((stderr, "inflate: stored length %u\n", s->sub.left)); + s->mode = s->sub.left ? STORED : (s->last ? DRY : TYPE); + break; + case STORED: + if (n == 0) + LEAVE + NEEDOUT + t = s->sub.left; + if (t > n) t = n; + if (t > m) t = m; + zmemcpy(q, p, t); + p += t; n -= t; + q += t; m -= t; + if ((s->sub.left -= t) != 0) + break; + Tracev((stderr, "inflate: stored end, %lu total out\n", + z->total_out + (q >= s->read ? q - s->read : + (s->end - s->read) + (q - s->window)))); + s->mode = s->last ? DRY : TYPE; + break; + case TABLE: + NEEDBITS(14) + s->sub.trees.table = t = (uInt)b & 0x3fff; +#ifndef PKZIP_BUG_WORKAROUND + if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) + { + s->mode = BAD; + z->msg = (char*)"too many length or distance symbols"; + r = Z_DATA_ERROR; + LEAVE + } +#endif + t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); + if ((s->sub.trees.blens = (uIntf*)ZALLOC(z, t, sizeof(uInt))) == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + DUMPBITS(14) + s->sub.trees.index = 0; + Tracev((stderr, "inflate: table sizes ok\n")); + s->mode = BTREE; + case BTREE: + while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10)) + { + NEEDBITS(3) + s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7; + DUMPBITS(3) + } + while (s->sub.trees.index < 19) + s->sub.trees.blens[border[s->sub.trees.index++]] = 0; + s->sub.trees.bb = 7; + t = inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb, + &s->sub.trees.tb, s->hufts, z); + if (t != Z_OK) + { + r = t; + if (r == Z_DATA_ERROR) + { + ZFREE(z, s->sub.trees.blens); + s->mode = BAD; + } + LEAVE + } + s->sub.trees.index = 0; + Tracev((stderr, "inflate: bits tree ok\n")); + s->mode = DTREE; + case DTREE: + while (t = s->sub.trees.table, + s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f)) + { + inflate_huft *h; + uInt i, j, c; + + t = s->sub.trees.bb; + NEEDBITS(t) + h = s->sub.trees.tb + ((uInt)b & inflate_mask[t]); + t = h->bits; + c = h->base; + if (c < 16) + { + DUMPBITS(t) + s->sub.trees.blens[s->sub.trees.index++] = c; + } + else /* c == 16..18 */ + { + i = c == 18 ? 7 : c - 14; + j = c == 18 ? 11 : 3; + NEEDBITS(t + i) + DUMPBITS(t) + j += (uInt)b & inflate_mask[i]; + DUMPBITS(i) + i = s->sub.trees.index; + t = s->sub.trees.table; + if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || + (c == 16 && i < 1)) + { + ZFREE(z, s->sub.trees.blens); + s->mode = BAD; + z->msg = (char*)"invalid bit length repeat"; + r = Z_DATA_ERROR; + LEAVE + } + c = c == 16 ? s->sub.trees.blens[i - 1] : 0; + do { + s->sub.trees.blens[i++] = c; + } while (--j); + s->sub.trees.index = i; + } + } + s->sub.trees.tb = Z_NULL; + { + uInt bl, bd; + inflate_huft *tl, *td; + inflate_codes_statef *c; + + bl = 9; /* must be <= 9 for lookahead assumptions */ + bd = 6; /* must be <= 9 for lookahead assumptions */ + t = s->sub.trees.table; + t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f), + s->sub.trees.blens, &bl, &bd, &tl, &td, + s->hufts, z); + if (t != Z_OK) + { + if (t == (uInt)Z_DATA_ERROR) + { + ZFREE(z, s->sub.trees.blens); + s->mode = BAD; + } + r = t; + LEAVE + } + Tracev((stderr, "inflate: trees ok\n")); + if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + s->sub.decode.codes = c; + } + ZFREE(z, s->sub.trees.blens); + s->mode = CODES; + case CODES: + UPDATE + if ((r = inflate_codes(s, z, r)) != Z_STREAM_END) + return inflate_flush(s, z, r); + r = Z_OK; + inflate_codes_free(s->sub.decode.codes, z); + LOAD + Tracev((stderr, "inflate: codes end, %lu total out\n", + z->total_out + (q >= s->read ? q - s->read : + (s->end - s->read) + (q - s->window)))); + if (!s->last) + { + s->mode = TYPE; + break; + } + s->mode = DRY; + case DRY: + FLUSH + if (s->read != s->write) + LEAVE + s->mode = DONE; + case DONE: + r = Z_STREAM_END; + LEAVE + case BAD: + r = Z_DATA_ERROR; + LEAVE + default: + r = Z_STREAM_ERROR; + LEAVE + } +} + + +int inflate_blocks_free(s, z) +inflate_blocks_statef *s; +z_streamp z; +{ + inflate_blocks_reset(s, z, Z_NULL); + ZFREE(z, s->window); + ZFREE(z, s->hufts); + ZFREE(z, s); + Tracev((stderr, "inflate: blocks freed\n")); + return Z_OK; +} + + +void inflate_set_dictionary(s, d, n) +inflate_blocks_statef *s; +const Bytef *d; +uInt n; +{ + zmemcpy(s->window, d, n); + s->read = s->write = s->window + n; +} + + +/* Returns true if inflate is currently at the end of a block generated + * by Z_SYNC_FLUSH or Z_FULL_FLUSH. + * IN assertion: s != Z_NULL + */ +int inflate_blocks_sync_point(s) +inflate_blocks_statef *s; +{ + return s->mode == LENS; +} diff --git a/zlib/infblock.h b/zlib/infblock.h new file mode 100644 index 0000000..173b226 --- /dev/null +++ b/zlib/infblock.h @@ -0,0 +1,39 @@ +/* infblock.h -- header to use infblock.c + * Copyright (C) 1995-2002 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +struct inflate_blocks_state; +typedef struct inflate_blocks_state FAR inflate_blocks_statef; + +extern inflate_blocks_statef * inflate_blocks_new OF(( + z_streamp z, + check_func c, /* check function */ + uInt w)); /* window size */ + +extern int inflate_blocks OF(( + inflate_blocks_statef *, + z_streamp , + int)); /* initial return code */ + +extern void inflate_blocks_reset OF(( + inflate_blocks_statef *, + z_streamp , + uLongf *)); /* check value on output */ + +extern int inflate_blocks_free OF(( + inflate_blocks_statef *, + z_streamp)); + +extern void inflate_set_dictionary OF(( + inflate_blocks_statef *s, + const Bytef *d, /* dictionary */ + uInt n)); /* dictionary length */ + +extern int inflate_blocks_sync_point OF(( + inflate_blocks_statef *s)); diff --git a/zlib/infcodes.c b/zlib/infcodes.c new file mode 100644 index 0000000..9abe541 --- /dev/null +++ b/zlib/infcodes.c @@ -0,0 +1,251 @@ +/* infcodes.c -- process literals and length/distance pairs + * Copyright (C) 1995-2002 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" +#include "infblock.h" +#include "infcodes.h" +#include "infutil.h" +#include "inffast.h" + +/* simplify the use of the inflate_huft type with some defines */ +#define exop word.what.Exop +#define bits word.what.Bits + +typedef enum { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */ + START, /* x: set up for LEN */ + LEN, /* i: get length/literal/eob next */ + LENEXT, /* i: getting length extra (have base) */ + DIST, /* i: get distance next */ + DISTEXT, /* i: getting distance extra */ + COPY, /* o: copying bytes in window, waiting for space */ + LIT, /* o: got literal, waiting for output space */ + WASH, /* o: got eob, possibly still output waiting */ + END, /* x: got eob and all data flushed */ + BADCODE} /* x: got error */ +inflate_codes_mode; + +/* inflate codes private state */ +struct inflate_codes_state { + + /* mode */ + inflate_codes_mode mode; /* current inflate_codes mode */ + + /* mode dependent information */ + uInt len; + union { + struct { + inflate_huft *tree; /* pointer into tree */ + uInt need; /* bits needed */ + } code; /* if LEN or DIST, where in tree */ + uInt lit; /* if LIT, literal */ + struct { + uInt get; /* bits to get for extra */ + uInt dist; /* distance back to copy from */ + } copy; /* if EXT or COPY, where and how much */ + } sub; /* submode */ + + /* mode independent information */ + Byte lbits; /* ltree bits decoded per branch */ + Byte dbits; /* dtree bits decoder per branch */ + inflate_huft *ltree; /* literal/length/eob tree */ + inflate_huft *dtree; /* distance tree */ + +}; + + +inflate_codes_statef *inflate_codes_new(bl, bd, tl, td, z) +uInt bl, bd; +inflate_huft *tl; +inflate_huft *td; /* need separate declaration for Borland C++ */ +z_streamp z; +{ + inflate_codes_statef *c; + + if ((c = (inflate_codes_statef *) + ZALLOC(z,1,sizeof(struct inflate_codes_state))) != Z_NULL) + { + c->mode = START; + c->lbits = (Byte)bl; + c->dbits = (Byte)bd; + c->ltree = tl; + c->dtree = td; + Tracev((stderr, "inflate: codes new\n")); + } + return c; +} + + +int inflate_codes(s, z, r) +inflate_blocks_statef *s; +z_streamp z; +int r; +{ + uInt j; /* temporary storage */ + inflate_huft *t; /* temporary pointer */ + uInt e; /* extra bits or operation */ + uLong b; /* bit buffer */ + uInt k; /* bits in bit buffer */ + Bytef *p; /* input data pointer */ + uInt n; /* bytes available there */ + Bytef *q; /* output window write pointer */ + uInt m; /* bytes to end of window or read pointer */ + Bytef *f; /* pointer to copy strings from */ + inflate_codes_statef *c = s->sub.decode.codes; /* codes state */ + + /* copy input/output information to locals (UPDATE macro restores) */ + LOAD + + /* process input and output based on current state */ + while (1) switch (c->mode) + { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */ + case START: /* x: set up for LEN */ +#ifndef SLOW + if (m >= 258 && n >= 10) + { + UPDATE + r = inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z); + LOAD + if (r != Z_OK) + { + c->mode = r == Z_STREAM_END ? WASH : BADCODE; + break; + } + } +#endif /* !SLOW */ + c->sub.code.need = c->lbits; + c->sub.code.tree = c->ltree; + c->mode = LEN; + case LEN: /* i: get length/literal/eob next */ + j = c->sub.code.need; + NEEDBITS(j) + t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); + DUMPBITS(t->bits) + e = (uInt)(t->exop); + if (e == 0) /* literal */ + { + c->sub.lit = t->base; + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", t->base)); + c->mode = LIT; + break; + } + if (e & 16) /* length */ + { + c->sub.copy.get = e & 15; + c->len = t->base; + c->mode = LENEXT; + break; + } + if ((e & 64) == 0) /* next table */ + { + c->sub.code.need = e; + c->sub.code.tree = t + t->base; + break; + } + if (e & 32) /* end of block */ + { + Tracevv((stderr, "inflate: end of block\n")); + c->mode = WASH; + break; + } + c->mode = BADCODE; /* invalid code */ + z->msg = (char*)"invalid literal/length code"; + r = Z_DATA_ERROR; + LEAVE + case LENEXT: /* i: getting length extra (have base) */ + j = c->sub.copy.get; + NEEDBITS(j) + c->len += (uInt)b & inflate_mask[j]; + DUMPBITS(j) + c->sub.code.need = c->dbits; + c->sub.code.tree = c->dtree; + Tracevv((stderr, "inflate: length %u\n", c->len)); + c->mode = DIST; + case DIST: /* i: get distance next */ + j = c->sub.code.need; + NEEDBITS(j) + t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); + DUMPBITS(t->bits) + e = (uInt)(t->exop); + if (e & 16) /* distance */ + { + c->sub.copy.get = e & 15; + c->sub.copy.dist = t->base; + c->mode = DISTEXT; + break; + } + if ((e & 64) == 0) /* next table */ + { + c->sub.code.need = e; + c->sub.code.tree = t + t->base; + break; + } + c->mode = BADCODE; /* invalid code */ + z->msg = (char*)"invalid distance code"; + r = Z_DATA_ERROR; + LEAVE + case DISTEXT: /* i: getting distance extra */ + j = c->sub.copy.get; + NEEDBITS(j) + c->sub.copy.dist += (uInt)b & inflate_mask[j]; + DUMPBITS(j) + Tracevv((stderr, "inflate: distance %u\n", c->sub.copy.dist)); + c->mode = COPY; + case COPY: /* o: copying bytes in window, waiting for space */ + f = q - c->sub.copy.dist; + while (f < s->window) /* modulo window size-"while" instead */ + f += s->end - s->window; /* of "if" handles invalid distances */ + while (c->len) + { + NEEDOUT + OUTBYTE(*f++) + if (f == s->end) + f = s->window; + c->len--; + } + c->mode = START; + break; + case LIT: /* o: got literal, waiting for output space */ + NEEDOUT + OUTBYTE(c->sub.lit) + c->mode = START; + break; + case WASH: /* o: got eob, possibly more output */ + if (k > 7) /* return unused byte, if any */ + { + Assert(k < 16, "inflate_codes grabbed too many bytes") + k -= 8; + n++; + p--; /* can always return one */ + } + FLUSH + if (s->read != s->write) + LEAVE + c->mode = END; + case END: + r = Z_STREAM_END; + LEAVE + case BADCODE: /* x: got error */ + r = Z_DATA_ERROR; + LEAVE + default: + r = Z_STREAM_ERROR; + LEAVE + } +#ifdef NEED_DUMMY_RETURN + return Z_STREAM_ERROR; /* Some dumb compilers complain without this */ +#endif +} + + +void inflate_codes_free(c, z) +inflate_codes_statef *c; +z_streamp z; +{ + ZFREE(z, c); + Tracev((stderr, "inflate: codes free\n")); +} diff --git a/zlib/infcodes.h b/zlib/infcodes.h new file mode 100644 index 0000000..46821a0 --- /dev/null +++ b/zlib/infcodes.h @@ -0,0 +1,27 @@ +/* infcodes.h -- header to use infcodes.c + * Copyright (C) 1995-2002 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +struct inflate_codes_state; +typedef struct inflate_codes_state FAR inflate_codes_statef; + +extern inflate_codes_statef *inflate_codes_new OF(( + uInt, uInt, + inflate_huft *, inflate_huft *, + z_streamp )); + +extern int inflate_codes OF(( + inflate_blocks_statef *, + z_streamp , + int)); + +extern void inflate_codes_free OF(( + inflate_codes_statef *, + z_streamp )); + diff --git a/zlib/inffast.c b/zlib/inffast.c new file mode 100644 index 0000000..aa7f1d4 --- /dev/null +++ b/zlib/inffast.c @@ -0,0 +1,183 @@ +/* inffast.c -- process literals and length/distance pairs fast + * Copyright (C) 1995-2002 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" +#include "infblock.h" +#include "infcodes.h" +#include "infutil.h" +#include "inffast.h" + +struct inflate_codes_state {int dummy;}; /* for buggy compilers */ + +/* simplify the use of the inflate_huft type with some defines */ +#define exop word.what.Exop +#define bits word.what.Bits + +/* macros for bit input with no checking and for returning unused bytes */ +#define GRABBITS(j) {while(k<(j)){b|=((uLong)NEXTBYTE)<avail_in-n;c=(k>>3)>3:c;n+=c;p-=c;k-=c<<3;} + +/* Called with number of bytes left to write in window at least 258 + (the maximum string length) and number of input bytes available + at least ten. The ten bytes are six bytes for the longest length/ + distance pair plus four bytes for overloading the bit buffer. */ + +int inflate_fast(bl, bd, tl, td, s, z) +uInt bl, bd; +inflate_huft *tl; +inflate_huft *td; /* need separate declaration for Borland C++ */ +inflate_blocks_statef *s; +z_streamp z; +{ + inflate_huft *t; /* temporary pointer */ + uInt e; /* extra bits or operation */ + uLong b; /* bit buffer */ + uInt k; /* bits in bit buffer */ + Bytef *p; /* input data pointer */ + uInt n; /* bytes available there */ + Bytef *q; /* output window write pointer */ + uInt m; /* bytes to end of window or read pointer */ + uInt ml; /* mask for literal/length tree */ + uInt md; /* mask for distance tree */ + uInt c; /* bytes to copy */ + uInt d; /* distance back to copy from */ + Bytef *r; /* copy source pointer */ + + /* load input, output, bit values */ + LOAD + + /* initialize masks */ + ml = inflate_mask[bl]; + md = inflate_mask[bd]; + + /* do until not enough input or output space for fast loop */ + do { /* assume called with m >= 258 && n >= 10 */ + /* get literal/length code */ + GRABBITS(20) /* max bits for literal/length code */ + if ((e = (t = tl + ((uInt)b & ml))->exop) == 0) + { + DUMPBITS(t->bits) + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: * literal '%c'\n" : + "inflate: * literal 0x%02x\n", t->base)); + *q++ = (Byte)t->base; + m--; + continue; + } + do { + DUMPBITS(t->bits) + if (e & 16) + { + /* get extra bits for length */ + e &= 15; + c = t->base + ((uInt)b & inflate_mask[e]); + DUMPBITS(e) + Tracevv((stderr, "inflate: * length %u\n", c)); + + /* decode distance base of block to copy */ + GRABBITS(15); /* max bits for distance code */ + e = (t = td + ((uInt)b & md))->exop; + do { + DUMPBITS(t->bits) + if (e & 16) + { + /* get extra bits to add to distance base */ + e &= 15; + GRABBITS(e) /* get extra bits (up to 13) */ + d = t->base + ((uInt)b & inflate_mask[e]); + DUMPBITS(e) + Tracevv((stderr, "inflate: * distance %u\n", d)); + + /* do the copy */ + m -= c; + r = q - d; + if (r < s->window) /* wrap if needed */ + { + do { + r += s->end - s->window; /* force pointer in window */ + } while (r < s->window); /* covers invalid distances */ + e = s->end - r; + if (c > e) + { + c -= e; /* wrapped copy */ + do { + *q++ = *r++; + } while (--e); + r = s->window; + do { + *q++ = *r++; + } while (--c); + } + else /* normal copy */ + { + *q++ = *r++; c--; + *q++ = *r++; c--; + do { + *q++ = *r++; + } while (--c); + } + } + else /* normal copy */ + { + *q++ = *r++; c--; + *q++ = *r++; c--; + do { + *q++ = *r++; + } while (--c); + } + break; + } + else if ((e & 64) == 0) + { + t += t->base; + e = (t += ((uInt)b & inflate_mask[e]))->exop; + } + else + { + z->msg = (char*)"invalid distance code"; + UNGRAB + UPDATE + return Z_DATA_ERROR; + } + } while (1); + break; + } + if ((e & 64) == 0) + { + t += t->base; + if ((e = (t += ((uInt)b & inflate_mask[e]))->exop) == 0) + { + DUMPBITS(t->bits) + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: * literal '%c'\n" : + "inflate: * literal 0x%02x\n", t->base)); + *q++ = (Byte)t->base; + m--; + break; + } + } + else if (e & 32) + { + Tracevv((stderr, "inflate: * end of block\n")); + UNGRAB + UPDATE + return Z_STREAM_END; + } + else + { + z->msg = (char*)"invalid literal/length code"; + UNGRAB + UPDATE + return Z_DATA_ERROR; + } + } while (1); + } while (m >= 258 && n >= 10); + + /* not enough input or output--restore pointers and return */ + UNGRAB + UPDATE + return Z_OK; +} diff --git a/zlib/inffast.h b/zlib/inffast.h new file mode 100644 index 0000000..a31a4bb --- /dev/null +++ b/zlib/inffast.h @@ -0,0 +1,17 @@ +/* inffast.h -- header to use inffast.c + * Copyright (C) 1995-2002 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +extern int inflate_fast OF(( + uInt, + uInt, + inflate_huft *, + inflate_huft *, + inflate_blocks_statef *, + z_streamp )); diff --git a/zlib/inffixed.h b/zlib/inffixed.h new file mode 100644 index 0000000..77f7e76 --- /dev/null +++ b/zlib/inffixed.h @@ -0,0 +1,151 @@ +/* inffixed.h -- table for decoding fixed codes + * Generated automatically by the maketree.c program + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +local uInt fixed_bl = 9; +local uInt fixed_bd = 5; +local inflate_huft fixed_tl[] = { + {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115}, + {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},192}, + {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},160}, + {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},224}, + {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},144}, + {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},208}, + {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},176}, + {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},240}, + {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227}, + {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},200}, + {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},168}, + {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},232}, + {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},152}, + {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},216}, + {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},184}, + {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},248}, + {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163}, + {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},196}, + {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},164}, + {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},228}, + {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},148}, + {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},212}, + {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},180}, + {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},244}, + {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},204}, + {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},172}, + {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},236}, + {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},156}, + {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},220}, + {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},188}, + {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},252}, + {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131}, + {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},194}, + {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},162}, + {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},226}, + {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},146}, + {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},210}, + {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},178}, + {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},242}, + {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258}, + {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},202}, + {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},170}, + {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},234}, + {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},154}, + {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},218}, + {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},186}, + {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},250}, + {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195}, + {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},198}, + {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},166}, + {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},230}, + {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},150}, + {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},214}, + {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},182}, + {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},246}, + {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},206}, + {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},174}, + {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},238}, + {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},158}, + {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},222}, + {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},190}, + {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},254}, + {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115}, + {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},193}, + {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},161}, + {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},225}, + {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},145}, + {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},209}, + {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},177}, + {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},241}, + {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227}, + {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},201}, + {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},169}, + {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},233}, + {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},153}, + {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},217}, + {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},185}, + {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},249}, + {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163}, + {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},197}, + {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},165}, + {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},229}, + {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},149}, + {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},213}, + {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},181}, + {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},245}, + {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},205}, + {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},173}, + {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},237}, + {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},157}, + {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},221}, + {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},189}, + {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},253}, + {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131}, + {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},195}, + {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},163}, + {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},227}, + {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},147}, + {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},211}, + {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},179}, + {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},243}, + {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258}, + {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},203}, + {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},171}, + {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},235}, + {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},155}, + {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},219}, + {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},187}, + {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},251}, + {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195}, + {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},199}, + {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},167}, + {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},231}, + {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},151}, + {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},215}, + {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},183}, + {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},247}, + {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},207}, + {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},175}, + {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},239}, + {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},159}, + {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},223}, + {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},191}, + {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},255} + }; +local inflate_huft fixed_td[] = { + {{{80,5}},1}, {{{87,5}},257}, {{{83,5}},17}, {{{91,5}},4097}, + {{{81,5}},5}, {{{89,5}},1025}, {{{85,5}},65}, {{{93,5}},16385}, + {{{80,5}},3}, {{{88,5}},513}, {{{84,5}},33}, {{{92,5}},8193}, + {{{82,5}},9}, {{{90,5}},2049}, {{{86,5}},129}, {{{192,5}},24577}, + {{{80,5}},2}, {{{87,5}},385}, {{{83,5}},25}, {{{91,5}},6145}, + {{{81,5}},7}, {{{89,5}},1537}, {{{85,5}},97}, {{{93,5}},24577}, + {{{80,5}},4}, {{{88,5}},769}, {{{84,5}},49}, {{{92,5}},12289}, + {{{82,5}},13}, {{{90,5}},3073}, {{{86,5}},193}, {{{192,5}},24577} + }; diff --git a/zlib/inflate.c b/zlib/inflate.c new file mode 100644 index 0000000..dfb2e86 --- /dev/null +++ b/zlib/inflate.c @@ -0,0 +1,366 @@ +/* inflate.c -- zlib interface to inflate modules + * Copyright (C) 1995-2002 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "infblock.h" + +struct inflate_blocks_state {int dummy;}; /* for buggy compilers */ + +typedef enum { + METHOD, /* waiting for method byte */ + FLAG, /* waiting for flag byte */ + DICT4, /* four dictionary check bytes to go */ + DICT3, /* three dictionary check bytes to go */ + DICT2, /* two dictionary check bytes to go */ + DICT1, /* one dictionary check byte to go */ + DICT0, /* waiting for inflateSetDictionary */ + BLOCKS, /* decompressing blocks */ + CHECK4, /* four check bytes to go */ + CHECK3, /* three check bytes to go */ + CHECK2, /* two check bytes to go */ + CHECK1, /* one check byte to go */ + DONE, /* finished check, done */ + BAD} /* got an error--stay here */ +inflate_mode; + +/* inflate private state */ +struct internal_state { + + /* mode */ + inflate_mode mode; /* current inflate mode */ + + /* mode dependent information */ + union { + uInt method; /* if FLAGS, method byte */ + struct { + uLong was; /* computed check value */ + uLong need; /* stream check value */ + } check; /* if CHECK, check values to compare */ + uInt marker; /* if BAD, inflateSync's marker bytes count */ + } sub; /* submode */ + + /* mode independent information */ + int nowrap; /* flag for no wrapper */ + uInt wbits; /* log2(window size) (8..15, defaults to 15) */ + inflate_blocks_statef + *blocks; /* current inflate_blocks state */ + +}; + + +int ZEXPORT inflateReset(z) +z_streamp z; +{ + if (z == Z_NULL || z->state == Z_NULL) + return Z_STREAM_ERROR; + z->total_in = z->total_out = 0; + z->msg = Z_NULL; + z->state->mode = z->state->nowrap ? BLOCKS : METHOD; + inflate_blocks_reset(z->state->blocks, z, Z_NULL); + Tracev((stderr, "inflate: reset\n")); + return Z_OK; +} + + +int ZEXPORT inflateEnd(z) +z_streamp z; +{ + if (z == Z_NULL || z->state == Z_NULL || z->zfree == Z_NULL) + return Z_STREAM_ERROR; + if (z->state->blocks != Z_NULL) + inflate_blocks_free(z->state->blocks, z); + ZFREE(z, z->state); + z->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} + + +int ZEXPORT inflateInit2_(z, w, version, stream_size) +z_streamp z; +int w; +const char *version; +int stream_size; +{ + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != sizeof(z_stream)) + return Z_VERSION_ERROR; + + /* initialize state */ + if (z == Z_NULL) + return Z_STREAM_ERROR; + z->msg = Z_NULL; + if (z->zalloc == Z_NULL) + { + z->zalloc = zcalloc; + z->opaque = (voidpf)0; + } + if (z->zfree == Z_NULL) z->zfree = zcfree; + if ((z->state = (struct internal_state FAR *) + ZALLOC(z,1,sizeof(struct internal_state))) == Z_NULL) + return Z_MEM_ERROR; + z->state->blocks = Z_NULL; + + /* handle undocumented nowrap option (no zlib header or check) */ + z->state->nowrap = 0; + if (w < 0) + { + w = - w; + z->state->nowrap = 1; + } + + /* set window size */ + if (w < 8 || w > 15) + { + inflateEnd(z); + return Z_STREAM_ERROR; + } + z->state->wbits = (uInt)w; + + /* create inflate_blocks state */ + if ((z->state->blocks = + inflate_blocks_new(z, z->state->nowrap ? Z_NULL : adler32, (uInt)1 << w)) + == Z_NULL) + { + inflateEnd(z); + return Z_MEM_ERROR; + } + Tracev((stderr, "inflate: allocated\n")); + + /* reset state */ + inflateReset(z); + return Z_OK; +} + + +int ZEXPORT inflateInit_(z, version, stream_size) +z_streamp z; +const char *version; +int stream_size; +{ + return inflateInit2_(z, DEF_WBITS, version, stream_size); +} + + +#define NEEDBYTE {if(z->avail_in==0)return r;r=f;} +#define NEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++) + +int ZEXPORT inflate(z, f) +z_streamp z; +int f; +{ + int r; + uInt b; + + if (z == Z_NULL || z->state == Z_NULL || z->next_in == Z_NULL) + return Z_STREAM_ERROR; + f = f == Z_FINISH ? Z_BUF_ERROR : Z_OK; + r = Z_BUF_ERROR; + while (1) switch (z->state->mode) + { + case METHOD: + NEEDBYTE + if (((z->state->sub.method = NEXTBYTE) & 0xf) != Z_DEFLATED) + { + z->state->mode = BAD; + z->msg = (char*)"unknown compression method"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + if ((z->state->sub.method >> 4) + 8 > z->state->wbits) + { + z->state->mode = BAD; + z->msg = (char*)"invalid window size"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + z->state->mode = FLAG; + case FLAG: + NEEDBYTE + b = NEXTBYTE; + if (((z->state->sub.method << 8) + b) % 31) + { + z->state->mode = BAD; + z->msg = (char*)"incorrect header check"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + Tracev((stderr, "inflate: zlib header ok\n")); + if (!(b & PRESET_DICT)) + { + z->state->mode = BLOCKS; + break; + } + z->state->mode = DICT4; + case DICT4: + NEEDBYTE + z->state->sub.check.need = (uLong)NEXTBYTE << 24; + z->state->mode = DICT3; + case DICT3: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE << 16; + z->state->mode = DICT2; + case DICT2: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE << 8; + z->state->mode = DICT1; + case DICT1: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE; + z->adler = z->state->sub.check.need; + z->state->mode = DICT0; + return Z_NEED_DICT; + case DICT0: + z->state->mode = BAD; + z->msg = (char*)"need dictionary"; + z->state->sub.marker = 0; /* can try inflateSync */ + return Z_STREAM_ERROR; + case BLOCKS: + r = inflate_blocks(z->state->blocks, z, r); + if (r == Z_DATA_ERROR) + { + z->state->mode = BAD; + z->state->sub.marker = 0; /* can try inflateSync */ + break; + } + if (r == Z_OK) + r = f; + if (r != Z_STREAM_END) + return r; + r = f; + inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was); + if (z->state->nowrap) + { + z->state->mode = DONE; + break; + } + z->state->mode = CHECK4; + case CHECK4: + NEEDBYTE + z->state->sub.check.need = (uLong)NEXTBYTE << 24; + z->state->mode = CHECK3; + case CHECK3: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE << 16; + z->state->mode = CHECK2; + case CHECK2: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE << 8; + z->state->mode = CHECK1; + case CHECK1: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE; + + if (z->state->sub.check.was != z->state->sub.check.need) + { + z->state->mode = BAD; + z->msg = (char*)"incorrect data check"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + Tracev((stderr, "inflate: zlib check ok\n")); + z->state->mode = DONE; + case DONE: + return Z_STREAM_END; + case BAD: + return Z_DATA_ERROR; + default: + return Z_STREAM_ERROR; + } +#ifdef NEED_DUMMY_RETURN + return Z_STREAM_ERROR; /* Some dumb compilers complain without this */ +#endif +} + + +int ZEXPORT inflateSetDictionary(z, dictionary, dictLength) +z_streamp z; +const Bytef *dictionary; +uInt dictLength; +{ + uInt length = dictLength; + + if (z == Z_NULL || z->state == Z_NULL || z->state->mode != DICT0) + return Z_STREAM_ERROR; + + if (adler32(1L, dictionary, dictLength) != z->adler) return Z_DATA_ERROR; + z->adler = 1L; + + if (length >= ((uInt)1<state->wbits)) + { + length = (1<state->wbits)-1; + dictionary += dictLength - length; + } + inflate_set_dictionary(z->state->blocks, dictionary, length); + z->state->mode = BLOCKS; + return Z_OK; +} + + +int ZEXPORT inflateSync(z) +z_streamp z; +{ + uInt n; /* number of bytes to look at */ + Bytef *p; /* pointer to bytes */ + uInt m; /* number of marker bytes found in a row */ + uLong r, w; /* temporaries to save total_in and total_out */ + + /* set up */ + if (z == Z_NULL || z->state == Z_NULL) + return Z_STREAM_ERROR; + if (z->state->mode != BAD) + { + z->state->mode = BAD; + z->state->sub.marker = 0; + } + if ((n = z->avail_in) == 0) + return Z_BUF_ERROR; + p = z->next_in; + m = z->state->sub.marker; + + /* search */ + while (n && m < 4) + { + static const Byte mark[4] = {0, 0, 0xff, 0xff}; + if (*p == mark[m]) + m++; + else if (*p) + m = 0; + else + m = 4 - m; + p++, n--; + } + + /* restore */ + z->total_in += p - z->next_in; + z->next_in = p; + z->avail_in = n; + z->state->sub.marker = m; + + /* return no joy or set up to restart on a new block */ + if (m != 4) + return Z_DATA_ERROR; + r = z->total_in; w = z->total_out; + inflateReset(z); + z->total_in = r; z->total_out = w; + z->state->mode = BLOCKS; + return Z_OK; +} + + +/* Returns true if inflate is currently at the end of a block generated + * by Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP + * implementation to provide an additional safety check. PPP uses Z_SYNC_FLUSH + * but removes the length bytes of the resulting empty stored block. When + * decompressing, PPP checks that at the end of input packet, inflate is + * waiting for these length bytes. + */ +int ZEXPORT inflateSyncPoint(z) +z_streamp z; +{ + if (z == Z_NULL || z->state == Z_NULL || z->state->blocks == Z_NULL) + return Z_STREAM_ERROR; + return inflate_blocks_sync_point(z->state->blocks); +} diff --git a/zlib/inftrees.c b/zlib/inftrees.c new file mode 100644 index 0000000..4c32ca3 --- /dev/null +++ b/zlib/inftrees.c @@ -0,0 +1,454 @@ +/* inftrees.c -- generate Huffman trees for efficient decoding + * Copyright (C) 1995-2002 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" + +#if !defined(BUILDFIXED) && !defined(STDC) +# define BUILDFIXED /* non ANSI compilers may not accept inffixed.h */ +#endif + +const char inflate_copyright[] = + " inflate 1.1.4 Copyright 1995-2002 Mark Adler "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ +struct internal_state {int dummy;}; /* for buggy compilers */ + +/* simplify the use of the inflate_huft type with some defines */ +#define exop word.what.Exop +#define bits word.what.Bits + + +local int huft_build OF(( + uIntf *, /* code lengths in bits */ + uInt, /* number of codes */ + uInt, /* number of "simple" codes */ + const uIntf *, /* list of base values for non-simple codes */ + const uIntf *, /* list of extra bits for non-simple codes */ + inflate_huft * FAR*,/* result: starting table */ + uIntf *, /* maximum lookup bits (returns actual) */ + inflate_huft *, /* space for trees */ + uInt *, /* hufts used in space */ + uIntf * )); /* space for values */ + +/* Tables for deflate from PKZIP's appnote.txt. */ +local const uInt cplens[31] = { /* Copy lengths for literal codes 257..285 */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + /* see note #13 above about 258 */ +local const uInt cplext[31] = { /* Extra bits for literal codes 257..285 */ + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112}; /* 112==invalid */ +local const uInt cpdist[30] = { /* Copy offsets for distance codes 0..29 */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577}; +local const uInt cpdext[30] = { /* Extra bits for distance codes */ + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + +/* + Huffman code decoding is performed using a multi-level table lookup. + The fastest way to decode is to simply build a lookup table whose + size is determined by the longest code. However, the time it takes + to build this table can also be a factor if the data being decoded + is not very long. The most common codes are necessarily the + shortest codes, so those codes dominate the decoding time, and hence + the speed. The idea is you can have a shorter table that decodes the + shorter, more probable codes, and then point to subsidiary tables for + the longer codes. The time it costs to decode the longer codes is + then traded against the time it takes to make longer tables. + + This results of this trade are in the variables lbits and dbits + below. lbits is the number of bits the first level table for literal/ + length codes can decode in one step, and dbits is the same thing for + the distance codes. Subsequent tables are also less than or equal to + those sizes. These values may be adjusted either when all of the + codes are shorter than that, in which case the longest code length in + bits is used, or when the shortest code is *longer* than the requested + table size, in which case the length of the shortest code in bits is + used. + + There are two different values for the two tables, since they code a + different number of possibilities each. The literal/length table + codes 286 possible values, or in a flat code, a little over eight + bits. The distance table codes 30 possible values, or a little less + than five bits, flat. The optimum values for speed end up being + about one bit more than those, so lbits is 8+1 and dbits is 5+1. + The optimum values may differ though from machine to machine, and + possibly even between compilers. Your mileage may vary. + */ + + +/* If BMAX needs to be larger than 16, then h and x[] should be uLong. */ +#define BMAX 15 /* maximum bit length of any code */ + +local int huft_build(b, n, s, d, e, t, m, hp, hn, v) +uIntf *b; /* code lengths in bits (all assumed <= BMAX) */ +uInt n; /* number of codes (assumed <= 288) */ +uInt s; /* number of simple-valued codes (0..s-1) */ +const uIntf *d; /* list of base values for non-simple codes */ +const uIntf *e; /* list of extra bits for non-simple codes */ +inflate_huft * FAR *t; /* result: starting table */ +uIntf *m; /* maximum lookup bits, returns actual */ +inflate_huft *hp; /* space for trees */ +uInt *hn; /* hufts used in space */ +uIntf *v; /* working area: values in order of bit length */ +/* Given a list of code lengths and a maximum table size, make a set of + tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR + if the given code set is incomplete (the tables are still built in this + case), or Z_DATA_ERROR if the input is invalid. */ +{ + + uInt a; /* counter for codes of length k */ + uInt c[BMAX+1]; /* bit length count table */ + uInt f; /* i repeats in table every f entries */ + int g; /* maximum code length */ + int h; /* table level */ + register uInt i; /* counter, current code */ + register uInt j; /* counter */ + register int k; /* number of bits in current code */ + int l; /* bits per table (returned in m) */ + uInt mask; /* (1 << w) - 1, to avoid cc -O bug on HP */ + register uIntf *p; /* pointer into c[], b[], or v[] */ + inflate_huft *q; /* points to current table */ + struct inflate_huft_s r; /* table entry for structure assignment */ + inflate_huft *u[BMAX]; /* table stack */ + register int w; /* bits before this table == (l * h) */ + uInt x[BMAX+1]; /* bit offsets, then code stack */ + uIntf *xp; /* pointer into x */ + int y; /* number of dummy codes added */ + uInt z; /* number of entries in current table */ + + + /* Generate counts for each bit length */ + p = c; +#define C0 *p++ = 0; +#define C2 C0 C0 C0 C0 +#define C4 C2 C2 C2 C2 + C4 /* clear c[]--assume BMAX+1 is 16 */ + p = b; i = n; + do { + c[*p++]++; /* assume all entries <= BMAX */ + } while (--i); + if (c[0] == n) /* null input--all zero length codes */ + { + *t = (inflate_huft *)Z_NULL; + *m = 0; + return Z_OK; + } + + + /* Find minimum and maximum length, bound *m by those */ + l = *m; + for (j = 1; j <= BMAX; j++) + if (c[j]) + break; + k = j; /* minimum code length */ + if ((uInt)l < j) + l = j; + for (i = BMAX; i; i--) + if (c[i]) + break; + g = i; /* maximum code length */ + if ((uInt)l > i) + l = i; + *m = l; + + + /* Adjust last length count to fill out codes, if needed */ + for (y = 1 << j; j < i; j++, y <<= 1) + if ((y -= c[j]) < 0) + return Z_DATA_ERROR; + if ((y -= c[i]) < 0) + return Z_DATA_ERROR; + c[i] += y; + + + /* Generate starting offsets into the value table for each length */ + x[1] = j = 0; + p = c + 1; xp = x + 2; + while (--i) { /* note that i == g from above */ + *xp++ = (j += *p++); + } + + + /* Make a table of values in order of bit lengths */ + p = b; i = 0; + do { + if ((j = *p++) != 0) + v[x[j]++] = i; + } while (++i < n); + n = x[g]; /* set n to length of v */ + + + /* Generate the Huffman codes and for each, make the table entries */ + x[0] = i = 0; /* first Huffman code is zero */ + p = v; /* grab values in bit order */ + h = -1; /* no tables yet--level -1 */ + w = -l; /* bits decoded == (l * h) */ + u[0] = (inflate_huft *)Z_NULL; /* just to keep compilers happy */ + q = (inflate_huft *)Z_NULL; /* ditto */ + z = 0; /* ditto */ + + /* go through the bit lengths (k already is bits in shortest code) */ + for (; k <= g; k++) + { + a = c[k]; + while (a--) + { + /* here i is the Huffman code of length k bits for value *p */ + /* make tables up to required level */ + while (k > w + l) + { + h++; + w += l; /* previous table always l bits */ + + /* compute minimum size table less than or equal to l bits */ + z = g - w; + z = z > (uInt)l ? l : z; /* table size upper limit */ + if ((f = 1 << (j = k - w)) > a + 1) /* try a k-w bit table */ + { /* too few codes for k-w bit table */ + f -= a + 1; /* deduct codes from patterns left */ + xp = c + k; + if (j < z) + while (++j < z) /* try smaller tables up to z bits */ + { + if ((f <<= 1) <= *++xp) + break; /* enough codes to use up j bits */ + f -= *xp; /* else deduct codes from patterns */ + } + } + z = 1 << j; /* table entries for j-bit table */ + + /* allocate new table */ + if (*hn + z > MANY) /* (note: doesn't matter for fixed) */ + return Z_DATA_ERROR; /* overflow of MANY */ + u[h] = q = hp + *hn; + *hn += z; + + /* connect to last table, if there is one */ + if (h) + { + x[h] = i; /* save pattern for backing up */ + r.bits = (Byte)l; /* bits to dump before this table */ + r.exop = (Byte)j; /* bits in this table */ + j = i >> (w - l); + r.base = (uInt)(q - u[h-1] - j); /* offset to this table */ + u[h-1][j] = r; /* connect to last table */ + } + else + *t = q; /* first table is returned result */ + } + + /* set up table entry in r */ + r.bits = (Byte)(k - w); + if (p >= v + n) + r.exop = 128 + 64; /* out of values--invalid code */ + else if (*p < s) + { + r.exop = (Byte)(*p < 256 ? 0 : 32 + 64); /* 256 is end-of-block */ + r.base = *p++; /* simple code is just the value */ + } + else + { + r.exop = (Byte)(e[*p - s] + 16 + 64);/* non-simple--look up in lists */ + r.base = d[*p++ - s]; + } + + /* fill code-like entries with r */ + f = 1 << (k - w); + for (j = i >> w; j < z; j += f) + q[j] = r; + + /* backwards increment the k-bit code i */ + for (j = 1 << (k - 1); i & j; j >>= 1) + i ^= j; + i ^= j; + + /* backup over finished tables */ + mask = (1 << w) - 1; /* needed on HP, cc -O bug */ + while ((i & mask) != x[h]) + { + h--; /* don't need to update q */ + w -= l; + mask = (1 << w) - 1; + } + } + } + + + /* Return Z_BUF_ERROR if we were given an incomplete table */ + return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK; +} + + +int inflate_trees_bits(c, bb, tb, hp, z) +uIntf *c; /* 19 code lengths */ +uIntf *bb; /* bits tree desired/actual depth */ +inflate_huft * FAR *tb; /* bits tree result */ +inflate_huft *hp; /* space for trees */ +z_streamp z; /* for messages */ +{ + int r; + uInt hn = 0; /* hufts used in space */ + uIntf *v; /* work area for huft_build */ + + if ((v = (uIntf*)ZALLOC(z, 19, sizeof(uInt))) == Z_NULL) + return Z_MEM_ERROR; + r = huft_build(c, 19, 19, (uIntf*)Z_NULL, (uIntf*)Z_NULL, + tb, bb, hp, &hn, v); + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed dynamic bit lengths tree"; + else if (r == Z_BUF_ERROR || *bb == 0) + { + z->msg = (char*)"incomplete dynamic bit lengths tree"; + r = Z_DATA_ERROR; + } + ZFREE(z, v); + return r; +} + + +int inflate_trees_dynamic(nl, nd, c, bl, bd, tl, td, hp, z) +uInt nl; /* number of literal/length codes */ +uInt nd; /* number of distance codes */ +uIntf *c; /* that many (total) code lengths */ +uIntf *bl; /* literal desired/actual bit depth */ +uIntf *bd; /* distance desired/actual bit depth */ +inflate_huft * FAR *tl; /* literal/length tree result */ +inflate_huft * FAR *td; /* distance tree result */ +inflate_huft *hp; /* space for trees */ +z_streamp z; /* for messages */ +{ + int r; + uInt hn = 0; /* hufts used in space */ + uIntf *v; /* work area for huft_build */ + + /* allocate work area */ + if ((v = (uIntf*)ZALLOC(z, 288, sizeof(uInt))) == Z_NULL) + return Z_MEM_ERROR; + + /* build literal/length tree */ + r = huft_build(c, nl, 257, cplens, cplext, tl, bl, hp, &hn, v); + if (r != Z_OK || *bl == 0) + { + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed literal/length tree"; + else if (r != Z_MEM_ERROR) + { + z->msg = (char*)"incomplete literal/length tree"; + r = Z_DATA_ERROR; + } + ZFREE(z, v); + return r; + } + + /* build distance tree */ + r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, hp, &hn, v); + if (r != Z_OK || (*bd == 0 && nl > 257)) + { + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed distance tree"; + else if (r == Z_BUF_ERROR) { +#ifdef PKZIP_BUG_WORKAROUND + r = Z_OK; + } +#else + z->msg = (char*)"incomplete distance tree"; + r = Z_DATA_ERROR; + } + else if (r != Z_MEM_ERROR) + { + z->msg = (char*)"empty distance tree with lengths"; + r = Z_DATA_ERROR; + } + ZFREE(z, v); + return r; +#endif + } + + /* done */ + ZFREE(z, v); + return Z_OK; +} + + +/* build fixed tables only once--keep them here */ +#ifdef BUILDFIXED +local int fixed_built = 0; +#define FIXEDH 544 /* number of hufts used by fixed tables */ +local inflate_huft fixed_mem[FIXEDH]; +local uInt fixed_bl; +local uInt fixed_bd; +local inflate_huft *fixed_tl; +local inflate_huft *fixed_td; +#else +#include "inffixed.h" +#endif + + +int inflate_trees_fixed(bl, bd, tl, td, z) +uIntf *bl; /* literal desired/actual bit depth */ +uIntf *bd; /* distance desired/actual bit depth */ +inflate_huft * FAR *tl; /* literal/length tree result */ +inflate_huft * FAR *td; /* distance tree result */ +z_streamp z; /* for memory allocation */ +{ +#ifdef BUILDFIXED + /* build fixed tables if not already */ + if (!fixed_built) + { + int k; /* temporary variable */ + uInt f = 0; /* number of hufts used in fixed_mem */ + uIntf *c; /* length list for huft_build */ + uIntf *v; /* work area for huft_build */ + + /* allocate memory */ + if ((c = (uIntf*)ZALLOC(z, 288, sizeof(uInt))) == Z_NULL) + return Z_MEM_ERROR; + if ((v = (uIntf*)ZALLOC(z, 288, sizeof(uInt))) == Z_NULL) + { + ZFREE(z, c); + return Z_MEM_ERROR; + } + + /* literal table */ + for (k = 0; k < 144; k++) + c[k] = 8; + for (; k < 256; k++) + c[k] = 9; + for (; k < 280; k++) + c[k] = 7; + for (; k < 288; k++) + c[k] = 8; + fixed_bl = 9; + huft_build(c, 288, 257, cplens, cplext, &fixed_tl, &fixed_bl, + fixed_mem, &f, v); + + /* distance table */ + for (k = 0; k < 30; k++) + c[k] = 5; + fixed_bd = 5; + huft_build(c, 30, 0, cpdist, cpdext, &fixed_td, &fixed_bd, + fixed_mem, &f, v); + + /* done */ + ZFREE(z, v); + ZFREE(z, c); + fixed_built = 1; + } +#endif + *bl = fixed_bl; + *bd = fixed_bd; + *tl = fixed_tl; + *td = fixed_td; + return Z_OK; +} diff --git a/zlib/inftrees.h b/zlib/inftrees.h new file mode 100644 index 0000000..04b73b7 --- /dev/null +++ b/zlib/inftrees.h @@ -0,0 +1,58 @@ +/* inftrees.h -- header to use inftrees.c + * Copyright (C) 1995-2002 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* Huffman code lookup table entry--this entry is four bytes for machines + that have 16-bit pointers (e.g. PC's in the small or medium model). */ + +typedef struct inflate_huft_s FAR inflate_huft; + +struct inflate_huft_s { + union { + struct { + Byte Exop; /* number of extra bits or operation */ + Byte Bits; /* number of bits in this code or subcode */ + } what; + uInt pad; /* pad structure to a power of 2 (4 bytes for */ + } word; /* 16-bit, 8 bytes for 32-bit int's) */ + uInt base; /* literal, length base, distance base, + or table offset */ +}; + +/* Maximum size of dynamic tree. The maximum found in a long but non- + exhaustive search was 1004 huft structures (850 for length/literals + and 154 for distances, the latter actually the result of an + exhaustive search). The actual maximum is not known, but the + value below is more than safe. */ +#define MANY 1440 + +extern int inflate_trees_bits OF(( + uIntf *, /* 19 code lengths */ + uIntf *, /* bits tree desired/actual depth */ + inflate_huft * FAR *, /* bits tree result */ + inflate_huft *, /* space for trees */ + z_streamp)); /* for messages */ + +extern int inflate_trees_dynamic OF(( + uInt, /* number of literal/length codes */ + uInt, /* number of distance codes */ + uIntf *, /* that many (total) code lengths */ + uIntf *, /* literal desired/actual bit depth */ + uIntf *, /* distance desired/actual bit depth */ + inflate_huft * FAR *, /* literal/length tree result */ + inflate_huft * FAR *, /* distance tree result */ + inflate_huft *, /* space for trees */ + z_streamp)); /* for messages */ + +extern int inflate_trees_fixed OF(( + uIntf *, /* literal desired/actual bit depth */ + uIntf *, /* distance desired/actual bit depth */ + inflate_huft * FAR *, /* literal/length tree result */ + inflate_huft * FAR *, /* distance tree result */ + z_streamp)); /* for memory allocation */ diff --git a/zlib/infutil.c b/zlib/infutil.c new file mode 100644 index 0000000..9a07622 --- /dev/null +++ b/zlib/infutil.c @@ -0,0 +1,87 @@ +/* inflate_util.c -- data and routines common to blocks and codes + * Copyright (C) 1995-2002 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "infblock.h" +#include "inftrees.h" +#include "infcodes.h" +#include "infutil.h" + +struct inflate_codes_state {int dummy;}; /* for buggy compilers */ + +/* And'ing with mask[n] masks the lower n bits */ +uInt inflate_mask[17] = { + 0x0000, + 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, + 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff +}; + + +/* copy as much as possible from the sliding window to the output area */ +int inflate_flush(s, z, r) +inflate_blocks_statef *s; +z_streamp z; +int r; +{ + uInt n; + Bytef *p; + Bytef *q; + + /* local copies of source and destination pointers */ + p = z->next_out; + q = s->read; + + /* compute number of bytes to copy as far as end of window */ + n = (uInt)((q <= s->write ? s->write : s->end) - q); + if (n > z->avail_out) n = z->avail_out; + if (n && r == Z_BUF_ERROR) r = Z_OK; + + /* update counters */ + z->avail_out -= n; + z->total_out += n; + + /* update check information */ + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(s->check, q, n); + + /* copy as far as end of window */ + zmemcpy(p, q, n); + p += n; + q += n; + + /* see if more to copy at beginning of window */ + if (q == s->end) + { + /* wrap pointers */ + q = s->window; + if (s->write == s->end) + s->write = s->window; + + /* compute bytes to copy */ + n = (uInt)(s->write - q); + if (n > z->avail_out) n = z->avail_out; + if (n && r == Z_BUF_ERROR) r = Z_OK; + + /* update counters */ + z->avail_out -= n; + z->total_out += n; + + /* update check information */ + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(s->check, q, n); + + /* copy */ + zmemcpy(p, q, n); + p += n; + q += n; + } + + /* update pointers */ + z->next_out = p; + s->read = q; + + /* done */ + return r; +} diff --git a/zlib/infutil.h b/zlib/infutil.h new file mode 100644 index 0000000..4401df8 --- /dev/null +++ b/zlib/infutil.h @@ -0,0 +1,98 @@ +/* infutil.h -- types and macros common to blocks and codes + * Copyright (C) 1995-2002 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +#ifndef _INFUTIL_H +#define _INFUTIL_H + +typedef enum { + TYPE, /* get type bits (3, including end bit) */ + LENS, /* get lengths for stored */ + STORED, /* processing stored block */ + TABLE, /* get table lengths */ + BTREE, /* get bit lengths tree for a dynamic block */ + DTREE, /* get length, distance trees for a dynamic block */ + CODES, /* processing fixed or dynamic block */ + DRY, /* output remaining window bytes */ + DONE, /* finished last block, done */ + BAD} /* got a data error--stuck here */ +inflate_block_mode; + +/* inflate blocks semi-private state */ +struct inflate_blocks_state { + + /* mode */ + inflate_block_mode mode; /* current inflate_block mode */ + + /* mode dependent information */ + union { + uInt left; /* if STORED, bytes left to copy */ + struct { + uInt table; /* table lengths (14 bits) */ + uInt index; /* index into blens (or border) */ + uIntf *blens; /* bit lengths of codes */ + uInt bb; /* bit length tree depth */ + inflate_huft *tb; /* bit length decoding tree */ + } trees; /* if DTREE, decoding info for trees */ + struct { + inflate_codes_statef + *codes; + } decode; /* if CODES, current state */ + } sub; /* submode */ + uInt last; /* true if this block is the last block */ + + /* mode independent information */ + uInt bitk; /* bits in bit buffer */ + uLong bitb; /* bit buffer */ + inflate_huft *hufts; /* single malloc for tree space */ + Bytef *window; /* sliding window */ + Bytef *end; /* one byte after sliding window */ + Bytef *read; /* window read pointer */ + Bytef *write; /* window write pointer */ + check_func checkfn; /* check function */ + uLong check; /* check on output */ + +}; + + +/* defines for inflate input/output */ +/* update pointers and return */ +#define UPDBITS {s->bitb=b;s->bitk=k;} +#define UPDIN {z->avail_in=n;z->total_in+=p-z->next_in;z->next_in=p;} +#define UPDOUT {s->write=q;} +#define UPDATE {UPDBITS UPDIN UPDOUT} +#define LEAVE {UPDATE return inflate_flush(s,z,r);} +/* get bytes and bits */ +#define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;} +#define NEEDBYTE {if(n)r=Z_OK;else LEAVE} +#define NEXTBYTE (n--,*p++) +#define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<>=(j);k-=(j);} +/* output bytes */ +#define WAVAIL (uInt)(qread?s->read-q-1:s->end-q) +#define LOADOUT {q=s->write;m=(uInt)WAVAIL;} +#define WRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=(uInt)WAVAIL;}} +#define FLUSH {UPDOUT r=inflate_flush(s,z,r); LOADOUT} +#define NEEDOUT {if(m==0){WRAP if(m==0){FLUSH WRAP if(m==0) LEAVE}}r=Z_OK;} +#define OUTBYTE(a) {*q++=(Byte)(a);m--;} +/* load local pointers */ +#define LOAD {LOADIN LOADOUT} + +/* masks for lower bits (size given to avoid silly warnings with Visual C++) */ +extern uInt inflate_mask[17]; + +/* copy as much as possible from the sliding window to the output area */ +extern int inflate_flush OF(( + inflate_blocks_statef *, + z_streamp , + int)); + +struct internal_state {int dummy;}; /* for buggy compilers */ + +#endif diff --git a/zlib/maketree.c b/zlib/maketree.c new file mode 100644 index 0000000..a16d4b1 --- /dev/null +++ b/zlib/maketree.c @@ -0,0 +1,85 @@ +/* maketree.c -- make inffixed.h table for decoding fixed codes + * Copyright (C) 1995-2002 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* This program is included in the distribution for completeness. + You do not need to compile or run this program since inffixed.h + is already included in the distribution. To use this program + you need to compile zlib with BUILDFIXED defined and then compile + and link this program with the zlib library. Then the output of + this program can be piped to inffixed.h. */ + +#include +#include +#include "zutil.h" +#include "inftrees.h" + +/* simplify the use of the inflate_huft type with some defines */ +#define exop word.what.Exop +#define bits word.what.Bits + +/* generate initialization table for an inflate_huft structure array */ +void maketree(uInt b, inflate_huft *t) +{ + int i, e; + + i = 0; + while (1) + { + e = t[i].exop; + if (e && (e & (16+64)) == 0) /* table pointer */ + { + fprintf(stderr, "maketree: cannot initialize sub-tables!\n"); + exit(1); + } + if (i % 4 == 0) + printf("\n "); + printf(" {{{%u,%u}},%u}", t[i].exop, t[i].bits, t[i].base); + if (++i == (1<, or to +Gilles Vollant for the Windows DLL version. +The zlib home page is http://www.cdrom.com/pub/infozip/zlib/ +The official zlib ftp site is ftp://ftp.cdrom.com/pub/infozip/zlib/ +Before reporting a problem, please check those sites to verify that +you have the latest version of zlib; otherwise get the latest version and +check whether the problem still exists or not. + +Mark Nelson wrote an article about zlib for the Jan. 1997 +issue of Dr. Dobb's Journal; a copy of the article is available in +http://web2.airmail.net/markn/articles/zlibtool/zlibtool.htm + +The changes made in version 1.1.3 are documented in the file ChangeLog. +The main changes since 1.1.2 are: + +- fix "an inflate input buffer bug that shows up on rare but persistent + occasions" (Mark) +- fix gzread and gztell for concatenated .gz files (Didier Le Botlan) +- fix gzseek(..., SEEK_SET) in write mode +- fix crc check after a gzeek (Frank Faubert) +- fix miniunzip when the last entry in a zip file is itself a zip file + (J Lillge) +- add contrib/asm586 and contrib/asm686 (Brian Raiter) + See http://www.muppetlabs.com/~breadbox/software/assembly.html +- add support for Delphi 3 in contrib/delphi (Bob Dellaca) +- add support for C++Builder 3 and Delphi 3 in contrib/delphi2 (Davide Moretti) +- do not exit prematurely in untgz if 0 at start of block (Magnus Holmgren) +- use macro EXTERN instead of extern to support DLL for BeOS (Sander Stoks) +- added a FAQ file + +plus many changes for portability. + +Unsupported third party contributions are provided in directory "contrib". + +A Java implementation of zlib is available in the Java Development Kit 1.1 +http://www.javasoft.com/products/JDK/1.1/docs/api/Package-java.util.zip.html +See the zlib home page http://www.cdrom.com/pub/infozip/zlib/ for details. + +A Perl interface to zlib written by Paul Marquess +is in the CPAN (Comprehensive Perl Archive Network) sites, such as: +ftp://ftp.cis.ufl.edu/pub/perl/CPAN/modules/by-module/Compress/Compress-Zlib* + +A Python interface to zlib written by A.M. Kuchling +is available in Python 1.5 and later versions, see +http://www.python.org/doc/lib/module-zlib.html + +A zlib binding for TCL written by Andreas Kupries +is availlable at http://www.westend.com/~kupries/doc/trf/man/man.html + +An experimental package to read and write files in .zip format, +written on top of zlib by Gilles Vollant , is +available at http://www.winimage.com/zLibDll/unzip.html +and also in the contrib/minizip directory of zlib. + + +Notes for some targets: + +- To build a Windows DLL version, include in a DLL project zlib.def, zlib.rc + and all .c files except example.c and minigzip.c; compile with -DZLIB_DLL + The zlib DLL support was initially done by Alessandro Iacopetti and is + now maintained by Gilles Vollant . Check the zlib DLL + home page at http://www.winimage.com/zLibDll + + From Visual Basic, you can call the DLL functions which do not take + a structure as argument: compress, uncompress and all gz* functions. + See contrib/visual-basic.txt for more information, or get + http://www.tcfb.com/dowseware/cmp-z-it.zip + +- For 64-bit Irix, deflate.c must be compiled without any optimization. + With -O, one libpng test fails. The test works in 32 bit mode (with + the -n32 compiler flag). The compiler bug has been reported to SGI. + +- zlib doesn't work with gcc 2.6.3 on a DEC 3000/300LX under OSF/1 2.1 + it works when compiled with cc. + +- on Digital Unix 4.0D (formely OSF/1) on AlphaServer, the cc option -std1 + is necessary to get gzprintf working correctly. This is done by configure. + +- zlib doesn't work on HP-UX 9.05 with some versions of /bin/cc. It works + with other compilers. Use "make test" to check your compiler. + +- gzdopen is not supported on RISCOS, BEOS and by some Mac compilers. + +- For Turbo C the small model is supported only with reduced performance to + avoid any far allocation; it was tested with -DMAX_WBITS=11 -DMAX_MEM_LEVEL=3 + +- For PalmOs, see http://www.cs.uit.no/~perm/PASTA/pilot/software.html + Per Harald Myrvang + + +Acknowledgments: + + The deflate format used by zlib was defined by Phil Katz. The deflate + and zlib specifications were written by L. Peter Deutsch. Thanks to all the + people who reported problems and suggested various improvements in zlib; + they are too numerous to cite here. + +Copyright notice: + + (C) 1995-1998 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + +If you use the zlib library in a product, we would appreciate *not* +receiving lengthy legal documents to sign. The sources are provided +for free but without warranty of any kind. The library has been +entirely written by Jean-loup Gailly and Mark Adler; it does not +include third-party code. + +If you redistribute modified sources, we would appreciate that you include +in the file ChangeLog history information documenting your changes. diff --git a/zlib/trees.c b/zlib/trees.c new file mode 100644 index 0000000..c98b000 --- /dev/null +++ b/zlib/trees.c @@ -0,0 +1,1214 @@ +/* trees.c -- output deflated data using Huffman coding + * Copyright (C) 1995-2002 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process uses several Huffman trees. The more + * common source values are represented by shorter bit sequences. + * + * Each code tree is stored in a compressed form which is itself + * a Huffman encoding of the lengths of all the code strings (in + * ascending order by source values). The actual code strings are + * reconstructed from the lengths in the inflate process, as described + * in the deflate specification. + * + * REFERENCES + * + * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". + * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc + * + * Storer, James A. + * Data Compression: Methods and Theory, pp. 49-50. + * Computer Science Press, 1988. ISBN 0-7167-8156-5. + * + * Sedgewick, R. + * Algorithms, p290. + * Addison-Wesley, 1983. ISBN 0-201-06672-6. + */ + +/* @(#) $Id: trees.c,v 1.2 2002/03/13 17:45:56 xodnizel Exp $ */ + +/* #define GEN_TREES_H */ + +#include "deflate.h" + +#ifdef DEBUG +# include +#endif + +/* =========================================================================== + * Constants + */ + +#define MAX_BL_BITS 7 +/* Bit length codes must not exceed MAX_BL_BITS bits */ + +#define END_BLOCK 256 +/* end of block literal code */ + +#define REP_3_6 16 +/* repeat previous bit length 3-6 times (2 bits of repeat count) */ + +#define REPZ_3_10 17 +/* repeat a zero length 3-10 times (3 bits of repeat count) */ + +#define REPZ_11_138 18 +/* repeat a zero length 11-138 times (7 bits of repeat count) */ + +local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ + = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; + +local const int extra_dbits[D_CODES] /* extra bits for each distance code */ + = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */ + = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; + +local const uch bl_order[BL_CODES] + = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; +/* The lengths of the bit length codes are sent in order of decreasing + * probability, to avoid transmitting the lengths for unused bit length codes. + */ + +#define Buf_size (8 * 2*sizeof(char)) +/* Number of bits used within bi_buf. (bi_buf might be implemented on + * more than 16 bits on some systems.) + */ + +/* =========================================================================== + * Local data. These are initialized only once. + */ + +#define DIST_CODE_LEN 512 /* see definition of array dist_code below */ + +#if defined(GEN_TREES_H) || !defined(STDC) +/* non ANSI compilers may not accept trees.h */ + +local ct_data static_ltree[L_CODES+2]; +/* The static literal tree. Since the bit lengths are imposed, there is no + * need for the L_CODES extra codes used during heap construction. However + * The codes 286 and 287 are needed to build a canonical tree (see _tr_init + * below). + */ + +local ct_data static_dtree[D_CODES]; +/* The static distance tree. (Actually a trivial tree since all codes use + * 5 bits.) + */ + +uch _dist_code[DIST_CODE_LEN]; +/* Distance codes. The first 256 values correspond to the distances + * 3 .. 258, the last 256 values correspond to the top 8 bits of + * the 15 bit distances. + */ + +uch _length_code[MAX_MATCH-MIN_MATCH+1]; +/* length code for each normalized match length (0 == MIN_MATCH) */ + +local int base_length[LENGTH_CODES]; +/* First normalized length for each code (0 = MIN_MATCH) */ + +local int base_dist[D_CODES]; +/* First normalized distance for each code (0 = distance of 1) */ + +#else +# include "trees.h" +#endif /* GEN_TREES_H */ + +struct static_tree_desc_s { + const ct_data *static_tree; /* static tree or NULL */ + const intf *extra_bits; /* extra bits for each code or NULL */ + int extra_base; /* base index for extra_bits */ + int elems; /* max number of elements in the tree */ + int max_length; /* max bit length for the codes */ +}; + +local static_tree_desc static_l_desc = +{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; + +local static_tree_desc static_d_desc = +{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; + +local static_tree_desc static_bl_desc = +{(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; + +/* =========================================================================== + * Local (static) routines in this file. + */ + +local void tr_static_init OF((void)); +local void init_block OF((deflate_state *s)); +local void pqdownheap OF((deflate_state *s, ct_data *tree, int k)); +local void gen_bitlen OF((deflate_state *s, tree_desc *desc)); +local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count)); +local void build_tree OF((deflate_state *s, tree_desc *desc)); +local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local void send_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local int build_bl_tree OF((deflate_state *s)); +local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, + int blcodes)); +local void compress_block OF((deflate_state *s, ct_data *ltree, + ct_data *dtree)); +local void set_data_type OF((deflate_state *s)); +local unsigned bi_reverse OF((unsigned value, int length)); +local void bi_windup OF((deflate_state *s)); +local void bi_flush OF((deflate_state *s)); +local void copy_block OF((deflate_state *s, charf *buf, unsigned len, + int header)); + +#ifdef GEN_TREES_H +local void gen_trees_header OF((void)); +#endif + +#ifndef DEBUG +# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) + /* Send a code of the given tree. c and tree must not have side effects */ + +#else /* DEBUG */ +# define send_code(s, c, tree) \ + { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \ + send_bits(s, tree[c].Code, tree[c].Len); } +#endif + +/* =========================================================================== + * Output a short LSB first on the stream. + * IN assertion: there is enough room in pendingBuf. + */ +#define put_short(s, w) { \ + put_byte(s, (uch)((w) & 0xff)); \ + put_byte(s, (uch)((ush)(w) >> 8)); \ +} + +/* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +#ifdef DEBUG +local void send_bits OF((deflate_state *s, int value, int length)); + +local void send_bits(s, value, length) + deflate_state *s; + int value; /* value to send */ + int length; /* number of bits */ +{ + Tracevv((stderr," l %2d v %4x ", length, value)); + Assert(length > 0 && length <= 15, "invalid length"); + s->bits_sent += (ulg)length; + + /* If not enough room in bi_buf, use (valid) bits from bi_buf and + * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) + * unused bits in value. + */ + if (s->bi_valid > (int)Buf_size - length) { + s->bi_buf |= (value << s->bi_valid); + put_short(s, s->bi_buf); + s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); + s->bi_valid += length - Buf_size; + } else { + s->bi_buf |= value << s->bi_valid; + s->bi_valid += length; + } +} +#else /* !DEBUG */ + +#define send_bits(s, value, length) \ +{ int len = length;\ + if (s->bi_valid > (int)Buf_size - len) {\ + int val = value;\ + s->bi_buf |= (val << s->bi_valid);\ + put_short(s, s->bi_buf);\ + s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ + s->bi_valid += len - Buf_size;\ + } else {\ + s->bi_buf |= (value) << s->bi_valid;\ + s->bi_valid += len;\ + }\ +} +#endif /* DEBUG */ + + +#define MAX(a,b) (a >= b ? a : b) +/* the arguments must not have side effects */ + +/* =========================================================================== + * Initialize the various 'constant' tables. + */ +local void tr_static_init() +{ +#if defined(GEN_TREES_H) || !defined(STDC) + static int static_init_done = 0; + int n; /* iterates over tree elements */ + int bits; /* bit counter */ + int length; /* length value */ + int code; /* code value */ + int dist; /* distance index */ + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + if (static_init_done) return; + + /* For some embedded targets, global variables are not initialized: */ + static_l_desc.static_tree = static_ltree; + static_l_desc.extra_bits = extra_lbits; + static_d_desc.static_tree = static_dtree; + static_d_desc.extra_bits = extra_dbits; + static_bl_desc.extra_bits = extra_blbits; + + /* Initialize the mapping length (0..255) -> length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES-1; code++) { + base_length[code] = length; + for (n = 0; n < (1< dist code (0..29) */ + dist = 0; + for (code = 0 ; code < 16; code++) { + base_dist[code] = dist; + for (n = 0; n < (1<>= 7; /* from now on, all distances are divided by 128 */ + for ( ; code < D_CODES; code++) { + base_dist[code] = dist << 7; + for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { + _dist_code[256 + dist++] = (uch)code; + } + } + Assert (dist == 256, "tr_static_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; + n = 0; + while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; + while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; + while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; + while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + static_dtree[n].Len = 5; + static_dtree[n].Code = bi_reverse((unsigned)n, 5); + } + static_init_done = 1; + +# ifdef GEN_TREES_H + gen_trees_header(); +# endif +#endif /* defined(GEN_TREES_H) || !defined(STDC) */ +} + +/* =========================================================================== + * Genererate the file trees.h describing the static trees. + */ +#ifdef GEN_TREES_H +# ifndef DEBUG +# include +# endif + +# define SEPARATOR(i, last, width) \ + ((i) == (last)? "\n};\n\n" : \ + ((i) % (width) == (width)-1 ? ",\n" : ", ")) + +void gen_trees_header() +{ + FILE *header = fopen("trees.h", "w"); + int i; + + Assert (header != NULL, "Can't open trees.h"); + fprintf(header, + "/* header created automatically with -DGEN_TREES_H */\n\n"); + + fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n"); + for (i = 0; i < L_CODES+2; i++) { + fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code, + static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5)); + } + + fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code, + static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5)); + } + + fprintf(header, "const uch _dist_code[DIST_CODE_LEN] = {\n"); + for (i = 0; i < DIST_CODE_LEN; i++) { + fprintf(header, "%2u%s", _dist_code[i], + SEPARATOR(i, DIST_CODE_LEN-1, 20)); + } + + fprintf(header, "const uch _length_code[MAX_MATCH-MIN_MATCH+1]= {\n"); + for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) { + fprintf(header, "%2u%s", _length_code[i], + SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20)); + } + + fprintf(header, "local const int base_length[LENGTH_CODES] = {\n"); + for (i = 0; i < LENGTH_CODES; i++) { + fprintf(header, "%1u%s", base_length[i], + SEPARATOR(i, LENGTH_CODES-1, 20)); + } + + fprintf(header, "local const int base_dist[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "%5u%s", base_dist[i], + SEPARATOR(i, D_CODES-1, 10)); + } + + fclose(header); +} +#endif /* GEN_TREES_H */ + +/* =========================================================================== + * Initialize the tree data structures for a new zlib stream. + */ +void _tr_init(s) + deflate_state *s; +{ + tr_static_init(); + + s->l_desc.dyn_tree = s->dyn_ltree; + s->l_desc.stat_desc = &static_l_desc; + + s->d_desc.dyn_tree = s->dyn_dtree; + s->d_desc.stat_desc = &static_d_desc; + + s->bl_desc.dyn_tree = s->bl_tree; + s->bl_desc.stat_desc = &static_bl_desc; + + s->bi_buf = 0; + s->bi_valid = 0; + s->last_eob_len = 8; /* enough lookahead for inflate */ +#ifdef DEBUG + s->compressed_len = 0L; + s->bits_sent = 0L; +#endif + + /* Initialize the first block of the first file: */ + init_block(s); +} + +/* =========================================================================== + * Initialize a new block. + */ +local void init_block(s) + deflate_state *s; +{ + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; + for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; + for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; + + s->dyn_ltree[END_BLOCK].Freq = 1; + s->opt_len = s->static_len = 0L; + s->last_lit = s->matches = 0; +} + +#define SMALLEST 1 +/* Index within the heap array of least frequent node in the Huffman tree */ + + +/* =========================================================================== + * Remove the smallest element from the heap and recreate the heap with + * one less element. Updates heap and heap_len. + */ +#define pqremove(s, tree, top) \ +{\ + top = s->heap[SMALLEST]; \ + s->heap[SMALLEST] = s->heap[s->heap_len--]; \ + pqdownheap(s, tree, SMALLEST); \ +} + +/* =========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ +#define smaller(tree, n, m, depth) \ + (tree[n].Freq < tree[m].Freq || \ + (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) + +/* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ +local void pqdownheap(s, tree, k) + deflate_state *s; + ct_data *tree; /* the tree to restore */ + int k; /* node to move down */ +{ + int v = s->heap[k]; + int j = k << 1; /* left son of k */ + while (j <= s->heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < s->heap_len && + smaller(tree, s->heap[j+1], s->heap[j], s->depth)) { + j++; + } + /* Exit if v is smaller than both sons */ + if (smaller(tree, v, s->heap[j], s->depth)) break; + + /* Exchange v with the smallest son */ + s->heap[k] = s->heap[j]; k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + s->heap[k] = v; +} + +/* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +local void gen_bitlen(s, desc) + deflate_state *s; + tree_desc *desc; /* the tree descriptor */ +{ + ct_data *tree = desc->dyn_tree; + int max_code = desc->max_code; + const ct_data *stree = desc->stat_desc->static_tree; + const intf *extra = desc->stat_desc->extra_bits; + int base = desc->stat_desc->extra_base; + int max_length = desc->stat_desc->max_length; + int h; /* heap index */ + int n, m; /* iterate over the tree elements */ + int bits; /* bit length */ + int xbits; /* extra bits */ + ush f; /* frequency */ + int overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */ + + for (h = s->heap_max+1; h < HEAP_SIZE; h++) { + n = s->heap[h]; + bits = tree[tree[n].Dad].Len + 1; + if (bits > max_length) bits = max_length, overflow++; + tree[n].Len = (ush)bits; + /* We overwrite tree[n].Dad which is no longer needed */ + + if (n > max_code) continue; /* not a leaf node */ + + s->bl_count[bits]++; + xbits = 0; + if (n >= base) xbits = extra[n-base]; + f = tree[n].Freq; + s->opt_len += (ulg)f * (bits + xbits); + if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits); + } + if (overflow == 0) return; + + Trace((stderr,"\nbit length overflow\n")); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length-1; + while (s->bl_count[bits] == 0) bits--; + s->bl_count[bits]--; /* move one leaf down the tree */ + s->bl_count[bits+1] += 2; /* move one overflow item as its brother */ + s->bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = s->bl_count[bits]; + while (n != 0) { + m = s->heap[--h]; + if (m > max_code) continue; + if (tree[m].Len != (unsigned) bits) { + Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); + s->opt_len += ((long)bits - (long)tree[m].Len) + *(long)tree[m].Freq; + tree[m].Len = (ush)bits; + } + n--; + } + } +} + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +local void gen_codes (tree, max_code, bl_count) + ct_data *tree; /* the tree to decorate */ + int max_code; /* largest code with non zero frequency */ + ushf *bl_count; /* number of codes at each bit length */ +{ + ush next_code[MAX_BITS+1]; /* next code value for each bit length */ + ush code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (code + bl_count[bits-1]) << 1; + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert (code + bl_count[MAX_BITS]-1 == (1<dyn_tree; + const ct_data *stree = desc->stat_desc->static_tree; + int elems = desc->stat_desc->elems; + int n, m; /* iterate over heap elements */ + int max_code = -1; /* largest code with non zero frequency */ + int node; /* new node being created */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + s->heap_len = 0, s->heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].Freq != 0) { + s->heap[++(s->heap_len)] = max_code = n; + s->depth[n] = 0; + } else { + tree[n].Len = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (s->heap_len < 2) { + node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0); + tree[node].Freq = 1; + s->depth[node] = 0; + s->opt_len--; if (stree) s->static_len -= stree[node].Len; + /* node is 0 or 1 so it does not have extra bits */ + } + desc->max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + node = elems; /* next internal node of the tree */ + do { + pqremove(s, tree, n); /* n = node of least frequency */ + m = s->heap[SMALLEST]; /* m = node of next least frequency */ + + s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */ + s->heap[--(s->heap_max)] = m; + + /* Create a new node father of n and m */ + tree[node].Freq = tree[n].Freq + tree[m].Freq; + s->depth[node] = (uch) (MAX(s->depth[n], s->depth[m]) + 1); + tree[n].Dad = tree[m].Dad = (ush)node; +#ifdef DUMP_BL_TREE + if (tree == s->bl_tree) { + fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", + node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); + } +#endif + /* and insert the new node in the heap */ + s->heap[SMALLEST] = node++; + pqdownheap(s, tree, SMALLEST); + + } while (s->heap_len >= 2); + + s->heap[--(s->heap_max)] = s->heap[SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen(s, (tree_desc *)desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes ((ct_data *)tree, max_code, s->bl_count); +} + +/* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. + */ +local void scan_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + if (nextlen == 0) max_count = 138, min_count = 3; + tree[max_code+1].Len = (ush)0xffff; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + s->bl_tree[curlen].Freq += count; + } else if (curlen != 0) { + if (curlen != prevlen) s->bl_tree[curlen].Freq++; + s->bl_tree[REP_3_6].Freq++; + } else if (count <= 10) { + s->bl_tree[REPZ_3_10].Freq++; + } else { + s->bl_tree[REPZ_11_138].Freq++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +local void send_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + /* tree[max_code+1].Len = -1; */ /* guard already set */ + if (nextlen == 0) max_count = 138, min_count = 3; + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { send_code(s, curlen, s->bl_tree); } while (--count != 0); + + } else if (curlen != 0) { + if (curlen != prevlen) { + send_code(s, curlen, s->bl_tree); count--; + } + Assert(count >= 3 && count <= 6, " 3_6?"); + send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2); + + } else if (count <= 10) { + send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3); + + } else { + send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +local int build_bl_tree(s) + deflate_state *s; +{ + int max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code); + scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code); + + /* Build the bit length tree: */ + build_tree(s, (tree_desc *)(&(s->bl_desc))); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { + if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; + } + /* Update opt_len to include the bit length tree and counts */ + s->opt_len += 3*(max_blindex+1) + 5+5+4; + Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", + s->opt_len, s->static_len)); + + return max_blindex; +} + +/* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +local void send_all_trees(s, lcodes, dcodes, blcodes) + deflate_state *s; + int lcodes, dcodes, blcodes; /* number of codes for each tree */ +{ + int rank; /* index in bl_order */ + + Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, + "too many codes"); + Tracev((stderr, "\nbl counts: ")); + send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */ + send_bits(s, dcodes-1, 5); + send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + Tracev((stderr, "\nbl code %2d ", bl_order[rank])); + send_bits(s, s->bl_tree[bl_order[rank]].Len, 3); + } + Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */ + Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */ + Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); +} + +/* =========================================================================== + * Send a stored block + */ +void _tr_stored_block(s, buf, stored_len, eof) + deflate_state *s; + charf *buf; /* input block */ + ulg stored_len; /* length of input block */ + int eof; /* true if this is the last block for a file */ +{ + send_bits(s, (STORED_BLOCK<<1)+eof, 3); /* send block type */ +#ifdef DEBUG + s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; + s->compressed_len += (stored_len + 4) << 3; +#endif + copy_block(s, buf, (unsigned)stored_len, 1); /* with header */ +} + +/* =========================================================================== + * Send one empty static block to give enough lookahead for inflate. + * This takes 10 bits, of which 7 may remain in the bit buffer. + * The current inflate code requires 9 bits of lookahead. If the + * last two codes for the previous block (real code plus EOB) were coded + * on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode + * the last real code. In this case we send two empty static blocks instead + * of one. (There are no problems if the previous block is stored or fixed.) + * To simplify the code, we assume the worst case of last real code encoded + * on one bit only. + */ +void _tr_align(s) + deflate_state *s; +{ + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ +#endif + bi_flush(s); + /* Of the 10 bits for the empty block, we have already sent + * (10 - bi_valid) bits. The lookahead for the last real code (before + * the EOB of the previous block) was thus at least one plus the length + * of the EOB plus what we have just sent of the empty static block. + */ + if (1 + s->last_eob_len + 10 - s->bi_valid < 9) { + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; +#endif + bi_flush(s); + } + s->last_eob_len = 7; +} + +/* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. + */ +void _tr_flush_block(s, buf, stored_len, eof) + deflate_state *s; + charf *buf; /* input block, or NULL if too old */ + ulg stored_len; /* length of input block */ + int eof; /* true if this is the last block for a file */ +{ + ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + int max_blindex = 0; /* index of last bit length code of non zero freq */ + + /* Build the Huffman trees unless a stored block is forced */ + if (s->level > 0) { + + /* Check if the file is ascii or binary */ + if (s->data_type == Z_UNKNOWN) set_data_type(s); + + /* Construct the literal and distance trees */ + build_tree(s, (tree_desc *)(&(s->l_desc))); + Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + + build_tree(s, (tree_desc *)(&(s->d_desc))); + Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(s); + + /* Determine the best encoding. Compute first the block length in bytes*/ + opt_lenb = (s->opt_len+3+7)>>3; + static_lenb = (s->static_len+3+7)>>3; + + Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", + opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, + s->last_lit)); + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + + } else { + Assert(buf != (char*)0, "lost buf"); + opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ + } + +#ifdef FORCE_STORED + if (buf != (char*)0) { /* force stored block */ +#else + if (stored_len+4 <= opt_lenb && buf != (char*)0) { + /* 4: two words for the lengths */ +#endif + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + _tr_stored_block(s, buf, stored_len, eof); + +#ifdef FORCE_STATIC + } else if (static_lenb >= 0) { /* force static trees */ +#else + } else if (static_lenb == opt_lenb) { +#endif + send_bits(s, (STATIC_TREES<<1)+eof, 3); + compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->static_len; +#endif + } else { + send_bits(s, (DYN_TREES<<1)+eof, 3); + send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1, + max_blindex+1); + compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->opt_len; +#endif + } + Assert (s->compressed_len == s->bits_sent, "bad compressed size"); + /* The above check is made mod 2^32, for files larger than 512 MB + * and uLong implemented on 32 bits. + */ + init_block(s); + + if (eof) { + bi_windup(s); +#ifdef DEBUG + s->compressed_len += 7; /* align on byte boundary */ +#endif + } + Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, + s->compressed_len-7*eof)); +} + +/* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +int _tr_tally (s, dist, lc) + deflate_state *s; + unsigned dist; /* distance of matched string */ + unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ +{ + s->d_buf[s->last_lit] = (ush)dist; + s->l_buf[s->last_lit++] = (uch)lc; + if (dist == 0) { + /* lc is the unmatched char */ + s->dyn_ltree[lc].Freq++; + } else { + s->matches++; + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + Assert((ush)dist < (ush)MAX_DIST(s) && + (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && + (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); + + s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++; + s->dyn_dtree[d_code(dist)].Freq++; + } + +#ifdef TRUNCATE_BLOCK + /* Try to guess if it is profitable to stop the current block here */ + if ((s->last_lit & 0x1fff) == 0 && s->level > 2) { + /* Compute an upper bound for the compressed length */ + ulg out_length = (ulg)s->last_lit*8L; + ulg in_length = (ulg)((long)s->strstart - s->block_start); + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (ulg)s->dyn_dtree[dcode].Freq * + (5L+extra_dbits[dcode]); + } + out_length >>= 3; + Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", + s->last_lit, in_length, out_length, + 100L - out_length*100L/in_length)); + if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1; + } +#endif + return (s->last_lit == s->lit_bufsize-1); + /* We avoid equality with lit_bufsize because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +local void compress_block(s, ltree, dtree) + deflate_state *s; + ct_data *ltree; /* literal tree */ + ct_data *dtree; /* distance tree */ +{ + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned lx = 0; /* running index in l_buf */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (s->last_lit != 0) do { + dist = s->d_buf[lx]; + lc = s->l_buf[lx++]; + if (dist == 0) { + send_code(s, lc, ltree); /* send a literal byte */ + Tracecv(isgraph(lc), (stderr," '%c' ", lc)); + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = _length_code[lc]; + send_code(s, code+LITERALS+1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= base_length[code]; + send_bits(s, lc, extra); /* send the extra length bits */ + } + dist--; /* dist is now the match distance - 1 */ + code = d_code(dist); + Assert (code < D_CODES, "bad d_code"); + + send_code(s, code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= base_dist[code]; + send_bits(s, dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + + /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ + Assert(s->pending < s->lit_bufsize + 2*lx, "pendingBuf overflow"); + + } while (lx < s->last_lit); + + send_code(s, END_BLOCK, ltree); + s->last_eob_len = ltree[END_BLOCK].Len; +} + +/* =========================================================================== + * Set the data type to ASCII or BINARY, using a crude approximation: + * binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise. + * IN assertion: the fields freq of dyn_ltree are set and the total of all + * frequencies does not exceed 64K (to fit in an int on 16 bit machines). + */ +local void set_data_type(s) + deflate_state *s; +{ + int n = 0; + unsigned ascii_freq = 0; + unsigned bin_freq = 0; + while (n < 7) bin_freq += s->dyn_ltree[n++].Freq; + while (n < 128) ascii_freq += s->dyn_ltree[n++].Freq; + while (n < LITERALS) bin_freq += s->dyn_ltree[n++].Freq; + s->data_type = (Byte)(bin_freq > (ascii_freq >> 2) ? Z_BINARY : Z_ASCII); +} + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +local unsigned bi_reverse(code, len) + unsigned code; /* the value to invert */ + int len; /* its bit length */ +{ + register unsigned res = 0; + do { + res |= code & 1; + code >>= 1, res <<= 1; + } while (--len > 0); + return res >> 1; +} + +/* =========================================================================== + * Flush the bit buffer, keeping at most 7 bits in it. + */ +local void bi_flush(s) + deflate_state *s; +{ + if (s->bi_valid == 16) { + put_short(s, s->bi_buf); + s->bi_buf = 0; + s->bi_valid = 0; + } else if (s->bi_valid >= 8) { + put_byte(s, (Byte)s->bi_buf); + s->bi_buf >>= 8; + s->bi_valid -= 8; + } +} + +/* =========================================================================== + * Flush the bit buffer and align the output on a byte boundary + */ +local void bi_windup(s) + deflate_state *s; +{ + if (s->bi_valid > 8) { + put_short(s, s->bi_buf); + } else if (s->bi_valid > 0) { + put_byte(s, (Byte)s->bi_buf); + } + s->bi_buf = 0; + s->bi_valid = 0; +#ifdef DEBUG + s->bits_sent = (s->bits_sent+7) & ~7; +#endif +} + +/* =========================================================================== + * Copy a stored block, storing first the length and its + * one's complement if requested. + */ +local void copy_block(s, buf, len, header) + deflate_state *s; + charf *buf; /* the input data */ + unsigned len; /* its length */ + int header; /* true if block header must be written */ +{ + bi_windup(s); /* align on byte boundary */ + s->last_eob_len = 8; /* enough lookahead for inflate */ + + if (header) { + put_short(s, (ush)len); + put_short(s, (ush)~len); +#ifdef DEBUG + s->bits_sent += 2*16; +#endif + } +#ifdef DEBUG + s->bits_sent += (ulg)len<<3; +#endif + while (len--) { + put_byte(s, *buf++); + } +} diff --git a/zlib/trees.h b/zlib/trees.h new file mode 100644 index 0000000..72facf9 --- /dev/null +++ b/zlib/trees.h @@ -0,0 +1,128 @@ +/* header created automatically with -DGEN_TREES_H */ + +local const ct_data static_ltree[L_CODES+2] = { +{{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}}, +{{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}}, +{{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}}, +{{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}}, +{{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}}, +{{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}}, +{{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}}, +{{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}}, +{{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}}, +{{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}}, +{{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}}, +{{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}}, +{{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}}, +{{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}}, +{{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}}, +{{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}}, +{{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}}, +{{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}}, +{{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}}, +{{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}}, +{{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}}, +{{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}}, +{{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}}, +{{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}}, +{{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}}, +{{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}}, +{{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}}, +{{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}}, +{{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}}, +{{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}}, +{{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}}, +{{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}}, +{{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}}, +{{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}}, +{{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}}, +{{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}}, +{{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}}, +{{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}}, +{{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}}, +{{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}}, +{{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}}, +{{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}}, +{{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}}, +{{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}}, +{{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}}, +{{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}}, +{{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}}, +{{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}}, +{{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}}, +{{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}}, +{{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}}, +{{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}}, +{{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}}, +{{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}}, +{{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}}, +{{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}}, +{{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}}, +{{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}} +}; + +local const ct_data static_dtree[D_CODES] = { +{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}}, +{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}}, +{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}}, +{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}}, +{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}}, +{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}} +}; + +const uch _dist_code[DIST_CODE_LEN] = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, +10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, +11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, +12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, +18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 +}; + +const uch _length_code[MAX_MATCH-MIN_MATCH+1]= { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, +13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, +17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, +19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, +22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 +}; + +local const int base_length[LENGTH_CODES] = { +0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, +64, 80, 96, 112, 128, 160, 192, 224, 0 +}; + +local const int base_dist[D_CODES] = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, + 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, + 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 +}; + diff --git a/zlib/uncompr.c b/zlib/uncompr.c new file mode 100644 index 0000000..3a71259 --- /dev/null +++ b/zlib/uncompr.c @@ -0,0 +1,58 @@ +/* uncompr.c -- decompress a memory buffer + * Copyright (C) 1995-2002 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id: uncompr.c,v 1.2 2002/03/13 17:45:56 xodnizel Exp $ */ + +#include "zlib.h" + +/* =========================================================================== + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + This function can be used to decompress a whole file at once if the + input file is mmap'ed. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted. +*/ +int ZEXPORT uncompress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; + + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + + err = inflateInit(&stream); + if (err != Z_OK) return err; + + err = inflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + inflateEnd(&stream); + return err == Z_OK ? Z_BUF_ERROR : err; + } + *destLen = stream.total_out; + + err = inflateEnd(&stream); + return err; +} diff --git a/zlib/unzip.c b/zlib/unzip.c new file mode 100644 index 0000000..d156fae --- /dev/null +++ b/zlib/unzip.c @@ -0,0 +1,1301 @@ +/* unzip.c -- IO on .zip files using zlib + Version 0.15 beta, Mar 19th, 1998, + + Read unzip.h for more info +*/ + + +#include +#include +#include +#include "zlib.h" +#include "unzip.h" + +#ifdef STDC +# include +# include +# include +#endif +#ifdef NO_ERRNO_H + extern int errno; +#else +# include +#endif + + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + + + +#if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES) && \ + !defined(CASESENSITIVITYDEFAULT_NO) +#define CASESENSITIVITYDEFAULT_NO +#endif + + +#ifndef UNZ_BUFSIZE +#define UNZ_BUFSIZE (16384) +#endif + +#ifndef UNZ_MAXFILENAMEINZIP +#define UNZ_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) + + +/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */ + +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif + +#ifndef SEEK_END +#define SEEK_END 2 +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +const char unz_copyright[] = + " unzip 0.15 Copyright 1998 Gilles Vollant "; + +/* unz_file_info_interntal contain internal info about a file in zipfile*/ +typedef struct unz_file_info_internal_s +{ + uLong offset_curfile;/* relative offset of local header 4 bytes */ +} unz_file_info_internal; + + +/* file_in_zip_read_info_s contain internal information about a file in zipfile, + when reading and decompress it */ +typedef struct +{ + char *read_buffer; /* internal buffer for compressed data */ + z_stream stream; /* zLib stream structure for inflate */ + + uLong pos_in_zipfile; /* position in byte on the zipfile, for fseek*/ + uLong stream_initialised; /* flag set if stream structure is initialised*/ + + uLong offset_local_extrafield;/* offset of the local extra field */ + uInt size_local_extrafield;/* size of the local extra field */ + uLong pos_local_extrafield; /* position in the local extra field in read*/ + + uLong crc32; /* crc32 of all data uncompressed */ + uLong crc32_wait; /* crc32 we must obtain after decompress all */ + uLong rest_read_compressed; /* number of byte to be decompressed */ + uLong rest_read_uncompressed;/*number of byte to be obtained after decomp*/ + FILE* file; /* io structore of the zipfile */ + uLong compression_method; /* compression method (0==store) */ + uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ +} file_in_zip_read_info_s; + + +/* unz_s contain internal information about the zipfile +*/ +typedef struct +{ + FILE* file; /* io structore of the zipfile */ + unz_global_info gi; /* public global information */ + uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + uLong num_file; /* number of the current file in the zipfile*/ + uLong pos_in_central_dir; /* pos of the current file in the central dir*/ + uLong current_file_ok; /* flag about the usability of the current file*/ + uLong central_pos; /* position of the beginning of the central dir*/ + + uLong size_central_dir; /* size of the central directory */ + uLong offset_central_dir; /* offset of start of central directory with + respect to the starting disk number */ + + unz_file_info cur_file_info; /* public info about the current file in zip*/ + unz_file_info_internal cur_file_info_internal; /* private info about it*/ + file_in_zip_read_info_s* pfile_in_zip_read; /* structure about the current + file if we are decompressing it */ +} unz_s; + + +/* =========================================================================== + Read a byte from a gz_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. +*/ + + +local int unzlocal_getByte(fin,pi) + FILE *fin; + int *pi; +{ + unsigned char c; + int err = fread(&c, 1, 1, fin); + if (err==1) + { + *pi = (int)c; + return UNZ_OK; + } + else + { + if (ferror(fin)) + return UNZ_ERRNO; + else + return UNZ_EOF; + } +} + + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets +*/ +local int unzlocal_getShort (fin,pX) + FILE* fin; + uLong *pX; +{ + uLong x ; + int i; + int err; + + err = unzlocal_getByte(fin,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int unzlocal_getLong (fin,pX) + FILE* fin; + uLong *pX; +{ + uLong x ; + int i; + int err; + + err = unzlocal_getByte(fin,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<16; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<24; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + + +/* My own strcmpi / strcasecmp */ +local int strcmpcasenosensitive_internal (fileName1,fileName2) + const char* fileName1; + const char* fileName2; +{ + for (;;) + { + char c1=*(fileName1++); + char c2=*(fileName2++); + if ((c1>='a') && (c1<='z')) + c1 -= 0x20; + if ((c2>='a') && (c2<='z')) + c2 -= 0x20; + if (c1=='\0') + return ((c2=='\0') ? 0 : -1); + if (c2=='\0') + return 1; + if (c1c2) + return 1; + } +} + + +#ifdef CASESENSITIVITYDEFAULT_NO +#define CASESENSITIVITYDEFAULTVALUE 2 +#else +#define CASESENSITIVITYDEFAULTVALUE 1 +#endif + +#ifndef STRCMPCASENOSENTIVEFUNCTION +#define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal +#endif + +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) + +*/ +extern int ZEXPORT unzStringFileNameCompare (fileName1,fileName2,iCaseSensitivity) + const char* fileName1; + const char* fileName2; + int iCaseSensitivity; +{ + if (iCaseSensitivity==0) + iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE; + + if (iCaseSensitivity==1) + return strcmp(fileName1,fileName2); + + return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2); +} + +#define BUFREADCOMMENT (0x400) + +/* + Locate the Central directory of a zipfile (at the end, just before + the global comment) +*/ +local uLong unzlocal_SearchCentralDir(fin) + FILE *fin; +{ + unsigned char* buf; + uLong uSizeFile; + uLong uBackRead; + uLong uMaxBack=0xffff; /* maximum size of global comment */ + uLong uPosFound=0; + + if (fseek(fin,0,SEEK_END) != 0) + return 0; + + + uSizeFile = ftell( fin ); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); + if (fseek(fin,uReadPos,SEEK_SET)!=0) + break; + + if (fread(buf,(uInt)uReadSize,1,fin)!=1) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + return uPosFound; +} + +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows NT computer "c:\\test\\zlib109.zip" or on an Unix computer + "zlib/zlib109.zip". + If the zipfile cannot be opened (file don't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. +*/ +extern unzFile ZEXPORT unzOpen (path) + const char *path; +{ + unz_s us; + unz_s *s; + uLong central_pos,uL; + FILE * fin ; + + uLong number_disk; /* number of the current dist, used for + spaning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number the the disk with central dir, used + for spaning ZIP, unsupported, always 0*/ + uLong number_entry_CD; /* total number of entries in + the central dir + (same than number_entry on nospan) */ + + int err=UNZ_OK; + + if (unz_copyright[0]!=' ') + return NULL; + + fin=fopen(path,"rb"); + if (fin==NULL) + return NULL; + + central_pos = unzlocal_SearchCentralDir(fin); + if (central_pos==0) + err=UNZ_ERRNO; + + if (fseek(fin,central_pos,SEEK_SET)!=0) + err=UNZ_ERRNO; + + /* the signature, already checked */ + if (unzlocal_getLong(fin,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of this disk */ + if (unzlocal_getShort(fin,&number_disk)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of the disk with the start of the central directory */ + if (unzlocal_getShort(fin,&number_disk_with_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central dir on this disk */ + if (unzlocal_getShort(fin,&us.gi.number_entry)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central dir */ + if (unzlocal_getShort(fin,&number_entry_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + if ((number_entry_CD!=us.gi.number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=UNZ_BADZIPFILE; + + /* size of the central directory */ + if (unzlocal_getLong(fin,&us.size_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + /* offset of start of central directory with respect to the + starting disk number */ + if (unzlocal_getLong(fin,&us.offset_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + /* zipfile comment length */ + if (unzlocal_getShort(fin,&us.gi.size_comment)!=UNZ_OK) + err=UNZ_ERRNO; + + if ((central_pospfile_in_zip_read!=NULL) + unzCloseCurrentFile(file); + + fclose(s->file); + TRYFREE(s); + return UNZ_OK; +} + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ +extern int ZEXPORT unzGetGlobalInfo (file,pglobal_info) + unzFile file; + unz_global_info *pglobal_info; +{ + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + *pglobal_info=s->gi; + return UNZ_OK; +} + + +/* + Translate date/time from Dos format to tm_unz (readable more easilty) +*/ +local void unzlocal_DosDateToTmuDate (ulDosDate, ptm) + uLong ulDosDate; + tm_unz* ptm; +{ + uLong uDate; + uDate = (uLong)(ulDosDate>>16); + ptm->tm_mday = (uInt)(uDate&0x1f) ; + ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; + ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; + + ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); + ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; + ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; +} + +/* + Get Info about the current file in the zipfile, with internal only info +*/ +local int unzlocal_GetCurrentFileInfoInternal OF((unzFile file, + unz_file_info *pfile_info, + unz_file_info_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); + +local int unzlocal_GetCurrentFileInfoInternal (file, + pfile_info, + pfile_info_internal, + szFileName, fileNameBufferSize, + extraField, extraFieldBufferSize, + szComment, commentBufferSize) + unzFile file; + unz_file_info *pfile_info; + unz_file_info_internal *pfile_info_internal; + char *szFileName; + uLong fileNameBufferSize; + void *extraField; + uLong extraFieldBufferSize; + char *szComment; + uLong commentBufferSize; +{ + unz_s* s; + unz_file_info file_info; + unz_file_info_internal file_info_internal; + int err=UNZ_OK; + uLong uMagic; + long lSeek=0; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (fseek(s->file,s->pos_in_central_dir+s->byte_before_the_zipfile,SEEK_SET)!=0) + err=UNZ_ERRNO; + + + /* we check the magic */ + if (err==UNZ_OK) + { + if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x02014b50) + err=UNZ_BADZIPFILE; + } + + if (unzlocal_getShort(s->file,&file_info.version) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.version_needed) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.flag) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.compression_method) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.dosDate) != UNZ_OK) + err=UNZ_ERRNO; + + unzlocal_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); + + if (unzlocal_getLong(s->file,&file_info.crc) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.compressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.uncompressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_filename) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_file_extra) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_file_comment) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.disk_num_start) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.internal_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.external_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info_internal.offset_curfile) != UNZ_OK) + err=UNZ_ERRNO; + + lSeek+=file_info.size_filename; + if ((err==UNZ_OK) && (szFileName!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_filename0) && (fileNameBufferSize>0)) + if (fread(szFileName,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + lSeek -= uSizeRead; + } + + + if ((err==UNZ_OK) && (extraField!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_extrafile,lSeek,SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) + if (fread(extraField,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + lSeek += file_info.size_file_extra - uSizeRead; + } + else + lSeek+=file_info.size_file_extra; + + + if ((err==UNZ_OK) && (szComment!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_commentfile,lSeek,SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + if ((file_info.size_file_comment>0) && (commentBufferSize>0)) + if (fread(szComment,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + lSeek+=file_info.size_file_comment - uSizeRead; + } + else + lSeek+=file_info.size_file_comment; + + if ((err==UNZ_OK) && (pfile_info!=NULL)) + *pfile_info=file_info; + + if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) + *pfile_info_internal=file_info_internal; + + return err; +} + + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. +*/ +extern int ZEXPORT unzGetCurrentFileInfo (file, + pfile_info, + szFileName, fileNameBufferSize, + extraField, extraFieldBufferSize, + szComment, commentBufferSize) + unzFile file; + unz_file_info *pfile_info; + char *szFileName; + uLong fileNameBufferSize; + void *extraField; + uLong extraFieldBufferSize; + char *szComment; + uLong commentBufferSize; +{ + return unzlocal_GetCurrentFileInfoInternal(file,pfile_info,NULL, + szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, + szComment,commentBufferSize); +} + +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ +extern int ZEXPORT unzGoToFirstFile (file) + unzFile file; +{ + int err=UNZ_OK; + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + s->pos_in_central_dir=s->offset_central_dir; + s->num_file=0; + err=unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ +extern int ZEXPORT unzGoToNextFile (file) + unzFile file; +{ + unz_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + if (s->num_file+1==s->gi.number_entry) + return UNZ_END_OF_LIST_OF_FILE; + + s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; + s->num_file++; + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzipStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ +extern int ZEXPORT unzLocateFile (file, szFileName, iCaseSensitivity) + unzFile file; + const char *szFileName; + int iCaseSensitivity; +{ + unz_s* s; + int err; + + + uLong num_fileSaved; + uLong pos_in_central_dirSaved; + + + if (file==NULL) + return UNZ_PARAMERROR; + + if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP) + return UNZ_PARAMERROR; + + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + num_fileSaved = s->num_file; + pos_in_central_dirSaved = s->pos_in_central_dir; + + err = unzGoToFirstFile(file); + + while (err == UNZ_OK) + { + char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; + unzGetCurrentFileInfo(file,NULL, + szCurrentFileName,sizeof(szCurrentFileName)-1, + NULL,0,NULL,0); + if (unzStringFileNameCompare(szCurrentFileName, + szFileName,iCaseSensitivity)==0) + return UNZ_OK; + err = unzGoToNextFile(file); + } + + s->num_file = num_fileSaved ; + s->pos_in_central_dir = pos_in_central_dirSaved ; + return err; +} + + +/* + Read the local header of the current zipfile + Check the coherency of the local header and info in the end of central + directory about this file + store in *piSizeVar the size of extra info in local header + (filename and size of extra field data) +*/ +local int unzlocal_CheckCurrentFileCoherencyHeader (s,piSizeVar, + poffset_local_extrafield, + psize_local_extrafield) + unz_s* s; + uInt* piSizeVar; + uLong *poffset_local_extrafield; + uInt *psize_local_extrafield; +{ + uLong uMagic,uData,uFlags; + uLong size_filename; + uLong size_extra_field; + int err=UNZ_OK; + + *piSizeVar = 0; + *poffset_local_extrafield = 0; + *psize_local_extrafield = 0; + + if (fseek(s->file,s->cur_file_info_internal.offset_curfile + + s->byte_before_the_zipfile,SEEK_SET)!=0) + return UNZ_ERRNO; + + + if (err==UNZ_OK) + { + if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x04034b50) + err=UNZ_BADZIPFILE; + } + + if (unzlocal_getShort(s->file,&uData) != UNZ_OK) + err=UNZ_ERRNO; +/* + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) + err=UNZ_BADZIPFILE; +*/ + if (unzlocal_getShort(s->file,&uFlags) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&uData) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) + err=UNZ_BADZIPFILE; + + if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* date/time */ + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* crc */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* size compr */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* size uncompr */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + + if (unzlocal_getShort(s->file,&size_filename) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) + err=UNZ_BADZIPFILE; + + *piSizeVar += (uInt)size_filename; + + if (unzlocal_getShort(s->file,&size_extra_field) != UNZ_OK) + err=UNZ_ERRNO; + *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + + SIZEZIPLOCALHEADER + size_filename; + *psize_local_extrafield = (uInt)size_extra_field; + + *piSizeVar += (uInt)size_extra_field; + + return err; +} + +/* + Open for reading data the current file in the zipfile. + If there is no error and the file is opened, the return value is UNZ_OK. +*/ +extern int ZEXPORT unzOpenCurrentFile (file) + unzFile file; +{ + int err=UNZ_OK; + int Store; + uInt iSizeVar; + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uLong offset_local_extrafield; /* offset of the local extra field */ + uInt size_local_extrafield; /* size of the local extra field */ + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_PARAMERROR; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + if (unzlocal_CheckCurrentFileCoherencyHeader(s,&iSizeVar, + &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) + return UNZ_BADZIPFILE; + + pfile_in_zip_read_info = (file_in_zip_read_info_s*) + ALLOC(sizeof(file_in_zip_read_info_s)); + if (pfile_in_zip_read_info==NULL) + return UNZ_INTERNALERROR; + + pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE); + pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; + pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; + pfile_in_zip_read_info->pos_local_extrafield=0; + + if (pfile_in_zip_read_info->read_buffer==NULL) + { + TRYFREE(pfile_in_zip_read_info); + return UNZ_INTERNALERROR; + } + + pfile_in_zip_read_info->stream_initialised=0; + + if ((s->cur_file_info.compression_method!=0) && + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + Store = s->cur_file_info.compression_method==0; + + pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; + pfile_in_zip_read_info->crc32=0; + pfile_in_zip_read_info->compression_method = + s->cur_file_info.compression_method; + pfile_in_zip_read_info->file=s->file; + pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; + + pfile_in_zip_read_info->stream.total_out = 0; + + if (!Store) + { + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + + err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=1; + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. + * In unzip, i don't wait absolutely Z_STREAM_END because I known the + * size of both compressed and uncompressed data + */ + } + pfile_in_zip_read_info->rest_read_compressed = + s->cur_file_info.compressed_size ; + pfile_in_zip_read_info->rest_read_uncompressed = + s->cur_file_info.uncompressed_size ; + + + pfile_in_zip_read_info->pos_in_zipfile = + s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + + iSizeVar; + + pfile_in_zip_read_info->stream.avail_in = (uInt)0; + + + s->pfile_in_zip_read = pfile_in_zip_read_info; + return UNZ_OK; +} + + +/* + Read bytes from the current file. + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ +extern int ZEXPORT unzReadCurrentFile (file, buf, len) + unzFile file; + voidp buf; + unsigned len; +{ + int err=UNZ_OK; + uInt iRead = 0; + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if ((pfile_in_zip_read_info->read_buffer == NULL)) + return UNZ_END_OF_LIST_OF_FILE; + if (len==0) + return 0; + + pfile_in_zip_read_info->stream.next_out = (Bytef*)buf; + + pfile_in_zip_read_info->stream.avail_out = (uInt)len; + + if (len>pfile_in_zip_read_info->rest_read_uncompressed) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_uncompressed; + + while (pfile_in_zip_read_info->stream.avail_out>0) + { + if ((pfile_in_zip_read_info->stream.avail_in==0) && + (pfile_in_zip_read_info->rest_read_compressed>0)) + { + uInt uReadThis = UNZ_BUFSIZE; + if (pfile_in_zip_read_info->rest_read_compressedrest_read_compressed; + if (uReadThis == 0) + return UNZ_EOF; + if (fseek(pfile_in_zip_read_info->file, + pfile_in_zip_read_info->pos_in_zipfile + + pfile_in_zip_read_info->byte_before_the_zipfile,SEEK_SET)!=0) + return UNZ_ERRNO; + if (fread(pfile_in_zip_read_info->read_buffer,uReadThis,1, + pfile_in_zip_read_info->file)!=1) + return UNZ_ERRNO; + pfile_in_zip_read_info->pos_in_zipfile += uReadThis; + + pfile_in_zip_read_info->rest_read_compressed-=uReadThis; + + pfile_in_zip_read_info->stream.next_in = + (Bytef*)pfile_in_zip_read_info->read_buffer; + pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; + } + + if (pfile_in_zip_read_info->compression_method==0) + { + uInt uDoCopy,i ; + if (pfile_in_zip_read_info->stream.avail_out < + pfile_in_zip_read_info->stream.avail_in) + uDoCopy = pfile_in_zip_read_info->stream.avail_out ; + else + uDoCopy = pfile_in_zip_read_info->stream.avail_in ; + + for (i=0;istream.next_out+i) = + *(pfile_in_zip_read_info->stream.next_in+i); + + pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32, + pfile_in_zip_read_info->stream.next_out, + uDoCopy); + pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; + pfile_in_zip_read_info->stream.avail_in -= uDoCopy; + pfile_in_zip_read_info->stream.avail_out -= uDoCopy; + pfile_in_zip_read_info->stream.next_out += uDoCopy; + pfile_in_zip_read_info->stream.next_in += uDoCopy; + pfile_in_zip_read_info->stream.total_out += uDoCopy; + iRead += uDoCopy; + } + else + { + uLong uTotalOutBefore,uTotalOutAfter; + const Bytef *bufBefore; + uLong uOutThis; + int flush=Z_SYNC_FLUSH; + + uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; + bufBefore = pfile_in_zip_read_info->stream.next_out; + + /* + if ((pfile_in_zip_read_info->rest_read_uncompressed == + pfile_in_zip_read_info->stream.avail_out) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + flush = Z_FINISH; + */ + err=inflate(&pfile_in_zip_read_info->stream,flush); + + uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; + uOutThis = uTotalOutAfter-uTotalOutBefore; + + pfile_in_zip_read_info->crc32 = + crc32(pfile_in_zip_read_info->crc32,bufBefore, + (uInt)(uOutThis)); + + pfile_in_zip_read_info->rest_read_uncompressed -= + uOutThis; + + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + + if (err==Z_STREAM_END) + return (iRead==0) ? UNZ_EOF : iRead; + if (err!=Z_OK) + break; + } + } + + if (err==Z_OK) + return iRead; + return err; +} + + +/* + Give the current position in uncompressed data +*/ +extern z_off_t ZEXPORT unztell (file) + unzFile file; +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + return (z_off_t)pfile_in_zip_read_info->stream.total_out; +} + + +/* + return 1 if the end of file was reached, 0 elsewhere +*/ +extern int ZEXPORT unzeof (file) + unzFile file; +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + return 1; + else + return 0; +} + + + +/* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field that can be read + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ +extern int ZEXPORT unzGetLocalExtrafield (file,buf,len) + unzFile file; + voidp buf; + unsigned len; +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uInt read_now; + uLong size_to_read; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + size_to_read = (pfile_in_zip_read_info->size_local_extrafield - + pfile_in_zip_read_info->pos_local_extrafield); + + if (buf==NULL) + return (int)size_to_read; + + if (len>size_to_read) + read_now = (uInt)size_to_read; + else + read_now = (uInt)len ; + + if (read_now==0) + return 0; + + if (fseek(pfile_in_zip_read_info->file, + pfile_in_zip_read_info->offset_local_extrafield + + pfile_in_zip_read_info->pos_local_extrafield,SEEK_SET)!=0) + return UNZ_ERRNO; + + if (fread(buf,(uInt)size_to_read,1,pfile_in_zip_read_info->file)!=1) + return UNZ_ERRNO; + + return (int)read_now; +} + +/* + Close the file in zip opened with unzipOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ +extern int ZEXPORT unzCloseCurrentFile (file) + unzFile file; +{ + int err=UNZ_OK; + + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + { + if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) + err=UNZ_CRCERROR; + } + + + TRYFREE(pfile_in_zip_read_info->read_buffer); + pfile_in_zip_read_info->read_buffer = NULL; + if (pfile_in_zip_read_info->stream_initialised) + inflateEnd(&pfile_in_zip_read_info->stream); + + pfile_in_zip_read_info->stream_initialised = 0; + TRYFREE(pfile_in_zip_read_info); + + s->pfile_in_zip_read=NULL; + + return err; +} + + +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ +extern int ZEXPORT unzGetGlobalComment (file, szComment, uSizeBuf) + unzFile file; + char *szComment; + uLong uSizeBuf; +{ + unz_s* s; + uLong uReadThis ; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + uReadThis = uSizeBuf; + if (uReadThis>s->gi.size_comment) + uReadThis = s->gi.size_comment; + + if (fseek(s->file,s->central_pos+22,SEEK_SET)!=0) + return UNZ_ERRNO; + + if (uReadThis>0) + { + *szComment='\0'; + if (fread(szComment,(uInt)uReadThis,1,s->file)!=1) + return UNZ_ERRNO; + } + + if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) + *(szComment+s->gi.size_comment)='\0'; + return (int)uReadThis; +} diff --git a/zlib/unzip.h b/zlib/unzip.h new file mode 100644 index 0000000..76692cb --- /dev/null +++ b/zlib/unzip.h @@ -0,0 +1,275 @@ +/* unzip.h -- IO for uncompress .zip files using zlib + Version 0.15 beta, Mar 19th, 1998, + + Copyright (C) 1998 Gilles Vollant + + This unzip package allow extract file from .ZIP file, compatible with PKZip 2.04g + WinZip, InfoZip tools and compatible. + Encryption and multi volume ZipFile (span) are not supported. + Old compressions used by old PKZip 1.x are not supported + + THIS IS AN ALPHA VERSION. AT THIS STAGE OF DEVELOPPEMENT, SOMES API OR STRUCTURE + CAN CHANGE IN FUTURE VERSION !! + I WAIT FEEDBACK at mail info@winimage.com + Visit also http://www.winimage.com/zLibDll/unzip.htm for evolution + + Condition of use and distribution are the same than zlib : + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + +*/ +/* for more info about .ZIP format, see + ftp://ftp.cdrom.com/pub/infozip/doc/appnote-970311-iz.zip + PkWare has also a specification at : + ftp://ftp.pkware.com/probdesc.zip */ + +#ifndef _unz_H +#define _unz_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagunzFile__ { int unused; } unzFile__; +typedef unzFile__ *unzFile; +#else +typedef voidp unzFile; +#endif + + +#define UNZ_OK (0) +#define UNZ_END_OF_LIST_OF_FILE (-100) +#define UNZ_ERRNO (Z_ERRNO) +#define UNZ_EOF (0) +#define UNZ_PARAMERROR (-102) +#define UNZ_BADZIPFILE (-103) +#define UNZ_INTERNALERROR (-104) +#define UNZ_CRCERROR (-105) + +/* tm_unz contain date/time info */ +typedef struct tm_unz_s +{ + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ +} tm_unz; + +/* unz_global_info structure contain global data about the ZIPfile + These data comes from the end of central dir */ +typedef struct unz_global_info_s +{ + uLong number_entry; /* total number of entries in + the central dir on this disk */ + uLong size_comment; /* size of the global comment of the zipfile */ +} unz_global_info; + + +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_info_s +{ + uLong version; /* version made by 2 bytes */ + uLong version_needed; /* version needed to extract 2 bytes */ + uLong flag; /* general purpose bit flag 2 bytes */ + uLong compression_method; /* compression method 2 bytes */ + uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ + uLong crc; /* crc-32 4 bytes */ + uLong compressed_size; /* compressed size 4 bytes */ + uLong uncompressed_size; /* uncompressed size 4 bytes */ + uLong size_filename; /* filename length 2 bytes */ + uLong size_file_extra; /* extra field length 2 bytes */ + uLong size_file_comment; /* file comment length 2 bytes */ + + uLong disk_num_start; /* disk number start 2 bytes */ + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ + + tm_unz tmu_date; +} unz_file_info; + +extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1, + const char* fileName2, + int iCaseSensitivity)); +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) +*/ + + +extern unzFile ZEXPORT unzOpen OF((const char *path)); +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows NT computer "c:\\zlib\\zlib111.zip" or on an Unix computer + "zlib/zlib111.zip". + If the zipfile cannot be opened (file don't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. +*/ + +extern int ZEXPORT unzClose OF((unzFile file)); +/* + Close a ZipFile opened with unzipOpen. + If there is files inside the .Zip opened with unzOpenCurrentFile (see later), + these files MUST be closed with unzipCloseCurrentFile before call unzipClose. + return UNZ_OK if there is no problem. */ + +extern int ZEXPORT unzGetGlobalInfo OF((unzFile file, + unz_global_info *pglobal_info)); +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ + + +extern int ZEXPORT unzGetGlobalComment OF((unzFile file, + char *szComment, + uLong uSizeBuf)); +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ + + +/***************************************************************************/ +/* Unzip package allow you browse the directory of the zipfile */ + +extern int ZEXPORT unzGoToFirstFile OF((unzFile file)); +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ + +extern int ZEXPORT unzGoToNextFile OF((unzFile file)); +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ + +extern int ZEXPORT unzLocateFile OF((unzFile file, + const char *szFileName, + int iCaseSensitivity)); +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ + + +extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file, + unz_file_info *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); +/* + Get Info about the current file + if pfile_info!=NULL, the *pfile_info structure will contain somes info about + the current file + if szFileName!=NULL, the filemane string will be copied in szFileName + (fileNameBufferSize is the size of the buffer) + if extraField!=NULL, the extra field information will be copied in extraField + (extraFieldBufferSize is the size of the buffer). + This is the Central-header version of the extra field + if szComment!=NULL, the comment string of the file will be copied in szComment + (commentBufferSize is the size of the buffer) +*/ + +/***************************************************************************/ +/* for reading the content of the current zipfile, you can open it, read data + from it, and close it (you can close it before reading all the file) + */ + +extern int ZEXPORT unzOpenCurrentFile OF((unzFile file)); +/* + Open for reading data the current file in the zipfile. + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT unzCloseCurrentFile OF((unzFile file)); +/* + Close the file in zip opened with unzOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ + + +extern int ZEXPORT unzReadCurrentFile OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read bytes from the current file (opened by unzOpenCurrentFile) + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ + +extern z_off_t ZEXPORT unztell OF((unzFile file)); +/* + Give the current position in uncompressed data +*/ + +extern int ZEXPORT unzeof OF((unzFile file)); +/* + return 1 if the end of file was reached, 0 elsewhere +*/ + +extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ + +#ifdef __cplusplus +} +#endif + +#endif /* _unz_H */ diff --git a/zlib/zconf.h b/zlib/zconf.h new file mode 100644 index 0000000..68a9f9c --- /dev/null +++ b/zlib/zconf.h @@ -0,0 +1,279 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2002 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id: zconf.h,v 1.2 2002/03/13 17:45:56 xodnizel Exp $ */ + +#ifndef _ZCONF_H +#define _ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + */ +#ifdef Z_PREFIX +# define deflateInit_ z_deflateInit_ +# define deflate z_deflate +# define deflateEnd z_deflateEnd +# define inflateInit_ z_inflateInit_ +# define inflate z_inflate +# define inflateEnd z_inflateEnd +# define deflateInit2_ z_deflateInit2_ +# define deflateSetDictionary z_deflateSetDictionary +# define deflateCopy z_deflateCopy +# define deflateReset z_deflateReset +# define deflateParams z_deflateParams +# define inflateInit2_ z_inflateInit2_ +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateReset z_inflateReset +# define compress z_compress +# define compress2 z_compress2 +# define uncompress z_uncompress +# define adler32 z_adler32 +# define crc32 z_crc32 +# define get_crc_table z_get_crc_table + +# define Byte z_Byte +# define uInt z_uInt +# define uLong z_uLong +# define Bytef z_Bytef +# define charf z_charf +# define intf z_intf +# define uIntf z_uIntf +# define uLongf z_uLongf +# define voidpf z_voidpf +# define voidp z_voidp +#endif + +#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) +# define WIN32 +#endif +#if defined(__GNUC__) || defined(WIN32) || defined(__386__) || defined(i386) +# ifndef __32BIT__ +# define __32BIT__ +# endif +#endif +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#if defined(MSDOS) && !defined(__32BIT__) +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#if (defined(MSDOS) || defined(_WINDOWS) || defined(WIN32)) && !defined(STDC) +# define STDC +#endif +#if defined(__STDC__) || defined(__cplusplus) || defined(__OS2__) +# ifndef STDC +# define STDC +# endif +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const +# endif +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__) || defined(applec) ||defined(THINK_C) ||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Old Borland C incorrectly complains about missing returns: */ +#if defined(__BORLANDC__) && (__BORLANDC__ < 0x500) +# define NEED_DUMMY_RETURN +#endif + + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#if (defined(M_I86SM) || defined(M_I86MM)) && !defined(__32BIT__) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +#endif +#if defined(__BORLANDC__) && (defined(__SMALL__) || defined(__MEDIUM__)) +# ifndef __32BIT__ +# define SMALL_MEDIUM +# define FAR _far +# endif +#endif + +/* Compile with -DZLIB_DLL for Windows DLL support */ +#if defined(ZLIB_DLL) +# if defined(_WINDOWS) || defined(WINDOWS) +# ifdef FAR +# undef FAR +# endif +# include +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR _cdecl _export +# endif +# endif +# if defined (__BORLANDC__) +# if (__BORLANDC__ >= 0x0500) && defined (WIN32) +# include +# define ZEXPORT __declspec(dllexport) WINAPI +# define ZEXPORTRVA __declspec(dllexport) WINAPIV +# else +# if defined (_Windows) && defined (__DLL__) +# define ZEXPORT _export +# define ZEXPORTVA _export +# endif +# endif +# endif +#endif + +#if defined (__BEOS__) +# if defined (ZLIB_DLL) +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +#endif + +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif +#ifndef ZEXTERN +# define ZEXTERN extern +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(MACOS) && !defined(TARGET_OS_MAC) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#ifdef HAVE_UNISTD_H +# include /* for off_t */ +# include /* for SEEK_* and off_t */ +# define z_off_t off_t +#endif +#ifndef SEEK_SET +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif +#ifndef z_off_t +# define z_off_t long +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) +# pragma map(deflateInit_,"DEIN") +# pragma map(deflateInit2_,"DEIN2") +# pragma map(deflateEnd,"DEEND") +# pragma map(inflateInit_,"ININ") +# pragma map(inflateInit2_,"ININ2") +# pragma map(inflateEnd,"INEND") +# pragma map(inflateSync,"INSY") +# pragma map(inflateSetDictionary,"INSEDI") +# pragma map(inflate_blocks,"INBL") +# pragma map(inflate_blocks_new,"INBLNE") +# pragma map(inflate_blocks_free,"INBLFR") +# pragma map(inflate_blocks_reset,"INBLRE") +# pragma map(inflate_codes_free,"INCOFR") +# pragma map(inflate_codes,"INCO") +# pragma map(inflate_fast,"INFA") +# pragma map(inflate_flush,"INFLU") +# pragma map(inflate_mask,"INMA") +# pragma map(inflate_set_dictionary,"INSEDI2") +# pragma map(inflate_copyright,"INCOPY") +# pragma map(inflate_trees_bits,"INTRBI") +# pragma map(inflate_trees_dynamic,"INTRDY") +# pragma map(inflate_trees_fixed,"INTRFI") +# pragma map(inflate_trees_free,"INTRFR") +#endif + +#endif /* _ZCONF_H */ diff --git a/zlib/zlib.h b/zlib/zlib.h new file mode 100644 index 0000000..52cb529 --- /dev/null +++ b/zlib/zlib.h @@ -0,0 +1,893 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.1.4, March 11th, 2002 + + Copyright (C) 1995-2002 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt + (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +*/ + +#ifndef _ZLIB_H +#define _ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.1.4" + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed + data. This version of the library supports only one compression method + (deflation) but other algorithms will be added later and will have the same + stream interface. + + Compression can be done in a single step if the buffers are large + enough (for example if an input file is mmap'ed), or can be done by + repeated calls of the compression function. In the latter case, the + application must provide more input and/or consume the output + (providing more output space) before each call. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never + crash even in case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total nb of input bytes read so far */ + + Bytef *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total nb of bytes output so far */ + + char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: ascii or binary */ + uLong adler; /* adler32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + The application must update next_in and avail_in when avail_in has + dropped to zero. It must update next_out and avail_out when avail_out + has dropped to zero. The application must initialize zalloc, zfree and + opaque before calling the init function. All other fields are set by the + compression library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this + if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, + pointers returned by zalloc for objects of exactly 65536 bytes *must* + have their offset normalized to zero. The default allocation function + provided by this library ensures this (see zutil.c). To reduce memory + requirements and avoid any allocation of 64K objects, at the expense of + compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or + progress reports. After compression, total_in holds the total size of + the uncompressed data and may be saved for use in the decompressor + (particularly if the decompressor wants to decompress everything in + a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */ +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 +/* Allowed flush values; see deflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative + * values are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_ASCII 1 +#define Z_UNKNOWN 2 +/* Possible values of the data_type field */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is + not compatible with the zlib.h header file used by the application. + This check is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. + If zalloc and zfree are set to Z_NULL, deflateInit updates them to + use default allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at + all (the input data is simply copied a block at a time). + Z_DEFAULT_COMPRESSION requests a default compromise between speed and + compression (currently equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if level is not a valid compression level, + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). + msg is set to null if there is no error message. deflateInit does not + perform any compression: this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce some + output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary (in interactive applications). + Some output may be provided even if flush is not set. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating avail_in or avail_out accordingly; avail_out + should never be zero before the call. The application can consume the + compressed output when it wants, for example when the output buffer is full + (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK + and with zero avail_out, it must be called again after making room in the + output buffer because there might be more output pending. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In particular + avail_in is zero after the call if enough output space has been provided + before the call.) Flushing may degrade compression for some compression + algorithms and so it should be used only when necessary. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + the compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there + was enough output space; if deflate returns with Z_OK, this function must be + called again with Z_FINISH and more output space (updated avail_out) but no + more input data, until it returns with Z_STREAM_END or an error. After + deflate has returned Z_STREAM_END, the only possible operations on the + stream are deflateReset or deflateEnd. + + Z_FINISH can be used immediately after deflateInit if all the compression + is to be done in a single step. In this case, avail_out must be at least + 0.1% larger than avail_in plus 12 bytes. If deflate does not return + Z_STREAM_END, then it must be called again as described above. + + deflate() sets strm->adler to the adler32 checksum of all input read + so far (that is, total_in bytes). + + deflate() may update data_type if it can make a good guess about + the input data type (Z_ASCII or Z_BINARY). In doubt, the data is considered + binary. This field is only for information purposes and does not affect + the compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible + (for example avail_in or avail_out was zero). +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, + msg may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. If next_in is not Z_NULL and avail_in is large enough (the exact + value depends on the compression method), inflateInit determines the + compression method from the zlib header and allocates all data structures + accordingly; otherwise the allocation will be deferred to the first call of + inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to + use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller. msg is set to null if there is no error + message. inflateInit does not perform any decompression apart from reading + the zlib header if present: this will be done by inflate(). (So next_in and + avail_in may be modified, but next_out and avail_out are unchanged.) +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may some + introduce some output latency (reading input without producing any output) + except when forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing + will resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there + is no more input data or no more space in the output buffer (see below + about the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating the next_* and avail_* values accordingly. + The application can consume the uncompressed output when it wants, for + example when the output buffer is full (avail_out == 0), or after each + call of inflate(). If inflate returns Z_OK and with zero avail_out, it + must be called again after making room in the output buffer because there + might be more output pending. + + If the parameter flush is set to Z_SYNC_FLUSH, inflate flushes as much + output as possible to the output buffer. The flushing behavior of inflate is + not specified for values of the flush parameter other than Z_SYNC_FLUSH + and Z_FINISH, but the current implementation actually flushes as much output + as possible anyway. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step + (a single call of inflate), the parameter flush should be set to + Z_FINISH. In this case all pending input is processed and all pending + output is flushed; avail_out must be large enough to hold all the + uncompressed data. (The size of the uncompressed data may have been saved + by the compressor for this purpose.) The next operation on this stream must + be inflateEnd to deallocate the decompression state. The use of Z_FINISH + is never required, but can be used to inform inflate that a faster routine + may be used for the single inflate() call. + + If a preset dictionary is needed at this point (see inflateSetDictionary + below), inflate sets strm-adler to the adler32 checksum of the + dictionary chosen by the compressor and returns Z_NEED_DICT; otherwise + it sets strm->adler to the adler32 checksum of all output produced + so far (that is, total_out bytes) and returns Z_OK, Z_STREAM_END or + an error code as described below. At the end of the stream, inflate() + checks that its computed adler32 checksum is equal to that saved by the + compressor and returns Z_STREAM_END only if the checksum is correct. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect + adler32 checksum), Z_STREAM_ERROR if the stream structure was inconsistent + (for example if next_in or next_out was NULL), Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if no progress is possible or if there was not + enough room in the output buffer when Z_FINISH is used. In the Z_DATA_ERROR + case, the application may then call inflateSync to look for a good + compression block. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by + the caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but + is slow and reduces compression ratio; memLevel=9 uses maximum memory + for optimal speed. The default value is 8. See zconf.h for total memory + usage as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), or Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match). Filtered data consists mostly of small values with a + somewhat random distribution. In this case, the compression algorithm is + tuned to compress them better. The effect of Z_FILTERED is to force more + Huffman coding and less string matching; it is somewhat intermediate + between Z_DEFAULT and Z_HUFFMAN_ONLY. The strategy parameter only affects + the compression ratio but not the correctness of the compressed output even + if it is not set appropriately. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid + method). msg is set to null if there is no error message. deflateInit2 does + not perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. This function must be called + immediately after deflateInit, deflateInit2 or deflateReset, before any + call of deflate. The compressor and decompressor must use exactly the same + dictionary (see inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size in + deflate or deflate2. Thus the strings most likely to be useful should be + put at the end of the dictionary, not at the front. + + Upon return of this function, strm->adler is set to the Adler32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The Adler32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if the compression method is bsort). deflateSetDictionary does not + perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and + can consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, + but does not free and reallocate all the internal compression state. + The stream will keep the same compression level and any other attributes + that may have been set by deflateInit2. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2. This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different + strategy. If the compression level is changed, the input available so far + is compressed with the old level (and may be flushed); the new level will + take effect only at the next call of deflate(). + + Before the call of deflateParams, the stream state must be set as for + a call of deflate(), since the currently available input may have to + be compressed and flushed. In particular, strm->avail_out must be non-zero. + + deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source + stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR + if strm->avail_out was zero. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. If a compressed stream with a larger window size is given as + input, inflate() will return with the error code Z_DATA_ERROR instead of + trying to allocate a larger window. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as a negative + memLevel). msg is set to null if there is no error message. inflateInit2 + does not perform any decompression apart from reading the zlib header if + present: this will be done by inflate(). (So next_in and avail_in may be + modified, but next_out and avail_out are unchanged.) +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate + if this call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the Adler32 value returned by this call of + inflate. The compressor and decompressor must use exactly the same + dictionary (see deflateSetDictionary). + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect Adler32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a full flush point (see above the + description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR + if no more input was provided, Z_DATA_ERROR if no flush point has been found, + or Z_STREAM_ERROR if the stream structure was inconsistent. In the success + case, the application may save the current current value of total_in which + indicates where valid compressed data was found. In the error case, the + application may repeatedly call inflateSync, providing more input each time, + until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate all the internal decompression state. + The stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + + + /* utility functions */ + +/* + The following utility functions are implemented on top of the + basic stream-oriented functions. To simplify the interface, some + default options are assumed (compression level and memory usage, + standard memory allocation functions). The source code of these + utility functions can easily be modified if you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be at least 0.1% larger than + sourceLen plus 12 bytes. Upon exit, destLen is the actual size of the + compressed buffer. + This function can be used to compress a whole file at once if the + input file is mmap'ed. + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least 0.1% larger than sourceLen plus + 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + This function can be used to decompress a whole file at once if the + input file is mmap'ed. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted. +*/ + + +typedef voidp gzFile; + +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); +/* + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb") but can also include a compression level + ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for + Huffman only compression as in "wb1h". (See the description + of deflateInit2 for more information about the strategy parameter.) + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. + + gzopen returns NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). */ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen() associates a gzFile with the file descriptor fd. File + descriptors are obtained from calls like open, dup, creat, pipe or + fileno (in the file has been previously opened with fopen). + The mode parameter is as in gzopen. + The next call of gzclose on the returned gzFile will also close the + file descriptor fd, just like fclose(fdopen(fd), mode) closes the file + descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode). + gzdopen returns NULL if there was insufficient memory to allocate + the (de)compression state. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. + gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not + opened for writing. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. + If the input file was not in gzip format, gzread copies the given number + of bytes into the buffer. + gzread returns the number of uncompressed bytes actually read (0 for + end of file, -1 for error). */ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + const voidp buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes actually written + (0 in case of error). +*/ + +ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the args to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written (0 in case of error). +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or + a newline character is read and transferred to buf, or an end-of-file + condition is encountered. The string is then terminated with a null + character. + gzgets returns buf, or Z_NULL in case of error. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. + gzputc returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte + or -1 in case of end of file or error. +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. The return value is the zlib + error number (see function gzerror below). gzflush returns Z_OK if + the flush parameter is Z_FINISH and all output could be flushed. + gzflush should be called only when strictly necessary because it can + degrade compression. +*/ + +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); +/* + Sets the starting position for the next gzread or gzwrite on the + given compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); +/* + Returns the starting position for the next gzread or gzwrite on the + given compressed file. This position represents a number of bytes in the + uncompressed data stream. + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns 1 when EOF has previously been detected reading the given + input stream, otherwise zero. +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. The return value is the zlib + error number (see function gzerror below). +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the + given compressed file. errnum is set to zlib error number. If an + error occurred in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the + compression library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); + +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is NULL, this function returns + the required initial value for the checksum. + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running crc with the bytes buf[0..len-1] and return the updated + crc. If buf is NULL, this function returns the required initial value + for the crc. Pre- and post-conditioning (one's complement) is performed + within this function so it shouldn't be done by the application. + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +#define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream)) +#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) + + +#if !defined(_Z_UTIL_H) && !defined(NO_DUMMY_DECL) + struct internal_state {int dummy;}; /* hack for buggy compilers */ +#endif + +ZEXTERN const char * ZEXPORT zError OF((int err)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp z)); +ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void)); + +#ifdef __cplusplus +} +#endif + +#endif /* _ZLIB_H */ diff --git a/zlib/zutil.c b/zlib/zutil.c new file mode 100644 index 0000000..08166c0 --- /dev/null +++ b/zlib/zutil.c @@ -0,0 +1,225 @@ +/* zutil.c -- target dependent utility functions for the compression library + * Copyright (C) 1995-2002 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id: zutil.c,v 1.2 2002/03/13 17:45:56 xodnizel Exp $ */ + +#include "zutil.h" + +struct internal_state {int dummy;}; /* for buggy compilers */ + +#ifndef STDC +extern void exit OF((int)); +#endif + +const char *z_errmsg[10] = { +"need dictionary", /* Z_NEED_DICT 2 */ +"stream end", /* Z_STREAM_END 1 */ +"", /* Z_OK 0 */ +"file error", /* Z_ERRNO (-1) */ +"stream error", /* Z_STREAM_ERROR (-2) */ +"data error", /* Z_DATA_ERROR (-3) */ +"insufficient memory", /* Z_MEM_ERROR (-4) */ +"buffer error", /* Z_BUF_ERROR (-5) */ +"incompatible version",/* Z_VERSION_ERROR (-6) */ +""}; + + +const char * ZEXPORT zlibVersion() +{ + return ZLIB_VERSION; +} + +#ifdef DEBUG + +# ifndef verbose +# define verbose 0 +# endif +int z_verbose = verbose; + +void z_error (m) + char *m; +{ + fprintf(stderr, "%s\n", m); + exit(1); +} +#endif + +/* exported to allow conversion of error code to string for compress() and + * uncompress() + */ +const char * ZEXPORT zError(err) + int err; +{ + return ERR_MSG(err); +} + + +#ifndef HAVE_MEMCPY + +void zmemcpy(dest, source, len) + Bytef* dest; + const Bytef* source; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = *source++; /* ??? to be unrolled */ + } while (--len != 0); +} + +int zmemcmp(s1, s2, len) + const Bytef* s1; + const Bytef* s2; + uInt len; +{ + uInt j; + + for (j = 0; j < len; j++) { + if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1; + } + return 0; +} + +void zmemzero(dest, len) + Bytef* dest; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = 0; /* ??? to be unrolled */ + } while (--len != 0); +} +#endif + +#ifdef __TURBOC__ +#if (defined( __BORLANDC__) || !defined(SMALL_MEDIUM)) && !defined(__32BIT__) +/* Small and medium model in Turbo C are for now limited to near allocation + * with reduced MAX_WBITS and MAX_MEM_LEVEL + */ +# define MY_ZCALLOC + +/* Turbo C malloc() does not allow dynamic allocation of 64K bytes + * and farmalloc(64K) returns a pointer with an offset of 8, so we + * must fix the pointer. Warning: the pointer must be put back to its + * original form in order to free it, use zcfree(). + */ + +#define MAX_PTR 10 +/* 10*64K = 640K */ + +local int next_ptr = 0; + +typedef struct ptr_table_s { + voidpf org_ptr; + voidpf new_ptr; +} ptr_table; + +local ptr_table table[MAX_PTR]; +/* This table is used to remember the original form of pointers + * to large buffers (64K). Such pointers are normalized with a zero offset. + * Since MSDOS is not a preemptive multitasking OS, this table is not + * protected from concurrent access. This hack doesn't work anyway on + * a protected system like OS/2. Use Microsoft C instead. + */ + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + voidpf buf = opaque; /* just to make some compilers happy */ + ulg bsize = (ulg)items*size; + + /* If we allocate less than 65520 bytes, we assume that farmalloc + * will return a usable pointer which doesn't have to be normalized. + */ + if (bsize < 65520L) { + buf = farmalloc(bsize); + if (*(ush*)&buf != 0) return buf; + } else { + buf = farmalloc(bsize + 16L); + } + if (buf == NULL || next_ptr >= MAX_PTR) return NULL; + table[next_ptr].org_ptr = buf; + + /* Normalize the pointer to seg:0 */ + *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4; + *(ush*)&buf = 0; + table[next_ptr++].new_ptr = buf; + return buf; +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + int n; + if (*(ush*)&ptr != 0) { /* object < 64K */ + farfree(ptr); + return; + } + /* Find the original pointer */ + for (n = 0; n < next_ptr; n++) { + if (ptr != table[n].new_ptr) continue; + + farfree(table[n].org_ptr); + while (++n < next_ptr) { + table[n-1] = table[n]; + } + next_ptr--; + return; + } + ptr = opaque; /* just to make some compilers happy */ + Assert(0, "zcfree: ptr not found"); +} +#endif +#endif /* __TURBOC__ */ + + +#if defined(M_I86) && !defined(__32BIT__) +/* Microsoft C in 16-bit mode */ + +# define MY_ZCALLOC + +#if (!defined(_MSC_VER) || (_MSC_VER <= 600)) +# define _halloc halloc +# define _hfree hfree +#endif + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + return _halloc((long)items, size); +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + _hfree(ptr); +} + +#endif /* MSC */ + + +#ifndef MY_ZCALLOC /* Any system without a special alloc function */ + +#ifndef STDC +extern voidp calloc OF((uInt items, uInt size)); +extern void free OF((voidpf ptr)); +#endif + +voidpf zcalloc (opaque, items, size) + voidpf opaque; + unsigned items; + unsigned size; +{ + if (opaque) items += size - size; /* make compiler happy */ + return (voidpf)calloc(items, size); +} + +void zcfree (opaque, ptr) + voidpf opaque; + voidpf ptr; +{ + free(ptr); + if (opaque) return; /* make compiler happy */ +} + +#endif /* MY_ZCALLOC */ diff --git a/zlib/zutil.h b/zlib/zutil.h new file mode 100644 index 0000000..507cab9 --- /dev/null +++ b/zlib/zutil.h @@ -0,0 +1,220 @@ +/* zutil.h -- internal interface and configuration of the compression library + * Copyright (C) 1995-2002 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id: zutil.h,v 1.2 2002/03/13 17:45:56 xodnizel Exp $ */ + +#ifndef _Z_UTIL_H +#define _Z_UTIL_H + +#include "zlib.h" + +#ifdef STDC +# include +# include +# include +#endif +#ifdef NO_ERRNO_H + extern int errno; +#else +# include +#endif + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +typedef unsigned char uch; +typedef uch FAR uchf; +typedef unsigned short ush; +typedef ush FAR ushf; +typedef unsigned long ulg; + +extern const char *z_errmsg[10]; /* indexed by 2-zlib_error */ +/* (size given to avoid silly warnings with Visual C++) */ + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = (char*)ERR_MSG(err), (err)) +/* To be used only when the state is known to be valid */ + + /* common constants */ + +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ + +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +/* default memLevel */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ + + /* target dependencies */ + +#ifdef MSDOS +# define OS_CODE 0x00 +# if defined(__TURBOC__) || defined(__BORLANDC__) +# if(__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) + /* Allow compilation with ANSI keywords only enabled */ + void _Cdecl farfree( void *block ); + void *_Cdecl farmalloc( unsigned long nbytes ); +# else +# include +# endif +# else /* MSC or DJGPP */ +# include +# endif +#endif + +#ifdef OS2 +# define OS_CODE 0x06 +#endif + +#ifdef WIN32 /* Window 95 & Windows NT */ +# define OS_CODE 0x0b +#endif + +#if defined(VAXC) || defined(VMS) +# define OS_CODE 0x02 +# define F_OPEN(name, mode) \ + fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") +#endif + +#ifdef AMIGA +# define OS_CODE 0x01 +#endif + +#if defined(ATARI) || defined(atarist) +# define OS_CODE 0x05 +#endif + +#if defined(MACOS) || defined(TARGET_OS_MAC) +# define OS_CODE 0x07 +# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os +# include /* for fdopen */ +# else +# ifndef fdopen +# define fdopen(fd,mode) NULL /* No fdopen() */ +# endif +# endif +#endif + +#ifdef __50SERIES /* Prime/PRIMOS */ +# define OS_CODE 0x0F +#endif + +#ifdef TOPS20 +# define OS_CODE 0x0a +#endif + +#if defined(_BEOS_) || defined(RISCOS) +# define fdopen(fd,mode) NULL /* No fdopen() */ +#endif + +#if (defined(_MSC_VER) && (_MSC_VER > 600)) +# define fdopen(fd,type) _fdopen(fd,type) +#endif + + + /* Common defaults */ + +#ifndef OS_CODE +# define OS_CODE 0x03 /* assume Unix */ +#endif + +#ifndef F_OPEN +# define F_OPEN(name, mode) fopen((name), (mode)) +#endif + + /* functions */ + +#ifdef HAVE_STRERROR + extern char *strerror OF((int)); +# define zstrerror(errnum) strerror(errnum) +#else +# define zstrerror(errnum) "" +#endif + +#if defined(pyr) +# define NO_MEMCPY +#endif +#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) + /* Use our own functions for small and medium model with MSC <= 5.0. + * You may have to use the same strategy for Borland C (untested). + * The __SC__ check is for Symantec. + */ +# define NO_MEMCPY +#endif +#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) +# define HAVE_MEMCPY +#endif +#ifdef HAVE_MEMCPY +# ifdef SMALL_MEDIUM /* MSDOS small or medium model */ +# define zmemcpy _fmemcpy +# define zmemcmp _fmemcmp +# define zmemzero(dest, len) _fmemset(dest, 0, len) +# else +# define zmemcpy memcpy +# define zmemcmp memcmp +# define zmemzero(dest, len) memset(dest, 0, len) +# endif +#else + extern void zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); + extern int zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); + extern void zmemzero OF((Bytef* dest, uInt len)); +#endif + +/* Diagnostic functions */ +#ifdef DEBUG +# include + extern int z_verbose; + extern void z_error OF((char *m)); +# define Assert(cond,msg) {if(!(cond)) z_error(msg);} +# define Trace(x) {if (z_verbose>=0) fprintf x ;} +# define Tracev(x) {if (z_verbose>0) fprintf x ;} +# define Tracevv(x) {if (z_verbose>1) fprintf x ;} +# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + + +typedef uLong (ZEXPORT *check_func) OF((uLong check, const Bytef *buf, + uInt len)); +voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size)); +void zcfree OF((voidpf opaque, voidpf ptr)); + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + +#endif /* _Z_UTIL_H */