diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e0292b1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.o +*.a diff --git a/AUTHORS.txt b/AUTHORS.txt new file mode 100755 index 0000000..116808b --- /dev/null +++ b/AUTHORS.txt @@ -0,0 +1,11 @@ +XRoar - a Dragon/Tandy Coco emulator + + Ciaran Anscomb xroar(at)6809.org.uk + + Homepage: http://www.6809.org.uk/dragon/xroar.shtml + +PSPDragon / GP2XDragon : Porting to PSP and GP2X + + Ludovic Jacomme alias Zx-81 zx81.zx81(at)gmail.com + + Homepage: http://zx81.zx81.free.fr diff --git a/COPYING.txt b/COPYING.txt new file mode 100755 index 0000000..e755437 --- /dev/null +++ b/COPYING.txt @@ -0,0 +1,341 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 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/ChangeLog.txt b/ChangeLog.txt new file mode 100755 index 0000000..51b42f6 --- /dev/null +++ b/ChangeLog.txt @@ -0,0 +1,16 @@ +1.0.2 + +- Fix sound issue using the patch sent by original X-roar's author, + aka Ciaran himself (big thanks to him !!) +- Bug fix with volume settings (it didn't work properly) +- A thumbnail image is now displayed in the file requester + while selecting any file with the same name of a previously + saved game (rom, keyboard, settings). + It might be very helpful to recognize that game later if you + have thousand different games on your disk and rom folders ! +- The virtual keyboard is now usable in the file requester menu to + choose the first letter of the game you search ! + +1.0.1 + +- Intial release diff --git a/README.txt b/README.txt new file mode 100755 index 0000000..527efcd --- /dev/null +++ b/README.txt @@ -0,0 +1,181 @@ + + Welcome to GP2X-Dragon + +Original Author of XRoar + + Ciaran Anscomb xroar(at)6809.org.uk + + Homepage: http://www.6809.org.uk/dragon/xroar.shtml + +Author of the PSP and GP2X port version + + Ludovic Jacomme alias Zx-81 zx81.zx81(at)gmail.com + + Homepage: http://zx81.zx81.free.fr + + +1. INTRODUCTION + ------------ + + Xroar is the best emulator of Dragon32/64 and Tandy Coco home computer + running on many systems such as Linux, Unix, Mac OS X, GP32 and Windows32. + See http://www.6809.org.uk/dragon/xroar.shtml for further informations. + + GP2X-Dragon is a port on GP2X of the PSP version. + + Special thanks to Danzel and Jeff Chen for their virtual keyboard, + Ciaran Anscomb for this great emulator, Raven's for eboot icons + and to all GP2X-SDK developpers. + + This package is under GPL Copyright, read COPYING file for + more information about it. + + +2. INSTALLATION + ------------ + + Unzip the zip file, and copy the content of the directory game to your + SD memory. + + For any comments or questions on this version, please visit + http://zx81.zx81.free.fr or http://www.gp32x.com/ + + +3. CONTROL + ------------ + +In the Dragon emulator window, there are three different mapping (standard, +left trigger, and right Trigger mappings). +You can toggle between while playing inside the emulator using the two GP2X +trigger keys. + + ------------------------------------- + GP2X Dragon (standard) + + A 2 + B ENTER + Y 1 + X Joystick Fire + Up Up + Down Down + Left Left + Right Right + + Joystick Joystick + + ------------------------------------- + GP2X Dragon (left trigger) + + A Fps + B Render + Y Load state + X Save state + Up Up + Down Down + Left Left + Right Right + + ------------------------------------- + GP2X Dragon (right trigger) + + A Space + B ENTER + Y 4 + X Auto fire on/off + Up Up + Down Down + Left Dec Fire + Right Inc Fire + + Analog Joystick + + Joystick Up, Down, Left, Rigth + + Press Select to enter in emulator main menu. + Press Start open/close the On-Screen keyboard + + In the main menu + + RTrigger Reset the emulator + + Y Go Up directory + X Valid + B Valid + A Go Back to the emulator window + + The On-Screen Keyboard of "Danzel" and "Jeff Chen" + + Use the stick to choose one of the 9 squares, and + use A, B, X, Y to choose one of the 4 letters + of the highlighted square. + + Use LTrigger and RTrigger to see other 9 squares + figures. + + +4. LOADING DISK FILES (DSK) + ------------ + +If you want to load disk image in the virtual disk +drive of your emulator, you have to put your disk +file (with .dsk file extension) on your memory +card in the 'disk' directory. + +Then, while inside emulator, just press SELECT to +enter in the emulator main menu, choose "Load Disc" +and then using the file selector choose one disc +file to load in your emulator. + +To list the content of the disk, you have to use +the virtual keyboard (press START key) and type the +command DIR followed by ENTER. + +To run the game MYGAME.BIN on your disk, you have +to use the virtual keyboard and type the command +LOADM"MYGAME" followed by ENTER. Once the game is +loaded, enter the command EXEC followed by ENTER. + +To run a basic game (with .BAS extention) use RUN +command instead of LOADM. + +If you want to specify the command to run for given +games then you can do it in the run.txt, using the +following syntax : + + tapename=CLOAD"":EXEC + diskname=LOADM"RunName":EXEC + diskname=RUN"RunName" + + +5. LOADING KEY MAPPING FILES + ------------ + +For given games, the default keyboard mapping between GP2X Keys and DRAGON +keys, is not suitable, and the game can't be played on DRAGON. + +To overcome the issue, you can write your own mapping file. Using notepad for +example you can edit a file with the .kbd extension and put it in the kbd +directory. + +For the exact syntax of those mapping files, have a look on sample files +already presents in the kbd directory (default.kbd etc ...). + +After writting such keyboard mapping file, you can load them using the main +menu inside the emulator. + +If the keyboard filename is the same as the cartridge or disk file then when +you load this file, the corresponding keyboard file is automatically loaded ! + +You can now use the Keyboard menu and edit, load and save your keyboard +mapping files inside the emulator. + +The Save option save the .kbd file in the kbd directory using the "Game Name" +as filename. The game name is displayed on the right corner in the emulator +menu. + +6. COMPILATION + ------------ + + It has been developped under Linux using gcc with GP2XSDK. + To rebuild the homebrew run the Makefile in the src archive. + diff --git a/src/Makefile b/src/Makefile new file mode 100755 index 0000000..cc5c25c --- /dev/null +++ b/src/Makefile @@ -0,0 +1,99 @@ +# +# Copyright (C) 2007 Ludovic Jacomme (ludovic.jacomme@gmail.com) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +TARGET = gp2xdragon.gpe +SDL_CONFIG = $(GP2XDEV)/bin/sdl-config +OBJS = gp2x_psp.o \ +psp_main.o \ +psp_danzeff.o \ +psp_fmgr.o \ +psp_dragon.o \ +psp_font.o \ +psp_kbd.o \ +psp_menu.o \ +psp_menu_help.o \ +psp_menu_kbd.o \ +psp_menu_set.o \ +psp_run.o \ +psp_sdl.o \ +global.o \ +core/cart.o \ +core/crc16.o \ +core/deltados.o \ +core/dragondos.o \ +core/events.o \ +core/filereq_cli.o \ +core/fs_unix.o \ +core/hexs19.o \ +core/joystick.o \ +core/keyboard.o \ +core/m6809.o \ +core/machine.o \ +core/main_unix.o \ +core/module.o \ +core/pia.o \ +core/rsdos.o \ +core/sam.o \ +core/snapshot.o \ +core/sound_null.o \ +core/sound_sdl.o \ +core/tape.o \ +core/ui_sdl.o \ +core/vdg_bitmaps.o \ +core/vdg.o \ +core/vdisk.o \ +core/vdrive.o \ +core/video_sdl.o \ +core/wd279x.o \ +core/xroar.o \ +core/keyboard_psp.o \ +\ +cpulcd.o + +#PROFILE=-fprofile-generate +PROFILE=-fprofile-use + +DEFAULT_CFLAGS = $(shell $(SDL_CONFIG) --cflags) + +MORE_CFLAGS = -DGP2X_MODE -static -I$(GP2XDEV)/include -I$(GP2XDEV)/include/SDL -DHAVE_CONFIG_H -I. -Iinclude \ + -DGP2X_MMU_HACK -DPSP \ + -DNO_STDIO_REDIRECT $(PROFILE) + +CFLAGS = $(DEFAULT_CFLAGS) $(MORE_CFLAGS) -O2 \ + -funroll-loops -ffast-math -fomit-frame-pointer -fno-strength-reduce -finline-functions -fsigned-char +CXXFLAGS = $(DEFAULT_CFLAGS) $(MORE_CFLAGS) -O2 -fno-exceptions -fno-rtti \ + -funroll-loops -ffast-math -fomit-frame-pointer -fno-strength-reduce -finline-functions -fsigned-char + +LIBS += -L$(GP2XDEV)/lib -lSDL_image -lSDL_fast -lpng -ljpeg -lz -lm -lpthread -lstdc++ + +.c.o: + open2x-gcc $(CFLAGS) -c $< -o $@ + +.cpp.o: + open2x-g++ $(CXXFLAGS) -c $< -o $@ + +$(TARGET): $(OBJS) + open2x-gcc $(CFLAGS) $(OBJS) $(LIBS) -o $(TARGET) $(PROFILE) + +install: $(TARGET) + cp $< /media/usbdisk/game/dragon/ + +clean: + rm -f $(OBJS) $(TARGET) + +ctags: + ctags *[ch] diff --git a/src/Makefile.miyoo b/src/Makefile.miyoo new file mode 100755 index 0000000..33205bd --- /dev/null +++ b/src/Makefile.miyoo @@ -0,0 +1,109 @@ +# +# Copyright (C) 2007 Ludovic Jacomme (ludovic.jacomme@gmail.com) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +TARGET = gp2xdragon.gpe + +CHAINPREFIX=/opt/miyoo +CROSS_COMPILE=$(CHAINPREFIX)/usr/bin/arm-linux- + +CC = $(CROSS_COMPILE)gcc +CXX = $(CROSS_COMPILE)g++ +STRIP = $(CROSS_COMPILE)strip +AR = $(CROSS_COMPILE)ar +RANLIB = $(CROSS_COMPILE)ranlib + +SYSROOT := $(shell $(CC) --print-sysroot) +SDL_CONFIG = $(SYSROOT)/usr/bin/sdl-config + +OBJS = gp2x_psp.o \ +psp_main.o \ +psp_danzeff.o \ +psp_fmgr.o \ +psp_dragon.o \ +psp_font.o \ +psp_kbd.o \ +psp_menu.o \ +psp_menu_help.o \ +psp_menu_kbd.o \ +psp_menu_set.o \ +psp_run.o \ +psp_sdl.o \ +global.o \ +core/cart.o \ +core/crc16.o \ +core/deltados.o \ +core/dragondos.o \ +core/events.o \ +core/filereq_cli.o \ +core/fs_unix.o \ +core/hexs19.o \ +core/joystick.o \ +core/keyboard.o \ +core/m6809.o \ +core/machine.o \ +core/main_unix.o \ +core/module.o \ +core/pia.o \ +core/rsdos.o \ +core/sam.o \ +core/snapshot.o \ +core/sound_null.o \ +core/sound_sdl.o \ +core/tape.o \ +core/ui_sdl.o \ +core/vdg_bitmaps.o \ +core/vdg.o \ +core/vdisk.o \ +core/vdrive.o \ +core/video_sdl.o \ +core/wd279x.o \ +core/xroar.o \ +core/keyboard_psp.o \ +\ +cpulcd.o + +#PROFILE=-fprofile-generate +PROFILE=-fprofile-use + +DEFAULT_CFLAGS = $(shell $(SDL_CONFIG) --cflags) + +MORE_CFLAGS = -DGP2X_MODE -s -I$(SYSROOT)/usr/include -I$(SYSROOT)/usr/lib -I$(SYSROOT)/lib -I. -Iinclude \ + -DGP2X_MMU_HACK -DPSP -DMIYOO_MODE \ + -DNO_STDIO_REDIRECT $(PROFILE) + +CFLAGS = $(DEFAULT_CFLAGS) $(MORE_CFLAGS) -O2 \ + -funroll-loops -ffast-math -fomit-frame-pointer -fno-strength-reduce -finline-functions -fsigned-char +CXXFLAGS = $(DEFAULT_CFLAGS) $(MORE_CFLAGS) -O2 -fno-exceptions -fno-rtti \ + -funroll-loops -ffast-math -fomit-frame-pointer -fno-strength-reduce -finline-functions -fsigned-char + +#LIBS += -L$(GP2XDEV)/lib -lSDL_image -lSDL_fast -lpng -ljpeg -lz -lm -lpthread -lstdc++ + +LIBS += -L$(SYSROOT)/usr/lib -lSDL_image -lSDL \ +-lstdc++ -lpng -ljpeg \ +-lz -lm -lpthread + +.c.o: + $(CC) $(CFLAGS) -c $< -o $@ + +.cpp.o: + $(CXX) $(CXXFLAGS) -c $< -o $@ + +$(TARGET): $(OBJS) + $(CC) $(CFLAGS) $(OBJS) $(LIBS) -o $(TARGET) $(PROFILE) + +clean: + rm -f $(OBJS) $(TARGET) \ No newline at end of file diff --git a/src/core/cart.c b/src/core/cart.c new file mode 100644 index 0000000..b28bffa --- /dev/null +++ b/src/core/cart.c @@ -0,0 +1,124 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * 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 "events.h" +#include "fs.h" +#include "logging.h" +#include "machine.h" +#include "pia.h" +#include "xroar.h" +#include "cart.h" + +static event_t *cart_event; +static char *cart_filename; +static int cart_autostart; + +static void cart_configure(const char *filename, int autostart); +static int cart_load(void); +static void cart_interrupt(void *context); + +void cart_helptext(void) { + puts(" -cart FILENAME specify ROM to load into cartridge area ($C000-)"); + puts(" -cartna FILENAME as -cart, but no auto-run"); +} + +void cart_getargs(int argc, char **argv) { + int i; + cart_filename = NULL; + cart_autostart = 0; + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-cart")) { + i++; + if (i >= argc) break; + cart_configure(argv[i], 1); + } else if (!strcmp(argv[i], "-cartna")) { + i++; + if (i >= argc) break; + cart_configure(argv[i], 0); + } + } +} + +void cart_init(void) { + cart_event = event_new(); + if (cart_event == NULL) { + LOG_WARN("Couldn't create cartridge event.\n"); + return; + } + cart_event->dispatch = cart_interrupt; +} + +void cart_reset(void) { + cart_load(); +} + +int cart_insert(char *filename, int autostart) { + cart_configure(filename, autostart); + return cart_load(); +} + +void cart_remove(void) { + if (cart_filename) { + free(cart_filename); + cart_filename = NULL; + } +} + +static void cart_configure(const char *filename, int autostart) { + if (cart_filename) + cart_remove(); + cart_filename = malloc(strlen(filename)+1); + if (cart_filename == NULL) + return; + strcpy(cart_filename, filename); + cart_autostart = autostart; +} + +static int cart_load(void) { + int fd; + if (cart_filename == NULL) + return -1; + if ((fd = fs_open(cart_filename, FS_READ)) == -1) { + cart_remove(); + return -1; + } + LOG_DEBUG(3, "Loading cartridge: %s\n", cart_filename); + fs_read(fd, rom0+0x4000, sizeof(rom0)-0x4000); + fs_close(fd); + if (cart_autostart) { + cart_event->at_cycle = current_cycle + (OSCILLATOR_RATE/10); + event_queue(cart_event); + } else { + event_dequeue(cart_event); + } + return 0; +} + +static void cart_interrupt(void *context) { + (void)context; + if (cart_filename) { + PIA_SET_P1CB1; + cart_event->at_cycle = current_cycle + (OSCILLATOR_RATE/10); + event_queue(cart_event); + } +} diff --git a/src/core/cart.h b/src/core/cart.h new file mode 100644 index 0000000..f07e335 --- /dev/null +++ b/src/core/cart.h @@ -0,0 +1,16 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * See COPYING.GPL for redistribution conditions. */ + +#ifndef __CART_H__ +#define __CART_H__ + +void cart_helptext(void); +void cart_getargs(int argc, char **argv); +void cart_init(void); +void cart_reset(void); +int cart_insert(char *filename, int autostart); +void cart_remove(void); + +#endif /* __CART_H__ */ diff --git a/src/core/common_windows32.h b/src/core/common_windows32.h new file mode 100644 index 0000000..847cacf --- /dev/null +++ b/src/core/common_windows32.h @@ -0,0 +1,13 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * See COPYING.GPL for redistribution conditions. */ + +#ifndef __COMMON_WINDOWS32_H__ +#define __COMMON_WINDOWS32_H__ + +#include + +extern HWND windows32_main_hwnd; + +#endif /* __COMMON_WINDOWS32_H__ */ diff --git a/src/core/config.h b/src/core/config.h new file mode 100644 index 0000000..86ca2d2 --- /dev/null +++ b/src/core/config.h @@ -0,0 +1,52 @@ +#ifndef __CONFIG_H__ +#define __CONFIG_H__ + +/* Use nested functions? */ +#define NESTED_FUNCTIONS_ALLOWED + +/* SDL? */ +#define HAVE_SDL + +/* SDL OpenGL support? */ +#undef HAVE_SDLGL + +/* GTK+? */ +#undef HAVE_GTK + +/* CLI? */ +#define HAVE_CLI + +/* Mac OS X Carbon? */ +#undef HAVE_CARBON + +/* OSS audio? */ +#undef HAVE_OSS_AUDIO + +/* Sun audio? */ +#undef HAVE_SUN_AUDIO + +/* Mac OS X Core Audio? */ +#undef HAVE_MACOSX_AUDIO + +/* Jack audio? */ +#undef HAVE_JACK_AUDIO + +/* libsndfile? */ +#undef HAVE_SNDFILE + +/* GP32? */ +#undef HAVE_GP32 + +/* NDS? */ +#undef HAVE_NDS + +/* PSP? */ +#define HAVE_PSP + +/* MinGW (Windows32)? */ +#undef WINDOWS32 + +/* Build trace mode? */ +#undef TRACE + +#endif /* def __CONFIG_H__ */ diff --git a/src/core/crc16.c b/src/core/crc16.c new file mode 100644 index 0000000..bcf2f00 --- /dev/null +++ b/src/core/crc16.c @@ -0,0 +1,61 @@ +/* CRC16 functions are public domain */ + +#include "types.h" +#include "crc16.h" + +/* "CRC-CCITT", Big-endian, x^16 + x^12 + x^5 + 1, preset 0xffff */ + +static uint16_t crc16_table[256] = { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 +}; + +static uint16_t crc16; + +void crc16_set_value(uint16_t value) { + crc16 = value; +} + +uint16_t crc16_value(void) { + return crc16; +} + +void crc16_byte(uint8_t value) { + uint16_t tmp = crc16; + crc16 = (tmp << 8) ^ crc16_table[(tmp >> 8) ^ value]; +} + +void crc16_block(uint8_t *block, int length) { + for (; length; length--) + crc16_byte(*(block++)); +} diff --git a/src/core/crc16.h b/src/core/crc16.h new file mode 100644 index 0000000..1492d44 --- /dev/null +++ b/src/core/crc16.h @@ -0,0 +1,18 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * See COPYING.GPL for redistribution conditions. */ + +#ifndef __CRC16_H__ +#define __CRC16_H__ + +#include "types.h" + +#define crc16_reset() crc16_set_value(0xffff) + +void crc16_set_value(uint16_t value); +uint16_t crc16_value(void); +void crc16_byte(uint8_t value); +void crc16_block(uint8_t *block, int length); + +#endif /* __CRC16_H__ */ diff --git a/src/core/deltados.c b/src/core/deltados.c new file mode 100644 index 0000000..579bb19 --- /dev/null +++ b/src/core/deltados.c @@ -0,0 +1,77 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * 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 + */ + +/* Sources: + * Delta cartridge detail: + * Partly inferred from disassembly of Delta ROM, + * Partly from information provided by Phill Harvey-Smith on + * www.dragon-archive.co.uk. + */ + +#include +#include + +#include "types.h" +#include "deltados.h" +#include "logging.h" +#include "vdrive.h" +#include "wd279x.h" +#include "xroar.h" + +/* Latch that's part of the Delta cart: */ +static unsigned int ic1_drive_select; +static unsigned int ic1_side_select; +static unsigned int ic1_density; + +void deltados_init(void) { +} + +void deltados_reset(void) { + wd279x_type = WD2791; + wd279x_set_drq_handler = NULL; + wd279x_reset_drq_handler = NULL; + wd279x_set_intrq_handler = NULL; + wd279x_reset_intrq_handler = NULL; + wd279x_reset(); + ic1_drive_select = 0xff; + ic1_side_select = 0xff; + ic1_density = 0xff; + deltados_ff44_write(0); +} + +/* Delta cartridge circuitry */ +void deltados_ff44_write(unsigned int octet) { + LOG_DEBUG(4, "Delta: Write to FF44: "); + if ((octet & 0x03) != ic1_drive_select) { + LOG_DEBUG(4, "DRIVE SELECT %01d, ", octet & 0x03); + } + if ((octet & 0x04) != ic1_side_select) { + LOG_DEBUG(4, "SIDE %s, ", (octet & 0x04)?"1":"0"); + } + octet ^= 0x08; + if ((octet & 0x08) != ic1_density) { + LOG_DEBUG(4, "DENSITY %s, ", (octet & 0x08)?"SINGLE":"DOUBLE"); + } + LOG_DEBUG(4, "\n"); + ic1_drive_select = octet & 0x03; + vdrive_set_drive(ic1_drive_select); + ic1_side_select = octet & 0x04; + vdrive_set_side(ic1_side_select ? 1 : 0); + ic1_density = octet & 0x08; + wd279x_set_density(ic1_density); +} diff --git a/src/core/deltados.h b/src/core/deltados.h new file mode 100644 index 0000000..78df359 --- /dev/null +++ b/src/core/deltados.h @@ -0,0 +1,13 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * See COPYING.GPL for redistribution conditions. */ + +#ifndef __DELTADOS_H__ +#define __DELTADOS_H__ + +void deltados_init(void); +void deltados_reset(void); +void deltados_ff44_write(unsigned int octet); + +#endif /* __DELTADOS_H__ */ diff --git a/src/core/dragondos.c b/src/core/dragondos.c new file mode 100644 index 0000000..37ab281 --- /dev/null +++ b/src/core/dragondos.c @@ -0,0 +1,129 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * 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 + */ + +/* Sources: + * DragonDOS cartridge detail: + * http://www.dragon-archive.co.uk/ + */ + +#include +#include + +#include "types.h" +#include "dragondos.h" +#include "events.h" +#include "logging.h" +#include "m6809.h" +#include "machine.h" +#include "pia.h" +#include "vdrive.h" +#include "wd279x.h" +#include "xroar.h" + +#define QUEUE_NMI() do { \ + nmi_event->at_cycle = current_cycle + 1; \ + event_queue(nmi_event); \ + } while (0) + +/* Handle signals from WD2797 */ +static void set_drq_handler(void); +static void reset_drq_handler(void); +static void set_intrq_handler(void); +static void reset_intrq_handler(void); + +/* NMIs queued to allow CPU to run next instruction */ +static event_t *nmi_event; +static void do_nmi(void *context); + +/* Latch that's part of the DragonDOS cart: */ +static unsigned int ic1_drive_select; +static unsigned int ic1_motor_enable; +static unsigned int ic1_precomp_enable; +static unsigned int ic1_density; +static unsigned int ic1_nmi_enable; + +void dragondos_init(void) { + nmi_event = event_new(); + nmi_event->dispatch = do_nmi; +} + +void dragondos_reset(void) { + wd279x_type = WD2797; + wd279x_set_drq_handler = set_drq_handler; + wd279x_reset_drq_handler = reset_drq_handler; + wd279x_set_intrq_handler = set_intrq_handler; + wd279x_reset_intrq_handler = reset_intrq_handler; + wd279x_reset(); + ic1_drive_select = 0xff; + ic1_motor_enable = 0xff; + ic1_precomp_enable = 0xff; + ic1_density = 0xff; + ic1_nmi_enable = 0xff; + dragondos_ff48_write(0); +} + +/* DragonDOS cartridge circuitry */ +void dragondos_ff48_write(unsigned int octet) { + LOG_DEBUG(4, "DragonDOS: Write to FF48: "); + if ((octet & 0x03) != ic1_drive_select) { + LOG_DEBUG(4, "DRIVE SELECT %01d, ", octet & 0x03); + } + if ((octet & 0x04) != ic1_motor_enable) { + LOG_DEBUG(4, "MOTOR %s, ", (octet & 0x04)?"ON":"OFF"); + } + if ((octet & 0x08) != ic1_density) { + LOG_DEBUG(4, "DENSITY %s, ", (octet & 0x08)?"SINGLE":"DOUBLE"); + } + if ((octet & 0x10) != ic1_precomp_enable) { + LOG_DEBUG(4, "PRECOMP %s, ", (octet & 0x10)?"ON":"OFF"); + } + if ((octet & 0x20) != ic1_nmi_enable) { + LOG_DEBUG(4, "NMI %s, ", (octet & 0x20)?"ENABLED":"DISABLED"); + } + LOG_DEBUG(4, "\n"); + ic1_drive_select = octet & 0x03; + vdrive_set_drive(ic1_drive_select); + ic1_motor_enable = octet & 0x04; + ic1_density = octet & 0x08; + wd279x_set_density(ic1_density); + ic1_precomp_enable = octet & 0x10; + ic1_nmi_enable = octet & 0x20; +} + +static void set_drq_handler(void) { + PIA_SET_P1CB1; +} + +static void reset_drq_handler(void) { + PIA_RESET_P1CB1; +} + +static void set_intrq_handler(void) { + QUEUE_NMI(); +} + +static void reset_intrq_handler(void) { + nmi = 0; +} + +static void do_nmi(void *context) { + (void)context; + if (ic1_nmi_enable) { + nmi = 1; + } +} diff --git a/src/core/dragondos.h b/src/core/dragondos.h new file mode 100644 index 0000000..0c328f2 --- /dev/null +++ b/src/core/dragondos.h @@ -0,0 +1,13 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * See COPYING.GPL for redistribution conditions. */ + +#ifndef __DRAGONDOS_H__ +#define __DRAGONDOS_H__ + +void dragondos_init(void); +void dragondos_reset(void); +void dragondos_ff48_write(unsigned int octet); + +#endif /* __DRAGONDOS_H__ */ diff --git a/src/core/events.c b/src/core/events.c new file mode 100644 index 0000000..8b8316b --- /dev/null +++ b/src/core/events.c @@ -0,0 +1,74 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * 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 "logging.h" + +#include + +#include "events.h" + +event_t *event_list = NULL; + +event_t *event_new(void) { + event_t *new = malloc(sizeof(event_t)); + if (new == NULL) + return NULL; + new->at_cycle = 0; + new->dispatch = NULL; + new->context = NULL; + new->queued = 0; + new->next = NULL; + return new; +} + +void event_free(event_t *event) { + event_dequeue(event); + free(event); +} + +void event_queue(event_t *event) { + event_t **entry; + if (event->queued) + event_dequeue(event); + event->queued = 1; + for (entry = &event_list; *entry; entry = &((*entry)->next)) { + if ((int)((*entry)->at_cycle - event->at_cycle) > 0) { + event->next = *entry; + *entry = event; + return; + } + } + *entry = event; + event->next = NULL; +} + +void event_dequeue(event_t *event) { + event_t **entry = &event_list; + event->queued = 0; + if (*entry == event) { + event_list = event->next; + return; + } + for (entry = &event_list; *entry; entry = &((*entry)->next)) { + if ((*entry)->next == event) { + (*entry)->next = event->next; + return; + } + } +} diff --git a/src/core/events.h b/src/core/events.h new file mode 100644 index 0000000..d9f3ae8 --- /dev/null +++ b/src/core/events.h @@ -0,0 +1,35 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * See COPYING.GPL for redistribution conditions. */ + +#ifndef __EVENT_H__ +#define __EVENT_H__ + +typedef struct event_t event_t; +struct event_t { + Cycle at_cycle; + void (*dispatch)(void *context); + void *context; + int queued; + event_t *next; +}; + +extern event_t *event_list; + +#define EVENT_EXISTS (event_list) +#define EVENT_PENDING (event_list && \ + (int)(current_cycle - event_list->at_cycle) >= 0) +#define DISPATCH_NEXT_EVENT do { \ + event_t *e = event_list; \ + event_list = event_list->next; \ + e->queued = 0; \ + e->dispatch(e->context); \ + } while (0) + +event_t *event_new(void); +void event_free(event_t *event); +void event_queue(event_t *event); +void event_dequeue(event_t *event); + +#endif /* __EVENT_H__ */ diff --git a/src/core/filereq_cli.c b/src/core/filereq_cli.c new file mode 100644 index 0000000..93a07b0 --- /dev/null +++ b/src/core/filereq_cli.c @@ -0,0 +1,58 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * 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 "types.h" +#include "logging.h" +#include "fs.h" +#include "module.h" + +static int init(int argc, char **argv); +static void shutdown(void); +static char *get_filename(const char **extensions); + +FileReqModule filereq_cli_module = { + { "cli", "Command-line file requester", + init, 0, shutdown, NULL }, + get_filename, get_filename +}; + +static char fnbuf[256]; + +static int init(int argc, char **argv) { + (void)argc; + (void)argv; + LOG_DEBUG(2, "Command-line file requester selected.\n"); + return 0; +} + +static void shutdown(void) { +} + +static char *get_filename(const char **extensions) { + char *cr; + (void)extensions; /* unused */ + printf("Filename? "); + fgets(fnbuf, sizeof(fnbuf), stdin); + cr = strrchr(fnbuf, '\n'); + if (cr) + *cr = 0; + return fnbuf; +} diff --git a/src/core/fs.h b/src/core/fs.h new file mode 100644 index 0000000..7f244de --- /dev/null +++ b/src/core/fs.h @@ -0,0 +1,34 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * See COPYING.GPL for redistribution conditions. */ + +#ifndef __FS_H__ +#define __FS_H__ + +#include "types.h" + +/* Some file handling abstractions. */ + +#define FS_READ 0x01 +#define FS_WRITE 0x02 + +void fs_init(void); + +int fs_chdir(const char *path); +int fs_open(const char *filename, int flags); +ssize_t fs_read(int fd, void *buffer, size_t size); +ssize_t fs_write(int fd, const void *buffer, size_t size); +void fs_close(int fd); +ssize_t fs_size(const char *filename); +char *fs_getcwd(char *buf, size_t size); + +ssize_t fs_load_file(char *filename, void *buf, size_t size); +int fs_write_byte(int fd, uint8_t octet); +int fs_write_word16(int fd, uint16_t word16); +int fs_write_word32(int fd, uint32_t word32); +int fs_read_byte(int fd, uint8_t *dest); +int fs_read_word16(int fd, uint16_t *dest); +int fs_read_word32(int fd, uint32_t *dest); + +#endif /* __FS_H__ */ diff --git a/src/core/fs_unix.c b/src/core/fs_unix.c new file mode 100644 index 0000000..6a090a3 --- /dev/null +++ b/src/core/fs_unix.c @@ -0,0 +1,166 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * 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 "types.h" +#include "fs.h" + +#ifdef WINDOWS32 +# define WRFLAGS (O_CREAT|O_WRONLY|O_TRUNC|O_BINARY) +# define RDFLAGS (O_RDONLY|O_BINARY) +#else +# define WRFLAGS (O_CREAT|O_WRONLY|O_TRUNC) +# define RDFLAGS (O_RDONLY) +#endif + +static const char *fs_error = ""; + +int fs_chdir(const char *path) { + return chdir(path); +} + +int fs_open(const char *filename, int flags) { + int fd; + if (filename == NULL) + return -1; + if (flags & FS_WRITE) + fd = open(filename, WRFLAGS, 0644); + else + fd = open(filename, RDFLAGS); + if (fd == -1) { + fs_error = strerror(errno); + return -1; + } + return fd; +} + +ssize_t fs_read(int fd, void *buffer, size_t size) { + int count = 0, tries = 3; + ssize_t r; + unsigned char *buf = (unsigned char *)buffer; + do { + do { + r = read(fd, buf, size); + } while (r == -1 && errno == EINTR && --tries); + if (r == -1) { + fs_error = strerror(errno); + return -1; + } + size -= r; + buf += r; + count += r; + } while (r && size > 0); + return count; +} + +ssize_t fs_write(int fd, const void *buffer, size_t size) { + int count = 0, tries = 3; + ssize_t r; + const unsigned char *buf = (const unsigned char *)buffer; + do { + do { + r = write(fd, buf, size); + } while (r == -1 && errno == EINTR && --tries); + if (r == -1) { + fs_error = strerror(errno); + return -1; + } + size -= r; + buf += r; + count += r; + } while (r && size > 0); + return count; +} + +void fs_close(int fd) { + close(fd); +} + +ssize_t fs_size(const char *filename) { + struct stat sb; + int ret; + ret = stat(filename, &sb); + if (ret == 0) + return (ssize_t)sb.st_size; + return -1; +} + +char *fs_getcwd(char *buf, size_t size) { + return getcwd(buf, size); +} + +ssize_t fs_load_file(char *filename, void *buf, size_t size) { + ssize_t count; + int fd; + if ((fd = fs_open(filename, FS_READ)) == -1) + return -1; + count = fs_read(fd, buf, size); + fs_close(fd); + return count; +} + +int fs_write_byte(int fd, uint8_t octet) { + return fs_write(fd, &octet, 1); +} + +int fs_write_word16(int fd, uint16_t word16) { + if (fs_write_byte(fd, word16 >> 8) == -1) + return -1; + return fs_write_byte(fd, word16 & 0xff); +} + +int fs_write_word32(int fd, uint32_t word32) { + if (fs_write_word16(fd, word32 >> 16) == -1) + return -1; + return fs_write_word16(fd, word32 & 0xffff); +} + +int fs_read_byte(int fd, uint8_t *dest) { + return fs_read(fd, dest, 1); +} + +int fs_read_word16(int fd, uint16_t *dest) { + uint8_t octet; + int ret; + if (fs_read(fd, &octet, 1) == -1) + return -1; + *dest = octet << 8; + if ((ret = fs_read(fd, &octet, 1)) == -1) + return -1; + *dest |= octet; + return ret; +} + +int fs_read_word32(int fd, uint32_t *dest) { + uint16_t word16; + int ret; + if (fs_read_word16(fd, &word16) == -1) + return -1; + *dest = word16 << 16; + if ((ret = fs_read_word16(fd, &word16)) == -1) + return -1; + *dest |= word16; + return ret; +} diff --git a/src/core/hexs19.c b/src/core/hexs19.c new file mode 100644 index 0000000..db3a274 --- /dev/null +++ b/src/core/hexs19.c @@ -0,0 +1,134 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * 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 "sam.h" +#include "logging.h" +#include "fs.h" +#include "m6809.h" +#include "hexs19.h" +#include "machine.h" + +static uint8_t read_nibble(int fd) { + uint8_t in; + fs_read_byte(fd, &in); + if (in >= '0' && in <= '9') + return (in-'0'); + in |= 0x20; + if (in >= 'a' && in <= 'f') + return (in-'a')+10; + return 0xff; +} + +static uint8_t read_byte(int fd) { + return read_nibble(fd) << 4 | read_nibble(fd); +} + +static uint16_t read_word(int fd) { + return read_byte(fd) << 8 | read_byte(fd); +} + +static int skip_eol(int fd) { + int r; + uint8_t d; + do { + r = fs_read_byte(fd, &d); + } while (r > 0 && d != 10); + if (r > 0) + return 1; + return 0; +} + +int intel_hex_read(char *filename) { + int fd; + int i, length, addr, type, sum; + uint8_t data; + if (filename == NULL) + return -1; + if ((fd = fs_open(filename, FS_READ)) == -1) + return -1; + LOG_DEBUG(2, "Reading HEX file\n"); + while (fs_read_byte(fd, &data) > 0) { + if (data != ':') { + fs_close(fd); + return -1; + } + length = read_byte(fd); + addr = read_word(fd); + type = read_byte(fd); + if (type == 0) { + LOG_DEBUG(3,"Loading %d bytes to %04x\n", length, addr); + } + for (i = 0; i < length; i++) { + data = read_byte(fd); + if (type == 0) { + LOG_DEBUG(5,"%02x ", (int)data); + if (addr < 0x8000) + ram0[addr] = data; + else + ram1[addr-0x8000] = data; + addr++; + } + } + if (type == 0) { + LOG_DEBUG(5,"\n"); + } + sum = read_byte(fd); + if (skip_eol(fd) == 0) + break; + if (type == 1) + break; + } + fs_close(fd); + return 0; +} + +int coco_bin_read(char *filename) { + int fd; + uint8_t tmp; + uint_least16_t length, load, exec; + int r; + if (filename == NULL) + return -1; + if ((fd = fs_open(filename, FS_READ)) == -1) + return -1; + LOG_DEBUG(2, "Reading BIN file\n"); + while (fs_read_byte(fd, &tmp) > 0) { + if (tmp == 0) { + fs_read_byte(fd, &tmp); length = (tmp << 8); + fs_read_byte(fd, &tmp); length |= tmp; + fs_read_byte(fd, &tmp); load = (tmp << 8); + fs_read_byte(fd, &tmp); load |= tmp; + LOG_DEBUG(3,"\tLoading %d bytes to $%04x\n", length, load); + r = fs_read(fd, &addrptr_low[load], length); + continue; + } + if (tmp == 0xff) { + fs_read_byte(fd, &tmp); /* skip 0 */ + fs_read_byte(fd, &tmp); /* skip 0 */ + fs_read_byte(fd, &tmp); exec = (tmp << 8); + fs_read_byte(fd, &tmp); exec |= tmp; + LOG_DEBUG(3,"\tExecuting from $%04x\n", exec); + m6809_jump(exec); + break; + } + } + fs_close(fd); + return 0; +} diff --git a/src/core/hexs19.h b/src/core/hexs19.h new file mode 100644 index 0000000..a961f60 --- /dev/null +++ b/src/core/hexs19.h @@ -0,0 +1,12 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * See COPYING.GPL for redistribution conditions. */ + +#ifndef __HEXS19_H__ +#define __HEXS19_H__ + +int intel_hex_read(char *filename); +int coco_bin_read(char *filename); + +#endif /* __HEXS19_H__ */ diff --git a/src/core/joystick.c b/src/core/joystick.c new file mode 100644 index 0000000..8201f03 --- /dev/null +++ b/src/core/joystick.c @@ -0,0 +1,59 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * 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 "logging.h" +#include "module.h" +#include "pia.h" +#include "joystick.h" + + int joystick_leftx, joystick_lefty; + int joystick_rightx, joystick_righty; + +void joystick_init(void) { + joystick_leftx = joystick_lefty = 128; + joystick_rightx = joystick_righty = 128; +} + +void joystick_shutdown(void) { +} + +void joystick_update(void) { + int xcompare, ycompare; + unsigned int octet = PIA_1A.port_output & 0xfc; + if (PIA_0B.control_register & 0x08) { + xcompare = (joystick_leftx >= octet); + ycompare = (joystick_lefty >= octet); + } else { + xcompare = (joystick_rightx >= octet); + ycompare = (joystick_righty >= octet); + } + if (PIA_0A.control_register & 0x08) { + if (ycompare) { + PIA_0A.port_input |= 0x80; + } else { + PIA_0A.port_input &= 0x7f; + } + } else { + if (xcompare) { + PIA_0A.port_input |= 0x80; + } else { + PIA_0A.port_input &= 0x7f; + } + } +} diff --git a/src/core/joystick.h b/src/core/joystick.h new file mode 100644 index 0000000..30a52e5 --- /dev/null +++ b/src/core/joystick.h @@ -0,0 +1,17 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * See COPYING.GPL for redistribution conditions. */ + +#ifndef __JOYSTICK_H__ +#define __JOYSTICK_H__ + +extern int joystick_leftx, joystick_lefty; +extern int joystick_rightx, joystick_righty; + +void joystick_init(void); +void joystick_shutdown(void); +void joystick_poll(void); +void joystick_update(void); + +#endif /* __JOYSTICK_H__ */ diff --git a/src/core/joystick_sdl.c b/src/core/joystick_sdl.c new file mode 100644 index 0000000..ecb8b61 --- /dev/null +++ b/src/core/joystick_sdl.c @@ -0,0 +1,95 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * 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 "events.h" +#include "joystick.h" +#include "logging.h" +#include "machine.h" +#include "module.h" +#include "pia.h" + +static int init(int argc, char **argv); +static void shutdown(void); + +JoystickModule joystick_sdl_module = { + { "sdl", "SDL joystick driver", + init, 0, shutdown, NULL } +}; + +static SDL_Joystick *joy = NULL; + +static event_t *poll_event; +static void do_poll(void *context); + +static int init(int argc, char **argv) { + (void)argc; + (void)argv; + poll_event = event_new(); + if (poll_event == NULL) { + LOG_WARN("Couldn't create joystick polling event.\n"); + return 1; + } + poll_event->dispatch = do_poll; + LOG_DEBUG(2,"Initialising SDL joystick driver\n"); + SDL_InitSubSystem(SDL_INIT_JOYSTICK); + if (SDL_NumJoysticks() <= 0) + return 1; + LOG_DEBUG(2,"\tFound %d joysticks, using first\n", SDL_NumJoysticks()); + joy = SDL_JoystickOpen(0); + if (joy == NULL) + return 1; + LOG_DEBUG(1,"\t%s\n", SDL_JoystickName(0)); + LOG_DEBUG(2,"\tNumber of Axes: %d\n", SDL_JoystickNumAxes(joy)); + LOG_DEBUG(2,"\tNumber of Buttons: %d\n", SDL_JoystickNumButtons(joy)); + LOG_DEBUG(2,"\tNumber of Balls: %d\n", SDL_JoystickNumBalls(joy)); + poll_event->at_cycle = current_cycle + (OSCILLATOR_RATE / 100); + event_queue(poll_event); + return 0; +} + +static void shutdown(void) { + LOG_DEBUG(2,"Shutting down SDL joystick driver\n"); + SDL_JoystickClose(joy); + joy = NULL; + SDL_QuitSubSystem(SDL_INIT_JOYSTICK); +} + +static void do_poll(void *context) { + (void)context; + SDL_JoystickUpdate(); + joystick_leftx = (SDL_JoystickGetAxis(joy, 3)+32768) >> 8; + joystick_lefty = (SDL_JoystickGetAxis(joy, 2)+32768) >> 8; + joystick_rightx = (SDL_JoystickGetAxis(joy, 0)+32768) >> 8; + joystick_righty = (SDL_JoystickGetAxis(joy, 1)+32768) >> 8; + if (SDL_JoystickGetButton(joy, 1)) { + PIA_0A.tied_low &= 0xfe; + } else { + PIA_0A.tied_low |= 0x01; + } + if (SDL_JoystickGetButton(joy, 0)) { + PIA_0A.tied_low &= 0xfd; + } else { + PIA_0A.tied_low |= 0x02; + } + joystick_update(); + poll_event->at_cycle += OSCILLATOR_RATE / 100; + event_queue(poll_event); +} diff --git a/src/core/keyboard.c b/src/core/keyboard.c new file mode 100644 index 0000000..9a36164 --- /dev/null +++ b/src/core/keyboard.c @@ -0,0 +1,129 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * 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 "events.h" +#include "keyboard.h" +#include "logging.h" +#include "machine.h" +#include "pia.h" + +/* These contain masks to be applied when the corresponding row/column is + * held low. eg, if row 1 is outputting a 0 , keyboard_column[1] will + * be applied on column reads */ +unsigned int keyboard_column[9]; +unsigned int keyboard_row[9]; + +unsigned int keyboard_buffer[256]; +unsigned int *keyboard_bufcur, *keyboard_buflast; + +static void keyboard_poll(void *context); +static event_t *poll_event; + +void keyboard_init(void) { + unsigned int i; + poll_event = event_new(); + poll_event->dispatch = keyboard_poll; + keyboard_bufcur = keyboard_buflast = keyboard_buffer; + for (i = 0; i < 8; i++) { + keyboard_column[i] = ~0; + keyboard_row[i] = ~0; + } + poll_event->at_cycle = current_cycle + 141050; + event_queue(poll_event); +} + +void keyboard_reset(void) +{ + unsigned int i; + for (i = 0; i < 8; i++) { + keyboard_column[i] = ~0; + keyboard_row[i] = ~0; + } +} + +void keyboard_column_update(void) { + unsigned int mask = PIA_0B.port_output; + unsigned int i, row = 0x7f; + for (i = 0; i < 8; i++) { + if (!(mask & (1 << i))) { + row &= keyboard_column[i]; + } + } + PIA_0A.port_input = (PIA_0A.port_input & 0x80) | row; +} + +void keyboard_row_update(void) { + unsigned int mask = PIA_0A.port_output; + unsigned int i, col = 0xff; + for (i = 0; i < 7; i++) { + if (!(mask & (1 << i))) { + col &= keyboard_row[i]; + } + } + PIA_0B.port_input = col; +} + +void keyboard_queue_string(const char *s) { + uint_least16_t c; + while ( (c = *(s++)) ) { + *(keyboard_buflast++) = (~c)&0x80; /* shift/unshift */ + *(keyboard_buflast++) = c & 0x7f; + *(keyboard_buflast++) = (c & 0x7f) | 0x80; + } + *(keyboard_buflast++) = 0x80; /* unshift */ +} + +void keyboard_queue(uint_least16_t c) { + int shift_state = keyboard_column[keymap[0].col] & (1<>8) { + case 1: + *(keyboard_buflast++) = 0; /* shift */ + break; + case 2: + *(keyboard_buflast++) = 0x80; /* unshift */ + break; + case 3: + *(keyboard_buflast++) = 0; /* shift */ + *(keyboard_buflast++) = 0x0c; /* clear */ + break; + default: + break; + } + *(keyboard_buflast++) = c & 0x7f; + *(keyboard_buflast++) = (c & 0x7f) | 0x80; + if ((c>>8) == 3) *(keyboard_buflast++) = 0x8c; /* unclear */ + *(keyboard_buflast++) = shift_state ? 0x80 : 0; /* last shift state */ +} + +static void keyboard_poll(void *context) { + (void)context; + if (KEYBOARD_HASQUEUE) { + unsigned int k; + KEYBOARD_DEQUEUE(k); + if (k & 0x80) { + KEYBOARD_RELEASE(k & 0x7f); + } else { + KEYBOARD_PRESS(k); + } + } + if (KEYBOARD_HASQUEUE) { + poll_event->at_cycle += OSCILLATOR_RATE / 50; + event_queue(poll_event); + } +} diff --git a/src/core/keyboard.h b/src/core/keyboard.h new file mode 100644 index 0000000..f439703 --- /dev/null +++ b/src/core/keyboard.h @@ -0,0 +1,55 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * See COPYING.GPL for redistribution conditions. */ + +#ifndef __KEYBOARD_H__ +#define __KEYBOARD_H__ + +#include "types.h" + +/* These contain masks to be applied when the corresponding row/column is + * held low. eg, if row 1 is outputting a 0 , keyboard_column[1] will + * be applied on column reads */ +extern unsigned int keyboard_column[9]; +extern unsigned int keyboard_row[9]; + +#define KEYBOARD_PRESS(s) do { \ + keyboard_column[keymap[s].col] &= ~(1< keyboard_bufcur) +/* For each key, queue: shift up/down as appropriate, key down, key up, + * shift up */ +#define KEYBOARD_QUEUE(c) do { \ + if ((c) & 0x80) \ + *(keyboard_buflast++) = 0; \ + else \ + *(keyboard_buflast++) = 0x80; \ + *(keyboard_buflast++) = (c) & 0x7f; \ + *(keyboard_buflast++) = (c) | 0x80; \ + *(keyboard_buflast++) = 0x80; \ + } while (0) + +#define KEYBOARD_DEQUEUE(c) do { \ + c = *(keyboard_bufcur++); \ + if (keyboard_bufcur >= keyboard_buflast) { \ + keyboard_bufcur = keyboard_buflast = keyboard_buffer; \ + } \ + } while (0) + +extern unsigned int keyboard_buffer[256]; +extern unsigned int *keyboard_bufcur, *keyboard_buflast; + +void keyboard_init(void); +void keyboard_column_update(void); +void keyboard_row_update(void); +void keyboard_queue_string(const char *s); +void keyboard_queue(uint_least16_t c); + +#endif /* __KEYBOARD_H__ */ diff --git a/src/core/keyboard_psp.c b/src/core/keyboard_psp.c new file mode 100644 index 0000000..c29820f --- /dev/null +++ b/src/core/keyboard_psp.c @@ -0,0 +1,426 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * 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 "logging.h" +#include "cart.h" +#include "events.h" +#include "hexs19.h" +#include "joystick.h" +#include "keyboard.h" +#include "machine.h" +#include "module.h" +#include "pia.h" +#include "snapshot.h" +#include "tape.h" +#include "vdisk.h" +#include "vdrive.h" +#include "xroar.h" + +#include "keyboard_psp.h" +#include "global.h" + +static int init(int argc, char **argv); +static void shutdown(void); + +KeyboardModule keyboard_psp_module = { + { "psp", "PSP keyboard driver", + init, 0, shutdown, NULL } +}; + +static event_t *poll_event; + +static void do_poll(void *context); +extern int psp_update_keys(void); + +static void +do_poll(void *context) +{ + (void)context; + psp_update_keys(); + poll_event->at_cycle += OSCILLATOR_RATE / 100; + event_queue(poll_event); +} + +static unsigned int loc_shift = 0; + +static int dragon_to_keymap[DRAGONK_MAX_KEY][2] = { + { '0', 0 }, // DRAGONK_0 + { '1', 0 }, // DRAGONK_1 + { '2', 0 }, // DRAGONK_2 + { '3', 0 }, // DRAGONK_3 + { '4', 0 }, // DRAGONK_4 + { '5', 0 }, // DRAGONK_5 + { '6', 0 }, // DRAGONK_6 + { '7', 0 }, // DRAGONK_7 + { '8', 0 }, // DRAGONK_8 + { '9', 0 }, // DRAGONK_9 + { 'A', 0 }, // DRAGONK_A + { 'B', 0 }, // DRAGONK_B + { 'C', 0 }, // DRAGONK_C + { 'D', 0 }, // DRAGONK_D + { 'E', 0 }, // DRAGONK_E + { 'F', 0 }, // DRAGONK_F + { 'G', 0 }, // DRAGONK_G + { 'H', 0 }, // DRAGONK_H + { 'I', 0 }, // DRAGONK_I + { 'J', 0 }, // DRAGONK_J + { 'K', 0 }, // DRAGONK_K + { 'L', 0 }, // DRAGONK_L + { 'M', 0 }, // DRAGONK_M + { 'N', 0 }, // DRAGONK_N + { 'O', 0 }, // DRAGONK_O + { 'P', 0 }, // DRAGONK_P + { 'Q', 0 }, // DRAGONK_Q + { 'R', 0 }, // DRAGONK_R + { 'S', 0 }, // DRAGONK_S + { 'T', 0 }, // DRAGONK_T + { 'U', 0 }, // DRAGONK_U + { 'V', 0 }, // DRAGONK_V + { 'W', 0 }, // DRAGONK_W + { 'X', 0 }, // DRAGONK_X + { 'Y', 0 }, // DRAGONK_Y + { 'Z', 0 }, // DRAGONK_Z + { 'a', 0 }, // DRAGONK_a + { 'b', 0 }, // DRAGONK_b + { 'c', 0 }, // DRAGONK_c + { 'd', 0 }, // DRAGONK_d + { 'e', 0 }, // DRAGONK_e + { 'f', 0 }, // DRAGONK_f + { 'g', 0 }, // DRAGONK_g + { 'h', 0 }, // DRAGONK_h + { 'i', 0 }, // DRAGONK_i + { 'j', 0 }, // DRAGONK_j + { 'k', 0 }, // DRAGONK_k + { 'l', 0 }, // DRAGONK_l + { 'm', 0 }, // DRAGONK_m + { 'n', 0 }, // DRAGONK_n + { 'o', 0 }, // DRAGONK_o + { 'p', 0 }, // DRAGONK_p + { 'q', 0 }, // DRAGONK_q + { 'r', 0 }, // DRAGONK_r + { 's', 0 }, // DRAGONK_s + { 't', 0 }, // DRAGONK_t + { 'u', 0 }, // DRAGONK_u + { 'v', 0 }, // DRAGONK_v + { 'w', 0 }, // DRAGONK_w + { 'x', 0 }, // DRAGONK_x + { 'y', 0 }, // DRAGONK_y + { 'z', 0 }, // DRAGONK_z + { '7', 1 }, // DRAGONK_QUOTE + { ',', 0 }, // DRAGONK_COMMA + { ',', 1 }, // DRAGONK_LESS + { '.', 0 }, // DRAGONK_PERIOD + { '.', 1 }, // DRAGONK_GREATER + { ';', 0 }, // DRAGONK_SEMICOLON + { ':', 0 }, // DRAGONK_COLON + { '_', 0 }, // DRAGONK_UNDERSCORE + { '|', 0 }, // DRAGONK_PIPE + { '-', 1 }, // DRAGONK_EQUAL + { ';', 1 }, // DRAGONK_PLUS + { '~', 0 }, // DRAGONK_TILDA + { '2', 1 }, // DRAGONK_DBLQUOTE + { '/' ,1 }, // DRAGONK_QUESTION + { '/' ,0 }, // DRAGONK_SLASH + { '-' ,0 }, // DRAGONK_MINUS + { '[' ,0 }, // DRAGONK_LBRACKET + { ']' ,0 }, // DRAGONK_RBRACKET + { '{' ,0 }, // DRAGONK_LCBRACE + { '}' ,0 }, // DRAGONK_RCBRACE + { ' ' ,0 }, // DRAGONK_SPACE + { '1' ,1 }, // DRAGONK_EXCLAMATN + { '@' ,0 }, // DRAGONK_AT + { '3' ,1 }, // DRAGONK_HASH + { '4' ,1 }, // DRAGONK_DOLLAR + { '5' ,1 }, // DRAGONK_PERCENT + { '^' ,0 }, // DRAGONK_POWER + { '6' ,1 }, // DRAGONK_AMPERSAND + { ':' ,1 }, // DRAGONK_ASTERISK + { '8' ,1 }, // DRAGONK_LPAREN + { '9' ,1 }, // DRAGONK_RPAREN + { '\\',0 }, // DRAGONK_BACKSLASH + { '`' ,0 }, // DRAGONK_BACKQUOTE + { 0x9 ,0 }, // DRAGONK_TAB + { 0x8 ,0 }, // DRAGONK_BACKSPACE + { 1 ,0 }, // DRAGONK_LEFT + { 1 ,0 }, // DRAGONK_RIGHT + { 1 ,0 }, // DRAGONK_UP + { 1 ,0 }, // DRAGONK_DOWN + { 0x8 ,0 }, // DRAGONK_DELETE + { 0xd ,0 }, // DRAGONK_RETURN + { 0 ,0 }, // DRAGONK_SHIFT + { 1 ,0 }, // DRAGONK_JOY_UP + { 1 ,0 }, // DRAGONK_JOY_DOWN + { 1 ,0 }, // DRAGONK_JOY_LEFT + { 1 ,0 }, // DRAGONK_JOY_RIGHT + { 1 ,0 }, // DRAGONK_JOY_FIRE + { 1 ,0 }, // CMD_FPS, + { 1 ,0 }, // CMD_JOY, + { 1 ,0 }, // CMD_RENDER, + { 1 ,0 }, // CMD_LOAD, + { 1 ,0 }, // CMD_SAVE, + { 1 ,0 }, // CMD_RESET, + { 1 ,0 }, // CMD_AUTOFIRE, + { 1 ,0 }, // CMD_INCFIRE, + { 1 ,0 }, // CMD_DECFIRE, + { 1 ,0 }, // CMD_RESET, + { 1 ,0 } // CMD_SCREEN + }; + +#define JOY_UNLOW(j) if (j < 127) j = 127; +#define JOY_UNHIGH(j) if (j > 128) j = 128; + +static void +loc_dragon_key_press(int key_id) +{ + if (! DRAGON.dragon_joystick_anal) { + if (DRAGON.dragon_joystick == 0) { + if (key_id == DRAGONK_JOY_UP) { joystick_lefty = 0; return; } + if (key_id == DRAGONK_JOY_DOWN) { joystick_lefty = 255; return; } + if (key_id == DRAGONK_JOY_LEFT) { joystick_leftx = 0; return; } + if (key_id == DRAGONK_JOY_RIGHT) { joystick_leftx = 255; return; } + } + if (DRAGON.dragon_joystick == 1) { + if (key_id == DRAGONK_JOY_UP) { joystick_righty = 0; return; } + if (key_id == DRAGONK_JOY_DOWN) { joystick_righty = 255; return; } + if (key_id == DRAGONK_JOY_LEFT) { joystick_rightx = 0; return; } + if (key_id == DRAGONK_JOY_RIGHT) { joystick_rightx = 255; return; } + } + } + if (DRAGON.dragon_joystick == 0) { + if (key_id == DRAGONK_JOY_FIRE) { PIA_0A.tied_low &= 0xfd; return; } + } + if (DRAGON.dragon_joystick == 1) { + if (key_id == DRAGONK_JOY_FIRE) { PIA_0A.tied_low &= 0xfe; return; } + } + if (key_id == DRAGONK_SHIFT) { + loc_shift = 1; + KEYBOARD_PRESS(0); + return; + } + if (key_id == DRAGONK_UP) { KEYBOARD_PRESS(94); return; } + if (key_id == DRAGONK_DOWN) { KEYBOARD_PRESS(10); return; } + if (key_id == DRAGONK_LEFT) { KEYBOARD_PRESS(8); return; } + if (key_id == DRAGONK_RIGHT) { KEYBOARD_PRESS(9); return; } + //LUDO: TO_BE_DONE ! if (key_id == DRAGONK_HOME) { KEYBOARD_PRESS(12); return; } +# if 0 //TO_BE_DONE ! + if (unicode == '\\') { + /* CoCo and Dragon 64 in 64K mode have a different way + * of scanning for '\' */ + if (IS_COCO_KEYMAP || (IS_DRAGON64 && !(PIA_1B.port_output & 0x04))) { + KEYBOARD_PRESS(0); + KEYBOARD_PRESS(12); + } else { + KEYBOARD_PRESS(0); + KEYBOARD_PRESS(12); + KEYBOARD_PRESS('/'); + } + return; + } + if (shift && (unicode == 8 || unicode == 127)) { + KEYBOARD_PRESS(0); + KEYBOARD_PRESS(8); + return; + } + if (unicode == 163) { + KEYBOARD_PRESS(0); + KEYBOARD_PRESS('3'); + return; + } + if (unicode < 128) { + unsigned int code = unicode_to_dragon[unicode]; + if (code & 128) + KEYBOARD_PRESS(0); + else + KEYBOARD_RELEASE(0); + KEYBOARD_PRESS(code & 0x7f); + } + return; +# endif + if (key_id < DRAGONK_MAX_KEY) { + unsigned int mapped = dragon_to_keymap[ key_id ][0]; + unsigned int shift = dragon_to_keymap[ key_id ][1]; + if (shift && !loc_shift) { + KEYBOARD_PRESS(0); + } + if (mapped) { + KEYBOARD_PRESS(mapped); + } + } +} + +void +dragon_key_press(int key_id) +{ + loc_dragon_key_press(key_id); + keyboard_column_update(); + keyboard_row_update(); +} + +void +dragon_joystick_event(int key_id) +{ + if (! DRAGON.dragon_joystick_anal) return; + + if (DRAGON.dragon_joystick == 0) { + if (key_id == DRAGONK_JOY_UP) { joystick_lefty -= DRAGON.dragon_joystick_step; } + else + if (key_id == DRAGONK_JOY_DOWN) { joystick_lefty += DRAGON.dragon_joystick_step; } + else + if (key_id == DRAGONK_JOY_LEFT) { joystick_leftx -= DRAGON.dragon_joystick_step; } + else + if (key_id == DRAGONK_JOY_RIGHT) { joystick_leftx += DRAGON.dragon_joystick_step; } + else + if (key_id == DRAGONK_JOY_FIRE) { PIA_0A.tied_low |= 0x02; return; } + + if (joystick_leftx < 1) joystick_leftx = 1; + if (joystick_lefty < 1) joystick_lefty = 1; + if (joystick_leftx > 255) joystick_leftx = 255; + if (joystick_lefty > 255) joystick_lefty = 255; + } + if (DRAGON.dragon_joystick == 1) { + if (key_id == DRAGONK_JOY_UP) { joystick_righty -= DRAGON.dragon_joystick_step; } + else + if (key_id == DRAGONK_JOY_DOWN) { joystick_righty += DRAGON.dragon_joystick_step; } + else + if (key_id == DRAGONK_JOY_LEFT) { joystick_rightx -= DRAGON.dragon_joystick_step; } + else + if (key_id == DRAGONK_JOY_RIGHT) { joystick_rightx += DRAGON.dragon_joystick_step; } + else + if (key_id == DRAGONK_JOY_FIRE) { PIA_0A.tied_low |= 0x01; return; } + + if (joystick_rightx < 1) joystick_rightx = 1; + if (joystick_righty < 1) joystick_righty = 1; + if (joystick_rightx > 255) joystick_rightx = 255; + if (joystick_righty > 255) joystick_righty = 255; + } +} + +static void +loc_dragon_key_release(int key_id) +{ + if (! DRAGON.dragon_joystick_anal) { + if (DRAGON.dragon_joystick == 0) { + if (key_id == DRAGONK_JOY_UP) { JOY_UNLOW(joystick_lefty); return; } + if (key_id == DRAGONK_JOY_DOWN) { JOY_UNHIGH(joystick_lefty); return; } + if (key_id == DRAGONK_JOY_LEFT) { JOY_UNLOW(joystick_leftx); return; } + if (key_id == DRAGONK_JOY_RIGHT) { JOY_UNHIGH(joystick_leftx); return; } + } + if (DRAGON.dragon_joystick == 1) { + if (key_id == DRAGONK_JOY_UP) { JOY_UNLOW(joystick_righty); return; } + if (key_id == DRAGONK_JOY_DOWN) { JOY_UNHIGH(joystick_righty); return; } + if (key_id == DRAGONK_JOY_LEFT) { JOY_UNLOW(joystick_rightx); return; } + if (key_id == DRAGONK_JOY_RIGHT) { JOY_UNHIGH(joystick_rightx); return; } + } + } + if (DRAGON.dragon_joystick == 0) { + if (key_id == DRAGONK_JOY_FIRE) { PIA_0A.tied_low |= 0x02; return; } + } + if (DRAGON.dragon_joystick == 1) { + if (key_id == DRAGONK_JOY_FIRE) { PIA_0A.tied_low |= 0x01; return; } + } + if (key_id == DRAGONK_SHIFT) { + loc_shift = 0; + KEYBOARD_RELEASE(0); + return; + } + if (key_id == DRAGONK_UP) { KEYBOARD_RELEASE(94); return; } + if (key_id == DRAGONK_DOWN) { KEYBOARD_RELEASE(10); return; } + if (key_id == DRAGONK_LEFT) { KEYBOARD_RELEASE(8); return; } + if (key_id == DRAGONK_RIGHT) { KEYBOARD_RELEASE(9); return; } + //LUDO: TO_BE_DONE ! if (key_id == DRAGONK_HOME) { KEYBOARD_RELEASE(12); return; } +# if 0 //TO_BE_DONE ! + if (unicode == '\\') { + /* CoCo and Dragon 64 in 64K mode have a different way + * of scanning for '\' */ + if (IS_COCO_KEYMAP || (IS_DRAGON64 && !(PIA_1B.port_output & 0x04))) { + KEYBOARD_RELEASE(0); + KEYBOARD_RELEASE(12); + } else { + KEYBOARD_RELEASE(0); + KEYBOARD_RELEASE(12); + KEYBOARD_RELEASE('/'); + } + return; + } + if (shift && (unicode == 8 || unicode == 127)) { + KEYBOARD_RELEASE(0); + KEYBOARD_RELEASE(8); + return; + } + if (unicode == 163) { + KEYBOARD_RELEASE(0); + KEYBOARD_RELEASE('3'); + return; + } + if (unicode < 128) { + unsigned int code = unicode_to_dragon[unicode]; + if (code & 128) + KEYBOARD_RELEASE(0); + else + KEYBOARD_RELEASE(0); + KEYBOARD_RELEASE(code & 0x7f); + } + return; +# endif + if (key_id < DRAGONK_MAX_KEY) { + unsigned int mapped = dragon_to_keymap[ key_id ][0]; + unsigned int shift = dragon_to_keymap[ key_id ][1]; + if (mapped) { + KEYBOARD_RELEASE(mapped); + } + if (shift && !loc_shift) { + KEYBOARD_RELEASE(0); + } + } +} + +void +dragon_reset_keyboard() +{ + keyboard_reset(); +} + +void +dragon_key_release(int key_id) +{ + loc_dragon_key_release(key_id); + keyboard_column_update(); + keyboard_row_update(); +} + +static int +init(int argc, char **argv) +{ + poll_event = event_new(); + poll_event->dispatch = do_poll; + poll_event->at_cycle = current_cycle + (OSCILLATOR_RATE / 100); + event_queue(poll_event); + return 0; +} + +static void +shutdown(void) +{ + event_free(poll_event); +} diff --git a/src/core/keyboard_psp.h b/src/core/keyboard_psp.h new file mode 100644 index 0000000..30f5f18 --- /dev/null +++ b/src/core/keyboard_psp.h @@ -0,0 +1,161 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +# ifndef _KEYBOARD_PSP_H_ +# define _KEYBOARD_PSP_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + enum dragon_keys_emum { + + DRAGONK_0, + DRAGONK_1, + DRAGONK_2, + DRAGONK_3, + DRAGONK_4, + DRAGONK_5, + DRAGONK_6, + DRAGONK_7, + DRAGONK_8, + DRAGONK_9, + + DRAGONK_A, + DRAGONK_B, + DRAGONK_C, + DRAGONK_D, + DRAGONK_E, + DRAGONK_F, + DRAGONK_G, + DRAGONK_H, + DRAGONK_I, + DRAGONK_J, + DRAGONK_K, + DRAGONK_L, + DRAGONK_M, + DRAGONK_N, + DRAGONK_O, + DRAGONK_P, + DRAGONK_Q, + DRAGONK_R, + DRAGONK_S, + DRAGONK_T, + DRAGONK_U, + DRAGONK_V, + DRAGONK_W, + DRAGONK_X, + DRAGONK_Y, + DRAGONK_Z, + + DRAGONK_a, + DRAGONK_b, + DRAGONK_c, + DRAGONK_d, + DRAGONK_e, + DRAGONK_f, + DRAGONK_g, + DRAGONK_h, + DRAGONK_i, + DRAGONK_j, + DRAGONK_k, + DRAGONK_l, + DRAGONK_m, + DRAGONK_n, + DRAGONK_o, + DRAGONK_p, + DRAGONK_q, + DRAGONK_r, + DRAGONK_s, + DRAGONK_t, + DRAGONK_u, + DRAGONK_v, + DRAGONK_w, + DRAGONK_x, + DRAGONK_y, + DRAGONK_z, + + DRAGONK_QUOTE, + DRAGONK_COMMA, + DRAGONK_LESS, + DRAGONK_PERIOD, + DRAGONK_GREATER, + DRAGONK_SEMICOLON, + DRAGONK_COLON, + DRAGONK_UNDERSCORE, + DRAGONK_PIPE, + DRAGONK_EQUAL, + DRAGONK_PLUS, + DRAGONK_TILDA, + DRAGONK_DBLQUOTE, + DRAGONK_QUESTION, + DRAGONK_SLASH, + DRAGONK_MINUS, + DRAGONK_LBRACKET, + DRAGONK_RBRACKET, + DRAGONK_LCBRACE, + DRAGONK_RCBRACE, + DRAGONK_SPACE, + DRAGONK_EXCLAMATN, + DRAGONK_AT, + DRAGONK_HASH, + DRAGONK_DOLLAR, + DRAGONK_PERCENT, + DRAGONK_POWER, + DRAGONK_AMPERSAND, + DRAGONK_ASTERISK, + DRAGONK_LPAREN, + DRAGONK_RPAREN, + DRAGONK_BACKSLASH, + DRAGONK_BACKQUOTE, + + DRAGONK_TAB, + DRAGONK_BACKSPACE, + DRAGONK_LEFT, + DRAGONK_RIGHT, + DRAGONK_UP, + DRAGONK_DOWN, + DRAGONK_DELETE, + DRAGONK_RETURN, + DRAGONK_SHIFT, + + DRAGONK_JOY_UP, + DRAGONK_JOY_DOWN, + DRAGONK_JOY_LEFT, + DRAGONK_JOY_RIGHT, + DRAGONK_JOY_FIRE, + CMD_FPS, + CMD_JOY, + CMD_RENDER, + CMD_LOAD, + CMD_SAVE, + CMD_RESET, + CMD_AUTOFIRE, + CMD_INCFIRE, + CMD_DECFIRE, + CMD_SCREEN, + + DRAGONK_MAX_KEY + + }; + + extern void dragon_key_press(int key_id); + extern void dragon_key_release(int key_id); + +#ifdef __cplusplus +} +#endif +# endif diff --git a/src/core/keyboard_sdl.c b/src/core/keyboard_sdl.c new file mode 100644 index 0000000..8e9b5c4 --- /dev/null +++ b/src/core/keyboard_sdl.c @@ -0,0 +1,488 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * 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 "logging.h" +#include "cart.h" +#include "events.h" +#include "hexs19.h" +#include "joystick.h" +#include "keyboard.h" +#include "machine.h" +#include "module.h" +#include "pia.h" +#include "snapshot.h" +#include "tape.h" +#include "vdisk.h" +#include "vdrive.h" +#include "xroar.h" + +static int init(int argc, char **argv); +static void shutdown(void); + +KeyboardModule keyboard_sdl_module = { + { "sdl", "SDL keyboard driver", + init, 0, shutdown, NULL } +}; + +static event_t *poll_event; +static void do_poll(void *context); + +struct keymap { + const char *name; + unsigned int *raw; +}; + +#include "keyboard_sdl_mappings.c" + +static unsigned int control = 0, shift = 0; +static unsigned int emulate_joystick = 0; + +static uint_least16_t sdl_to_keymap[768]; + +static unsigned int unicode_to_dragon[128] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 8, 9, 10, 0, 12, 13, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 27, 0, 0, 0, 0, + ' ', 128+'1', 128+'2', 128+'3', 128+'4', 128+'5', 128+'6', 128+'7', + 128+'8', 128+'9', 128+':', 128+';', ',', '-', '.', '/', + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', ':', ';', 128+',', 128+'-', 128+'.', 128+'/', + '@', 128+'a', 128+'b', 128+'c', 128+'d', 128+'e', 128+'f', 128+'g', + 128+'h', 128+'i', 128+'j', 128+'k', 128+'l', 128+'m', 128+'n', 128+'o', + 128+'p', 128+'q', 128+'r', 128+'s', 128+'t', 128+'u', 128+'v', 128+'w', + 128+'x', 128+'y', 128+'z', 128+10, 128+12, 128+9, '^', 128+'^', + 12, 'a', 'b', 'c', 'd', 'e', 'f', 'g', + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', + 'x', 'y', 'z', 0, 0, 0, 128+'@', 8 +}; + +/* Track which unicode value was last generated by key presses (SDL + * only guarantees to fill in the unicode field on key *releases*). */ +static unsigned int unicode_last_keysym[SDLK_LAST]; + +static const char *keymap_option; +static unsigned int *selected_keymap; +static int translated_keymap; + +static void map_keyboard(unsigned int *map) { + int i; + for (i = 0; i < 256; i++) + sdl_to_keymap[i] = i & 0x7f; + for (i = 0; i < SDLK_LAST; i++) + unicode_last_keysym[i] = 0; + if (map == NULL) + return; + while (*map) { + unsigned int sdlkey = *(map++); + unsigned int dgnkey = *(map++); + sdl_to_keymap[sdlkey & 0xff] = dgnkey & 0x7f; + } +} + +static int init(int argc, char **argv) { + int i; + keymap_option = NULL; + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-keymap")) { + i++; + if (i >= argc) break; + keymap_option = argv[i]; + } + } + selected_keymap = NULL; + for (i = 0; mappings[i].name; i++) { + if (selected_keymap == NULL + && !strcmp("uk", mappings[i].name)) { + selected_keymap = mappings[i].raw; + } + if (keymap_option && !strcmp(keymap_option, mappings[i].name)) { + selected_keymap = mappings[i].raw; + LOG_DEBUG(2,"\tSelecting '%s' keymap\n",keymap_option); + } + } + map_keyboard(selected_keymap); + translated_keymap = 0; + SDL_EnableUNICODE(translated_keymap); + poll_event = event_new(); + poll_event->dispatch = do_poll; + poll_event->at_cycle = current_cycle + (OSCILLATOR_RATE / 100); + event_queue(poll_event); + return 0; +} + +static void shutdown(void) { + event_free(poll_event); +} + +static void keypress(SDL_keysym *keysym) { + SDLKey sym = keysym->sym; + if (emulate_joystick == 1) { + if (sym == SDLK_UP) { joystick_lefty = 0; return; } + if (sym == SDLK_DOWN) { joystick_lefty = 255; return; } + if (sym == SDLK_LEFT) { joystick_leftx = 0; return; } + if (sym == SDLK_RIGHT) { joystick_leftx = 255; return; } + if (sym == SDLK_LALT) { PIA_0A.tied_low &= 0xfd; return; } + } + if (emulate_joystick == 2) { + if (sym == SDLK_UP) { joystick_righty = 0; return; } + if (sym == SDLK_DOWN) { joystick_righty = 255; return; } + if (sym == SDLK_LEFT) { joystick_rightx = 0; return; } + if (sym == SDLK_RIGHT) { joystick_rightx = 255; return; } + if (sym == SDLK_LALT) { PIA_0A.tied_low &= 0xfe; return; } + } + if (sym == SDLK_LSHIFT || sym == SDLK_RSHIFT) { + shift = 1; + KEYBOARD_PRESS(0); + return; + } + if (sym == SDLK_LCTRL || sym == SDLK_RCTRL) { control = 1; return; } + if (sym == SDLK_F12) { + noratelimit = 1; + frameskip = 10; + } + if (control) { + switch (sym) { + case SDLK_1: case SDLK_2: case SDLK_3: case SDLK_4: + if (shift) { + struct vdisk *new_disk; + int drive = sym - SDLK_1; + LOG_DEBUG(4, "Creating blank disk in drive %d\n", 1 + drive); + vdrive_eject_disk(drive); + new_disk = vdisk_blank_disk(2, 42, VDISK_LENGTH_5_25); + if (new_disk == NULL) break; + if (new_disk->filename == NULL) { + new_disk->filetype = FILETYPE_DMK; + new_disk->filename = malloc(13); + if (new_disk->filename != NULL) { + snprintf(new_disk->filename, 13, "newdisk%d.dmk", drive); + } + new_disk->file_write_protect = VDISK_WRITE_ENABLE; + } + vdrive_insert_disk(drive, new_disk); + } else { + const char *disk_exts[] = { "DMK", "JVC", "VDK", "DSK", NULL }; + char *filename = filereq_module->load_filename(disk_exts); + if (filename) { + vdrive_eject_disk(sym - SDLK_1); + vdrive_insert_disk(sym - SDLK_1, vdisk_load(filename)); + } + } + break; + case SDLK_5: case SDLK_6: case SDLK_7: case SDLK_8: + if (shift) { + int drive = sym - SDLK_5; + struct vdisk *disk = vdrive_disk_in_drive(drive); + if (disk != NULL) { + if (disk->file_write_protect == VDISK_WRITE_ENABLE) { + disk->file_write_protect = VDISK_WRITE_PROTECT; + LOG_DEBUG(2, "File for disk in drive %d write protected.\n", drive); + } else { + disk->file_write_protect = VDISK_WRITE_ENABLE; + LOG_DEBUG(2, "File for disk in drive %d write enabled.\n", drive); + } + } + } else { + int drive = sym - SDLK_5; + struct vdisk *disk = vdrive_disk_in_drive(drive); + if (disk != NULL) { + if (disk->write_protect == VDISK_WRITE_ENABLE) { + disk->write_protect = VDISK_WRITE_PROTECT; + LOG_DEBUG(2, "Disk in drive %d write protected.\n", drive); + } else { + disk->write_protect = VDISK_WRITE_ENABLE; + LOG_DEBUG(2, "Disk in drive %d write enabled.\n", drive); + } + } + } + break; + case SDLK_a: + DRAGON.video_artifact_mode++; + if (DRAGON.video_artifact_mode > 2) + DRAGON.video_artifact_mode = 0; + vdg_set_mode(); + break; + case SDLK_c: + exit(0); + break; + case SDLK_e: + requested_config.dos_type = DOS_ENABLED ? DOS_NONE : ANY_AUTO; + break; + case SDLK_f: + if (video_module->set_fullscreen) + video_module->set_fullscreen(!video_module->is_fullscreen); + break; + case SDLK_i: + { + const char *cart_exts[] = { "ROM", NULL }; + char *filename = filereq_module->load_filename(cart_exts); + if (filename) + cart_insert(filename, shift ? 0 : 1); + else + cart_remove(); + } + break; + case SDLK_j: + emulate_joystick++; + if (emulate_joystick > 2) + emulate_joystick = 0; + break; + case SDLK_k: + machine_set_keymap(running_config.keymap + 1); + break; + case SDLK_b: + case SDLK_h: + case SDLK_l: + case SDLK_t: + { + char *filename; + int type; + filename = filereq_module->load_filename(NULL); + if (filename == NULL) + break; + type = xroar_filetype_by_ext(filename); + switch (type) { + case FILETYPE_VDK: case FILETYPE_JVC: + case FILETYPE_DMK: + vdrive_eject_disk(0); + vdrive_insert_disk(0, vdisk_load(filename)); + break; + case FILETYPE_BIN: + coco_bin_read(filename); break; + case FILETYPE_HEX: + intel_hex_read(filename); break; + case FILETYPE_SNA: + read_snapshot(filename); break; + case FILETYPE_CAS: default: + if (shift) + tape_autorun(filename); + else + tape_open_reading(filename); + break; + } + } + break; + case SDLK_m: + machine_clear_requested_config(); + requested_machine = running_machine + 1; + machine_reset(RESET_HARD); + break; + case SDLK_n: + //if (shift) video_next(); + //else sound_next(); + break; + case SDLK_r: + machine_reset(shift ? RESET_HARD : RESET_SOFT); + break; + case SDLK_s: + { + const char *snap_exts[] = { "SNA", NULL }; + char *filename = filereq_module->save_filename(snap_exts); + if (filename) + write_snapshot(filename); + } + break; + case SDLK_w: + { + const char *tape_exts[] = { "CAS", NULL }; + char *filename = filereq_module->save_filename(tape_exts); + if (filename) { + tape_open_writing(filename); + } + break; + } +#ifdef TRACE + case SDLK_v: + trace = !trace; + break; +#endif + case SDLK_z: // running out of letters... + translated_keymap = !translated_keymap; + /* UNICODE translation only used in + * translation mode */ + SDL_EnableUNICODE(translated_keymap); + break; + default: + break; + } + return; + } + if (sym == SDLK_UP) { KEYBOARD_PRESS(94); return; } + if (sym == SDLK_DOWN) { KEYBOARD_PRESS(10); return; } + if (sym == SDLK_LEFT) { KEYBOARD_PRESS(8); return; } + if (sym == SDLK_RIGHT) { KEYBOARD_PRESS(9); return; } + if (sym == SDLK_HOME) { KEYBOARD_PRESS(12); return; } + if (translated_keymap) { + unsigned int unicode; + if (sym >= SDLK_LAST) + return; + unicode = keysym->unicode; + unicode_last_keysym[sym] = unicode; + if (unicode == '\\') { + /* CoCo and Dragon 64 in 64K mode have a different way + * of scanning for '\' */ + if (IS_COCO_KEYMAP || (IS_DRAGON64 && !(PIA_1B.port_output & 0x04))) { + KEYBOARD_PRESS(0); + KEYBOARD_PRESS(12); + } else { + KEYBOARD_PRESS(0); + KEYBOARD_PRESS(12); + KEYBOARD_PRESS('/'); + } + return; + } + if (shift && (unicode == 8 || unicode == 127)) { + KEYBOARD_PRESS(0); + KEYBOARD_PRESS(8); + return; + } + if (unicode == 163) { + KEYBOARD_PRESS(0); + KEYBOARD_PRESS('3'); + return; + } + if (unicode < 128) { + unsigned int code = unicode_to_dragon[unicode]; + if (code & 128) + KEYBOARD_PRESS(0); + else + KEYBOARD_RELEASE(0); + KEYBOARD_PRESS(code & 0x7f); + } + return; + } + if (sym < 256) { + unsigned int mapped = sdl_to_keymap[sym]; + KEYBOARD_PRESS(mapped); + } +} + +#define JOY_UNLOW(j) if (j < 127) j = 127; +#define JOY_UNHIGH(j) if (j > 128) j = 128; + +static void keyrelease(SDL_keysym *keysym) { + SDLKey sym = keysym->sym; + if (emulate_joystick == 1) { + if (sym == SDLK_UP) { JOY_UNLOW(joystick_lefty); return; } + if (sym == SDLK_DOWN) { JOY_UNHIGH(joystick_lefty); return; } + if (sym == SDLK_LEFT) { JOY_UNLOW(joystick_leftx); return; } + if (sym == SDLK_RIGHT) { JOY_UNHIGH(joystick_leftx); return; } + if (sym == SDLK_LALT) { PIA_0A.tied_low |= 0x02; return; } + } + if (emulate_joystick == 2) { + if (sym == SDLK_UP) { JOY_UNLOW(joystick_righty); return; } + if (sym == SDLK_DOWN) { JOY_UNHIGH(joystick_righty); return; } + if (sym == SDLK_LEFT) { JOY_UNLOW(joystick_rightx); return; } + if (sym == SDLK_RIGHT) { JOY_UNHIGH(joystick_rightx); return; } + if (sym == SDLK_LALT) { PIA_0A.tied_low |= 0x01; return; } + } + if (sym == SDLK_LSHIFT || sym == SDLK_RSHIFT) { + shift = 0; + KEYBOARD_RELEASE(0); + return; + } + if (sym == SDLK_LCTRL || sym == SDLK_RCTRL) { control = 0; return; } + if (sym == SDLK_F12) { + noratelimit = 0; + frameskip = requested_frameskip; + } + if (sym == SDLK_UP) { KEYBOARD_RELEASE(94); return; } + if (sym == SDLK_DOWN) { KEYBOARD_RELEASE(10); return; } + if (sym == SDLK_LEFT) { KEYBOARD_RELEASE(8); return; } + if (sym == SDLK_RIGHT) { KEYBOARD_RELEASE(9); return; } + if (sym == SDLK_HOME) { KEYBOARD_RELEASE(12); return; } + if (translated_keymap) { + unsigned int unicode; + if (sym >= SDLK_LAST) + return; + unicode = unicode_last_keysym[sym]; + if (unicode == '\\') { + /* CoCo and Dragon 64 in 64K mode have a different way + * of scanning for '\' */ + if (IS_COCO_KEYMAP || (IS_DRAGON64 && !(PIA_1B.port_output & 0x04))) { + KEYBOARD_RELEASE(0); + KEYBOARD_RELEASE(12); + } else { + KEYBOARD_RELEASE(0); + KEYBOARD_RELEASE(12); + KEYBOARD_RELEASE('/'); + } + return; + } + if (shift && (unicode == 8 || unicode == 127)) { + KEYBOARD_RELEASE(0); + KEYBOARD_RELEASE(8); + return; + } + if (unicode == 163) { + KEYBOARD_RELEASE(0); + KEYBOARD_RELEASE('3'); + return; + } + if (unicode < 128) { + unsigned int code = unicode_to_dragon[unicode]; + if (code & 128) + KEYBOARD_RELEASE(0); + if (shift) + KEYBOARD_PRESS(0); + KEYBOARD_RELEASE(code & 0x7f); + } + return; + } + if (sym < 256) { + unsigned int mapped = sdl_to_keymap[sym]; + KEYBOARD_RELEASE(mapped); + } +} + +static void do_poll(void *context) { + (void)context; + SDL_Event event; + while (SDL_PollEvent(&event) == 1) { + switch(event.type) { + case SDL_VIDEORESIZE: + if (video_module->resize) { + video_module->resize(event.resize.w, event.resize.h); + } + break; + case SDL_QUIT: + exit(0); break; + case SDL_KEYDOWN: + keypress(&event.key.keysym); + keyboard_column_update(); + keyboard_row_update(); + break; + case SDL_KEYUP: + keyrelease(&event.key.keysym); + keyboard_column_update(); + keyboard_row_update(); + break; + default: + break; + } + } + poll_event->at_cycle += OSCILLATOR_RATE / 100; + event_queue(poll_event); +} diff --git a/src/core/keyboard_sdl_mappings.c b/src/core/keyboard_sdl_mappings.c new file mode 100644 index 0000000..b2137fb --- /dev/null +++ b/src/core/keyboard_sdl_mappings.c @@ -0,0 +1,62 @@ +/* The Dragon keyboard layout: + * + * 1 2 3 4 5 6 7 8 9 0 : - brk + * up Q W E R T Y U I O P @ lft rgt + * dwn A S D F G H J K L ; enter clr + * shft Z X C V B N M , . / shft + * space + */ + +/* Keymaps are lists of pairs of 'key' to 'mapping'. + * 'key' is the SDL keysym, 'mapping' is the Dragon key it gets mapped to. + */ + +static unsigned int raw_keymap_uk[] = { + '-', ':', + '=', '-', + '[', '@', + 0, 0 +}; +static unsigned int raw_keymap_fr[] = { + '&', '1', + SDLK_WORLD_73, '2', + '"', '3', + '\'', '4', + '(', '5', + '-', '6', + SDLK_WORLD_72, '7', + '_', '8', + SDLK_WORLD_71, '9', + SDLK_WORLD_64, '0', + ')', ':', + '=', '-', + 'a', 'q', + 'z', 'w', + '^', '@', + 'q', 'a', + 'm', ';', + 'w', 'z', + ',', 'm', + ';', ',', + ':', '.', + '!', '/', + 0, 0 +}; +static unsigned int raw_keymap_de[] = { + SDLK_WORLD_63, ':', + '\'', '-', + 'z', 'y', + SDLK_WORLD_92, '@', + SDLK_WORLD_86, ';', + 'y', 'z', + '-', '/', + 0, 0 +}; + +static struct keymap mappings[] = { + { "uk", raw_keymap_uk }, + { "us", raw_keymap_uk }, + { "fr", raw_keymap_fr }, + { "de", raw_keymap_de }, + { NULL, NULL } +}; diff --git a/src/core/logging.h b/src/core/logging.h new file mode 100644 index 0000000..9a2b888 --- /dev/null +++ b/src/core/logging.h @@ -0,0 +1,30 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * See COPYING.GPL for redistribution conditions. */ + +#ifndef __LOGGING_H__ +#define __LOGGING_H__ + +#include + +#if defined(HAVE_GP32) || defined(HAVE_PSP) + +#define LOG_DEBUG(...) +#define LOG_WARN(...) +#define LOG_ERROR(...) + +#else +/* 0 - Silent, 1 - Title, 2 - Info, 3 - Details, 4 - Verbose, 5 - Silly */ +/* Normally 3 */ +#ifndef DEBUG_LEVEL +# define DEBUG_LEVEL 3 +#endif + +#define LOG_DEBUG(l,...) do { if (DEBUG_LEVEL >= l) { fprintf(stderr, __VA_ARGS__); } } while (0) +#define LOG_WARN(...) fprintf(stderr, "WARNING: " __VA_ARGS__) +#define LOG_ERROR(...) fprintf(stderr, "ERROR: " __VA_ARGS__) + +#endif + +#endif /* __LOGGING_H__ */ diff --git a/src/core/m6809.c b/src/core/m6809.c new file mode 100644 index 0000000..168c699 --- /dev/null +++ b/src/core/m6809.c @@ -0,0 +1,1462 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * 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 "logging.h" +#include "m6809.h" +#include "m6809_dasm.h" +#include "machine.h" +#include "sam.h" +#include "xroar.h" + +/* Condition Code manipulation macros */ + +#define CC_E 0x80 +#define CC_F 0x40 +#define CC_H 0x20 +#define CC_I 0x10 +#define CC_N 0x08 +#define CC_Z 0x04 +#define CC_V 0x02 +#define CC_C 0x01 +#define N_EOR_V ((reg_cc & CC_N)^((reg_cc & CC_V)<<2)) + +#define CLR_HNZVC do { reg_cc &= ~(CC_H|CC_N|CC_Z|CC_V|CC_C); } while (0) +#define CLR_NZV do { reg_cc &= ~(CC_N|CC_Z|CC_V); } while (0) +#define CLR_NZVC do { reg_cc &= ~(CC_N|CC_Z|CC_V|CC_C); } while (0) +#define CLR_Z do { reg_cc &= ~(CC_Z); } while (0) +#define CLR_NZC do { reg_cc &= ~(CC_N|CC_Z|CC_C); } while (0) +#define CLR_NVC do { reg_cc &= ~(CC_N|CC_V|CC_C); } while (0) +#define CLR_ZC do { reg_cc &= ~(CC_Z|CC_C); } while (0) + +#define SET_Z(a) do { if (!(a)) reg_cc |= CC_Z; } while (0) +#define SET_N8(a) do { reg_cc |= (a&0x80)>>4; } while (0) +#define SET_N16(a) do { reg_cc |= (a&0x8000)>>12; } while (0) +#define SET_H(a,b,r) do { reg_cc |= ((a^b^r)&0x10)<<1; } while (0) +#define SET_C8(a) do { reg_cc |= (a&0x100)>>8; } while (0) +#define SET_C16(a) do { reg_cc |= (a&0x10000)>>16; } while (0) +#define SET_V8(a,b,r) do { reg_cc |= ((a^b^r^(r>>1))&0x80)>>6; } while (0) +#define SET_V16(a,b,r) do { reg_cc |= ((a^b^r^(r>>1))&0x8000)>>14; } while (0) +#define SET_NZ8(a) do { SET_N8(a); SET_Z(a&0xff); } while (0) +#define SET_NZ16(a) do { SET_N16(a);SET_Z(a&0xffff); } while (0) +#define SET_NZC8(a) do { SET_N8(a); SET_Z(a&0xff);SET_C8(a); } while (0) +#define SET_NZVC8(a,b,r) do { SET_N8(r); SET_Z(r&0xff);SET_V8(a,b,r);SET_C8(r); } while (0) +#define SET_NZVC16(a,b,r) do { SET_N16(r);SET_Z(r&0xffff);SET_V16(a,b,r);SET_C16(r); } while (0) + +/* CPU fetch/store goes via SAM */ +#define fetch_byte(a) (sam_read_byte(a)) +#define fetch_word(a) (fetch_byte(a) << 8 | fetch_byte((a)+1)) +#define store_byte(a,v) do { sam_store_byte((a),(v)); } while (0) +/* This one only used to try and get correct timing: */ +#define peek_byte(a) sam_peek_byte(a) + +#define EA_DIRECT(a) do { a = reg_dp << 8 | fetch_byte(reg_pc); IF_TRACE(m6809_dasm_byte(a & 0xff, reg_pc)) reg_pc += 1; TAKEN_CYCLES(1); } while (0) +#define EA_EXTENDED(a) do { a = fetch_byte(reg_pc) << 8 | fetch_byte(reg_pc+1); IF_TRACE(m6809_dasm_byte(a >> 8, reg_pc); m6809_dasm_byte(a & 0xff, reg_pc + 1)) reg_pc += 2; TAKEN_CYCLES(1); } while (0) + +/* These macros are designed to be "passed as an argument" to the op-code + * macros. */ +#define BYTE_IMMEDIATE(a,v) { v = fetch_byte(reg_pc); IF_TRACE(m6809_dasm_byte(v, reg_pc)) reg_pc++; } +#define BYTE_DIRECT(a,v) { EA_DIRECT(a); v = fetch_byte(a); } +#define BYTE_INDEXED(a,v) { EA_INDEXED(a); v = fetch_byte(a); } +#define BYTE_EXTENDED(a,v) { EA_EXTENDED(a); v = fetch_byte(a); } +#define WORD_IMMEDIATE(a,v) { v = fetch_byte(reg_pc) << 8 | fetch_byte(reg_pc+1); IF_TRACE(m6809_dasm_byte(v >> 8, reg_pc); m6809_dasm_byte(v & 0xff, reg_pc + 1)) reg_pc += 2; } +#define WORD_DIRECT(a,v) { EA_DIRECT(a); v = fetch_byte(a) << 8 | fetch_byte(a+1); } +#define WORD_INDEXED(a,v) { EA_INDEXED(a); v = fetch_byte(a) << 8 | fetch_byte(a+1); } +#define WORD_EXTENDED(a,v) { EA_EXTENDED(a); v = fetch_byte(a) << 8 | fetch_byte(a+1); } + +#define SHORT_RELATIVE(r) { BYTE_IMMEDIATE(0,r); r = sex(r); } +#define LONG_RELATIVE(r) WORD_IMMEDIATE(0,r) + +#define TAKEN_CYCLES(c) current_cycle += ((c) * sam_topaddr_cycles) + +#define PUSHWORD(s,r) { s -= 2; store_byte(s+1, r & 0xff); store_byte(s, r >> 8); } +#define PUSHBYTE(s,r) { s--; store_byte(s, r); } +#define PULLBYTE(s,r) { r = fetch_byte(s); s++; } +#define PULLWORD(s,r) { r = fetch_byte(s) << 8; r |= fetch_byte(s+1); s += 2; } + +#define PUSHR(s,a) { \ + unsigned int postbyte; \ + BYTE_IMMEDIATE(0,postbyte); \ + TAKEN_CYCLES(2); \ + peek_byte(s); \ + if (postbyte & 0x80) { PUSHWORD(s, reg_pc); } \ + if (postbyte & 0x40) { PUSHWORD(s, a); } \ + if (postbyte & 0x20) { PUSHWORD(s, reg_y); } \ + if (postbyte & 0x10) { PUSHWORD(s, reg_x); } \ + if (postbyte & 0x08) { PUSHBYTE(s, reg_dp); } \ + if (postbyte & 0x04) { PUSHBYTE(s, reg_b); } \ + if (postbyte & 0x02) { PUSHBYTE(s, reg_a); } \ + if (postbyte & 0x01) { PUSHBYTE(s, reg_cc); } \ + } + +#define PULLR(s,a) { \ + unsigned int postbyte; \ + BYTE_IMMEDIATE(0,postbyte); \ + TAKEN_CYCLES(2); \ + if (postbyte & 0x01) { PULLBYTE(s, reg_cc); } \ + if (postbyte & 0x02) { PULLBYTE(s, reg_a); } \ + if (postbyte & 0x04) { PULLBYTE(s, reg_b); } \ + if (postbyte & 0x08) { PULLBYTE(s, reg_dp); } \ + if (postbyte & 0x10) { PULLWORD(s, reg_x); } \ + if (postbyte & 0x20) { PULLWORD(s, reg_y); } \ + if (postbyte & 0x40) { PULLWORD(s, a); } \ + if (postbyte & 0x80) { PULLWORD(s, reg_pc); } \ + peek_byte(s); \ + } + +#define INTERRUPT_REGISTER_PUSH(e) \ + { \ + PUSHWORD(reg_s, reg_pc); \ + if (e) { \ + reg_cc |= CC_E; \ + PUSHWORD(reg_s, reg_u); \ + PUSHWORD(reg_s, reg_y); \ + PUSHWORD(reg_s, reg_x); \ + PUSHBYTE(reg_s, reg_dp); \ + PUSHBYTE(reg_s, reg_b); \ + PUSHBYTE(reg_s, reg_a); \ + } else { reg_cc &= ~CC_E; } \ + PUSHBYTE(reg_s, reg_cc); \ + } + +#define TEST_INTERRUPT(i,m,v,e,cm,cyc,nom) do { \ + if (!skip_register_push) /* SYNC */ \ + wait_for_interrupt = 0; \ + if (!(reg_cc & m)) { \ + IF_TRACE(LOG_DEBUG(0, "Interrupt " #i " taken\n")) \ + wait_for_interrupt = 0; \ + INTERRUPT_REGISTER_PUSH(e); \ + skip_register_push = 0; \ + reg_cc |= (cm); reg_pc = fetch_word(v); \ + TAKEN_CYCLES(cyc); \ + } \ + } while (0) + +#define ARM_NMI do { nmi_armed = 1; } while (0) +#define DISARM_NMI do { nmi_armed = 0; } while (0) + +/* MPU registers */ +static unsigned int register_cc; +static uint8_t register_a, register_b; +static unsigned int register_dp; +static uint16_t register_x, register_y, register_u, register_s; +static uint16_t register_pc; + +#define reg_d ((reg_a&0xff) << 8 | (reg_b&0xff)) +#define set_reg_d(v) do { reg_a = ((v)>>8)&0xff; reg_b = (v)&0xff; } while (0) + +/* MPU interrupt state variables */ +int halt, nmi, firq, irq; +static int wait_for_interrupt; +static int skip_register_push; +static int nmi_armed; + +#define sex5(v) ((int)(((v) & 0x0f) - ((v) & 0x10))) +#define sex(v) ((int)(((v) & 0x7f) - ((v) & 0x80))) + +/* ------------------------------------------------------------------------- */ + +#define EA_ROFF0(b,r) case (b): ea = (r); peek_byte(reg_pc) +#define EA_ROFF5P(b,r,o) case ((b)+(o)): ea = (r) + (o); peek_byte(reg_pc); TAKEN_CYCLES(1) +#define EA_ROFF5N(b,r,o) case ((b)+32+(o)): ea = (r) + (o); peek_byte(reg_pc); TAKEN_CYCLES(1) +#define EA_ROFF8(b,r) case (b): BYTE_IMMEDIATE(0,ea); ea = sex(ea) + (r); TAKEN_CYCLES(1) +#define EA_ROFF16(b,r) case (b): WORD_IMMEDIATE(0,ea); ea += (r); peek_byte(reg_pc); TAKEN_CYCLES(2) +#define EA_ROFFA(b,r) case (b): ea = (r) + sex(reg_a); peek_byte(reg_pc); TAKEN_CYCLES(1) +#define EA_ROFFB(b,r) case (b): ea = (r) + sex(reg_b); peek_byte(reg_pc); TAKEN_CYCLES(1) +#define EA_ROFFD(b,r) case (b): ea = (r) + reg_d; peek_byte(reg_pc); peek_byte(reg_pc+1); peek_byte(reg_pc+2); TAKEN_CYCLES(2) +#define EA_RI1(b,r) case (b): ea = (r); r += 1; peek_byte(reg_pc); TAKEN_CYCLES(2) +#define EA_RI2(b,r) case (b): ea = (r); r += 2; peek_byte(reg_pc); TAKEN_CYCLES(3) +#define EA_RD1(b,r) case (b): r -= 1; ea = (r); peek_byte(reg_pc); TAKEN_CYCLES(2) +#define EA_RD2(b,r) case (b): r -= 2; ea = (r); peek_byte(reg_pc); TAKEN_CYCLES(3) +#define EA_PCOFF8(b,r) case (b): BYTE_IMMEDIATE(0,ea); ea = sex(ea) + reg_pc; TAKEN_CYCLES(1) +#define EA_PCOFF16(b,r) case (b): WORD_IMMEDIATE(0,ea); ea += reg_pc; peek_byte(reg_pc); TAKEN_CYCLES(3) +/* Illegal instruction, but seems to work on a real 6809: */ +#define EA_EXT case 0x8f: WORD_IMMEDIATE(0,ea); peek_byte(reg_pc); break +#define EA_EXTIND case 0x9f: WORD_IMMEDIATE(0,ea); peek_byte(reg_pc); ea = fetch_byte(ea) << 8 | fetch_byte(ea+1); TAKEN_CYCLES(1); break + +#define EA_ROFF5(b,r) \ + case (b)+0: case (b)+1: case (b)+2: case (b)+3: case (b)+4: \ + case (b)+5: case (b)+6: case (b)+7: case (b)+8: case (b)+9: \ + case (b)+10: case (b)+11: case (b)+12: case (b)+13: case (b)+14: \ + case (b)+15: case (b)+16: case (b)+17: case (b)+18: case (b)+19: \ + case (b)+20: case (b)+21: case (b)+22: case (b)+23: case (b)+24: \ + case (b)+25: case (b)+26: case (b)+27: case (b)+28: case (b)+29: \ + case (b)+30: case (b)+31: \ + peek_byte(reg_pc); \ + TAKEN_CYCLES(1); \ + ea = (r) + sex5(postbyte) + +#define EA_ALLR(m,b) m((b), reg_x); break; m((b)+0x20, reg_y); break; \ + m((b)+0x40, reg_u); break; m((b)+0x60, reg_s); break +#define EA_ALLRI(m,b) EA_IND(m,(b),reg_x); break; \ + EA_IND(m,(b)+0x20,reg_y); break; \ + EA_IND(m,(b)+0x40,reg_u); break; \ + EA_IND(m,(b)+0x60,reg_s); break +#define EA_IND(m,b,r) m(b,r); ea = fetch_byte(ea) << 8 | fetch_byte(ea+1); TAKEN_CYCLES(1) + +#define EA_INDEXED(a) do { a = ea_indexed(); } while (0) + +/* ------------------------------------------------------------------------- */ + +/* Operation templates */ + +#define OP_NEG(a) { uint_least16_t octet, result; a(addr,octet); result = ~(octet) + 1; CLR_NZVC; SET_NZVC8(0, octet, result); TAKEN_CYCLES(1); store_byte(addr,result); } +#define OP_COM(a) { uint_least16_t octet; a(addr,octet); octet = ~(octet); CLR_NZV; SET_NZ8(octet); reg_cc |= CC_C; TAKEN_CYCLES(1); store_byte(addr,octet); } +#define OP_LSR(a) { uint_least16_t octet; a(addr,octet); CLR_NZC; reg_cc |= (octet & CC_C); octet &= 0xff; octet >>= 1; SET_Z(octet); TAKEN_CYCLES(1); store_byte(addr,octet); } +#define OP_ROR(a) { uint_least16_t octet, result; a(addr,octet); result = (reg_cc & CC_C) << 7; CLR_NZC; reg_cc |= octet & CC_C; result |= (octet & 0xff) >> 1; SET_NZ8(result); TAKEN_CYCLES(1); store_byte(addr,result); } +#define OP_ASR(a) { uint_least16_t octet; CLR_NZC; a(addr,octet); reg_cc |= (octet & CC_C); octet = (octet & 0x80) | ((octet & 0xff) >> 1); SET_NZ8(octet); TAKEN_CYCLES(1); store_byte(addr,octet); } +#define OP_ASL(a) { uint_least16_t octet,result; a(addr,octet); result = octet << 1; CLR_NZVC; SET_NZVC8(octet, octet, result); TAKEN_CYCLES(1); store_byte(addr,result); } +#define OP_ROL(a) { uint_least16_t octet,result; a(addr,octet); result = (octet<<1)|(reg_cc & 1); CLR_NZVC; SET_NZVC8(octet, octet, result); TAKEN_CYCLES(1); store_byte(addr,result); } +#define OP_DEC(a) { uint_least16_t octet; a(addr,octet); octet--; octet &= 0xff; CLR_NZV; SET_NZ8(octet); if (octet == 0x7f) reg_cc |= CC_V; TAKEN_CYCLES(1); store_byte(addr,octet); } +#define OP_INC(a) { uint_least16_t octet; a(addr,octet); octet++; octet &= 0xff; CLR_NZV; SET_NZ8(octet); if (octet == 0x80) reg_cc |= CC_V; TAKEN_CYCLES(1); store_byte(addr,octet); } +#define OP_TST(a) { uint_least16_t octet; a(addr,octet); CLR_NZV; SET_NZ8(octet); TAKEN_CYCLES(2); } +#define OP_JMP(a) { a(addr); reg_pc = addr; } +#define OP_CLR(a) { uint_least16_t octet; a(addr,octet); TAKEN_CYCLES(1); store_byte(addr,0); CLR_NVC; reg_cc |= CC_Z; } + +#define OP_NEGR(r) { uint_least16_t result = ~(r) + 1; CLR_NZVC; SET_NZVC8(0, r, result); r = result; peek_byte(reg_pc); } +#define OP_COMR(r) { r = ~(r); CLR_NZV; SET_NZ8(r); reg_cc |= CC_C; peek_byte(reg_pc); } +#define OP_LSRR(r) { CLR_NZC; reg_cc |= (r & CC_C); r >>= 1; SET_Z(r); peek_byte(reg_pc); } +#define OP_RORR(r) { uint_least16_t result = (reg_cc & CC_C) << 7; CLR_NZC; reg_cc |= r & CC_C; result |= (r & 0xff) >> 1; SET_NZ8(result); r = result; peek_byte(reg_pc); } +#define OP_ASRR(r) { CLR_NZC; reg_cc |= (r & CC_C); r = (r & 0x80) | ((r & 0xff) >> 1); SET_NZ8(r); peek_byte(reg_pc); } +#define OP_ASLR(r) { uint_least16_t result = r << 1; CLR_NZVC; SET_NZVC8(r, r, result); r = result; peek_byte(reg_pc); } +#define OP_ROLR(r) { uint_least16_t result = (reg_cc & CC_C) | (r << 1); CLR_NZVC; SET_NZVC8(r, r, result); r = result; peek_byte(reg_pc); } +/* Note: this used to be "r--; r &= 0xff;", but gcc optimises too much away */ +#define OP_DECR(r) { r = (r - 1) & 0xff; CLR_NZV; SET_NZ8(r); if (r == 0x7f) reg_cc |= CC_V; peek_byte(reg_pc); } +#define OP_INCR(r) { r = (r + 1) & 0xff; CLR_NZV; SET_NZ8(r); if (r == 0x80) reg_cc |= CC_V; peek_byte(reg_pc); } +#define OP_TSTR(r) { CLR_NZV; SET_NZ8(r); peek_byte(reg_pc); } +#define OP_CLRR(r) { r = 0; CLR_NVC; reg_cc |= CC_Z; peek_byte(reg_pc); } + +#define OP_SUBD(a) { uint_least32_t tmp = reg_d, octet, result; a(addr,octet); result = tmp - octet; CLR_NZVC; SET_NZVC16(tmp, octet, result); set_reg_d(result); TAKEN_CYCLES(1); } +#define OP_ADDD(a) { uint_least32_t tmp = reg_d, octet, result; a(addr,octet); result = tmp + octet; CLR_NZVC; SET_NZVC16(tmp, octet, result); set_reg_d(result); TAKEN_CYCLES(1); } +#define OP_CMPD(a) { uint_least32_t tmp = reg_d, octet, result; a(addr,octet); result = tmp - octet; CLR_NZVC; SET_NZVC16(tmp, octet, result); TAKEN_CYCLES(1); } +#define OP_LDD(a) { uint_least16_t tmp; a(addr,tmp); CLR_NZV; SET_NZ16(tmp); set_reg_d(tmp); } +#define OP_STD(a) { uint_least16_t tmp = reg_d; a(addr); store_byte(addr, reg_a); store_byte(addr+1, reg_b); CLR_NZV; SET_NZ16(tmp); } + +#define OP_SUB16(r,a) { uint_least32_t octet, result; a(addr,octet); result = r - octet; CLR_NZVC; SET_NZVC16(r, octet, result); r = result; TAKEN_CYCLES(1); } +#define OP_ADD16(r,a) { uint_least32_t octet, result; a(addr,octet); result = r + octet; CLR_NZVC; SET_NZVC16(r, octet, result); r = result; TAKEN_CYCLES(1); } +#define OP_CMP16(r,a) { uint_least32_t octet, result; a(addr,octet); result = r - octet; CLR_NZVC; SET_NZVC16(r, octet, result); TAKEN_CYCLES(1); } +#define OP_LD16(r,a) { a(addr,r); CLR_NZV; SET_NZ16(r); } +#define OP_ST16(r,a) { a(addr); store_byte(addr, r >> 8); store_byte(addr+1, r & 0xff); CLR_NZV; SET_NZ16(r); } +#define OP_JSR(a) { a(addr); peek_byte(addr); PUSHWORD(reg_s, reg_pc); reg_pc = addr; } + +#define OP_SUB(r,a) { uint_least16_t octet, result; a(addr,octet); result = r - octet; CLR_NZVC; SET_NZVC8(r, octet, result); r = result; } +#define OP_CMP(r,a) { uint_least16_t octet, result; a(addr,octet); result = r - octet; CLR_NZVC; SET_NZVC8(r, octet, result); } +#define OP_SBC(r,a) { uint_least16_t octet, result; a(addr,octet); result = r - octet - (reg_cc & CC_C); CLR_NZVC; SET_NZVC8(r, octet, result); r = result; } +#define OP_AND(r,a) { uint_least16_t octet; a(addr,octet); r &= octet; CLR_NZV; SET_NZ8(r); } +#define OP_BIT(r,a) { uint_least16_t octet, result; a(addr,octet); result = r & octet; CLR_NZV; SET_NZ8(result); } +#define OP_LD(r,a) { a(addr,r); CLR_NZV; SET_NZ8(r); } +#define OP_ST(r,a) { a(addr); store_byte(addr, r); CLR_NZV; SET_NZ8(r); } +#define OP_EOR(r,a) { uint_least16_t octet; a(addr,octet); r ^= octet; CLR_NZV; SET_NZ8(r); } +#define OP_ADC(r,a) { uint_least16_t octet, result; a(addr,octet); result = r + octet + (reg_cc & CC_C); CLR_HNZVC; SET_NZVC8(r, octet, result); SET_H(r, octet, result); r = result; } +#define OP_OR(r,a) { uint_least16_t octet; a(addr,octet); r |= octet; CLR_NZV; SET_NZ8(r); } +#define OP_ADD(r,a) { uint_least16_t octet, result; a(addr,octet); result = r + octet; CLR_HNZVC; SET_NZVC8(r, octet, result); SET_H(r, octet, result); r = result; } + +#define BRANCHS(cond) { uint_least16_t RA; SHORT_RELATIVE(RA); \ + TAKEN_CYCLES(1); if (cond) { reg_pc += RA; } \ + } +#define BRANCHL(cond) { uint_least16_t RA; LONG_RELATIVE(RA); \ + if (cond) { reg_pc += RA; TAKEN_CYCLES(2); } \ + else { TAKEN_CYCLES(1); } \ + } + +/* ------------------------------------------------------------------------- */ + +void m6809_init(void) { +} + +void m6809_reset(void) { + halt = nmi = firq = irq = 0; + DISARM_NMI; + wait_for_interrupt = 0; + skip_register_push = 0; + register_cc = 0xff; + register_a = register_b = 0; + register_dp = 0; + register_x = register_y = register_u = register_s = 0; + register_pc = fetch_word(0xfffe); + m6809_dasm_reset(); +} + +void m6809_cycle(Cycle until) { + __label__ restore_regs, switched_block_page2, switched_block_page3; + uint_least16_t addr; + unsigned int reg_cc = register_cc; + uint8_t reg_a = register_a; + uint8_t reg_b = register_b; + unsigned int reg_dp = register_dp; + uint16_t reg_x = register_x; + uint16_t reg_y = register_y; + uint16_t reg_u = register_u; + uint16_t reg_s = register_s; + uint16_t reg_pc = register_pc; + + /* Bit of a GCC-ism here: */ + auto uint16_t ea_indexed(void); + uint16_t ea_indexed(void) { + unsigned int ea; + uint8_t postbyte; + BYTE_IMMEDIATE(0,postbyte); + switch (postbyte) { + EA_ALLR(EA_ROFF5, 0x00); + EA_ALLR(EA_RI1, 0x80); + EA_ALLR(EA_RI2, 0x81); EA_ALLRI(EA_RI2, 0x91); + EA_ALLR(EA_RD1, 0x82); + EA_ALLR(EA_RD2, 0x83); EA_ALLRI(EA_RD2, 0x93); + EA_ALLR(EA_ROFF0, 0x84); EA_ALLRI(EA_ROFF0, 0x94); + EA_ALLR(EA_ROFFB, 0x85); EA_ALLRI(EA_ROFFB, 0x95); + EA_ALLR(EA_ROFFA, 0x86); EA_ALLRI(EA_ROFFA, 0x96); + EA_ALLR(EA_ROFF8, 0x88); EA_ALLRI(EA_ROFF8, 0x98); + EA_ALLR(EA_ROFF16, 0x89); EA_ALLRI(EA_ROFF16, 0x99); + EA_ALLR(EA_ROFFD, 0x8b); EA_ALLRI(EA_ROFFD, 0x9b); + EA_ALLR(EA_PCOFF8, 0x8c); EA_ALLRI(EA_PCOFF8, 0x9c); + EA_ALLR(EA_PCOFF16, 0x8d); EA_ALLRI(EA_PCOFF16, 0x9d); + EA_EXT; EA_EXTIND; + default: ea = 0; break; + } + return ea; + } + + if (halt && !wait_for_interrupt) { + while ((int)(current_cycle - until) < 0) + TAKEN_CYCLES(1); + goto restore_regs; + } + if (nmi_armed && nmi) { + DISARM_NMI; + TEST_INTERRUPT(nmi, 0, 0xfffc, CC_E, CC_F|CC_I, 7, "NMI"); + /* NMI is latched. Assume that means latched only on + * high-to-low transition... */ + nmi = 0; + } + if (firq) { + TEST_INTERRUPT(firq, CC_F, 0xfff6, 0, CC_F|CC_I, 7, "FIRQ"); + /* FIRQ and IRQ aren't latched: can remain set */ + } + if (irq) { + TEST_INTERRUPT(irq, CC_I, 0xfff8, CC_E, CC_I, 7, "IRQ"); + /* FIRQ and IRQ aren't latched: can remain set */ + } + if (halt || wait_for_interrupt) { + while ((int)(current_cycle - until) < 0) + TAKEN_CYCLES(1); + goto restore_regs; + } + + while ((int)(current_cycle - until) < 0 && !wait_for_interrupt && !halt) { + IF_TRACE(LOG_DEBUG(0, "%04x| ", reg_pc)); + /* Fetch op-code and process */ + { + unsigned int op; + BYTE_IMMEDIATE(0,op); + switch(op) { + /* 0x00 NEG direct */ + case 0x00: + case 0x01: OP_NEG(BYTE_DIRECT); break; + /* 0x01 NEGCOM direct (illegal) */ + case 0x02: + if (reg_cc & CC_C) { + OP_COM(BYTE_DIRECT); + } else { + OP_NEG(BYTE_DIRECT); + } + break; + /* 0x03 COM direct */ + case 0x03: OP_COM(BYTE_DIRECT); break; + /* 0x04 LSR direct */ + case 0x04: + case 0x05: OP_LSR(BYTE_DIRECT); break; + /* 0x06 ROR direct */ + case 0x06: OP_ROR(BYTE_DIRECT); break; + /* 0x07 ASR direct */ + case 0x07: OP_ASR(BYTE_DIRECT); break; + /* 0x08 ASL,LSL direct */ + case 0x08: OP_ASL(BYTE_DIRECT); break; + /* 0x09 ROL direct */ + case 0x09: OP_ROL(BYTE_DIRECT); break; + /* 0x0A DEC direct */ + case 0x0A: + case 0x0B: OP_DEC(BYTE_DIRECT); break; + /* 0x0C INC direct */ + case 0x0C: OP_INC(BYTE_DIRECT); break; + /* 0x0D TST direct */ + case 0x0D: OP_TST(BYTE_DIRECT); break; + /* 0x0E JMP direct */ + case 0x0E: OP_JMP(EA_DIRECT); break; + /* 0x0F CLR direct */ + case 0x0F: OP_CLR(BYTE_DIRECT); break; + /* 0x10 Page 2 */ + case 0x10: goto switched_block_page2; break; + /* 0x11 Page 3 */ + case 0x11: goto switched_block_page3; break; + /* 0x12 NOP inherent */ + case 0x12: peek_byte(reg_pc); break; + /* 0x13 SYNC inherent */ + case 0x13: + wait_for_interrupt = 1; + peek_byte(reg_pc); + TAKEN_CYCLES(3); + break; + /* 0x16 LBRA relative */ + case 0x16: BRANCHL(1); break; + /* 0x17 LBSR relative */ + case 0x17: { + uint_least16_t RA; + uint16_t dest; + LONG_RELATIVE(RA); + dest = reg_pc + RA; + TAKEN_CYCLES(1); + peek_byte(dest); + TAKEN_CYCLES(1); + PUSHWORD(reg_s, reg_pc); + reg_pc = dest; + } break; + /* 0x19 DAA inherent */ + case 0x19: { + uint_least16_t result = 0; + if ((reg_a&0x0f) >= 0x0a || reg_cc & CC_H) result |= 0x06; + if (reg_a >= 0x90 && (reg_a&0x0f) >= 0x0a) result |= 0x60; + if (reg_a >= 0xa0 || reg_cc & CC_C) result |= 0x60; + result += reg_a; + reg_a = result & 0xff; + CLR_NZVC; /* was CLR_NZV */ + SET_NZC8(result); + peek_byte(reg_pc); + } break; + /* 0x1A ORCC immediate */ + case 0x1a: { + unsigned int v; + BYTE_IMMEDIATE(0,v); + reg_cc |= v; + peek_byte(reg_pc); + } break; + /* 0x1C ANDCC immediate */ + case 0x1c: { + unsigned int v; + BYTE_IMMEDIATE(0,v); + reg_cc &= v; + peek_byte(reg_pc); + } break; + /* 0x1D SEX inherent */ + case 0x1d: { + uint_least16_t tmp; + reg_a = (reg_b&0x80)?0xff:0; + tmp = reg_d; + CLR_NZV; + SET_NZ16(tmp); + peek_byte(reg_pc); + } + break; + /* 0x1E EXG immediate */ + case 0x1e: { + uint8_t postbyte; + unsigned int tmp; + BYTE_IMMEDIATE(0,postbyte); + switch (postbyte) { + case 0x01: case 0x10: tmp = reg_x; reg_x = reg_d; set_reg_d(tmp); break; + case 0x02: case 0x20: tmp = reg_y; reg_y = reg_d; set_reg_d(tmp); break; + case 0x03: case 0x30: tmp = reg_u; reg_u = reg_d; set_reg_d(tmp); break; + case 0x04: case 0x40: tmp = reg_s; reg_s = reg_d; set_reg_d(tmp); break; + case 0x05: case 0x50: tmp = reg_pc; reg_pc = reg_d; set_reg_d(tmp); break; + case 0x06: case 0x60: reg_a = reg_b = 0xff; break; + case 0x07: case 0x70: reg_a = reg_b = 0xff; break; + case 0x08: case 0x80: reg_b = reg_a; reg_a = 0xff; break; + case 0x09: case 0x90: reg_a = 0xff; break; + case 0x0a: case 0xa0: tmp = reg_cc; reg_cc = reg_b; reg_a = 0xff; reg_b = tmp; break; + case 0x0b: case 0xb0: tmp = reg_dp; reg_dp = reg_b; reg_a = 0xff; reg_b = tmp; break; + case 0x0c: case 0xc0: reg_a = reg_b = 0xff; break; + case 0x0d: case 0xd0: reg_a = reg_b = 0xff; break; + case 0x0e: case 0xe0: reg_a = reg_b = 0xff; break; + case 0x0f: case 0xf0: reg_a = reg_b = 0xff; break; + case 0x12: case 0x21: tmp = reg_y; reg_y = reg_x; reg_x = tmp; break; + case 0x13: case 0x31: tmp = reg_u; reg_u = reg_x; reg_x = tmp; break; + case 0x14: case 0x41: tmp = reg_s; reg_s = reg_x; reg_x = tmp; break; + case 0x15: case 0x51: tmp = reg_pc; reg_pc = reg_x; reg_x = tmp; break; + case 0x16: case 0x61: reg_x = 0xffff; break; + case 0x17: case 0x71: reg_x = 0xffff; break; + case 0x18: case 0x81: tmp = reg_a; reg_a = reg_x & 0xff; reg_x = 0xff00 | tmp; break; + case 0x19: case 0x91: tmp = reg_b; reg_b = reg_x & 0xff; reg_x = 0xff00 | tmp; break; + case 0x1a: case 0xa1: tmp = reg_cc; reg_cc = reg_x & 0xff; reg_x = 0xff00 | tmp; break; + case 0x1b: case 0xb1: tmp = reg_dp; reg_dp = reg_x & 0xff; reg_x = 0xff00 | tmp; break; + case 0x1c: case 0xc1: reg_x = 0xffff; break; + case 0x1d: case 0xd1: reg_x = 0xffff; break; + case 0x1e: case 0xe1: reg_x = 0xffff; break; + case 0x1f: case 0xf1: reg_x = 0xffff; break; + case 0x23: case 0x32: tmp = reg_u; reg_u = reg_y; reg_y = tmp; break; + case 0x24: case 0x42: tmp = reg_s; reg_s = reg_y; reg_y = tmp; break; + case 0x25: case 0x52: tmp = reg_pc; reg_pc = reg_y; reg_y = tmp; break; + case 0x26: case 0x62: reg_y = 0xffff; break; + case 0x27: case 0x72: reg_y = 0xffff; break; + case 0x28: case 0x82: tmp = reg_a; reg_a = reg_y & 0xff; reg_y = 0xff00 | tmp; break; + case 0x29: case 0x92: tmp = reg_b; reg_b = reg_y & 0xff; reg_y = 0xff00 | tmp; break; + case 0x2a: case 0xa2: tmp = reg_cc; reg_cc = reg_y & 0xff; reg_y = 0xff00 | tmp; break; + case 0x2b: case 0xb2: tmp = reg_dp; reg_dp = reg_y & 0xff; reg_y = 0xff00 | tmp; break; + case 0x2c: case 0xc2: reg_y = 0xffff; break; + case 0x2d: case 0xd2: reg_y = 0xffff; break; + case 0x2e: case 0xe2: reg_y = 0xffff; break; + case 0x2f: case 0xf2: reg_y = 0xffff; break; + case 0x34: case 0x43: tmp = reg_s; reg_s = reg_u; reg_u = tmp; break; + case 0x35: case 0x53: tmp = reg_pc; reg_pc = reg_u; reg_u = tmp; break; + case 0x36: case 0x63: reg_u = 0xffff; break; + case 0x37: case 0x73: reg_u = 0xffff; break; + case 0x38: case 0x83: tmp = reg_a; reg_a = reg_u & 0xff; reg_u = 0xff00 | tmp; break; + case 0x39: case 0x93: tmp = reg_b; reg_b = reg_u & 0xff; reg_u = 0xff00 | tmp; break; + case 0x3a: case 0xa3: tmp = reg_cc; reg_cc = reg_u & 0xff; reg_u = 0xff00 | tmp; break; + case 0x3b: case 0xb3: tmp = reg_dp; reg_dp = reg_u & 0xff; reg_u = 0xff00 | tmp; break; + case 0x3c: case 0xc3: reg_u = 0xffff; break; + case 0x3d: case 0xd3: reg_u = 0xffff; break; + case 0x3e: case 0xe3: reg_u = 0xffff; break; + case 0x3f: case 0xf3: reg_u = 0xffff; break; + case 0x45: case 0x54: tmp = reg_pc; reg_pc = reg_s; reg_s = tmp; break; + case 0x46: case 0x64: reg_s = 0xffff; break; + case 0x47: case 0x74: reg_s = 0xffff; break; + case 0x48: case 0x84: tmp = reg_a; reg_a = reg_s & 0xff; reg_s = 0xff00 | tmp; break; + case 0x49: case 0x94: tmp = reg_b; reg_b = reg_s & 0xff; reg_s = 0xff00 | tmp; break; + case 0x4a: case 0xa4: tmp = reg_cc; reg_cc = reg_s & 0xff; reg_s = 0xff00 | tmp; break; + case 0x4b: case 0xb4: tmp = reg_dp; reg_dp = reg_s & 0xff; reg_s = 0xff00 | tmp; break; + case 0x4c: case 0xc4: reg_s = 0xffff; break; + case 0x4d: case 0xd4: reg_s = 0xffff; break; + case 0x4e: case 0xe4: reg_s = 0xffff; break; + case 0x4f: case 0xf4: reg_s = 0xffff; break; + case 0x56: case 0x65: reg_pc = 0xffff; break; + case 0x57: case 0x75: reg_pc = 0xffff; break; + case 0x58: case 0x85: tmp = reg_a; reg_a = reg_pc & 0xff; reg_pc = 0xff00 | tmp; break; + case 0x59: case 0x95: tmp = reg_b; reg_b = reg_pc & 0xff; reg_pc = 0xff00 | tmp; break; + case 0x5a: case 0xa5: tmp = reg_cc; reg_cc = reg_pc & 0xff; reg_pc = 0xff00 | tmp; break; + case 0x5b: case 0xb5: tmp = reg_dp; reg_dp = reg_pc & 0xff; reg_pc = 0xff00 | tmp; break; + case 0x5c: case 0xc5: reg_pc = 0xffff; break; + case 0x5d: case 0xd5: reg_pc = 0xffff; break; + case 0x5e: case 0xe5: reg_pc = 0xffff; break; + case 0x5f: case 0xf5: reg_pc = 0xffff; break; + case 0x68: case 0x86: reg_a = 0xff; break; + case 0x69: case 0x96: reg_b = 0xff; break; + case 0x6a: case 0xa6: reg_cc = 0xff; break; + case 0x6b: case 0xb6: reg_dp = 0xff; break; + case 0x78: case 0x87: reg_a = 0xff; break; + case 0x79: case 0x97: reg_b = 0xff; break; + case 0x7a: case 0xa7: reg_cc = 0xff; break; + case 0x7b: case 0xb7: reg_dp = 0xff; break; + case 0x89: case 0x98: tmp = reg_b; reg_b = reg_a; reg_a = tmp; break; + case 0x8a: case 0xa8: tmp = reg_cc; reg_cc = reg_a; reg_a = tmp; break; + case 0x8b: case 0xb8: tmp = reg_dp; reg_dp = reg_a; reg_a = tmp; break; + case 0x8c: case 0xc8: reg_a = 0xff; break; + case 0x8d: case 0xd8: reg_a = 0xff; break; + case 0x8e: case 0xe8: reg_a = 0xff; break; + case 0x8f: case 0xf8: reg_a = 0xff; break; + case 0x9a: case 0xa9: tmp = reg_cc; reg_cc = reg_b; reg_b = tmp; break; + case 0x9b: case 0xb9: tmp = reg_dp; reg_dp = reg_b; reg_b = tmp; break; + case 0x9c: case 0xc9: reg_b = 0xff; break; + case 0x9d: case 0xd9: reg_b = 0xff; break; + case 0x9e: case 0xe9: reg_b = 0xff; break; + case 0x9f: case 0xf9: reg_b = 0xff; break; + case 0xab: case 0xba: tmp = reg_dp; reg_dp = reg_cc; reg_cc = tmp; break; + case 0xac: case 0xca: reg_cc = 0xff; break; + case 0xad: case 0xda: reg_cc = 0xff; break; + case 0xae: case 0xea: reg_cc = 0xff; break; + case 0xaf: case 0xfa: reg_cc = 0xff; break; + case 0xbc: case 0xcb: reg_dp = 0xff; break; + case 0xbd: case 0xdb: reg_dp = 0xff; break; + case 0xbe: case 0xeb: reg_dp = 0xff; break; + case 0xbf: case 0xfb: reg_dp = 0xff; break; + default: break; + } + TAKEN_CYCLES(6); + } break; + /* 0x1F TFR immediate */ + case 0x1f: { + uint8_t postbyte; + BYTE_IMMEDIATE(0,postbyte); + switch (postbyte) { + case 0x01: reg_x = reg_d; break; + case 0x02: reg_y = reg_d; break; + case 0x03: reg_u = reg_d; break; + case 0x04: reg_s = reg_d; break; + case 0x05: reg_pc = reg_d; break; + case 0x08: reg_a = reg_b; break; + case 0x09: break; + case 0x0a: reg_cc = reg_b; break; + case 0x0b: reg_dp = reg_b; break; + case 0x10: set_reg_d(reg_x); break; + case 0x12: reg_y = reg_x; break; + case 0x13: reg_u = reg_x; break; + case 0x14: reg_s = reg_x; break; + case 0x15: reg_pc = reg_x; break; + case 0x18: reg_a = reg_x & 0xff; break; + case 0x19: reg_b = reg_x & 0xff; break; + case 0x1a: reg_cc = reg_x & 0xff; break; + case 0x1b: reg_dp = reg_x & 0xff; break; + case 0x20: set_reg_d(reg_y); break; + case 0x21: reg_x = reg_y; break; + case 0x23: reg_u = reg_y; break; + case 0x24: reg_s = reg_y; break; + case 0x25: reg_pc = reg_y; break; + case 0x28: reg_a = reg_y & 0xff; break; + case 0x29: reg_b = reg_y & 0xff; break; + case 0x2a: reg_cc = reg_y & 0xff; break; + case 0x2b: reg_dp = reg_y & 0xff; break; + case 0x30: set_reg_d(reg_u); break; + case 0x31: reg_x = reg_u; break; + case 0x32: reg_y = reg_u; break; + case 0x34: reg_s = reg_u; break; + case 0x35: reg_pc = reg_u; break; + case 0x38: reg_a = reg_u & 0xff; break; + case 0x39: reg_b = reg_u & 0xff; break; + case 0x3a: reg_cc = reg_u & 0xff; break; + case 0x3b: reg_dp = reg_u & 0xff; break; + case 0x40: set_reg_d(reg_s); break; + case 0x41: reg_x = reg_s; break; + case 0x42: reg_y = reg_s; break; + case 0x43: reg_u = reg_s; break; + case 0x45: reg_pc = reg_s; break; + case 0x48: reg_a = reg_s & 0xff; break; + case 0x49: reg_b = reg_s & 0xff; break; + case 0x4a: reg_cc = reg_s & 0xff; break; + case 0x4b: reg_dp = reg_s & 0xff; break; + case 0x50: set_reg_d(reg_pc); break; + case 0x51: reg_x = reg_pc; break; + case 0x52: reg_y = reg_pc; break; + case 0x53: reg_u = reg_pc; break; + case 0x54: reg_s = reg_pc; break; + case 0x58: reg_a = reg_pc & 0xff; break; + case 0x59: reg_b = reg_pc & 0xff; break; + case 0x5a: reg_cc = reg_pc & 0xff; break; + case 0x5b: reg_dp = reg_pc & 0xff; break; + case 0x60: reg_a = reg_b = 0xff; break; + case 0x61: reg_x = 0xffff; break; + case 0x62: reg_y = 0xffff; break; + case 0x63: reg_u = 0xffff; break; + case 0x64: reg_s = 0xffff; break; + case 0x65: reg_pc = 0xffff; break; + case 0x68: reg_a = 0xff; break; + case 0x69: reg_b = 0xff; break; + case 0x6a: reg_cc = 0xff; break; + case 0x6b: reg_dp = 0xff; break; + case 0x70: reg_a = reg_b = 0xff; break; + case 0x71: reg_x = 0xffff; break; + case 0x72: reg_y = 0xffff; break; + case 0x73: reg_u = 0xffff; break; + case 0x74: reg_s = 0xffff; break; + case 0x75: reg_pc = 0xffff; break; + case 0x78: reg_a = 0xff; break; + case 0x79: reg_b = 0xff; break; + case 0x7a: reg_cc = 0xff; break; + case 0x7b: reg_dp = 0xff; break; + case 0x80: reg_b = reg_a; reg_a = 0xff; break; + case 0x81: reg_x = 0xff00 | reg_a; break; + case 0x82: reg_y = 0xff00 | reg_a; break; + case 0x83: reg_u = 0xff00 | reg_a; break; + case 0x84: reg_s = 0xff00 | reg_a; break; + case 0x85: reg_pc = 0xff00 | reg_a; break; + case 0x89: reg_b = reg_a; break; + case 0x8a: reg_cc = reg_a; break; + case 0x8b: reg_dp = reg_a; break; + case 0x90: reg_a = 0xff; break; + case 0x91: reg_x = 0xff00 | reg_b; break; + case 0x92: reg_y = 0xff00 | reg_b; break; + case 0x93: reg_u = 0xff00 | reg_b; break; + case 0x94: reg_s = 0xff00 | reg_b; break; + case 0x95: reg_pc = 0xff00 | reg_b; break; + case 0x98: reg_a = reg_b; break; + case 0x9a: reg_cc = reg_b; break; + case 0x9b: reg_dp = reg_b; break; + case 0xa0: reg_a = 0xff; reg_b = reg_cc; break; + case 0xa1: reg_x = 0xff00 | reg_cc; break; + case 0xa2: reg_y = 0xff00 | reg_cc; break; + case 0xa3: reg_u = 0xff00 | reg_cc; break; + case 0xa4: reg_s = 0xff00 | reg_cc; break; + case 0xa5: reg_pc = 0xff00 | reg_cc; break; + case 0xa8: reg_a = reg_cc; break; + case 0xa9: reg_b = reg_cc; break; + case 0xab: reg_dp = reg_cc; break; + case 0xb0: reg_a = 0xff; reg_b = reg_dp; break; + case 0xb1: reg_x = 0xff00 | reg_dp; break; + case 0xb2: reg_y = 0xff00 | reg_dp; break; + case 0xb3: reg_u = 0xff00 | reg_dp; break; + case 0xb4: reg_s = 0xff00 | reg_dp; break; + case 0xb5: reg_pc = 0xff00 | reg_dp; break; + case 0xb8: reg_a = reg_dp; break; + case 0xb9: reg_b = reg_dp; break; + case 0xba: reg_cc = reg_dp; break; + case 0xc0: reg_a = reg_b = 0xff; break; + case 0xc1: reg_x = 0xffff; break; + case 0xc2: reg_y = 0xffff; break; + case 0xc3: reg_u = 0xffff; break; + case 0xc4: reg_s = 0xffff; break; + case 0xc5: reg_pc = 0xffff; break; + case 0xc8: reg_a = 0xff; break; + case 0xc9: reg_b = 0xff; break; + case 0xca: reg_cc = 0xff; break; + case 0xcb: reg_dp = 0xff; break; + case 0xd0: reg_a = reg_b = 0xff; break; + case 0xd1: reg_x = 0xffff; break; + case 0xd2: reg_y = 0xffff; break; + case 0xd3: reg_u = 0xffff; break; + case 0xd4: reg_s = 0xffff; break; + case 0xd5: reg_pc = 0xffff; break; + case 0xd8: reg_a = 0xff; break; + case 0xd9: reg_b = 0xff; break; + case 0xda: reg_cc = 0xff; break; + case 0xdb: reg_dp = 0xff; break; + case 0xe0: reg_a = reg_b = 0xff; break; + case 0xe1: reg_x = 0xffff; break; + case 0xe2: reg_y = 0xffff; break; + case 0xe3: reg_u = 0xffff; break; + case 0xe4: reg_s = 0xffff; break; + case 0xe5: reg_pc = 0xffff; break; + case 0xe8: reg_a = 0xff; break; + case 0xe9: reg_b = 0xff; break; + case 0xea: reg_cc = 0xff; break; + case 0xeb: reg_dp = 0xff; break; + case 0xf0: reg_a = reg_b = 0xff; break; + case 0xf1: reg_x = 0xffff; break; + case 0xf2: reg_y = 0xffff; break; + case 0xf3: reg_u = 0xffff; break; + case 0xf4: reg_s = 0xffff; break; + case 0xf5: reg_pc = 0xffff; break; + case 0xf8: reg_a = 0xff; break; + case 0xf9: reg_b = 0xff; break; + case 0xfa: reg_cc = 0xff; break; + case 0xfb: reg_dp = 0xff; break; + default: break; + } + TAKEN_CYCLES(4); + } break; + /* 0x20 BRA relative */ + case 0x20: BRANCHS(1); break; + /* 0x21 BRN relative */ + case 0x21: BRANCHS(0); break; + /* 0x22 BHI relative */ + case 0x22: BRANCHS(!(reg_cc & (CC_Z|CC_C))); break; + /* 0x23 BLS relative */ + case 0x23: BRANCHS(reg_cc & (CC_Z|CC_C)); break; + /* 0x24 BHS,BCC relative */ + case 0x24: BRANCHS(!(reg_cc & CC_C)); break; + /* 0x25 BLO,BCS relative */ + case 0x25: BRANCHS(reg_cc & CC_C); break; + /* 0x26 BNE relative */ + case 0x26: BRANCHS(!(reg_cc & CC_Z)); break; + /* 0x27 BEQ relative */ + case 0x27: BRANCHS(reg_cc & CC_Z); break; + /* 0x28 BVC relative */ + case 0x28: BRANCHS(!(reg_cc & CC_V)); break; + /* 0x29 BVS relative */ + case 0x29: BRANCHS(reg_cc & CC_V); break; + /* 0x2A BPL relative */ + case 0x2a: BRANCHS(!(reg_cc & CC_N)); break; + /* 0x2B BMI relative */ + case 0x2b: BRANCHS(reg_cc & CC_N); break; + /* 0x2C BGE relative */ + case 0x2c: BRANCHS(!N_EOR_V); break; + /* 0x2D BLT relative */ + case 0x2d: BRANCHS(N_EOR_V); break; + /* 0x2E BGT relative */ + case 0x2e: BRANCHS(!(N_EOR_V || reg_cc & CC_Z)); break; + /* 0x2F BLE relative */ + case 0x2f: BRANCHS(N_EOR_V || reg_cc & CC_Z); break; + /* 0x30 LEAX indexed */ + case 0x30: EA_INDEXED(reg_x); + CLR_Z; + SET_Z(reg_x); + TAKEN_CYCLES(1); + break; + /* 0x31 LEAY indexed */ + case 0x31: EA_INDEXED(reg_y); + CLR_Z; + SET_Z(reg_y); + TAKEN_CYCLES(1); + break; + /* 0x32 LEAS indexed */ + case 0x32: EA_INDEXED(reg_s); + TAKEN_CYCLES(1); + ARM_NMI; /* XXX: Really? */ + break; + /* 0x33 LEAU indexed */ + case 0x33: EA_INDEXED(reg_u); + TAKEN_CYCLES(1); + break; + /* 0x34 PSHS immediate */ + case 0x34: PUSHR(reg_s, reg_u); break; + /* 0x35 PULS immediate */ + case 0x35: PULLR(reg_s, reg_u); break; + /* 0x36 PSHU immediate */ + case 0x36: PUSHR(reg_u, reg_s); break; + /* 0x37 PULU immediate */ + case 0x37: PULLR(reg_u, reg_s); break; + /* 0x39 RTS inherent */ + case 0x39: + peek_byte(reg_pc); + PULLWORD(reg_s, reg_pc); + TAKEN_CYCLES(1); + break; + /* 0x3A ABX inherent */ + case 0x3a: + reg_x += reg_b; + peek_byte(reg_pc); + TAKEN_CYCLES(1); + break; + /* 0x3B RTI inherent */ + case 0x3b: + peek_byte(reg_pc); + PULLBYTE(reg_s, reg_cc); + if (reg_cc & CC_E) { + PULLBYTE(reg_s, reg_a); + PULLBYTE(reg_s, reg_b); + PULLBYTE(reg_s, reg_dp); + PULLWORD(reg_s, reg_x); + PULLWORD(reg_s, reg_y); + PULLWORD(reg_s, reg_u); + PULLWORD(reg_s, reg_pc); + } else { + PULLWORD(reg_s, reg_pc); + } + ARM_NMI; + peek_byte(reg_s); + break; + /* 0x3C CWAI immediate */ + case 0x3c: { + unsigned int v; + BYTE_IMMEDIATE(0,v); + reg_cc &= v; + peek_byte(reg_pc); + TAKEN_CYCLES(1); + //INTERRUPT_REGISTER_PUSH(1); + wait_for_interrupt = 1; + skip_register_push = 1; + TAKEN_CYCLES(1); + } break; + /* 0x3D MUL inherent */ + case 0x3d: { + uint16_t tmp = reg_a * reg_b; + set_reg_d(tmp); + CLR_ZC; + SET_Z(tmp); + if (tmp & 0x80) + reg_cc |= CC_C; + peek_byte(reg_pc); + TAKEN_CYCLES(9); + } + break; + /* 0x3E RESET (undocumented) */ + case 0x3e: + m6809_reset(); + TAKEN_CYCLES(1); + break; + /* 0x3F SWI inherent */ + case 0x3f: + reg_cc |= CC_E; + peek_byte(reg_pc); + TAKEN_CYCLES(1); + PUSHWORD(reg_s, reg_pc); + PUSHWORD(reg_s, reg_u); + PUSHWORD(reg_s, reg_y); + PUSHWORD(reg_s, reg_x); + PUSHBYTE(reg_s, reg_dp); + PUSHBYTE(reg_s, reg_b); + PUSHBYTE(reg_s, reg_a); + PUSHBYTE(reg_s, reg_cc); + reg_cc |= CC_F|CC_I; + TAKEN_CYCLES(1); + reg_pc = fetch_word(0xfffa); + TAKEN_CYCLES(1); + break; + /* 0x40 NEGA inherent */ + case 0x40: + case 0x41: OP_NEGR(reg_a); break; + /* 0x43 COMA inherent */ + case 0x42: + case 0x43: OP_COMR(reg_a); break; + /* 0x44 LSRA inherent */ + case 0x44: + case 0x45: OP_LSRR(reg_a); break; + /* 0x46 RORA inherent */ + case 0x46: OP_RORR(reg_a); break; + /* 0x47 ASRA inherent */ + case 0x47: OP_ASRR(reg_a); break; + /* 0x48 ASLA,LSLA inherent */ + case 0x48: OP_ASLR(reg_a); break; + /* 0x49 ROLA inherent */ + case 0x49: OP_ROLR(reg_a); break; + /* 0x4A DECA inherent */ + case 0x4a: + case 0x4b: OP_DECR(reg_a); break; + /* 0x4C INCA inherent */ + case 0x4c: OP_INCR(reg_a); break; + /* 0x4D TSTA inherent */ + case 0x4d: OP_TSTR(reg_a); break; + /* 0x4F CLRA inherent */ + case 0x4f: OP_CLRR(reg_a); break; + /* 0x50 NEGB inherent */ + case 0x50: + case 0x51: OP_NEGR(reg_b); break; + /* 0x53 COMB inherent */ + case 0x52: + case 0x53: OP_COMR(reg_b); break; + /* 0x54 LSRB inherent */ + case 0x54: + case 0x55: OP_LSRR(reg_b); break; + /* 0x56 RORB inherent */ + case 0x56: OP_RORR(reg_b); break; + /* 0x57 ASRB inherent */ + case 0x57: OP_ASRR(reg_b); break; + /* 0x58 ASLB,LSLB inherent */ + case 0x58: OP_ASLR(reg_b); break; + /* 0x59 ROLB inherent */ + case 0x59: OP_ROLR(reg_b); break; + /* 0x5A DECB inherent */ + case 0x5a: + case 0x5b: OP_DECR(reg_b); break; + /* 0x5C INCB inherent */ + case 0x5c: OP_INCR(reg_b); break; + /* 0x5D TSTB inherent */ + case 0x5d: OP_TSTR(reg_b); break; + /* 0x5F CLRB inherent */ + case 0x5e: + case 0x5f: OP_CLRR(reg_b); break; + /* 0x60 NEG indexed */ + case 0x60: + case 0x61: OP_NEG(BYTE_INDEXED); break; + /* 0x63 COM indexed */ + case 0x62: + case 0x63: OP_COM(BYTE_INDEXED); break; + /* 0x64 LSR indexed */ + case 0x64: + case 0x65: OP_LSR(BYTE_INDEXED); break; + /* 0x66 ROR indexed */ + case 0x66: OP_ROR(BYTE_INDEXED); break; + /* 0x67 ASR indexed */ + case 0x67: OP_ASR(BYTE_INDEXED); break; + /* 0x68 ASL,LSL indexed */ + case 0x68: OP_ASL(BYTE_INDEXED); break; + /* 0x69 ROL indexed */ + case 0x69: OP_ROL(BYTE_INDEXED); break; + /* 0x6A DEC indexed */ + case 0x6a: + case 0x6b: OP_DEC(BYTE_INDEXED); break; + /* 0x6C INC indexed */ + case 0x6c: OP_INC(BYTE_INDEXED); break; + /* 0x6D TST indexed */ + case 0x6d: OP_TST(BYTE_INDEXED); break; + /* 0x6E JMP indexed */ + case 0x6e: OP_JMP(EA_INDEXED); break; + /* 0x6F CLR indexed */ + case 0x6f: OP_CLR(BYTE_INDEXED); break; + /* 0x70 NEG extended */ + case 0x70: + case 0x71: OP_NEG(BYTE_EXTENDED); break; + /* 0x73 COM extended */ + case 0x72: + case 0x73: OP_COM(BYTE_EXTENDED); break; + /* 0x74 LSR extended */ + case 0x74: + case 0x75: OP_LSR(BYTE_EXTENDED); break; + /* 0x76 ROR extended */ + case 0x76: OP_ROR(BYTE_EXTENDED); break; + /* 0x77 ASR extended */ + case 0x77: OP_ASR(BYTE_EXTENDED); break; + /* 0x78 ASL,LSL extended */ + case 0x78: OP_ASL(BYTE_EXTENDED); break; + /* 0x79 ROL extended */ + case 0x79: OP_ROL(BYTE_EXTENDED); break; + /* 0x7A DEC extended */ + case 0x7a: + case 0x7b: OP_DEC(BYTE_EXTENDED); break; + /* 0x7C INC extended */ + case 0x7c: OP_INC(BYTE_EXTENDED); break; + /* 0x7D TST extended */ + case 0x7d: OP_TST(BYTE_EXTENDED); break; + /* 0x7E JMP extended */ + case 0x7e: OP_JMP(EA_EXTENDED); break; + /* 0x7F CLR extended */ + case 0x7f: OP_CLR(BYTE_EXTENDED); break; + /* 0x80 SUBA immediate */ + case 0x80: OP_SUB(reg_a, BYTE_IMMEDIATE); break; + /* 0x81 CMPA immediate */ + case 0x81: OP_CMP(reg_a, BYTE_IMMEDIATE); break; + /* 0x82 SBCA immediate */ + case 0x82: OP_SBC(reg_a, BYTE_IMMEDIATE); break; + /* 0x83 SUBD immediate */ + case 0x83: OP_SUBD(WORD_IMMEDIATE); break; + /* 0x84 ANDA immediate */ + case 0x84: OP_AND(reg_a, BYTE_IMMEDIATE); break; + /* 0x85 BITA immediate */ + case 0x85: OP_BIT(reg_a, BYTE_IMMEDIATE); break; + /* 0x86 LDA immediate */ + case 0x86: OP_LD(reg_a, BYTE_IMMEDIATE); break; + /* 0x88 EORA immediate */ + case 0x88: OP_EOR(reg_a, BYTE_IMMEDIATE); break; + /* 0x89 ADCA immediate */ + case 0x89: OP_ADC(reg_a, BYTE_IMMEDIATE); break; + /* 0x8A ORA immediate */ + case 0x8a: OP_OR(reg_a, BYTE_IMMEDIATE); break; + /* 0x8B ADDA immediate */ + case 0x8b: OP_ADD(reg_a, BYTE_IMMEDIATE); break; + /* 0x8C CMPX immediate */ + case 0x8c: OP_CMP16(reg_x, WORD_IMMEDIATE); break; + /* 0x8D BSR relative */ + case 0x8d: { + uint_least16_t RA; + uint16_t dest; + SHORT_RELATIVE(RA); + dest = reg_pc + RA; + peek_byte(dest); + TAKEN_CYCLES(1); + PUSHWORD(reg_s, reg_pc); + reg_pc = dest; + } + break; + /* 0x8E LDX immediate */ + case 0x8e: OP_LD16(reg_x, WORD_IMMEDIATE); break; + /* 0x90 SUBA direct */ + case 0x90: OP_SUB(reg_a, BYTE_DIRECT); break; + /* 0x91 CMPA direct */ + case 0x91: OP_CMP(reg_a, BYTE_DIRECT); break; + /* 0x92 SBCA direct */ + case 0x92: OP_SBC(reg_a, BYTE_DIRECT); break; + /* 0x93 SUBD direct */ + case 0x93: OP_SUBD(WORD_DIRECT); break; + /* 0x94 ANDA direct */ + case 0x94: OP_AND(reg_a, BYTE_DIRECT); break; + /* 0x95 BITA direct */ + case 0x95: OP_BIT(reg_a, BYTE_DIRECT); break; + /* 0x96 LDA direct */ + case 0x96: OP_LD(reg_a, BYTE_DIRECT); break; + /* 0x97 STA direct */ + case 0x97: OP_ST(reg_a, EA_DIRECT); break; + /* 0x98 EORA direct */ + case 0x98: OP_EOR(reg_a, BYTE_DIRECT); break; + /* 0x99 ADCA direct */ + case 0x99: OP_ADC(reg_a, BYTE_DIRECT); break; + /* 0x9A ORA direct */ + case 0x9a: OP_OR(reg_a, BYTE_DIRECT); break; + /* 0x9B ADDA direct */ + case 0x9b: OP_ADD(reg_a, BYTE_DIRECT); break; + /* 0x9C CMPX direct */ + case 0x9c: OP_CMP16(reg_x, WORD_DIRECT); break; + /* 0x9D JSR direct */ + case 0x9d: OP_JSR(EA_DIRECT); break; + /* 0x9E LDX direct */ + case 0x9e: OP_LD16(reg_x, WORD_DIRECT); break; + /* 0x9F STX direct */ + case 0x9f: OP_ST16(reg_x, EA_DIRECT); break; + /* 0xA0 SUBA indexed */ + case 0xa0: OP_SUB(reg_a, BYTE_INDEXED); break; + /* 0xA1 CMPA indexed */ + case 0xa1: OP_CMP(reg_a, BYTE_INDEXED); break; + /* 0xA2 SBCA indexed */ + case 0xa2: OP_SBC(reg_a, BYTE_INDEXED); break; + /* 0xA3 SUBD indexed */ + case 0xa3: OP_SUBD(WORD_INDEXED); break; + /* 0xA4 ANDA indexed */ + case 0xa4: OP_AND(reg_a, BYTE_INDEXED); break; + /* 0xA5 BITA indexed */ + case 0xa5: OP_BIT(reg_a, BYTE_INDEXED); break; + /* 0xA6 LDA indexed */ + case 0xa6: OP_LD(reg_a, BYTE_INDEXED); break; + /* 0xA7 STA indexed */ + case 0xa7: OP_ST(reg_a, EA_INDEXED); break; + /* 0xA8 EORA indexed */ + case 0xa8: OP_EOR(reg_a, BYTE_INDEXED); break; + /* 0xA9 ADCA indexed */ + case 0xa9: OP_ADC(reg_a, BYTE_INDEXED); break; + /* 0xAA ORA indexed */ + case 0xaa: OP_OR(reg_a, BYTE_INDEXED); break; + /* 0xAB ADDA indexed */ + case 0xab: OP_ADD(reg_a, BYTE_INDEXED); break; + /* 0xAC CMPX indexed */ + case 0xac: OP_CMP16(reg_x, WORD_INDEXED); break; + /* 0xAD JSR indexed */ + case 0xad: OP_JSR(EA_INDEXED); break; + /* 0xAE LDX indexed */ + case 0xae: OP_LD16(reg_x, WORD_INDEXED); break; + /* 0xAF STX indexed */ + case 0xaf: OP_ST16(reg_x, EA_INDEXED); break; + /* 0xB0 SUBA extended */ + case 0xb0: OP_SUB(reg_a, BYTE_EXTENDED); break; + /* 0xB1 CMPA extended */ + case 0xb1: OP_CMP(reg_a, BYTE_EXTENDED); break; + /* 0xB2 SBCA extended */ + case 0xb2: OP_SBC(reg_a, BYTE_EXTENDED); break; + /* 0xB3 SUBD extended */ + case 0xb3: OP_SUBD(WORD_EXTENDED); break; + /* 0xB4 ANDA extended */ + case 0xb4: OP_AND(reg_a, BYTE_EXTENDED); break; + /* 0xB5 BITA extended */ + case 0xb5: OP_BIT(reg_a, BYTE_EXTENDED); break; + /* 0xB6 LDA extended */ + case 0xb6: OP_LD(reg_a, BYTE_EXTENDED); break; + /* 0xB7 STA extended */ + case 0xb7: OP_ST(reg_a, EA_EXTENDED); break; + /* 0xB8 EORA extended */ + case 0xb8: OP_EOR(reg_a, BYTE_EXTENDED); break; + /* 0xB9 ADCA extended */ + case 0xb9: OP_ADC(reg_a, BYTE_EXTENDED); break; + /* 0xBA ORA extended */ + case 0xba: OP_OR(reg_a, BYTE_EXTENDED); break; + /* 0xBB ADDA extended */ + case 0xbb: OP_ADD(reg_a, BYTE_EXTENDED); break; + /* 0xBC CMPX extended */ + case 0xbc: OP_CMP16(reg_x, WORD_EXTENDED); break; + /* 0xBD JSR extended */ + case 0xbd: OP_JSR(EA_EXTENDED); break; + /* 0xBE LDX extended */ + case 0xbe: OP_LD16(reg_x, WORD_EXTENDED); break; + /* 0xBF STX extended */ + case 0xbf: OP_ST16(reg_x, EA_EXTENDED); break; + /* 0xC0 SUBB immediate */ + case 0xc0: OP_SUB(reg_b, BYTE_IMMEDIATE); break; + /* 0xC1 CMPB immediate */ + case 0xc1: OP_CMP(reg_b, BYTE_IMMEDIATE); break; + /* 0xC2 SBCB immediate */ + case 0xc2: OP_SBC(reg_b, BYTE_IMMEDIATE); break; + /* 0xC3 ADDD immediate */ + case 0xc3: OP_ADDD(WORD_IMMEDIATE); break; + /* 0xC4 ANDB immediate */ + case 0xc4: OP_AND(reg_b, BYTE_IMMEDIATE); break; + /* 0xC5 BITB immediate */ + case 0xc5: OP_BIT(reg_b, BYTE_IMMEDIATE); break; + /* 0xC6 LDB immediate */ + case 0xc6: OP_LD(reg_b, BYTE_IMMEDIATE); break; + /* 0xC8 EORB immediate */ + case 0xc8: OP_EOR(reg_b, BYTE_IMMEDIATE); break; + /* 0xC9 ADCB immediate */ + case 0xc9: OP_ADC(reg_b, BYTE_IMMEDIATE); break; + /* 0xCA ORB immediate */ + case 0xca: OP_OR(reg_b, BYTE_IMMEDIATE); break; + /* 0xCB ADDB immediate */ + case 0xcb: OP_ADD(reg_b, BYTE_IMMEDIATE); break; + /* 0xCC LDD immediate */ + case 0xcc: OP_LDD(WORD_IMMEDIATE); break; + /* 0xCE LDU immediate */ + case 0xce: OP_LD16(reg_u, WORD_IMMEDIATE); break; + /* 0xD0 SUBB direct */ + case 0xd0: OP_SUB(reg_b, BYTE_DIRECT); break; + /* 0xD1 CMPB direct */ + case 0xd1: OP_CMP(reg_b, BYTE_DIRECT); break; + /* 0xD2 SBCB direct */ + case 0xd2: OP_SBC(reg_b, BYTE_DIRECT); break; + /* 0xD3 ADDD direct */ + case 0xd3: OP_ADDD(WORD_DIRECT); break; + /* 0xD4 ANDB direct */ + case 0xd4: OP_AND(reg_b, BYTE_DIRECT); break; + /* 0xD5 BITB direct */ + case 0xd5: OP_BIT(reg_b, BYTE_DIRECT); break; + /* 0xD6 LDB direct */ + case 0xd6: OP_LD(reg_b, BYTE_DIRECT); break; + /* 0xD7 STB direct */ + case 0xd7: OP_ST(reg_b, EA_DIRECT); break; + /* 0xD8 EORB direct */ + case 0xd8: OP_EOR(reg_b, BYTE_DIRECT); break; + /* 0xD9 ADCB direct */ + case 0xd9: OP_ADC(reg_b, BYTE_DIRECT); break; + /* 0xDA ORB direct */ + case 0xda: OP_OR(reg_b, BYTE_DIRECT); break; + /* 0xDB ADDB direct */ + case 0xdb: OP_ADD(reg_b, BYTE_DIRECT); break; + /* 0xDC LDD direct */ + case 0xdc: OP_LDD(WORD_DIRECT); break; + /* 0xDD STD direct */ + case 0xdd: OP_STD(EA_DIRECT); break; + /* 0xDE LDU direct */ + case 0xde: OP_LD16(reg_u, WORD_DIRECT); break; + /* 0xDF STU direct */ + case 0xdf: OP_ST16(reg_u, EA_DIRECT); break; + /* 0xE0 SUBB indexed */ + case 0xe0: OP_SUB(reg_b, BYTE_INDEXED); break; + /* 0xE1 CMPB indexed */ + case 0xe1: OP_CMP(reg_b, BYTE_INDEXED); break; + /* 0xE2 SBCB indexed */ + case 0xe2: OP_SBC(reg_b, BYTE_INDEXED); break; + /* 0xE3 ADDD indexed */ + case 0xe3: OP_ADDD(WORD_INDEXED); break; + /* 0xE4 ANDB indexed */ + case 0xe4: OP_AND(reg_b, BYTE_INDEXED); break; + /* 0xE5 BITB indexed */ + case 0xe5: OP_BIT(reg_b, BYTE_INDEXED); break; + /* 0xE6 LDB indexed */ + case 0xe6: OP_LD(reg_b, BYTE_INDEXED); break; + /* 0xE7 STB indexed */ + case 0xe7: OP_ST(reg_b, EA_INDEXED); break; + /* 0xE8 EORB indexed */ + case 0xe8: OP_EOR(reg_b, BYTE_INDEXED); break; + /* 0xE9 ADCB indexed */ + case 0xe9: OP_ADC(reg_b, BYTE_INDEXED); break; + /* 0xEA ORB indexed */ + case 0xea: OP_OR(reg_b, BYTE_INDEXED); break; + /* 0xEB ADDB indexed */ + case 0xeb: OP_ADD(reg_b, BYTE_INDEXED); break; + /* 0xEC LDD indexed */ + case 0xec: OP_LDD(WORD_INDEXED); break; + /* 0xED STD indexed */ + case 0xed: OP_STD(EA_INDEXED); break; + /* 0xEE LDU indexed */ + case 0xee: OP_LD16(reg_u, WORD_INDEXED); break; + /* 0xEF STU indexed */ + case 0xef: OP_ST16(reg_u, EA_INDEXED); break; + /* 0xF0 SUBB extended */ + case 0xf0: OP_SUB(reg_b, BYTE_EXTENDED); break; + /* 0xF1 CMPB extended */ + case 0xf1: OP_CMP(reg_b, BYTE_EXTENDED); break; + /* 0xF2 SBCB extended */ + case 0xf2: OP_SBC(reg_b, BYTE_EXTENDED); break; + /* 0xF3 ADDD extended */ + case 0xf3: OP_ADDD(WORD_EXTENDED); break; + /* 0xF4 ANDB extended */ + case 0xf4: OP_AND(reg_b, BYTE_EXTENDED); break; + /* 0xF5 BITB extended */ + case 0xf5: OP_BIT(reg_b, BYTE_EXTENDED); break; + /* 0xF6 LDB extended */ + case 0xf6: OP_LD(reg_b, BYTE_EXTENDED); break; + /* 0xF7 STB extended */ + case 0xf7: OP_ST(reg_b, EA_EXTENDED); break; + /* 0xF8 EORB extended */ + case 0xf8: OP_EOR(reg_b, BYTE_EXTENDED); break; + /* 0xF9 ADCB extended */ + case 0xf9: OP_ADC(reg_b, BYTE_EXTENDED); break; + /* 0xFA ORB extended */ + case 0xfa: OP_OR(reg_b, BYTE_EXTENDED); break; + /* 0xFB ADDB extended */ + case 0xfb: OP_ADD(reg_b, BYTE_EXTENDED); break; + /* 0xFC LDD extended */ + case 0xfc: OP_LDD(WORD_EXTENDED); break; + /* 0xFD STD extended */ + case 0xfd: OP_STD(EA_EXTENDED); break; + /* 0xFE LDU extended */ + case 0xfe: OP_LD16(reg_u, WORD_EXTENDED); break; + /* 0xFF STU extended */ + case 0xff: OP_ST16(reg_u, EA_EXTENDED); break; + /* Illegal instruction */ + default: TAKEN_CYCLES(1); break; + } + IF_TRACE(LOG_DEBUG(0, "\tcc=%02x a=%02x b=%02x dp=%02x x=%04x y=%04x u=%04x s=%04x\n", reg_cc, reg_a, reg_b, reg_dp, reg_x, reg_y, reg_u, reg_s)); + continue; +switched_block_page2: + BYTE_IMMEDIATE(0,op); + switch(op) { + /* 0x10 Page 2 */ + case 0x10: goto switched_block_page2; break; + /* 0x11 Page 3 */ + case 0x11: goto switched_block_page3; break; + /* 0x1021 LBRN relative */ + case 0x21: BRANCHL(0); break; + /* 0x1022 LBHI relative */ + case 0x22: BRANCHL(!(reg_cc & (CC_Z|CC_C))); break; + /* 0x1023 LBLS relative */ + case 0x23: BRANCHL(reg_cc & (CC_Z|CC_C)); break; + /* 0x1024 LBHS,LBCC relative */ + case 0x24: BRANCHL(!(reg_cc & CC_C)); break; + /* 0x1025 LBLO,LBCS relative */ + case 0x25: BRANCHL(reg_cc & CC_C); break; + /* 0x1026 LBNE relative */ + case 0x26: BRANCHL(!(reg_cc & CC_Z)); break; + /* 0x1027 LBEQ relative */ + case 0x27: BRANCHL(reg_cc & CC_Z); break; + /* 0x1028 LBVC relative */ + case 0x28: BRANCHL(!(reg_cc & CC_V)); break; + /* 0x1029 LBVS relative */ + case 0x29: BRANCHL(reg_cc & CC_V); break; + /* 0x102A LBPL relative */ + case 0x2a: BRANCHL(!(reg_cc & CC_N)); break; + /* 0x102B LBMI relative */ + case 0x2b: BRANCHL(reg_cc & CC_N); break; + /* 0x102C LBGE relative */ + case 0x2c: BRANCHL(!N_EOR_V); break; + /* 0x102D LBLT relative */ + case 0x2d: BRANCHL(N_EOR_V); break; + /* 0x102E LBGT relative */ + case 0x2e: BRANCHL(!(N_EOR_V || reg_cc & CC_Z)); break; + /* 0x102F LBLE relative */ + case 0x2f: BRANCHL(N_EOR_V || (reg_cc & CC_Z)); break; + /* 0x103F SWI2 inherent */ + case 0x3f: + reg_cc |= CC_E; + peek_byte(reg_pc); + TAKEN_CYCLES(1); + PUSHWORD(reg_s, reg_pc); + PUSHWORD(reg_s, reg_u); + PUSHWORD(reg_s, reg_y); + PUSHWORD(reg_s, reg_x); + PUSHBYTE(reg_s, reg_dp); + PUSHBYTE(reg_s, reg_b); + PUSHBYTE(reg_s, reg_a); + PUSHBYTE(reg_s, reg_cc); + TAKEN_CYCLES(1); + reg_pc = fetch_word(0xfff4); + TAKEN_CYCLES(1); + break; + /* 0x1083 CMPD immediate */ + case 0x83: OP_CMPD(WORD_IMMEDIATE); break; + /* 0x108C CMPY immediate */ + case 0x8c: OP_CMP16(reg_y, WORD_IMMEDIATE); break; + /* 0x108E LDY immediate */ + case 0x8e: OP_LD16(reg_y, WORD_IMMEDIATE); break; + /* 0x1093 CMPD direct */ + case 0x93: OP_CMPD(WORD_DIRECT); break; + /* 0x109C CMPY direct */ + case 0x9c: OP_CMP16(reg_y, WORD_DIRECT); break; + /* 0x109E LDY direct */ + case 0x9e: OP_LD16(reg_y, WORD_DIRECT); break; + /* 0x109F STY direct */ + case 0x9f: OP_ST16(reg_y, EA_DIRECT); break; + /* 0x10A3 CMPD indexed */ + case 0xa3: OP_CMPD(WORD_INDEXED); break; + /* 0x10AC CMPY indexed */ + case 0xac: OP_CMP16(reg_y, WORD_INDEXED); break; + /* 0x10AE LDY indexed */ + case 0xae: OP_LD16(reg_y, WORD_INDEXED); break; + /* 0x10AF STY indexed */ + case 0xaf: OP_ST16(reg_y, EA_INDEXED); break; + /* 0x10B3 CMPD extended */ + case 0xb3: OP_CMPD(WORD_EXTENDED); break; + /* 0x10BC CMPY extended */ + case 0xbc: OP_CMP16(reg_y, WORD_EXTENDED); break; + /* 0x10BE LDY extended */ + case 0xbe: OP_LD16(reg_y, WORD_EXTENDED); break; + /* 0x10BF STY extended */ + case 0xbf: OP_ST16(reg_y, EA_EXTENDED); break; + /* 0x10CE LDS immediate */ + case 0xce: OP_LD16(reg_s, WORD_IMMEDIATE); ARM_NMI; break; + /* 0x10DE LDS direct */ + case 0xde: OP_LD16(reg_s, WORD_DIRECT); ARM_NMI; break; + /* 0x10DF STS direct */ + case 0xdf: OP_ST16(reg_s, EA_DIRECT); break; + /* 0x10EE LDS indexed */ + case 0xee: OP_LD16(reg_s, WORD_INDEXED); ARM_NMI; break; + /* 0x10EF STS indexed */ + case 0xef: OP_ST16(reg_s, EA_INDEXED); break; + /* 0x10FE LDS extended */ + case 0xfe: OP_LD16(reg_s, WORD_EXTENDED); ARM_NMI; break; + /* 0x10FF STS extended */ + case 0xff: OP_ST16(reg_s, EA_EXTENDED); break; + /* Illegal instruction */ + default: TAKEN_CYCLES(1); break; + } + IF_TRACE(LOG_DEBUG(0, "\tcc=%02x a=%02x b=%02x dp=%02x x=%04x y=%04x u=%04x s=%04x\n", reg_cc, reg_a, reg_b, reg_dp, reg_x, reg_y, reg_u, reg_s)); + continue; +switched_block_page3: + BYTE_IMMEDIATE(0,op); + switch(op) { + /* 0x10 Page 2 */ + case 0x10: goto switched_block_page2; break; + /* 0x11 Page 3 */ + case 0x11: goto switched_block_page3; break; + /* 0x113F SWI3 inherent */ + case 0x3f: + reg_cc |= CC_E; + peek_byte(reg_pc); + TAKEN_CYCLES(1); + PUSHWORD(reg_s, reg_pc); + PUSHWORD(reg_s, reg_u); + PUSHWORD(reg_s, reg_y); + PUSHWORD(reg_s, reg_x); + PUSHBYTE(reg_s, reg_dp); + PUSHBYTE(reg_s, reg_b); + PUSHBYTE(reg_s, reg_a); + PUSHBYTE(reg_s, reg_cc); + TAKEN_CYCLES(1); + reg_pc = fetch_word(0xfff2); + TAKEN_CYCLES(1); + break; + /* 0x1183 CMPU immediate */ + case 0x83: OP_CMP16(reg_u, WORD_IMMEDIATE); break; + /* 0x118C CMPS immediate */ + case 0x8c: OP_CMP16(reg_s, WORD_IMMEDIATE); break; + /* 0x1193 CMPU direct */ + case 0x93: OP_CMP16(reg_u, WORD_DIRECT); break; + /* 0x119C CMPS direct */ + case 0x9c: OP_CMP16(reg_s, WORD_DIRECT); break; + /* 0x11A3 CMPU indexed */ + case 0xa3: OP_CMP16(reg_u, WORD_INDEXED); break; + /* 0x11AC CMPS indexed */ + case 0xac: OP_CMP16(reg_s, WORD_INDEXED); break; + /* 0x11B3 CMPU extended */ + case 0xb3: OP_CMP16(reg_u, WORD_EXTENDED); break; + /* 0x11BC CMPS extended */ + case 0xbc: OP_CMP16(reg_s, WORD_EXTENDED); break; + /* Illegal instruction */ + default: TAKEN_CYCLES(1); break; + } + IF_TRACE(LOG_DEBUG(0, "\tcc=%02x a=%02x b=%02x dp=%02x x=%04x y=%04x u=%04x s=%04x\n", reg_cc, reg_a, reg_b, reg_dp, reg_x, reg_y, reg_u, reg_s)); + continue; + } + } + +restore_regs: + register_cc = reg_cc; + register_a = reg_a; + register_b = reg_b; + register_dp = reg_dp; + register_x = reg_x; + register_y = reg_y; + register_u = reg_u; + register_s = reg_s; + register_pc = reg_pc; +} + +void m6809_get_state(M6809State *state) { + state->reg_cc = register_cc; + state->reg_a = register_a; + state->reg_b = register_b; + state->reg_dp = register_dp; + state->reg_x = register_x; + state->reg_y = register_y; + state->reg_u = register_u; + state->reg_s = register_s; + state->reg_pc = register_pc; + state->halt = halt; + state->nmi = nmi; + state->firq = firq; + state->irq = irq; + state->wait_for_interrupt = wait_for_interrupt; + state->skip_register_push = skip_register_push; + state->nmi_armed = nmi_armed; +} + +void m6809_set_state(M6809State *state) { + register_cc = state->reg_cc; + register_a = state->reg_a; + register_b = state->reg_b; + register_dp = state->reg_dp; + register_x = state->reg_x; + register_y = state->reg_y; + register_u = state->reg_u; + register_s = state->reg_s; + register_pc = state->reg_pc; + halt = state->halt; + nmi = state->nmi; + firq = state->firq; + irq = state->irq; + wait_for_interrupt = state->wait_for_interrupt; + skip_register_push = state->skip_register_push; + nmi_armed = state->nmi_armed; +} + +/* Kept for old snapshots */ +void m6809_set_registers(uint8_t *regs) { + register_cc = regs[0]; + register_a = regs[1]; + register_b = regs[2]; + register_dp = regs[3]; + register_x = regs[4] << 8 | regs[5]; + register_y = regs[6] << 8 | regs[7]; + register_u = regs[8] << 8 | regs[9]; + register_s = regs[10] << 8 | regs[11]; + register_pc = regs[12] << 8 | regs[13]; +} + +void m6809_jump(uint_least16_t pc) { + register_pc = pc & 0xffff; +} diff --git a/src/core/m6809.h b/src/core/m6809.h new file mode 100644 index 0000000..6766cbf --- /dev/null +++ b/src/core/m6809.h @@ -0,0 +1,32 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * See COPYING.GPL for redistribution conditions. */ + +#ifndef __M6809_H__ +#define __M6809_H__ + +#include "types.h" + +typedef struct { + uint8_t reg_cc, reg_a, reg_b, reg_dp; + uint16_t reg_x, reg_y, reg_u, reg_s, reg_pc; + uint8_t halt, nmi, firq, irq; + uint8_t wait_for_interrupt; + uint8_t skip_register_push; + uint8_t nmi_armed; +} M6809State; + +extern int halt, nmi, firq, irq; + +void m6809_init(void); +void m6809_reset(void); +void m6809_cycle(Cycle until); +void m6809_get_state(M6809State *state); +void m6809_set_state(M6809State *state); +void m6809_jump(uint_least16_t pc); + +/* Old */ +void m6809_set_registers(uint8_t *regs); + +#endif /* __M6809_H__ */ diff --git a/src/core/m6809_dasm.h b/src/core/m6809_dasm.h new file mode 100644 index 0000000..b23e8e8 --- /dev/null +++ b/src/core/m6809_dasm.h @@ -0,0 +1,17 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * See COPYING.GPL for redistribution conditions. */ + +#ifndef __M6809_DASM_H__ +#define __M6809_DASM_H__ + +#ifdef TRACE +void m6809_dasm_reset(void); +void m6809_dasm_byte(unsigned int byte, unsigned int pc); +#else +# define m6809_dasm_reset(...) +# define m6809_dasm_byte(...) +#endif + +#endif /* __M6809_DASM_H__ */ diff --git a/src/core/machine.c b/src/core/machine.c new file mode 100644 index 0000000..532df3a --- /dev/null +++ b/src/core/machine.c @@ -0,0 +1,467 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * 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 "cart.h" +#include "deltados.h" +#include "dragondos.h" +#include "fs.h" +#include "keyboard.h" +#include "logging.h" +#include "m6809.h" +#include "machine.h" +#include "pia.h" +#include "rsdos.h" +#include "sam.h" +#include "tape.h" +#include "vdg.h" +#include "vdrive.h" +#include "wd279x.h" +#include "xroar.h" + +#include "global.h" + +const char *machine_names[NUM_MACHINE_TYPES] = { + "Dragon 32", "Dragon 64", "Tano Dragon (NTSC)", "Tandy CoCo (PAL)", "Tandy CoCo (NTSC)" +}; + +MachineConfig machine_defaults[NUM_MACHINE_TYPES] = { + { ARCH_DRAGON32, ROMSET_DRAGON32, KEYMAP_DRAGON, TV_PAL, 32, DOS_DRAGONDOS, NULL, NULL, NULL, NULL }, + { ARCH_DRAGON64, ROMSET_DRAGON64, KEYMAP_DRAGON, TV_PAL, 64, DOS_DRAGONDOS, NULL, NULL, NULL, NULL }, + { ARCH_DRAGON64, ROMSET_DRAGON64, KEYMAP_DRAGON, TV_NTSC, 64, DOS_DRAGONDOS, NULL, NULL, NULL, NULL }, + { ARCH_COCO, ROMSET_COCO, KEYMAP_COCO, TV_PAL, 64, DOS_RSDOS, NULL, NULL, NULL, NULL }, + { ARCH_COCO, ROMSET_COCO, KEYMAP_COCO, TV_NTSC, 64, DOS_RSDOS, NULL, NULL, NULL, NULL } +}; + +MachineConfig requested_config = { + ANY_AUTO, ANY_AUTO, ANY_AUTO, ANY_AUTO, ANY_AUTO, ANY_AUTO, NULL, NULL, NULL, NULL +}; +int running_machine; +MachineConfig running_config; + +uint_least16_t machine_page0_ram = 0x8000; /* Base RAM in bytes, up to 32K */ +uint_least16_t machine_page1_ram = 0x8000; /* Generally 0 or 32K */ +Keymap keymap; +uint8_t ram0[0x8000]; +uint8_t ram1[0x8000]; +uint8_t rom0[0x8000]; +uint8_t rom1[0x8000]; + +Cycle current_cycle; +int noextbas; + +static Keymap dragon_keymap = { + {7,6}, {8,8}, {8,8}, {8,8}, {8,8}, {8,8}, {8,8}, {8,8}, /* 0 - 7 */ + {5,5}, {6,5}, {4,5}, {8,8}, {1,6}, {0,6}, {8,8}, {8,8}, /* 8 - 15 */ + {8,8}, {8,8}, {8,8}, {8,8}, {8,8}, {8,8}, {8,8}, {8,8}, /* 16 - 23 */ + {8,8}, {8,8}, {8,8}, {2,6}, {8,8}, {8,8}, {8,8}, {8,8}, /* 24 - 31 */ + {7,5}, {8,8}, {8,8}, {8,8}, {8,8}, {8,8}, {8,8}, {8,8}, /* 32 - 39 */ + {8,8}, {8,8}, {8,8}, {8,8}, {4,1}, {5,1}, {6,1}, {7,1}, /* 40 - 47 */ + {0,0}, {1,0}, {2,0}, {3,0}, {4,0}, {5,0}, {6,0}, {7,0}, /* 48 - 55 */ + {0,1}, {1,1}, {2,1}, {3,1}, {8,8}, {8,8}, {8,8}, {8,8}, /* 56 - 63 */ + {0,2}, {1,2}, {2,2}, {3,2}, {4,2}, {5,2}, {6,2}, {7,2}, /* 64 - 71 */ + {0,3}, {1,3}, {2,3}, {3,3}, {4,3}, {5,3}, {6,3}, {7,3}, /* 72 - 79 */ + {0,4}, {1,4}, {2,4}, {3,4}, {4,4}, {5,4}, {6,4}, {7,4}, /* 80 - 87 */ + {0,5}, {1,5}, {2,5}, {8,8}, {8,8}, {8,8}, {3,5}, {8,8}, /* 88 - 95 */ + {1,6}, {1,2}, {2,2}, {3,2}, {4,2}, {5,2}, {6,2}, {7,2}, /* 96 - 103 */ + {0,3}, {1,3}, {2,3}, {3,3}, {4,3}, {5,3}, {6,3}, {7,3}, /* 104 - 111 */ + {0,4}, {1,4}, {2,4}, {3,4}, {4,4}, {5,4}, {6,4}, {7,4}, /* 112 - 119 */ + {0,5}, {1,5}, {2,5}, {8,8}, {8,8}, {8,8}, {8,8}, {5,5}, /* 120 - 127 */ +}; +static Keymap coco_keymap = { + {7,6}, {8,8}, {8,8}, {8,8}, {8,8}, {8,8}, {8,8}, {8,8}, /* 0 - 7 */ + {5,3}, {6,3}, {4,3}, {8,8}, {1,6}, {0,6}, {8,8}, {8,8}, /* 8 - 15 */ + {8,8}, {8,8}, {8,8}, {8,8}, {8,8}, {8,8}, {8,8}, {8,8}, /* 16 - 23 */ + {8,8}, {8,8}, {8,8}, {2,6}, {8,8}, {8,8}, {8,8}, {8,8}, /* 24 - 31 */ + {7,3}, {8,8}, {8,8}, {8,8}, {8,8}, {8,8}, {8,8}, {8,8}, /* 32 - 39 */ + {8,8}, {8,8}, {8,8}, {8,8}, {4,5}, {5,5}, {6,5}, {7,5}, /* 40 - 47 */ + {0,4}, {1,4}, {2,4}, {3,4}, {4,4}, {5,4}, {6,4}, {7,4}, /* 48 - 55 */ + {0,5}, {1,5}, {2,5}, {3,5}, {8,8}, {5,5}, {8,8}, {8,8}, /* 56 - 63 */ + {0,0}, {1,0}, {2,0}, {3,0}, {4,0}, {5,0}, {6,0}, {7,0}, /* 64 - 71 */ + {0,1}, {1,1}, {2,1}, {3,1}, {4,1}, {5,1}, {6,1}, {7,1}, /* 72 - 79 */ + {0,2}, {1,2}, {2,2}, {3,2}, {4,2}, {5,2}, {6,2}, {7,2}, /* 80 - 87 */ + {0,3}, {1,3}, {2,3}, {0,0}, {8,8}, {8,8}, {3,3}, {8,8}, /* 88 - 95 */ + {1,6}, {1,0}, {2,0}, {3,0}, {4,0}, {5,0}, {6,0}, {7,0}, /* 96 - 103 */ + {0,1}, {1,1}, {2,1}, {3,1}, {4,1}, {5,1}, {6,1}, {7,1}, /* 104 - 111 */ + {0,2}, {1,2}, {2,2}, {3,2}, {4,2}, {5,2}, {6,2}, {7,2}, /* 112 - 119 */ + {0,3}, {1,3}, {2,3}, {8,8}, {8,8}, {8,8}, {8,8}, {5,3}, /* 120 - 127 */ +}; + +static const char *d32_extbas_roms[] = { "d32", "dragon32", "d32rom", "dragon", NULL }; +static const char *d64_extbas_roms[] = { "d64_1", "d64rom1", "dragrom", "dragon", NULL }; +static const char *d64_altbas_roms[] = { "d64_2", "d64rom2", NULL }; +static const char *coco_bas_roms[] = { "bas13", "bas12", "bas11", "bas10", NULL }; +static const char *coco_extbas_roms[] = { "extbas11", "extbas10", NULL }; +static const char *dragondos_roms[] = { "dplus49b", "dplus48", "sdose6", "sdose5", "sdose4", "ddos40", "ddos15", "ddos10", NULL }; +static const char *deltados_roms[] = { "delta", "deltados", NULL }; +static const char *rsdos_roms[] = { "disk11", "disk10", NULL }; + +static struct { + const char **bas; + const char **extbas; + const char **altbas; +} rom_list[] = { + { NULL, d32_extbas_roms, NULL }, + { NULL, d64_extbas_roms, d64_altbas_roms }, + { coco_bas_roms, coco_extbas_roms, NULL } +}; + +static const char **dos_rom_list[] = { + dragondos_roms, rsdos_roms, deltados_roms +}; + +const char *dos_type_names[NUM_DOS_TYPES] = { + "No DOS cartridge", "DragonDOS", "RS-DOS", "Delta System" +}; + +static const char *machine_options[NUM_MACHINE_TYPES] = { + "dragon32", "dragon64", "tano", "coco", "cocous" +}; + +static const char *dos_type_options[NUM_DOS_TYPES] = { + "none", "dragondos", "rsdos", "delta" +}; + +static int load_rom_from_list(const char *preferred, const char **list, + uint8_t *dest, size_t max_size); +static int load_rom(const char *romname, uint8_t *dest, size_t max_size); + +/**************************************************************************/ + +# if 0 //LUDO: +void machine_helptext(void) +{ + puts(" -machine MACHINE emulated machine (-machine help for a list)"); + puts(" -bas FILENAME specify BASIC ROM to use (CoCo only)"); + puts(" -extbas FILENAME specify Extended BASIC ROM to use"); + puts(" -altbas FILENAME specify alternate BASIC ROM (Dragon 64)"); + puts(" -noextbas disable Extended BASIC"); + puts(" -dostype DOS type of DOS cartridge (-dostype help for a list)"); + puts(" -dos FILENAME specify DOS ROM (or CoCo Disk BASIC)"); + puts(" -nodos disable DOS (ROM and hardware emulation)"); + puts(" -pal emulate PAL (50Hz) video"); + puts(" -ntsc emulate NTSC (60Hz) video"); + puts(" -ram KBYTES specify amount of RAM in K"); +} + +void machine_getargs(int argc, char **argv) +{ + int i; + machine_clear_requested_config(); + noextbas = 0; + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-machine") && i+1architecture % NUM_ARCHITECTURES; + if (running_config.romset == ANY_AUTO) + running_config.romset = defaults->romset % NUM_ROMSETS; + if (running_config.keymap == ANY_AUTO) + running_config.keymap = defaults->keymap % NUM_KEYMAPS; + if (running_config.tv_standard == ANY_AUTO) + running_config.tv_standard = defaults->tv_standard % NUM_TV_STANDARDS; + if (running_config.ram == ANY_AUTO) + running_config.ram = defaults->ram; + if (running_config.dos_type == ANY_AUTO) + running_config.dos_type = defaults->dos_type % NUM_DOS_TYPES; + /* Load appropriate ROMs */ + memset(rom0, 0x7e, sizeof(rom0)); + memset(rom1, 0x7e, sizeof(rom1)); + /* ... BASIC */ + load_rom_from_list(running_config.bas_rom, + rom_list[running_config.romset].bas, + rom0 + 0x2000, sizeof(rom0) - 0x2000); + /* ... Extended BASIC */ + if (!noextbas) { + load_rom_from_list(running_config.extbas_rom, + rom_list[running_config.romset].extbas, + rom0, sizeof(rom0)); + } + /* ... DOS */ + if (DOS_ENABLED) { + load_rom_from_list(running_config.dos_rom, + dos_rom_list[running_config.dos_type - 1], + rom0 + 0x4000, sizeof(rom0) - 0x4000); + } + /* ... Alternate BASIC ROM */ + load_rom_from_list(running_config.altbas_rom, + rom_list[running_config.romset].altbas, + rom1, sizeof(rom1)); + /* Configure keymap */ + machine_set_keymap(running_config.keymap); + /* Configure RAM */ + memset(ram0, 0x00, sizeof(ram0)); + memset(ram1, 0x00, sizeof(ram1)); + machine_set_page0_ram_size(running_config.ram * 1024); + if (running_config.ram > 32) { + machine_set_page1_ram_size((running_config.ram-32) * 1024); + } else { + machine_set_page1_ram_size(0); + } + } + pia_reset(); + if (IS_DRAGONDOS) + dragondos_reset(); + if (IS_RSDOS) + rsdos_reset(); + if (IS_DELTADOS) + deltados_reset(); + sam_reset(); + m6809_reset(); + vdg_reset(); + tape_reset(); + cart_reset(); +} + +void machine_clear_requested_config(void) +{ + requested_config.architecture = ANY_AUTO; + requested_config.romset = ANY_AUTO; + requested_config.keymap = ANY_AUTO; + requested_config.tv_standard = ANY_AUTO; + requested_config.ram = ANY_AUTO; + requested_config.dos_type = ANY_AUTO; + requested_config.bas_rom = NULL; + requested_config.extbas_rom = NULL; + requested_config.altbas_rom = NULL; + requested_config.dos_rom = NULL; +} + +/* Keymap can be changed on-the-fly */ +void machine_set_keymap(int map) { + map %= NUM_KEYMAPS; + running_config.keymap = map; + switch (map) { + default: + case KEYMAP_DRAGON: + memcpy(keymap, dragon_keymap, sizeof(keymap)); + break; + case KEYMAP_COCO: + memcpy(keymap, coco_keymap, sizeof(keymap)); + break; + } +} + +/* Set RAM sizes for page0, page1 */ +void machine_set_page0_ram_size(unsigned int size) { + if (size > 0x8000) + size = 0x8000; + machine_page0_ram = size; + if (size < 0x8000) + memset(ram0 + size, 0x7e, 0x8000 - size); +} + +void machine_set_page1_ram_size(unsigned int size) { + if (size > 0x8000) + size = 0x8000; + machine_page1_ram = size; + if (size < 0x8000) + memset(ram1 + size, 0x7e, 0x8000 - size); +} + +/**************************************************************************/ + +/* load_rom searches a path (specified in ROMPATH macro at compile time) to + * find roms. It searches each element in the (colon separated) path for + * the supplied rom name with ".rom" or ".dgn" as a suffix. load_rom_f does + * the actual loading. */ + +#ifndef ROMPATH +# define ROMPATH ".","/usr/local/share/xroar/roms" +#endif + +static const char *rom_path[] = { ROMPATH, NULL }; + +static int load_rom_f(const char *filename, uint8_t *dest, size_t max_size) { + int fd; + if ((fd = fs_open(filename, FS_READ)) == -1) + return -1; + LOG_DEBUG(3, "Loading ROM: %s\n", filename); + fs_read(fd, dest, max_size); + fs_close(fd); + return 0; +} + +static int load_dgn_f(const char *filename, uint8_t *dest, size_t max_size) { + int fd; + if ((fd = fs_open(filename, FS_READ)) == -1) + return -1; + LOG_DEBUG(3, "Loading DGN: %s\n", filename); + fs_read(fd, dest, 16); + fs_read(fd, dest, max_size); + fs_close(fd); + return 0; +} + +static char *construct_path(const char *path, const char *filename) { + char *buf; + char *home = NULL; + int path_length; + + /* Account for path, "/", filename and trailing null */ + path_length = strlen(path) + strlen(filename) + 2; + if (path[0] == '~' && (path[1] == '/' || path[1] == '\\')) { + home = getenv("HOME"); + if (home) { + /* Skip "~/" */ + path += 2; + path_length -= 2; + /* Account for home, "/" */ + path_length += strlen(home) + 1; /* trailing '/' */ + } + } + buf = malloc(path_length); + if (!buf) + return NULL; + buf[0] = 0; + if (home) { + strcpy(buf, home); + strcat(buf, "/"); + } + strcat(buf, path); + strcat(buf, "/"); + strcat(buf, filename); + return buf; +} + +static int load_rom(const char *romname, uint8_t *dest, size_t max_size) { + int i; + char *filename; + char name_dot_rom[13], name_dot_dgn[13]; + unsigned int ret; + if (!romname) + return -1; + /* Try ROM without any extension first */ + if (load_rom_f(romname, dest, max_size) == 0) + return 0; + if (strlen(romname) > 8) + return -1; + strcpy(name_dot_rom, romname); + strcat(name_dot_rom, ".rom"); + strcpy(name_dot_dgn, romname); + strcat(name_dot_dgn, ".dgn"); + for (i = 0; rom_path[i]; i++) { + filename = construct_path(rom_path[i], name_dot_rom); + if (filename == NULL) + return -1; + ret = load_rom_f(filename, dest, max_size); + free(filename); + if (ret == 0) + return 0; + filename = construct_path(rom_path[i], name_dot_dgn); + if (filename == NULL) + return -1; + ret = load_dgn_f(filename, dest, max_size); + free(filename); + if (ret == 0) + return 0; + } + return -1; +} + +static int load_rom_from_list(const char *preferred, const char **list, + uint8_t *dest, size_t max_size) { + int i; + if (list == NULL) + return 1; + if (preferred && !load_rom(preferred, dest, max_size)) + return 1; + for (i = 0; list[i] && load_rom(list[i], dest, max_size); i++); + return 1; +} diff --git a/src/core/machine.h b/src/core/machine.h new file mode 100644 index 0000000..0be9bdf --- /dev/null +++ b/src/core/machine.h @@ -0,0 +1,109 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * See COPYING.GPL for redistribution conditions. */ + +#ifndef __MACHINE_H__ +#define __MACHINE_H__ + +#include "types.h" + +/* Dragon 64s and later Dragon 32s used a 14.218MHz crystal + * (ref: "Dragon 64 differences", Graham E. Kinns and then a motherboard) */ +//#define OSCILLATOR_RATE 14218000 +#define OSCILLATOR_RATE 14318180 + +#define CPU_SLOW_DIVISOR 16 +#define CPU_FAST_DIVISOR 8 + +#define IS_DRAGON64 (running_config.architecture == ARCH_DRAGON64) +#define IS_DRAGON32 (running_config.architecture == ARCH_DRAGON32) +#define IS_DRAGON (!IS_COCO) +#define IS_COCO (running_config.architecture == ARCH_COCO) + +#define IS_PAL (running_config.tv_standard == TV_PAL) +#define IS_NTSC (running_config.tv_standard == TV_NTSC) + +#define IS_DRAGON_KEYMAP (running_config.keymap == KEYMAP_DRAGON) +#define IS_COCO_KEYMAP (running_config.keymap == KEYMAP_COCO) + +#define DOS_ENABLED (running_config.dos_type != DOS_NONE) +#define IS_DRAGONDOS (running_config.dos_type == DOS_DRAGONDOS) +#define IS_RSDOS (running_config.dos_type == DOS_RSDOS) +#define IS_DELTADOS (running_config.dos_type == DOS_DELTADOS) + +#define ANY_AUTO (-1) +#define MACHINE_DRAGON32 (0) +#define MACHINE_DRAGON64 (1) +#define MACHINE_TANO (2) +#define MACHINE_COCO (3) +#define MACHINE_COCOUS (4) +#define ARCH_DRAGON32 (0) +#define ARCH_DRAGON64 (1) +#define ARCH_COCO (2) +#define ROMSET_DRAGON32 (0) +#define ROMSET_DRAGON64 (1) +#define ROMSET_COCO (2) +#define KEYMAP_DRAGON (0) +#define KEYMAP_COCO (1) +#define TV_PAL (0) +#define TV_NTSC (1) +#define DOS_NONE (0) +#define DOS_DRAGONDOS (1) +#define DOS_RSDOS (2) +#define DOS_DELTADOS (3) + +#define NUM_MACHINE_TYPES (5) +#define NUM_ARCHITECTURES (3) +#define NUM_ROMSETS (3) +#define NUM_KEYMAPS (2) +#define NUM_TV_STANDARDS (2) +#define NUM_DOS_TYPES (4) + +typedef struct { unsigned int col, row; } Key; +typedef Key Keymap[128]; + +typedef struct { + int architecture; + int romset; + int keymap; + int tv_standard; + int ram; + int dos_type; + const char *bas_rom; + const char *extbas_rom; + const char *altbas_rom; + const char *dos_rom; +} MachineConfig; + +extern const char *machine_names[NUM_MACHINE_TYPES]; +extern MachineConfig machine_defaults[NUM_MACHINE_TYPES]; +extern int requested_machine; +extern int running_machine; +extern MachineConfig requested_config; +extern MachineConfig running_config; + +extern uint_least16_t machine_page0_ram; /* Base RAM in bytes, up to 32K */ +extern uint_least16_t machine_page1_ram; /* Generally 0 or 32K */ +extern Keymap keymap; +extern uint8_t ram0[0x8000]; +extern uint8_t ram1[0x8000]; +extern uint8_t rom0[0x8000]; +extern uint8_t rom1[0x8000]; + +extern Cycle current_cycle; +extern int noextbas; + +void machine_helptext(void); +void machine_getargs(int argc, char **argv); +void machine_init(void); +void machine_shutdown(void); +void machine_reset(int hard); + +void machine_clear_requested_config(void); +void machine_set_keymap(int map); + +void machine_set_page0_ram_size(unsigned int size); +void machine_set_page1_ram_size(unsigned int size); + +#endif /* __MACHINE_H__ */ diff --git a/src/core/main_unix.c b/src/core/main_unix.c new file mode 100644 index 0000000..75a778d --- /dev/null +++ b/src/core/main_unix.c @@ -0,0 +1,48 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * 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 "config.h" + +#include +#include +#ifdef HAVE_SDL +# include +#endif + +#include "xroar.h" +#include "logging.h" +#include "global.h" + +int a_main(int argc, char **argv) +{ + dragon_global_init(); + + atexit(xroar_shutdown); + if (xroar_init(argc, argv) != 0) { + exit(1); + } + + psp_run_load_file(); + psp_sdl_black_screen(); + + DRAGON.emu_started = 1; + dragon_set_video_artifact(DRAGON.video_artifact_mode); + + xroar_mainloop(); + return 0; +} diff --git a/src/core/module.c b/src/core/module.c new file mode 100644 index 0000000..d0566c3 --- /dev/null +++ b/src/core/module.c @@ -0,0 +1,188 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * 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 "types.h" +#include "logging.h" +#include "module.h" + +/**** Default user interface module list ****/ + +extern UIModule ui_sdl_module; +extern UIModule ui_gp32_module; +extern UIModule ui_nds_module; +static UIModule *default_ui_module_list[] = { +#ifdef HAVE_SDL + &ui_sdl_module, +#endif +#ifdef HAVE_GP32 + &ui_gp32_module, +#endif +#ifdef HAVE_NDS + &ui_nds_module, +#endif + NULL +}; + +/**** Default file requester module list ****/ + +extern FileReqModule filereq_carbon_module; +extern FileReqModule filereq_windows32_module; +extern FileReqModule filereq_gtk_module; +extern FileReqModule filereq_cli_module; +static FileReqModule *default_filereq_module_list[] = { +#ifdef HAVE_CARBON + &filereq_carbon_module, +#endif +#ifdef WINDOWS32 + &filereq_windows32_module, +#endif +#ifdef HAVE_GTK + &filereq_gtk_module, +#endif +#ifdef HAVE_CLI + &filereq_cli_module, +#endif + NULL +}; + +/**** Default sound module list ****/ + +extern SoundModule sound_macosx_module; +extern SoundModule sound_sun_module; +extern SoundModule sound_sdl_module; +extern SoundModule sound_oss_module; +extern SoundModule sound_jack_module; +extern SoundModule sound_null_module; +static SoundModule *default_sound_module_list[] = { +#ifdef HAVE_MACOSX_AUDIO + &sound_macosx_module, +#endif +#ifdef HAVE_SUN_AUDIO + &sound_sun_module, +#endif +#ifdef HAVE_SDL + &sound_sdl_module, +#endif +#ifdef HAVE_OSS_AUDIO + &sound_oss_module, +#endif +#ifdef HAVE_JACK_AUDIO + &sound_jack_module, +#endif + &sound_null_module, + NULL +}; + +/**** Default joystick module list ****/ + +extern JoystickModule joystick_sdl_module; +static JoystickModule *default_joystick_module_list[] = { +# ifndef PSP //LUDO: +#ifdef HAVE_SDL + &joystick_sdl_module, +#endif +# endif + NULL +}; + +UIModule **ui_module_list = default_ui_module_list; +UIModule *ui_module = NULL; +FileReqModule **filereq_module_list = default_filereq_module_list; +FileReqModule *filereq_module = NULL; +VideoModule **video_module_list = NULL; +VideoModule *video_module = NULL; +SoundModule **sound_module_list = default_sound_module_list; +SoundModule *sound_module = NULL; +KeyboardModule **keyboard_module_list = NULL; +KeyboardModule *keyboard_module = NULL; +JoystickModule **joystick_module_list = default_joystick_module_list; +JoystickModule *joystick_module = NULL; + +void module_print_list(Module **list) { + int i; + if (list == NULL || list[0]->common.name == NULL) { + puts("\tNone found."); + return; + } + for (i = 0; list[i]; i++) { + printf("\t%-10s%s\n", list[i]->common.name, list[i]->common.description); + } +} + +Module *module_select(Module **list, const char *name) { + int i; + if (list == NULL) + return NULL; + for (i = 0; list[i]; i++) { + if (!strcmp(list[i]->common.name, name)) + return list[i]; + } + return NULL; +} + +Module *module_select_by_arg(Module **list, const char *arg, int argc, char **argv) { + int i; + for (i = 1; i < argc; i++) { + if (strcmp(argv[i], arg) == 0 && i+1common.init(argc, argv) == 0) { + module->common.initialised = 1; + return module; + } + return NULL; +} + +Module *module_init_from_list(Module **list, Module *module, int argc, char **argv) { + int i; + /* First attempt to initialise selected module (if given) */ + if (module_init(module, argc, argv)) + return module; + if (list == NULL) + return NULL; + /* If that fails, try every *other* module in the list */ + for (i = 0; list[i]; i++) { + if (list[i] != module && (module_init(list[i], argc, argv))) + return list[i]; + } + return NULL; +} + +void module_shutdown(Module *module) { + if (module && module->common.initialised) + module->common.shutdown(); +} + +void module_helptext(Module *module) { + if (module && module->common.helptext) { + module->common.helptext(); + } +} diff --git a/src/core/module.h b/src/core/module.h new file mode 100644 index 0000000..cd617ae --- /dev/null +++ b/src/core/module.h @@ -0,0 +1,93 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * See COPYING.GPL for redistribution conditions. */ + +#ifndef __MODULE_H__ +#define __MODULE_H__ + +typedef struct { + const char *name; + const char *description; + int (*init)(int argc, char **argv); + int initialised; + void (*shutdown)(void); + void (*helptext)(void); +} Module_Common; + +typedef struct { + Module_Common common; + char *(*load_filename)(const char **extensions); + char *(*save_filename)(const char **extensions); +} FileReqModule; + +typedef struct { + Module_Common common; + void (*resize)(unsigned int w, unsigned int h); + int (*set_fullscreen)(int fullscreen); + int is_fullscreen; + void (*vdg_vsync)(void); + void (*vdg_set_mode)(unsigned int mode); + void (*vdg_render_sg4)(void); + void (*vdg_render_sg6)(void); + void (*vdg_render_cg1)(void); + void (*vdg_render_rg1)(void); + void (*vdg_render_cg2)(void); + void (*vdg_render_rg6)(void); + void (*render_border)(void); +} VideoModule; + +typedef struct { + Module_Common common; + void (*update)(void); +} SoundModule; + +typedef struct { + Module_Common common; +} KeyboardModule; + +typedef struct { + Module_Common common; +} JoystickModule; + +typedef struct { + Module_Common common; + FileReqModule **filereq_module_list; + VideoModule **video_module_list; + SoundModule **sound_module_list; + KeyboardModule **keyboard_module_list; + JoystickModule **joystick_module_list; +} UIModule; + +typedef union { + Module_Common common; + UIModule ui; + FileReqModule filereq; + VideoModule video; + SoundModule sound; + KeyboardModule keyboard; + JoystickModule joystick; +} Module; + +extern UIModule **ui_module_list; +extern UIModule *ui_module; +extern FileReqModule **filereq_module_list; +extern FileReqModule *filereq_module; +extern VideoModule **video_module_list; +extern VideoModule *video_module; +extern SoundModule **sound_module_list; +extern SoundModule *sound_module; +extern KeyboardModule **keyboard_module_list; +extern KeyboardModule *keyboard_module; +extern JoystickModule **joystick_module_list; +extern JoystickModule *joystick_module; + +void module_print_list(Module **list); +Module *module_select(Module **list, const char *name); +Module *module_select_by_arg(Module **list, const char *arg, int argc, char **argv); +Module *module_init(Module *module, int argc, char **argv); +Module *module_init_from_list(Module **list, Module *module, int argc, char **argv); +void module_shutdown(Module *module); +void module_helptext(Module *module); + +#endif /* __MODULE_H__ */ diff --git a/src/core/pia.c b/src/core/pia.c new file mode 100644 index 0000000..3715bfb --- /dev/null +++ b/src/core/pia.c @@ -0,0 +1,57 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * 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 "logging.h" +#include "m6809.h" +#include "machine.h" +#include "xroar.h" +#include "pia.h" + +pia_port PIA_0A, PIA_0B, PIA_1A, PIA_1B; + +void pia_init(void) { +} + +void pia_reset(void) { + /* Side A */ + PIA_0A.control_register = PIA_1A.control_register = 0; + PIA_0A.direction_register = PIA_1A.direction_register = 0; + PIA_0A.output_register = PIA_1A.output_register = 0; + PIA_0A.port_input = PIA_1A.port_input = 0xff; + PIA_0A.tied_low = PIA_1A.tied_low = 0xff; + PIA_UPDATE_OUTPUT(PIA_0A); + PIA_UPDATE_OUTPUT(PIA_1A); + /* Side B */ + PIA_0B.control_register = PIA_1B.control_register = 0; + PIA_0B.direction_register = PIA_1B.direction_register = 0; + PIA_0B.output_register = PIA_1B.output_register = 0; + PIA_0B.port_input = PIA_1B.port_input = 0; + PIA_0B.tied_low = PIA_1B.tied_low = 0xff; + if (IS_DRAGON64) { + PIA_1B.port_input |= (1<<2); + } + PIA_UPDATE_OUTPUT(PIA_0B); + PIA_UPDATE_OUTPUT(PIA_1B); + /* Clear interrupt lines */ + PIA_0A.interrupt_received = PIA_1A.interrupt_received = 0; + PIA_0B.interrupt_received = PIA_1B.interrupt_received = 0; + irq = firq = 0; +} diff --git a/src/core/pia.h b/src/core/pia.h new file mode 100644 index 0000000..23e27a2 --- /dev/null +++ b/src/core/pia.h @@ -0,0 +1,151 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * See COPYING.GPL for redistribution conditions. */ + +#ifndef __PIA_H__ +#define __PIA_H__ + +#include "types.h" +#include "joystick.h" +#include "keyboard.h" +#include "m6809.h" +#include "module.h" +#include "tape.h" +#include "vdg.h" + +#ifdef HAVE_GP32 +#define tape_update_input() +#endif + +#define MAX_WRFUNCS 4 + +typedef struct { + unsigned int control_register; + unsigned int direction_register; + unsigned int output_register; + unsigned int port_output; + unsigned int port_input; + unsigned int tied_low; + unsigned int interrupt_received; +} pia_port; + +#define PIA_SELECT_DDR(p) do { p.control_register &= 0xfb; } while (0) +#define PIA_SELECT_PDR(p) do { p.control_register |= 0x04; } while (0) + +#define PIA_INTERRUPT_ENABLED(p) (p.control_register & 0x01) +#define PIA_ACTIVE_TRANSITION(p) (p.control_register & 0x02) +#define PIA_DDR_SELECTED(p) (!(p.control_register & 0x04)) +#define PIA_PDR_SELECTED(p) (p.control_register & 0x04) + +extern pia_port PIA_0A, PIA_0B, PIA_1A, PIA_1B; + +#define PIA_SET_Cx1(p,i,bv) do { \ + if (PIA_ACTIVE_TRANSITION(p)) { \ + p.interrupt_received = 0x80; \ + if (PIA_INTERRUPT_ENABLED(p)) { \ + i |= (bv); \ + } else { \ + i &= ~(bv); \ + } \ + } \ + } while (0) + +#define PIA_SET_P0CA1 do { \ + PIA_SET_Cx1(PIA_0A, irq, 1); \ + } while (0) +#define PIA_SET_P0CB1 do { \ + PIA_SET_Cx1(PIA_0B, irq, 2); \ + } while (0) +#define PIA_SET_P1CA1 do { \ + PIA_SET_Cx1(PIA_1A, firq, 1); \ + } while (0) +#define PIA_SET_P1CB1 do { \ + PIA_SET_Cx1(PIA_1B, firq, 2); \ + } while (0) + +#define PIA_RESET_Cx1(p,i,bv) do { \ + if (!PIA_ACTIVE_TRANSITION(p)) { \ + p.interrupt_received = 0x80; \ + if (PIA_INTERRUPT_ENABLED(p)) { \ + i |= (bv); \ + } else { \ + i &= ~(bv); \ + } \ + } \ + } while (0) + +#define PIA_RESET_P0CA1 do { \ + PIA_RESET_Cx1(PIA_0A, irq, 1); \ + } while (0) +#define PIA_RESET_P0CB1 do { \ + PIA_RESET_Cx1(PIA_0B, irq, 2); \ + } while (0) +#define PIA_RESET_P1CA1 do { \ + PIA_RESET_Cx1(PIA_1A, firq, 1); \ + } while (0) +#define PIA_RESET_P1CB1 do { \ + PIA_RESET_Cx1(PIA_1B, firq, 2); \ + } while (0) + +#define PIA_CONTROL_READ(p) (p.control_register | p.interrupt_received) + +#define PIA_READ_P0CA PIA_CONTROL_READ(PIA_0A) +#define PIA_READ_P0CB PIA_CONTROL_READ(PIA_0B) +#define PIA_READ_P1CA PIA_CONTROL_READ(PIA_1A) +#define PIA_READ_P1CB PIA_CONTROL_READ(PIA_1B) + +#define PIA_CONTROL_WRITE(p,v,i,bv) do { \ + p.control_register = v & 0x3f; \ + if (PIA_INTERRUPT_ENABLED(p)) { \ + if (p.interrupt_received) \ + i |= (bv); \ + } else { \ + i &= ~(bv); \ + } \ + } while (0) + +#define PIA_WRITE_P0CA(v) do { PIA_CONTROL_WRITE(PIA_0A,v,irq,1); sound_module->update(); } while (0) +#define PIA_WRITE_P0CB(v) do { PIA_CONTROL_WRITE(PIA_0B,v,irq,2); sound_module->update(); } while (0) +#define PIA_WRITE_P1CA(v) do { PIA_CONTROL_WRITE(PIA_1A,v,firq,1); tape_update_motor(); } while (0) +#define PIA_WRITE_P1CB(v) do { PIA_CONTROL_WRITE(PIA_1B,v,firq,2); sound_module->update(); } while (0) + +#define PIA_READ(p,i,bv,r) do { \ + if (PIA_PDR_SELECTED(p)) { \ + p.interrupt_received = 0; \ + i &= ~(bv); \ + r = ((p.port_input & p.tied_low) & ~p.direction_register) | (p.output_register & p.direction_register); \ + } else { \ + r = p.direction_register; \ + } \ + } while (0) + +#define PIA_READ_P0DA(r) PIA_READ(PIA_0A, irq, 1, r) +#define PIA_READ_P0DB(r) PIA_READ(PIA_0B, irq, 2, r) +#define PIA_READ_P1DA(r) do { tape_update_input(); PIA_READ(PIA_1A, firq, 1, r); } while (0) +#define PIA_READ_P1DB(r) PIA_READ(PIA_1B, firq, 2, r) + +#define PIA_WRITE(p,v) do { \ + if (PIA_PDR_SELECTED(p)) { \ + p.output_register = v; \ + v &= p.direction_register; \ + } else { \ + p.direction_register = v; \ + v &= p.output_register; \ + } \ + p.port_output = (v | (p.port_input & ~(p.direction_register))) & p.tied_low; \ + } while (0) + +#define PIA_UPDATE_OUTPUT(p) do { \ + p.port_output = ((p.output_register & p.direction_register) | (p.port_input & ~(p.direction_register))) & p.tied_low; \ + } while (0) + +#define PIA_WRITE_P0DA(v) do { PIA_WRITE(PIA_0A, v); keyboard_row_update(); } while (0) +#define PIA_WRITE_P0DB(v) do { PIA_WRITE(PIA_0B, v); keyboard_column_update(); } while (0) +#define PIA_WRITE_P1DA(v) do { PIA_WRITE(PIA_1A, v); sound_module->update(); joystick_update(); tape_update_output(); } while (0) +#define PIA_WRITE_P1DB(v) do { PIA_WRITE(PIA_1B, v); sound_module->update(); vdg_set_mode(); } while (0) + +void pia_init(void); +void pia_reset(void); + +#endif /* __PIA_H__ */ diff --git a/src/core/rsdos.c b/src/core/rsdos.c new file mode 100644 index 0000000..ec7ca7d --- /dev/null +++ b/src/core/rsdos.c @@ -0,0 +1,156 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * 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 + */ + +/* Sources: + * CoCo DOS cartridge detail: + * http://www.coco3.com/unravalled/disk-basic-unravelled.pdf + */ + +#include +#include + +#include "types.h" +#include "events.h" +#include "logging.h" +#include "m6809.h" +#include "machine.h" +#include "rsdos.h" +#include "vdrive.h" +#include "wd279x.h" +#include "xroar.h" + +#define QUEUE_NMI() do { \ + nmi_event->at_cycle = current_cycle + 1; \ + event_queue(nmi_event); \ + } while (0) + +/* Handle signals from WD2793 */ +static void set_drq_handler(void); +static void reset_drq_handler(void); +static void set_intrq_handler(void); +static void reset_intrq_handler(void); + +/* NMIs queued to allow CPU to run next instruction */ +static event_t *nmi_event; +static void do_nmi(void *context); + +/* Latch that's part of the RSDOS cart: */ +static unsigned int ic1_drive_select; +static unsigned int ic1_motor_enable; +static unsigned int ic1_precomp_enable; +static unsigned int ic1_density; +static int drq_flag; +static int intrq_flag; +static unsigned int halt_enable; + +void rsdos_init(void) { + nmi_event = event_new(); + nmi_event->dispatch = do_nmi; +} + +void rsdos_reset(void) { + wd279x_type = WD2793; + wd279x_set_drq_handler = set_drq_handler; + wd279x_reset_drq_handler = reset_drq_handler; + wd279x_set_intrq_handler = set_intrq_handler; + wd279x_reset_intrq_handler = reset_intrq_handler; + wd279x_reset(); + ic1_drive_select = 0xff; + ic1_motor_enable = 0xff; + ic1_precomp_enable = 0xff; + ic1_density = 0xff; + halt_enable = 0xff; + drq_flag = intrq_flag = 0; + rsdos_ff40_write(0); +} + +/* RSDOS cartridge circuitry */ +void rsdos_ff40_write(unsigned int octet) { + unsigned int new_drive_select = 0; + octet ^= 0x20; + if (octet & 0x01) { + new_drive_select = 0; + } else if (octet & 0x02) { + new_drive_select = 1; + } else if (octet & 0x04) { + new_drive_select = 2; + } + vdrive_set_side(octet & 0x40 ? 1 : 0); + LOG_DEBUG(4, "RSDOS: Write to FF40: "); + if (new_drive_select != ic1_drive_select) { + LOG_DEBUG(4, "DRIVE SELECT %d, ", new_drive_select); + } + if ((octet & 0x08) != ic1_motor_enable) { + LOG_DEBUG(4, "MOTOR %s, ", (octet & 0x08)?"ON":"OFF"); + } + if ((octet & 0x20) != ic1_density) { + LOG_DEBUG(4, "DENSITY %s, ", (octet & 0x20)?"SINGLE":"DOUBLE"); + } + if ((octet & 0x10) != ic1_precomp_enable) { + LOG_DEBUG(4, "PRECOMP %s, ", (octet & 0x10)?"ON":"OFF"); + } + if ((octet & 0x80) != halt_enable) { + LOG_DEBUG(4, "HALT %s, ", (octet & 0x80)?"ENABLED":"DISABLED"); + } + LOG_DEBUG(4, "\n"); + ic1_drive_select = new_drive_select; + vdrive_set_drive(ic1_drive_select); + ic1_motor_enable = octet & 0x08; + ic1_precomp_enable = octet & 0x10; + ic1_density = octet & 0x20; + wd279x_set_density(ic1_density); + if (ic1_density && intrq_flag) { + nmi = 1; + } + halt_enable = octet & 0x80; + if (halt_enable && !drq_flag) { + halt = 1; + } else { + halt = 0; + } +} + +static void set_drq_handler(void) { + drq_flag = 1; + halt = 0; +} + +static void reset_drq_handler(void) { + drq_flag = 0; + if (halt_enable) { + halt = 1; + } +} + +static void set_intrq_handler(void) { + intrq_flag = 1; + halt_enable = halt = 0; + QUEUE_NMI(); +} + +static void reset_intrq_handler(void) { + intrq_flag = 0; + nmi = 0; +} + +static void do_nmi(void *context) { + (void)context; + if (!ic1_density && intrq_flag) { + nmi = 1; + } +} diff --git a/src/core/rsdos.h b/src/core/rsdos.h new file mode 100644 index 0000000..a816598 --- /dev/null +++ b/src/core/rsdos.h @@ -0,0 +1,13 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * See COPYING.GPL for redistribution conditions. */ + +#ifndef __RSDOS_H__ +#define __RSDOS_H__ + +void rsdos_init(void); +void rsdos_reset(void); +void rsdos_ff40_write(unsigned int octet); + +#endif /* __RSDOS_H__ */ diff --git a/src/core/sam.c b/src/core/sam.c new file mode 100644 index 0000000..cdbd67e --- /dev/null +++ b/src/core/sam.c @@ -0,0 +1,268 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * 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 "deltados.h" +#include "dragondos.h" +#include "joystick.h" +#include "keyboard.h" +#include "logging.h" +#include "machine.h" +#include "pia.h" +#include "rsdos.h" +#include "sam.h" +#include "tape.h" +#include "vdg.h" +#include "wd279x.h" +#include "xroar.h" + +uint8_t *addrptr_low; +uint8_t *addrptr_high; +static uint_least16_t map_type; + +uint_least16_t sam_register; + +uint_least16_t sam_vdg_base; +uint_least16_t sam_vdg_line_base; +unsigned int sam_vdg_mode; +uint_least16_t sam_vdg_address; +unsigned int sam_vdg_mod_xdiv; +unsigned int sam_vdg_mod_ydiv; +uint_least16_t sam_vdg_mod_clear; +unsigned int sam_vdg_xcount; +unsigned int sam_vdg_ycount; +#ifndef HAVE_GP32 +unsigned int sam_topaddr_cycles; +#endif + +static unsigned int vdg_mod_xdiv[8] = { 1, 3, 1, 2, 1, 1, 1, 1 }; +static unsigned int vdg_mod_ydiv[8] = { 12, 1, 3, 1, 2, 1, 1, 1 }; +static uint_least16_t vdg_mod_clear[8] = { ~30, ~14, ~30, ~14, ~30, ~14, ~30, ~0 }; + +void sam_init(void) { +} + +void sam_reset(void) { + sam_register = 0; + sam_update_from_register(); + sam_vdg_fsync(); +} + +unsigned int sam_read_byte(uint_least16_t addr) { + unsigned int ret; + addr &= 0xffff; + if (addr < 0x8000) { + current_cycle += CPU_SLOW_DIVISOR; + if (addr < machine_page0_ram) + return addrptr_low[addr]; + return 0x7e; + } + if (addr < 0xff00) { + current_cycle += sam_topaddr_cycles; + return addrptr_high[addr-0x8000]; + } + if (addr < 0xff20) { + current_cycle += CPU_SLOW_DIVISOR; + if (IS_COCO) { + if ((addr & 3) == 0) { PIA_READ_P0DA(ret); return ret; } + if ((addr & 3) == 1) return PIA_READ_P0CA; + if ((addr & 3) == 2) { PIA_READ_P0DB(ret); return ret; } + if ((addr & 3) == 3) return PIA_READ_P0CB; + } else { + if ((addr & 7) == 0) { PIA_READ_P0DA(ret); return ret; } + if ((addr & 7) == 1) return PIA_READ_P0CA; + if ((addr & 7) == 2) { PIA_READ_P0DB(ret); return ret; } + if ((addr & 7) == 3) return PIA_READ_P0CB; + /* Not yet implemented: + if ((addr & 7) == 4) return serial_stuff; + if ((addr & 7) == 5) return serial_stuff; + if ((addr & 7) == 6) return serial_stuff; + if ((addr & 7) == 7) return serial_stuff; + */ + } + return 0x7e; + } + current_cycle += sam_topaddr_cycles; + if (addr < 0xff40) { + if ((addr & 3) == 0) { PIA_READ_P1DA(ret); return ret; } + if ((addr & 3) == 1) return PIA_READ_P1CA; + if ((addr & 3) == 2) { PIA_READ_P1DB(ret); return ret; } + return PIA_READ_P1CB; + } + if (addr < 0xff60) { + if (!DOS_ENABLED) + return 0x7e; + if (IS_RSDOS) { + /* CoCo floppy disk controller */ + if ((addr & 15) == 8) return wd279x_status_read(); + if ((addr & 15) == 9) return wd279x_track_register_read(); + if ((addr & 15) == 10) return wd279x_sector_register_read(); + if ((addr & 15) == 11) return wd279x_data_register_read(); + } + if (IS_DRAGONDOS) { + /* Dragon floppy disk controller */ + if ((addr & 15) == 0) return wd279x_status_read(); + if ((addr & 15) == 1) return wd279x_track_register_read(); + if ((addr & 15) == 2) return wd279x_sector_register_read(); + if ((addr & 15) == 3) return wd279x_data_register_read(); + } + if (IS_DELTADOS) { + /* Delta floppy disk controller */ + if ((addr & 7) == 0) return wd279x_status_read(); + if ((addr & 7) == 1) return wd279x_track_register_read(); + if ((addr & 7) == 2) return wd279x_sector_register_read(); + if ((addr & 7) == 3) return wd279x_data_register_read(); + } + return 0x7e; + } + if (addr < 0xffe0) + return 0x7f; + return rom0[addr-0xc000]; +} + +void sam_store_byte(uint_least16_t addr, unsigned int octet) { + addr &= 0xffff; + if (addr < 0x8000) { + current_cycle += CPU_SLOW_DIVISOR; + if (addr < machine_page0_ram) + addrptr_low[addr] = octet; + return; + } + if (addr < 0xff00) { + current_cycle += sam_topaddr_cycles; + if (map_type) { + if (IS_DRAGON32 && machine_page1_ram == 0) { + ram0[addr-0x8000] = rom0[addr-0x8000]; + return; + } + if ((addr-0x8000) < machine_page1_ram) { + ram1[addr-0x8000] = octet; + return; + } + } + return; + } + if (addr < 0xff20) { + current_cycle += CPU_SLOW_DIVISOR; + if (IS_COCO) { + if ((addr & 3) == 0) PIA_WRITE_P0DA(octet); + if ((addr & 3) == 1) PIA_WRITE_P0CA(octet); + if ((addr & 3) == 2) PIA_WRITE_P0DB(octet); + if ((addr & 3) == 3) PIA_WRITE_P0CB(octet); + } else { + if ((addr & 7) == 0) PIA_WRITE_P0DA(octet); + if ((addr & 7) == 1) PIA_WRITE_P0CA(octet); + if ((addr & 7) == 2) PIA_WRITE_P0DB(octet); + if ((addr & 7) == 3) PIA_WRITE_P0CB(octet); + /* Not yet implemented: + if ((addr & 7) == 4) serial_stuff; + if ((addr & 7) == 5) serial_stuff; + if ((addr & 7) == 6) serial_stuff; + if ((addr & 7) == 7) serial_stuff; + */ + } + return; + } + current_cycle += sam_topaddr_cycles; + if (addr < 0xff40) { + if ((addr & 3) == 0) PIA_WRITE_P1DA(octet); + if ((addr & 3) == 1) PIA_WRITE_P1CA(octet); + if ((addr & 3) == 2) { + PIA_WRITE_P1DB(octet); + /* Update ROM select on Dragon 64 */ + if (IS_DRAGON64 && !map_type) + addrptr_high = (PIA_1B.port_output & 0x04) ? rom0 : rom1; + } + if ((addr & 3) == 3) PIA_WRITE_P1CB(octet); + return; + } + if (addr < 0xff60) { + if (!DOS_ENABLED) + return; + if (IS_RSDOS) { + /* CoCo floppy disk controller */ + if ((addr & 15) == 8) wd279x_command_write(octet); + if ((addr & 15) == 9) wd279x_track_register_write(octet); + if ((addr & 15) == 10) wd279x_sector_register_write(octet); + if ((addr & 15) == 11) wd279x_data_register_write(octet); + if (!(addr & 8)) rsdos_ff40_write(octet); + } + if (IS_DRAGONDOS) { + /* Dragon floppy disk controller */ + if ((addr & 15) == 0) wd279x_command_write(octet); + if ((addr & 15) == 1) wd279x_track_register_write(octet); + if ((addr & 15) == 2) wd279x_sector_register_write(octet); + if ((addr & 15) == 3) wd279x_data_register_write(octet); + if (addr & 8) dragondos_ff48_write(octet); + } + if (IS_DELTADOS) { + /* Delta floppy disk controller */ + if ((addr & 7) == 0) wd279x_command_write(octet); + if ((addr & 7) == 1) wd279x_track_register_write(octet); + if ((addr & 7) == 2) wd279x_sector_register_write(octet); + if ((addr & 7) == 3) wd279x_data_register_write(octet); + if (addr & 4) deltados_ff44_write(octet); + } + return; + } + if (addr < 0xffc0) + return; + if (addr < 0xffe0) { + addr -= 0xffc0; + if (addr & 1) + sam_register |= 1 << (addr>>1); + else + sam_register &= ~(1 << (addr>>1)); + sam_update_from_register(); + } +} + +void sam_update_from_register(void) { + sam_vdg_mode = sam_register & 0x0007; + sam_vdg_base = (sam_register & 0x03f8) << 6; + sam_vdg_mod_xdiv = vdg_mod_xdiv[sam_vdg_mode]; + sam_vdg_mod_ydiv = vdg_mod_ydiv[sam_vdg_mode]; + sam_vdg_mod_clear = vdg_mod_clear[sam_vdg_mode]; + if ((map_type = sam_register & 0x8000)) { + /* Map type 1 */ + addrptr_low = ram0; + if (machine_page1_ram == 0) + addrptr_high = ram0; + else + addrptr_high = ram1; +#ifndef HAVE_GP32 + sam_topaddr_cycles = CPU_SLOW_DIVISOR; +#endif + } else { + /* Map type 0 */ + if (sam_register & 0x0400) { + /* Page #1 */ + addrptr_low = ram1; + } else { + /* Page #0 */ + addrptr_low = ram0; + } + if (IS_DRAGON64 && !(PIA_1B.port_output & 0x04)) + addrptr_high = rom1; + else + addrptr_high = rom0; +#ifndef HAVE_GP32 + sam_topaddr_cycles = (sam_register & 0x0800) ? CPU_FAST_DIVISOR : CPU_SLOW_DIVISOR; +#endif + } +} diff --git a/src/core/sam.h b/src/core/sam.h new file mode 100644 index 0000000..8a777ba --- /dev/null +++ b/src/core/sam.h @@ -0,0 +1,87 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * See COPYING.GPL for redistribution conditions. */ + +#ifndef __SAM_H__ +#define __SAM_H__ + +#include "types.h" + +#define sam_vram_ptr(a) (a<0x8000 ? &addrptr_low[a] : &addrptr_high[a-0x8000]) + +/* Simple macro for use in place of sam_read_byte() when the result isn't + * required, just appropriate timing. Side-effects of reads obviously won't + * happen, but in practice that should almost certainly never matter. */ +#ifdef HAVE_GP32 +# define sam_peek_byte(a) do { current_cycle += CPU_SLOW_DIVISOR; } while (0) +#else +#define sam_peek_byte(a) do { \ + if ((((a)&0xffff) >= 0x8000) && ((((a)&0xffff) < 0xff00) \ + || (((a)&0xffff) >= 0xff20))) \ + current_cycle += sam_topaddr_cycles; \ + else \ + current_cycle += CPU_SLOW_DIVISOR; \ + } while(0) +#endif + +extern uint8_t *addrptr_low; +extern uint8_t *addrptr_high; +extern uint_least16_t sam_register; +extern uint_least16_t sam_vdg_base; +extern uint_least16_t sam_vdg_line_base; +extern unsigned int sam_vdg_mode; +extern uint_least16_t sam_vdg_address; +extern unsigned int sam_vdg_mod_xdiv; +extern unsigned int sam_vdg_mod_ydiv; +extern uint_least16_t sam_vdg_mod_clear; +extern unsigned int sam_vdg_xcount; +extern unsigned int sam_vdg_ycount; +#ifdef HAVE_GP32 +# define sam_topaddr_cycles CPU_SLOW_DIVISOR +#else +extern unsigned int sam_topaddr_cycles; +#endif + +static inline void sam_vdg_xstep(int b) { + sam_vdg_address += b; + sam_vdg_xcount++; + if (sam_vdg_xcount >= sam_vdg_mod_xdiv) { + sam_vdg_xcount = 0; + } else { + if (sam_vdg_mode != 7) { + if (sam_vdg_address & 8) { + sam_vdg_address += 6; + } else { + sam_vdg_address -= 6; + } + sam_vdg_address &= ~15; + } + } +} + +static inline void sam_vdg_hsync(int a) { + sam_vdg_address += a; + sam_vdg_ycount++; + if (sam_vdg_ycount >= sam_vdg_mod_ydiv) { + sam_vdg_ycount = 0; + } else { + sam_vdg_address = sam_vdg_line_base; + } + sam_vdg_address &= sam_vdg_mod_clear; + sam_vdg_line_base = sam_vdg_address; +} + +static inline void sam_vdg_fsync(void) { + sam_vdg_line_base = sam_vdg_address = sam_vdg_base; + sam_vdg_xcount = 0; + sam_vdg_ycount = 0; +} + +void sam_init(void); +void sam_reset(void); +unsigned int sam_read_byte(uint_least16_t addr); +void sam_store_byte(uint_least16_t addr, unsigned int octet); +void sam_update_from_register(void); + +#endif /* __SAM_H__ */ diff --git a/src/core/snapshot.c b/src/core/snapshot.c new file mode 100644 index 0000000..f77a5a2 --- /dev/null +++ b/src/core/snapshot.c @@ -0,0 +1,375 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * 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 "types.h" +#include "fs.h" +#include "logging.h" +#include "m6809.h" +#include "machine.h" +#include "pia.h" +#include "sam.h" +#include "snapshot.h" +#include "tape.h" +#include "vdg.h" +#include "vdisk.h" +#include "vdrive.h" +#include "xroar.h" +#include "global.h" + +/* Write files in 'chunks', each with an identifying byte and a 16-bit + * length. This should mean no changes are required that break the + * format. */ + +/* Note: Setting up the correct ROM select for Dragon 64 depends on SAM + * register update following PIA configuration. */ + +#define ID_REGISTER_DUMP (0) /* deprecated */ +#define ID_RAM_PAGE0 (1) +#define ID_PIA_REGISTERS (2) +#define ID_SAM_REGISTERS (3) +#define ID_M6809_STATE (4) +#define ID_KEYBOARD_MAP (5) +#define ID_ARCHITECTURE (6) +#define ID_RAM_PAGE1 (7) +#define ID_MACHINECONFIG (8) +#define ID_SNAPVERSION (9) +#define ID_VDISK_FILE (10) + +#define SNAPSHOT_VERSION_MAJOR 1 +#define SNAPSHOT_VERSION_MINOR 3 + +int write_snapshot(const char *filename) { + int fd; + M6809State cpu_state; + if ((fd = fs_open(filename, FS_WRITE)) == -1) + return -1; + fs_write(fd, "XRoar snapshot.\012\000", 17); + /* Snapshot version */ + fs_write_byte(fd, ID_SNAPVERSION); fs_write_word16(fd, 3); + fs_write_byte(fd, SNAPSHOT_VERSION_MAJOR); + fs_write_word16(fd, SNAPSHOT_VERSION_MINOR); + /* Machine running config */ + fs_write_byte(fd, ID_MACHINECONFIG); fs_write_word16(fd, 7); + fs_write_byte(fd, running_machine); + fs_write_byte(fd, running_config.architecture); + fs_write_byte(fd, running_config.romset); + fs_write_byte(fd, running_config.keymap); + fs_write_byte(fd, running_config.tv_standard); + fs_write_byte(fd, running_config.ram); + fs_write_byte(fd, running_config.dos_type); + /* RAM page 0 */ + fs_write_byte(fd, ID_RAM_PAGE0); fs_write_word16(fd, machine_page0_ram); + fs_write(fd, ram0, machine_page0_ram); + /* RAM page 1 */ + if (machine_page1_ram > 0) { + fs_write_byte(fd, ID_RAM_PAGE1); + fs_write_word16(fd, machine_page1_ram); + fs_write(fd, ram1, machine_page1_ram); + } + /* PIA state written before CPU state because PIA may have + * unacknowledged interrupts pending already cleared in the CPU + * state */ + fs_write_byte(fd, ID_PIA_REGISTERS); fs_write_word16(fd, 3*4); + /* PIA_0A */ + fs_write_byte(fd, PIA_0A.direction_register); + fs_write_byte(fd, PIA_0A.output_register); + fs_write_byte(fd, PIA_0A.control_register); + /* PIA_0B */ + fs_write_byte(fd, PIA_0B.direction_register); + fs_write_byte(fd, PIA_0B.output_register); + fs_write_byte(fd, PIA_0B.control_register); + /* PIA_1A */ + fs_write_byte(fd, PIA_1A.direction_register); + fs_write_byte(fd, PIA_1A.output_register); + fs_write_byte(fd, PIA_1A.control_register); + /* PIA_1B */ + fs_write_byte(fd, PIA_1B.direction_register); + fs_write_byte(fd, PIA_1B.output_register); + fs_write_byte(fd, PIA_1B.control_register); + /* M6809 state */ + fs_write_byte(fd, ID_M6809_STATE); fs_write_word16(fd, 21); + m6809_get_state(&cpu_state); + fs_write_byte(fd, cpu_state.reg_cc); + fs_write_byte(fd, cpu_state.reg_a); + fs_write_byte(fd, cpu_state.reg_b); + fs_write_byte(fd, cpu_state.reg_dp); + fs_write_word16(fd, cpu_state.reg_x); + fs_write_word16(fd, cpu_state.reg_y); + fs_write_word16(fd, cpu_state.reg_u); + fs_write_word16(fd, cpu_state.reg_s); + fs_write_word16(fd, cpu_state.reg_pc); + fs_write_byte(fd, cpu_state.halt); + fs_write_byte(fd, cpu_state.nmi); + fs_write_byte(fd, cpu_state.firq); + fs_write_byte(fd, cpu_state.irq); + fs_write_byte(fd, cpu_state.wait_for_interrupt); + fs_write_byte(fd, cpu_state.skip_register_push); + fs_write_byte(fd, cpu_state.nmi_armed); + /* SAM */ + fs_write_byte(fd, ID_SAM_REGISTERS); fs_write_word16(fd, 2); + fs_write_word16(fd, sam_register); + /* Attached virtual disk filenames */ + { + struct vdisk *disk; + int drive; + for (drive = 0; drive < VDRIVE_MAX_DRIVES; drive++) { + disk = vdrive_disk_in_drive(drive); + if (disk != NULL && disk->filename != NULL) { + int length = strlen(disk->filename) + 1; + fs_write_byte(fd, ID_VDISK_FILE); + fs_write_word16(fd, 1 + length); + fs_write_byte(fd, drive); + fs_write(fd, disk->filename, length); + } + } + } + /* Finish up */ + fs_close(fd); + return 0; +} + +static int old_arch_mapping[4] = { + MACHINE_DRAGON32, + MACHINE_DRAGON64, + MACHINE_TANO, + MACHINE_COCOUS +}; + +int read_snapshot(const char *filename) +{ + int fd; + uint8_t buffer[17]; + uint8_t section, tmp8; + uint16_t tmp16; + unsigned int size; + M6809State cpu_state; + if (filename == NULL) + return -1; + if ((fd = fs_open(filename, FS_READ)) == -1) + return -1; + fs_read(fd, buffer, 17); + if (strncmp((char *)buffer, "XRoar snapshot.\012\000", 17)) { + /* Very old-style snapshot. Register dump always came first. + * Also, it used to be written out as only taking 12 bytes. */ + if (buffer[0] != ID_REGISTER_DUMP || buffer[1] != 0 + || (buffer[2] != 12 && buffer[2] != 14)) { + LOG_WARN("Snapshot format not recognised.\n"); + fs_close(fd); + return -1; + } + } + /* Default to Dragon 64 for old snapshots */ + int requested_machine = MACHINE_DRAGON64; + machine_clear_requested_config(); + requested_config.dos_type = DOS_NONE; + /* Need reset in case old snapshot doesn't trigger one */ + machine_reset(RESET_HARD); + /* If old snapshot, buffer contains register dump */ + if (buffer[0] != 'X') { + m6809_set_registers(buffer + 3); + } + while (fs_read_byte(fd, §ion) > 0) { + fs_read_word16(fd, &tmp16); + size = tmp16 ? tmp16 : 0x10000; + switch (section) { + case ID_ARCHITECTURE: + /* Deprecated: Machine architecture */ + if (size < 1) break; + fs_read_byte(fd, &tmp8); + tmp8 %= 4; + requested_machine = old_arch_mapping[tmp8]; + machine_reset(RESET_HARD); + size--; + break; + case ID_KEYBOARD_MAP: + /* Deprecated: Keyboard map */ + if (size < 1) break; + fs_read_byte(fd, &tmp8); + requested_config.keymap = tmp8; + machine_set_keymap(tmp8); + size--; + break; + case ID_REGISTER_DUMP: + /* Deprecated */ + if (size < 14) break; + size -= fs_read(fd, buffer, 14); + m6809_set_registers(buffer); + break; + case ID_M6809_STATE: + /* M6809 state */ + if (size < 21) break; + fs_read_byte(fd, &cpu_state.reg_cc); + fs_read_byte(fd, &cpu_state.reg_a); + fs_read_byte(fd, &cpu_state.reg_b); + fs_read_byte(fd, &cpu_state.reg_dp); + fs_read_word16(fd, &cpu_state.reg_x); + fs_read_word16(fd, &cpu_state.reg_y); + fs_read_word16(fd, &cpu_state.reg_u); + fs_read_word16(fd, &cpu_state.reg_s); + fs_read_word16(fd, &cpu_state.reg_pc); + fs_read_byte(fd, &cpu_state.halt); + fs_read_byte(fd, &cpu_state.nmi); + fs_read_byte(fd, &cpu_state.firq); + fs_read_byte(fd, &cpu_state.irq); + fs_read_byte(fd, &cpu_state.wait_for_interrupt); + fs_read_byte(fd, &cpu_state.skip_register_push); + fs_read_byte(fd, &cpu_state.nmi_armed); + cpu_state.firq &= 3; + cpu_state.irq &= 3; + m6809_set_state(&cpu_state); + size -= 21; + break; + case ID_MACHINECONFIG: + /* Machine running config */ + if (size < 7) break; + fs_read_byte(fd, &tmp8); + requested_machine = tmp8; + fs_read_byte(fd, &tmp8); + requested_config.architecture = tmp8; + fs_read_byte(fd, &tmp8); + requested_config.romset = tmp8; + fs_read_byte(fd, &tmp8); + requested_config.keymap = tmp8; + fs_read_byte(fd, &tmp8); + requested_config.tv_standard = tmp8; + fs_read_byte(fd, &tmp8); + requested_config.ram = tmp8; + fs_read_byte(fd, &tmp8); + requested_config.dos_type = tmp8; + machine_reset(RESET_HARD); + size -= 7; + break; + case ID_PIA_REGISTERS: + /* PIA_0A */ + if (size < 3) break; + fs_read_byte(fd, &tmp8); + PIA_SELECT_DDR(PIA_0A); + PIA_WRITE_P0DA(tmp8); /* DDR */ + fs_read_byte(fd, &tmp8); + PIA_SELECT_PDR(PIA_0A); + PIA_WRITE_P0DA(tmp8); /* OR */ + fs_read_byte(fd, &tmp8); + PIA_WRITE_P0CA(tmp8); /* CR */ + size -= 3; + /* PIA_0B */ + if (size < 3) break; + fs_read_byte(fd, &tmp8); + PIA_SELECT_DDR(PIA_0B); + PIA_WRITE_P0DB(tmp8); /* DDR */ + fs_read_byte(fd, &tmp8); + PIA_SELECT_PDR(PIA_0B); + PIA_WRITE_P0DB(tmp8); /* OR */ + fs_read_byte(fd, &tmp8); + PIA_WRITE_P0CB(tmp8); /* CR */ + size -= 3; + /* PIA_1A */ + if (size < 3) break; + fs_read_byte(fd, &tmp8); + PIA_SELECT_DDR(PIA_1A); + PIA_WRITE_P1DA(tmp8); /* DDR */ + fs_read_byte(fd, &tmp8); + PIA_SELECT_PDR(PIA_1A); + PIA_WRITE_P1DA(tmp8); /* OR */ + fs_read_byte(fd, &tmp8); + PIA_WRITE_P1CA(tmp8); /* CR */ + size -= 3; + /* PIA_1B */ + if (size < 3) break; + fs_read_byte(fd, &tmp8); + PIA_SELECT_DDR(PIA_1B); + PIA_WRITE_P1DB(tmp8); /* DDR */ + fs_read_byte(fd, &tmp8); + PIA_SELECT_PDR(PIA_1B); + PIA_WRITE_P1DB(tmp8); /* OR */ + fs_read_byte(fd, &tmp8); + PIA_WRITE_P1CB(tmp8); /* CR */ + size -= 3; + break; + case ID_RAM_PAGE0: + if (size <= sizeof(ram0)) { + size -= fs_read(fd, ram0, size); + } else { + size -= fs_read(fd, ram0, sizeof(ram0)); + } + break; + case ID_RAM_PAGE1: + if (size <= sizeof(ram1)) { + size -= fs_read(fd, ram1, size); + } else { + size -= fs_read(fd, ram1, sizeof(ram1)); + } + break; + case ID_SAM_REGISTERS: + /* SAM */ + if (size < 2) break; + fs_read_word16(fd, &tmp16); + size -= 2; + sam_register = tmp16; + sam_update_from_register(); + break; + case ID_SNAPVERSION: + /* Snapshot version - abort if snapshot + * contains stuff we don't understand */ + if (size < 3) break; + fs_read_byte(fd, &tmp8); + fs_read_word16(fd, &tmp16); + size -= 3; + if (tmp8 != SNAPSHOT_VERSION_MAJOR || tmp16 > SNAPSHOT_VERSION_MINOR) { + LOG_WARN("Snapshot version %d.%d not supported.\n", tmp8, tmp16); + fs_close(fd); + return -1; + } + break; + case ID_VDISK_FILE: + /* Attached virtual disk filenames */ + { + int drive; + fs_read_byte(fd, &tmp8); + size--; + drive = tmp8; + vdrive_eject_disk(drive); + if (size > 0) { + char *name = malloc(size); + if (name != NULL) { + size -= fs_read(fd, name, size); + vdrive_insert_disk(drive, vdisk_load(name)); + } + } + } + break; + default: + /* Unknown chunk */ + LOG_WARN("Unknown chunk in snaphot.\n"); + break; + } + if (size > 0) { + LOG_WARN("Skipping extra bytes in snapshot chunk.\n"); + for (; size; size--) + fs_read_byte(fd, &tmp8); + } + } + fs_close(fd); + + if (requested_machine != DRAGON.dragon_model) { + DRAGON.dragon_model = requested_machine; + } + return 0; +} diff --git a/src/core/snapshot.h b/src/core/snapshot.h new file mode 100644 index 0000000..8dee9c3 --- /dev/null +++ b/src/core/snapshot.h @@ -0,0 +1,12 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * See COPYING.GPL for redistribution conditions. */ + +#ifndef __SNAPSHOT_H__ +#define __SNAPSHOT_H__ + +int write_snapshot(const char *filename); +int read_snapshot(const char *filename); + +#endif /* __SNAPSHOT_H__ */ diff --git a/src/core/sound_gp32.h b/src/core/sound_gp32.h new file mode 100644 index 0000000..0c2b223 --- /dev/null +++ b/src/core/sound_gp32.h @@ -0,0 +1,11 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * See COPYING.GPL for redistribution conditions. */ + +#ifndef __SOUND_GP32_H__ +#define __SOUND_GP32_H__ + +void sound_silence(void); + +#endif /* __SOUND_GP32_H__ */ diff --git a/src/core/sound_null.c b/src/core/sound_null.c new file mode 100644 index 0000000..50e0d51 --- /dev/null +++ b/src/core/sound_null.c @@ -0,0 +1,128 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * 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 "config.h" +#if (0) +#include +#include +#include +#elif defined(HAVE_SDL) +#include +#endif + +#include "types.h" +#include "logging.h" +#include "events.h" +#include "machine.h" +#include "module.h" +#include "xroar.h" + +static int init(int argc, char **argv); +static void shutdown(void); +static void update(void); + +SoundModule sound_null_module = { + { "null", "No audio", + init, 0, shutdown, NULL }, + update +}; + +#define CYCLES_PER_MS (OSCILLATOR_RATE / 1000) + +static Cycle last_pause_cycle; +static unsigned int last_pause_ms; + +static void flush_frame(void *context); +static event_t *flush_event; + +static unsigned int current_time(void); +static void sleep_ms(unsigned int ms); + +static int init(int argc, char **argv) { + (void)argc; + (void)argv; + LOG_DEBUG(2,"Initialising null audio driver\n"); + last_pause_cycle = current_cycle; + last_pause_ms = current_time(); + flush_event = event_new(); + flush_event->dispatch = flush_frame; + flush_event->at_cycle = current_cycle + (10 * CYCLES_PER_MS); + event_queue(flush_event); + return 0; +} + +static void shutdown(void) { +} + +static void update(void) { +} + +static unsigned int current_time(void) { +#if (0) + struct timeval tp; + gettimeofday(&tp, NULL); + return (tp.tv_sec % 1000) * 1000 + (tp.tv_usec / 1000); +#elif defined(HAVE_SDL) + return SDL_GetTicks(); +#else + return 0; +#endif +} + +static void sleep_ms(unsigned int ms) { +#if (0) + struct timespec elapsed, tv; + elapsed.tv_sec = (ms) / 1000; + elapsed.tv_nsec = ((ms) % 1000) * 1000000; + do { + errno = 0; + tv.tv_sec = elapsed.tv_sec; + tv.tv_nsec = elapsed.tv_nsec; + } while (nanosleep(&tv, &elapsed) && errno == EINTR); +#elif defined(HAVE_SDL) + SDL_Delay(ms); +#else + (void)ms; +#endif +} + +static void flush_frame(void *context) { + Cycle elapsed_cycles = current_cycle - last_pause_cycle; + unsigned int expected_elapsed_ms = elapsed_cycles / CYCLES_PER_MS; + unsigned int actual_elapsed_ms, difference_ms; + (void)context; + actual_elapsed_ms = current_time() - last_pause_ms; + difference_ms = expected_elapsed_ms - actual_elapsed_ms; + if (difference_ms >= 10) { + if (noratelimit || difference_ms > 1000) { + last_pause_ms = current_time(); + last_pause_cycle = current_cycle; + } else { + sleep_ms(difference_ms); + difference_ms = current_time() - last_pause_ms; + last_pause_ms += difference_ms; + last_pause_cycle += difference_ms * CYCLES_PER_MS; + } + } + flush_event->at_cycle = current_cycle + (10 * CYCLES_PER_MS); + event_queue(flush_event); +} diff --git a/src/core/sound_oss.c b/src/core/sound_oss.c new file mode 100644 index 0000000..36fe686 --- /dev/null +++ b/src/core/sound_oss.c @@ -0,0 +1,209 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * 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 "types.h" +#include "events.h" +#include "logging.h" +#include "machine.h" +#include "module.h" +#include "pia.h" +#include "xroar.h" + +static int init(int argc, char **argv); +static void shutdown(void); +static void update(void); + +SoundModule sound_oss_module = { + { "oss", "OSS audio", + init, 0, shutdown, NULL }, + update +}; + +typedef int8_t Sample; /* 8-bit mono */ + +#define FRAGMENTS 2 +#define FRAME_SIZE 512 +#define SAMPLE_CYCLES ((int)(OSCILLATOR_RATE / sample_rate)) +#define FRAME_CYCLES (SAMPLE_CYCLES * FRAME_SIZE) + +static unsigned int sample_rate; +static int sound_fd; +static int channels; +static int format; +unsigned int bytes_per_sample; + +static Cycle frame_cycle_base; +static Sample *buffer; +static Sample *wrptr; +static Sample lastsample; +static int8_t *convbuf; + +static void flush_frame(void *context); +static event_t *flush_event; + +static int init(int argc, char **argv) { + const char *device = "/dev/dsp"; + int fragment_param, tmp; + + (void)argc; + (void)argv; + LOG_DEBUG(2,"Initialising OSS audio driver\n"); + sound_fd = open(device, O_WRONLY); + if (sound_fd == -1) + goto failed; + /* The order these ioctls are tried (format, channels, sample rate) + * is important: OSS docs say setting format can affect channels and + * sample rate, and setting channels can affect sample rate. */ + /* Set audio format. Only support AFMT_S8 (signed 8-bit) and + * AFMT_S16_NE (signed 16-bit, host-endian) */ + if (ioctl(sound_fd, SNDCTL_DSP_GETFMTS, &format) == -1) + goto failed; + if ((format & (AFMT_S8 | AFMT_S16_NE)) == 0) { + LOG_ERROR("No desired audio formats supported by device\n"); + return 1; + } + if (format & AFMT_S8) { + format = AFMT_S8; + bytes_per_sample = 1; + } else { + format = AFMT_S16_NE; + bytes_per_sample = 2; + } + if (ioctl(sound_fd, SNDCTL_DSP_SETFMT, &format) == -1) + goto failed; + /* Set device to mono if possible */ + channels = 0; + if (ioctl(sound_fd, SNDCTL_DSP_STEREO, &channels) == -1) + goto failed; + channels++; + /* Attempt to set sample_rate to 44.1kHz, but live with whatever + * we get */ + sample_rate = 44100; + if (ioctl(sound_fd, SNDCTL_DSP_SPEED, &sample_rate) == -1) + goto failed; + /* Set number of fragments low */ + fragment_param = 0; + tmp = FRAME_SIZE * bytes_per_sample * channels; + while (tmp > 1) { + tmp >>= 1; + fragment_param++; + } + fragment_param |= (FRAGMENTS << 16); + tmp = fragment_param; + if (ioctl(sound_fd, SNDCTL_DSP_SETFRAGMENT, &fragment_param) == -1) + goto failed; + LOG_DEBUG(2, "\t%d channel%s, %dHz, %d bytes per sample\n", channels, channels > 1 ? "s" : "", sample_rate, bytes_per_sample); + if (tmp != fragment_param) + LOG_WARN("Couldn't set desired buffer parameters: sync to audio might not be ideal\n"); + buffer = malloc(FRAME_SIZE * sizeof(Sample)); + convbuf = malloc(FRAME_SIZE * bytes_per_sample * channels); + flush_event = event_new(); + flush_event->dispatch = flush_frame; + + ioctl(sound_fd, SNDCTL_DSP_RESET, 0); + memset(buffer, 0, FRAME_SIZE * sizeof(Sample)); + wrptr = buffer; + frame_cycle_base = current_cycle; + flush_event->at_cycle = frame_cycle_base + FRAME_CYCLES; + event_queue(flush_event); + lastsample = 0; + return 0; +failed: + LOG_ERROR("Failed to initialiase OSS audio driver\n"); + return 1; +} + +static void shutdown(void) { + LOG_DEBUG(2,"Shutting down OSS audio driver\n"); + event_free(flush_event); + ioctl(sound_fd, SNDCTL_DSP_RESET, 0); + close(sound_fd); + free(buffer); +} + +static void update(void) { + Cycle elapsed_cycles = current_cycle - frame_cycle_base; + Sample *fill_to; + if (elapsed_cycles >= FRAME_CYCLES) { + fill_to = buffer + FRAME_SIZE; + } else { + fill_to = buffer + (elapsed_cycles/(Cycle)SAMPLE_CYCLES); + } + while (wrptr < fill_to) + *(wrptr++) = lastsample; + if (!(PIA_1B.control_register & 0x08)) { + /* Single-bit sound */ + lastsample = (PIA_1B.port_output & 0x02) ? 0x7c : 0; + } else { + if (PIA_0B.control_register & 0x08) { + /* Sound disabled */ + lastsample = 0; + } else { + /* DAC output */ + lastsample = (PIA_1A.port_output & 0xfc) >> 1; + } + } +} + +static void flush_frame(void *context) { + int8_t *source = buffer; + Sample *fill_to = buffer + FRAME_SIZE; + (void)context; + while (wrptr < fill_to) + *(wrptr++) = lastsample; + frame_cycle_base += FRAME_CYCLES; + flush_event->at_cycle = frame_cycle_base + FRAME_CYCLES; + event_queue(flush_event); + wrptr = buffer; + if (noratelimit) + return; + /* Convert buffer and write to device */ + if (format == AFMT_S8) { + int8_t *dest = convbuf; + int8_t tmp; + int i, j; + for (i = FRAME_SIZE; i; i--) { + tmp = *(source++); + for (j = 0; j < channels; j++) + *(dest++) = tmp; + } + write(sound_fd, convbuf, FRAME_SIZE * channels); + return; + } + if (format == AFMT_S16_NE) { + int16_t *dest = (int16_t *)convbuf; + int16_t tmp; + int i, j; + for (i = FRAME_SIZE; i; i--) { + tmp = *(source++) << 8; + for (j = 0; j < channels; j++) + *(dest++) = tmp; + } + write(sound_fd, convbuf, FRAME_SIZE * channels * 2); + return; + } +} diff --git a/src/core/sound_sdl.c b/src/core/sound_sdl.c new file mode 100644 index 0000000..6037d0a --- /dev/null +++ b/src/core/sound_sdl.c @@ -0,0 +1,187 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * 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 "types.h" +#include "events.h" +#include "logging.h" +#include "machine.h" +#include "module.h" +#include "pia.h" +#include "xroar.h" +#include "global.h" + +static int init(int argc, char **argv); +static void shutdown(void); +static void update(void); + +SoundModule sound_sdl_module = { + { "sdl", "SDL ring-buffer audio", + init, 0, shutdown, NULL }, + update +}; + +typedef Sint8 Sample; /* 8-bit mono (SDL type) */ + +#ifdef WINDOWS32 +# define SAMPLE_RATE 22050 +#else +# define SAMPLE_RATE 44100 +#endif +/* The lower the FRAME_SIZE, the better. Windows32 seems to have problems + * with very small frame sizes though. */ +#ifdef WINDOWS32 +# define FRAME_SIZE 1024 +#else +# define FRAME_SIZE 512 +#endif +#define SAMPLE_CYCLES ((int)(OSCILLATOR_RATE / SAMPLE_RATE)) +#define FRAME_CYCLES (SAMPLE_CYCLES * FRAME_SIZE) + +static SDL_AudioSpec audiospec; +static Cycle frame_cycle_base; +static Sample *buffer; +static Sample *wrptr; +static Sample lastsample; +//static SDL_mutex *halt_mutex; +//static SDL_cond *halt_cv; +//static int haltflag; + +static void flush_frame(void *context); +static event_t *flush_event; + +static void callback(void *userdata, Uint8 *stream, int len); + +static int init(int argc, char **argv) { + (void)argc; + (void)argv; + LOG_DEBUG(2,"Initialising SDL audio driver\n"); +#ifdef WINDOWS32 + //if (!getenv("SDL_AUDIODRIVER")) + //putenv("SDL_AUDIODRIVER=windib"); + //if (!getenv("SDL_MIXERDRIVER")) + //putenv("SDL_MIXERDRIVER=windib"); +#endif + if (!SDL_WasInit(SDL_INIT_NOPARACHUTE)) { + if (SDL_Init(SDL_INIT_NOPARACHUTE) < 0) { + LOG_ERROR("Failed to initialiase SDL\n"); + return 1; + } + } + if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) { + LOG_ERROR("Failed to initialiase SDL audio driver\n"); + return 1; + } + audiospec.freq = SAMPLE_RATE; + audiospec.format = AUDIO_S8; + audiospec.samples = FRAME_SIZE; + audiospec.channels = 1; + audiospec.callback = callback; + audiospec.userdata = NULL; + if (SDL_OpenAudio(&audiospec, NULL) < 0) { + LOG_ERROR("Couldn't open audio: %s\n", SDL_GetError()); + SDL_QuitSubSystem(SDL_INIT_AUDIO); + return 1; + } + buffer = (Sample *)malloc(FRAME_SIZE * sizeof(Sample)); + //halt_mutex = SDL_CreateMutex(); + //halt_cv = SDL_CreateCond(); + flush_event = event_new(); + flush_event->dispatch = flush_frame; + + memset(buffer, 0, FRAME_SIZE * sizeof(Sample)); + SDL_PauseAudio(0); + wrptr = buffer; + frame_cycle_base = current_cycle; + flush_event->at_cycle = frame_cycle_base + FRAME_CYCLES; + event_queue(flush_event); + lastsample = 0; + return 0; +} + +static void shutdown(void) { + LOG_DEBUG(2,"Shutting down SDL audio driver\n"); + event_free(flush_event); + //SDL_DestroyCond(halt_cv); + //SDL_DestroyMutex(halt_mutex); + SDL_CloseAudio(); + SDL_QuitSubSystem(SDL_INIT_AUDIO); + free(buffer); +} + +static void update(void) { + Cycle elapsed_cycles = current_cycle - frame_cycle_base; + Sample *fill_to; + if (elapsed_cycles >= FRAME_CYCLES) { + fill_to = buffer + FRAME_SIZE; + } else { + fill_to = buffer + (elapsed_cycles/(Cycle)SAMPLE_CYCLES); + } + while (wrptr < fill_to) + *(wrptr++) = lastsample; + if (!(PIA_1B.control_register & 0x08)) { + /* Single-bit sound */ + lastsample = (PIA_1B.port_output & 0x02) ? 0x3f : 0; + } else { + if (PIA_0B.control_register & 0x08) { + /* Sound disabled */ + lastsample = 0; + } else { + /* DAC output */ + lastsample = (PIA_1A.port_output & 0xfc) >> 1; + } + } +} + +static void flush_frame(void *context) { + (void)context; + Sample *fill_to = buffer + FRAME_SIZE; + while (wrptr < fill_to) + *(wrptr++) = lastsample; + frame_cycle_base += FRAME_CYCLES; + flush_event->at_cycle = frame_cycle_base + FRAME_CYCLES; + event_queue(flush_event); + wrptr = buffer; + if (!noratelimit) { + //SDL_LockMutex(halt_mutex); + //haltflag = 1; + //while (haltflag) + //SDL_CondWait(halt_cv, halt_mutex); + //SDL_UnlockMutex(halt_mutex); + } +} + +static void callback(void *userdata, Uint8 *stream, int len) { + (void)userdata; /* unused */ + if (! DRAGON.dragon_snd_enable) { + memset(stream, 0, len); + } else + if (len == FRAME_SIZE) { + long volume = (SDL_MIX_MAXVOLUME * gp2xGetSoundVolume()) / 100; + SDL_MixAudio(stream, (unsigned char *)buffer, len, volume); + } + //SDL_LockMutex(halt_mutex); + //haltflag = 0; + //SDL_CondSignal(halt_cv); + //SDL_UnlockMutex(halt_mutex); +} diff --git a/src/core/tape.c b/src/core/tape.c new file mode 100644 index 0000000..c63b68b --- /dev/null +++ b/src/core/tape.c @@ -0,0 +1,427 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * 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 "config.h" + +#include +#ifdef HAVE_SNDFILE +#include +#include +#endif + +#include "types.h" +#include "events.h" +#include "fs.h" +#include "logging.h" +#include "machine.h" +#include "pia.h" +#include "tape.h" +#include "xroar.h" + +static int read_fd, write_fd; +static unsigned int motor; + +static void waggle_bit(void *context); +static event_t *waggle_event; + +/* For reading */ +static int input_type; +static int fake_leader; +static int bytes_remaining = 0; +static int bits_remaining = 0; +static uint8_t read_buf[512]; +static uint8_t *read_buf_ptr; +/* WAV input support */ +#ifdef HAVE_SNDFILE +static SNDFILE *wav_read_file; +static int wav_samples_remaining; +#define SF_BUF_LENGTH (512) +static short *wav_read_buf = NULL; +static short *wav_read_ptr; +static Cycle wav_last_sample_cycle; +static int wav_sample_rate; +static int wav_channels; +#define SAMPLE_CYCLES ((Cycle)(OSCILLATOR_RATE / wav_sample_rate)) +#endif + +/* For writing */ +static int have_bytes = 0; +static int have_bits = 0; +static uint8_t write_buf[512]; +static uint8_t *write_buf_ptr; +static unsigned int last_sample; +static Cycle last_read_time; + +static int bit_in(void); +static int byte_in(void); +static void buffer_in(void); + +static void bit_out(int); +static void byte_out(int); +static void buffer_out(void); + +#ifdef HAVE_SNDFILE +static void wav_buffer_in(void); +static short wav_sample_in(void); +#endif + +void tape_init(void) { +#ifdef HAVE_SNDFILE + wav_read_file = NULL; +#endif + read_fd = write_fd = -1; + read_buf_ptr = read_buf; + bits_remaining = bytes_remaining = 0; + fake_leader = 0; + waggle_event = event_new(); + waggle_event->dispatch = waggle_bit; + write_buf_ptr = write_buf; + have_bits = have_bytes = 0; +} + +void tape_reset(void) { + tape_close_reading(); + tape_close_writing(); + motor = 0; + if (waggle_event) + event_dequeue(waggle_event); +} + +void tape_shutdown(void) { + tape_reset(); +} + +int tape_open_reading(char *filename) { +#ifdef HAVE_SNDFILE + SF_INFO info; +#endif + tape_close_reading(); + input_type = xroar_filetype_by_ext(filename); + switch (input_type) { + case FILETYPE_CAS: + if ((read_fd = fs_open(filename, FS_READ)) == -1) + return -1; + bits_remaining = bytes_remaining = 0; + /* If motor is on, enable the bit waggler */ + if (motor) { + waggle_event->at_cycle = current_cycle + (OSCILLATOR_RATE / 2); + event_queue(waggle_event); + } + LOG_DEBUG(2,"Attached virtual cassette %s\n", filename); + break; +#ifdef HAVE_SNDFILE + default: + info.format = 0; + wav_read_file = sf_open(filename, SFM_READ, &info); + wav_channels = info.channels; + wav_sample_rate = info.samplerate; + if (wav_read_buf != NULL) { + free(wav_read_buf); + wav_read_buf = NULL; + } + if (wav_sample_rate == 0) { + tape_close_reading(); + return -1; + } + wav_read_buf = malloc(SF_BUF_LENGTH * sizeof(short) * wav_channels); + wav_samples_remaining = 0; + LOG_DEBUG(2,"Attached audio file %s\n\t%dHz, %d channel%s\n", filename, wav_sample_rate, wav_channels, (wav_channels==1)?"":"s"); + break; +#endif + } + return 0; +} + +void tape_close_reading(void) { + if (read_fd != -1) + fs_close(read_fd); +#ifdef HAVE_SNDFILE + if (wav_read_file) { + sf_close(wav_read_file); + wav_read_file = NULL; + } +#endif + read_fd = -1; +} + +int tape_open_writing(char *filename) { + tape_close_writing(); + if ((write_fd = fs_open(filename, FS_WRITE)) == -1) + return -1; + have_bits = have_bytes = 0; + return 0; +} + +void tape_close_writing(void) { + while (have_bits) + bit_out(0); + if (have_bytes) + buffer_out(); + if (write_fd != -1) + fs_close(write_fd); + write_fd = -1; +} + +/* Close any currently-open tape file, open a new one and read the first + * bufferful of data. Tries to guess the filetype. Returns -1 on error, + * 0 for a BASIC program, 1 for data and 2 for M/C. */ +int tape_autorun(char *filename) { + int state, type, count = 0; + if (filename == NULL) + return -1; + if (tape_open_reading(filename) == -1) + return -1; + /* Little state machine to try and determine type of first + * file on tape */ + state = 0; + type = 1; /* default to data - no autorun (if trying to) */ + //buffer_in(); + while ((bytes_remaining > 0 || read_fd != -1) && state >= 0) { + uint8_t b = byte_in(); + switch(state) { + case 0: + if (b != 0x55) state = -1; + if (b == 0x3c) state = 1; + break; + case 1: + state = 2; + count = 10; + if (b != 0x00) state = -1; + break; + case 2: + count--; + if (count == 0) { + state = -1; + type = b; + } + break; + default: + break; + } + //bytes_remaining--; + //if (bytes_remaining < 1) + //buffer_in(); + } + tape_close_reading(); + if (tape_open_reading(filename) == -1) + return -1; + switch (type) { + /* BASIC programs don't autorun yet */ + case 0: keyboard_queue_string("CLOAD\r"); + break; + case 2: keyboard_queue_string("CLOADM:EXEC\r"); + break; + default: + break; + } + return type; +} + +/* Called whenever PIA_1A control register is written to. + * Detects changes in motor status. */ +void tape_update_motor(void) { + if ((PIA_1A.control_register & 0x08) != motor) { + motor = PIA_1A.control_register & 0x08; + if (motor) { + switch (input_type) { + case FILETYPE_CAS: + if (bytes_remaining > 0 || read_fd != -1) { + /* If motor turned on and tape file + * attached, enable the tape input bit + * waggler */ + fake_leader = 64; + waggle_event->at_cycle = current_cycle + (OSCILLATOR_RATE / 2); + event_queue(waggle_event); + } + break; + default: +#ifdef HAVE_SNDFILE + wav_last_sample_cycle = current_cycle; +#endif + break; + } + } else { + event_dequeue(waggle_event); + } + } +} + +/* Called whenever PIA_1A data register is written to. + * Detects change frequency for writing out bits. */ +void tape_update_output(void) { + unsigned int new_sample; + if (!motor) + return; + new_sample = PIA_1A.port_output & 0xfc; + if (!last_sample && new_sample > 0xf4) { + last_sample = 1; + last_read_time = current_cycle; + return; + } + if (last_sample && new_sample < 0x04) { + int delta = current_cycle - last_read_time; + last_sample = 0; + if (delta < (OSCILLATOR_RATE/3600)) { + bit_out(1); + } else { + bit_out(0); + } + } +} + +#ifndef HAVE_GP32 +void tape_update_input(void) { +#ifdef HAVE_SNDFILE + short sample; + if (!motor || input_type == FILETYPE_CAS) + return; + sample = wav_sample_in(); + if (sample > 0) + PIA_1A.port_input |= 0x01; + else + PIA_1A.port_input &= ~0x01; +#endif +} +#endif + +static int bit_in(void) { + static int cur_byte; + int ret; + if (bits_remaining == 0) { + if ((cur_byte = byte_in()) == -1) + return -1; + bits_remaining = 8; + } + ret = cur_byte & 1; + cur_byte >>= 1; + bits_remaining--; + return ret; +} + +static int byte_in(void) { + if (fake_leader) { + fake_leader--; + return 0x55; + } + if (bytes_remaining == 0) { + buffer_in(); + if (bytes_remaining <= 0) + return -1; + } + bytes_remaining--; + return *(read_buf_ptr++); +} + +static void buffer_in(void) { + read_buf_ptr = read_buf; + bytes_remaining = 0; + if (read_fd != -1) { + bytes_remaining = fs_read(read_fd, read_buf, sizeof(read_buf)); + if (bytes_remaining < (int)sizeof(read_buf)) + tape_close_reading(); + } +} + +static void waggle_bit(void *context) { + static int cur_bit = 0; + static int waggle_state = 0; + (void)context; + switch (waggle_state) { + default: + case 0: + if (!motor || (cur_bit = bit_in()) == -1) { + event_dequeue(waggle_event); + return; + } + PIA_1A.port_input |= 0x01; + waggle_state = 1; + break; + case 1: + PIA_1A.port_input &= 0xfe; + waggle_state = 0; + break; + } + /* Single cycles of 1200 baud for 0s, and 2400 baud for 1s */ + if (cur_bit == 0) + waggle_event->at_cycle += (OSCILLATOR_RATE / 2400); + else + waggle_event->at_cycle += (OSCILLATOR_RATE / 4800); + event_queue(waggle_event); +} + +static void bit_out(int value) { + static int cur_byte = 0; + cur_byte >>= 1; + if (value) + cur_byte |= 0x80; + have_bits++; + if (have_bits < 8) + return; + byte_out(cur_byte); + cur_byte = 0; + have_bits = 0; +} + +static void byte_out(int value) { + *(write_buf_ptr++) = value; + have_bytes++; + if (have_bytes < (int)sizeof(write_buf)) + return; + buffer_out(); +} + +static void buffer_out(void) { + if (write_fd != -1) { + fs_write(write_fd, write_buf, have_bytes); + } + have_bytes = 0; + write_buf_ptr = write_buf; +} + +#ifdef HAVE_SNDFILE +static void wav_buffer_in(void) { + wav_read_ptr = wav_read_buf; + wav_samples_remaining = 0; + if (wav_read_file == NULL || wav_read_buf == NULL) + return; + wav_samples_remaining = sf_read_short(wav_read_file, wav_read_buf, SF_BUF_LENGTH); + if (wav_samples_remaining < SF_BUF_LENGTH) + tape_close_reading(); +} + +static short wav_sample_in(void) { + Cycle elapsed_cycles; + if (wav_sample_rate == 0) + return 0; + elapsed_cycles = current_cycle - wav_last_sample_cycle; + while (elapsed_cycles >= SAMPLE_CYCLES) { + wav_read_ptr++; + if (wav_samples_remaining == 0) { + wav_buffer_in(); + if (wav_samples_remaining <= 0) + return 0; + } + wav_last_sample_cycle += SAMPLE_CYCLES; + wav_samples_remaining--; + elapsed_cycles -= SAMPLE_CYCLES; + } + if (wav_read_ptr == NULL) + return 0; + return *wav_read_ptr; +} +#endif diff --git a/src/core/tape.h b/src/core/tape.h new file mode 100644 index 0000000..2eaec30 --- /dev/null +++ b/src/core/tape.h @@ -0,0 +1,26 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * See COPYING.GPL for redistribution conditions. */ + +#ifndef __TAPE_H__ +#define __TAPE_H__ + +#include "types.h" + +void tape_init(void); +void tape_reset(void); +void tape_shutdown(void); + +int tape_open_reading(char *filename); +void tape_close_reading(void); +int tape_open_writing(char *filename); +void tape_close_writing(void); + +int tape_autorun(char *filename); + +void tape_update_motor(void); +void tape_update_output(void); +void tape_update_input(void); + +#endif /* __TAPE_H__ */ diff --git a/src/core/types.h b/src/core/types.h new file mode 100644 index 0000000..f467121 --- /dev/null +++ b/src/core/types.h @@ -0,0 +1,72 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * See COPYING.GPL for redistribution conditions. */ + +#ifndef __TYPES_H__ +#define __TYPES_H__ + +#include "config.h" + +#ifdef HAVE_GP32 /* GP32 types & macros */ + +#include "gp32/types.h" /* base types */ + +/* FCLK = 100/50/50MHz */ +/* +#define SPEED_FAST GpClockSpeedChange(100000000, 0x2a011, 2) +#define SPEED_SLOW GpClockSpeedChange(50000000, 0x2a012, 0) +#define PCLK 50000000 +*/ + +/* FCLK = 90/45/45MHz */ + +#define SPEED_FAST GpClockSpeedChange(90000000, 0x25011, 2) +#define SPEED_SLOW GpClockSpeedChange(45000000, 0x25012, 0) +#define PCLK 45000000 + + +/* FCLK = 80/40/40MHz */ +/* +#define SPEED_FAST GpClockSpeedChange(80000000, 0x48012, 2) +#define SPEED_SLOW GpClockSpeedChange(40000000, 0x48013, 0) +#define PCLK 40000000 +*/ + +/* FCLK = 72/36/36MHz */ +/* +#define SPEED_FAST GpClockSpeedChange(72000000, 0x28002, 2) +#define SPEED_SLOW GpClockSpeedChange(36000000, 0x28003, 0) +#define PCLK 36000000 +*/ + +/* FCLK = 66/33/33MHz */ +/* +#define SPEED_FAST GpClockSpeedChange(66000000, 0x24002, 2) +#define SPEED_SLOW GpClockSpeedChange(33000000, 0x24003, 0) +#define PCLK 33000000 +*/ + +/* These are in the SDK but not declared: */ +void ARMEnableInterrupt(void); +void ARMDisableInterrupt(void); +void swi_install_irq(int irqnum, void *service_routine); +void swi_uninstall_irq(int irqnum, void *service_routine); +void swi_mmu_change(void *mempos_start, void *mempos_end, int mode); + +/* For use with swi_mmu_change: */ +#define MMU_NCNB (0xff2) /* Noncached, nonbuffered */ +#define MMU_NCB (0xff6) /* Noncached, buffered */ +#define MMU_WT (0xffa) /* Cached write-through mode */ +#define MMU_WB (0xffe) /* Cached write-back mode */ + +#else /* Unix types & macros */ + +#include +#include + +typedef int32_t Cycle; + +#endif /* Tests for architecture */ + +#endif /* __TYPES_H__ */ diff --git a/src/core/ui_gp32.h b/src/core/ui_gp32.h new file mode 100644 index 0000000..c9218c7 --- /dev/null +++ b/src/core/ui_gp32.h @@ -0,0 +1,11 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * See COPYING.GPL for redistribution conditions. */ + +#ifndef __UI_GP32_H__ +#define __UI_GP32_H__ + +void gp32_main_loop(void); + +#endif /* __UI_GP32_H__ */ diff --git a/src/core/ui_sdl.c b/src/core/ui_sdl.c new file mode 100644 index 0000000..0b570a2 --- /dev/null +++ b/src/core/ui_sdl.c @@ -0,0 +1,78 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * 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 "logging.h" +#include "module.h" + +static int init(int argc, char **argv); +static void shutdown(void); + +extern VideoModule video_sdl_module; +static VideoModule *sdl_video_module_list[] = { + &video_sdl_module, + NULL +}; + +#ifdef PSP //LUDO: +extern KeyboardModule keyboard_psp_module; +#else +extern KeyboardModule keyboard_sdl_module; +#endif +static KeyboardModule *sdl_keyboard_module_list[] = { +# ifdef PSP //LUDO: + &keyboard_psp_module, +# else + &keyboard_sdl_module, +# endif + NULL +}; + +/* Note: SDL sound and joystick modules not listed here as they can be used + * outside of the usual SDL UI */ + +UIModule ui_sdl_module = { + { "sdl", "SDL user-interface", + init, 0, shutdown, NULL }, + NULL, /* use default filereq module list */ + sdl_video_module_list, + NULL, /* use default sound module list */ + sdl_keyboard_module_list, + NULL /* use default joystick module list */ +}; + +int sdl_video_want_fullscreen; + +static int init(int argc, char **argv) { + int i; + sdl_video_want_fullscreen = 0; + for (i = 1; i < argc; i++) { + if (strcmp(argv[i], "-fs") == 0) { + sdl_video_want_fullscreen = 1; + } + } + return 0; +} + +static void shutdown(void) { +} diff --git a/src/core/ui_sdl.h b/src/core/ui_sdl.h new file mode 100644 index 0000000..342d6d6 --- /dev/null +++ b/src/core/ui_sdl.h @@ -0,0 +1,11 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * See COPYING.GPL for redistribution conditions. */ + +#ifndef __UI_SDL_H__ +#define __UI_SDL_H__ + +extern int sdl_video_want_fullscreen; + +#endif /* __UI_SDL_H__ */ diff --git a/src/core/vdg.c b/src/core/vdg.c new file mode 100644 index 0000000..add0842 --- /dev/null +++ b/src/core/vdg.c @@ -0,0 +1,220 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * 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 "events.h" +#include "logging.h" +#include "m6809.h" +#include "machine.h" +#include "pia.h" +#include "sam.h" +#include "vdg.h" +#include "xroar.h" + +#include "global.h" + +#if defined(HAVE_GP32) || defined(HAVE_NDS) +# define FAST_VDG +#endif + +Cycle scanline_start; +int beam_pos; + +static void (*vdg_render_scanline)(void); +static int_least16_t scanline; +#ifndef FAST_VDG +static int inhibit_mode_change; +#endif +static int frame; + +static event_t *hs_fall_event, *hs_rise_event; +static event_t *fs_fall_event, *fs_rise_event; +static void do_hs_fall(void *context); +static void do_hs_rise(void *context); +static void do_fs_fall(void *context); +static void do_fs_rise(void *context); + +#define SCANLINE(s) ((s) % VDG_FRAME_DURATION) + +void vdg_init(void) { + vdg_render_scanline = video_module->vdg_render_sg4; + hs_fall_event = event_new(); + hs_fall_event->dispatch = do_hs_fall; + hs_rise_event = event_new(); + hs_rise_event->dispatch = do_hs_rise; + fs_fall_event = event_new(); + fs_fall_event->dispatch = do_fs_fall; + fs_rise_event = event_new(); + fs_rise_event->dispatch = do_fs_rise; +} + +void vdg_reset(void) { + video_module->vdg_vsync(); + scanline = 0; + scanline_start = current_cycle; + hs_fall_event->at_cycle = current_cycle + VDG_LINE_DURATION; + event_queue(hs_fall_event); + vdg_set_mode(); + beam_pos = 0; +#ifndef FAST_VDG + inhibit_mode_change = 0; +#endif + frameskip = requested_frameskip; + frame = 0; +} + +static void do_hs_fall(void *context) { + (void)context; + /* Finish rendering previous scanline */ +#ifdef FAST_VDG + /* Skip borders, etc. */ + if (frame == 0 && scanline >= VDG_ACTIVE_AREA_START + && scanline < VDG_ACTIVE_AREA_END +//#ifdef HAVE_GP32 + /* GP32 renders 4 scanlines at once */ + && (scanline & 3) == ((VDG_ACTIVE_AREA_START+3)&3) +//#endif + ) { + vdg_render_scanline(); + } +#else + /* Normal code */ + if (frame == 0 && scanline >= (VDG_TOP_BORDER_START + 1)) { + if (scanline < VDG_ACTIVE_AREA_START) { + video_module->render_border(); + } else if (scanline < VDG_ACTIVE_AREA_END) { + vdg_render_scanline(); + } else if (scanline < (VDG_BOTTOM_BORDER_END - 2)) { + video_module->render_border(); + } + } +#endif + /* Next scanline */ + scanline = (scanline + 1) % VDG_FRAME_DURATION; + scanline_start = hs_fall_event->at_cycle; + beam_pos = 0; + PIA_RESET_P0CA1; +#ifdef FAST_VDG + /* Faster, less accurate timing for GP32/NDS */ + PIA_SET_P0CA1; +#else + /* Everything else schedule HS rise for later */ + hs_rise_event->at_cycle = scanline_start + VDG_HS_RISING_EDGE; + event_queue(hs_rise_event); +#endif + hs_fall_event->at_cycle = scanline_start + VDG_LINE_DURATION; + /* Frame sync */ + if (scanline == SCANLINE(VDG_VBLANK_START)) { + sam_vdg_fsync(); + frame--; + if (frame < 0) + frame = frameskip; + if (frame == 0) + video_module->vdg_vsync(); + } +#ifndef FAST_VDG + /* Enable mode changes at beginning of active area */ + if (scanline == SCANLINE(VDG_ACTIVE_AREA_START)) { + inhibit_mode_change = 0; + vdg_set_mode(); + } +#endif + /* FS falling edge at end of this scanline */ + if (scanline == SCANLINE(VDG_ACTIVE_AREA_END - 1)) { + fs_fall_event->at_cycle = scanline_start + VDG_LINE_DURATION + 16; + event_queue(fs_fall_event); + } +#ifndef FAST_VDG + /* Disable mode changes after end of active area */ + if (scanline == SCANLINE(VDG_ACTIVE_AREA_END)) { + inhibit_mode_change = 1; + } +#endif + /* PAL delay 24 lines after FS falling edge */ + if (IS_PAL && (scanline == SCANLINE(VDG_ACTIVE_AREA_END + 23))) { + hs_fall_event->at_cycle += 25 * VDG_PAL_PADDING_LINE; + } + /* FS rising edge at end of this scanline */ + if (scanline == SCANLINE(VDG_ACTIVE_AREA_END + 31)) { + /* Fig. 8, VDG data sheet: tWFS = 32 * (227.5 * 1/f) */ + fs_rise_event->at_cycle = scanline_start + VDG_LINE_DURATION + 16; + event_queue(fs_rise_event); + /* PAL delay after FS rising edge */ + if (IS_PAL) { + hs_fall_event->at_cycle += 25 * VDG_PAL_PADDING_LINE; + } + } + event_queue(hs_fall_event); +} + +static void do_hs_rise(void *context) { + (void)context; + PIA_SET_P0CA1; +} + +static void do_fs_fall(void *context) { + (void)context; + PIA_RESET_P0CB1; +} + +static void do_fs_rise(void *context) { + (void)context; + PIA_SET_P0CB1; +} + +void vdg_set_mode(void) { + unsigned int mode; +#ifndef FAST_VDG + /* No need to inhibit mode changes during borders on GP32/NDS, as + * they're not rendered anyway. */ + if (inhibit_mode_change) + return; + /* Render scanline so far before changing modes (disabled for speed + * on GP32/NDS). */ + if (frame == 0 && scanline >= VDG_ACTIVE_AREA_START && scanline < VDG_ACTIVE_AREA_END) { + vdg_render_scanline(); + } +#endif + mode = PIA_1B.port_output; + /* Update video module */ + video_module->vdg_set_mode(mode); + switch ((mode & 0xf0) >> 4) { + case 0: case 2: case 4: case 6: + vdg_render_scanline = video_module->vdg_render_sg4; + break; + case 1: case 3: case 5: case 7: + vdg_render_scanline = video_module->vdg_render_sg6; + break; + case 8: + vdg_render_scanline = video_module->vdg_render_cg1; + break; + case 9: case 11: case 13: + vdg_render_scanline = video_module->vdg_render_rg1; + break; + case 10: case 12: case 14: + vdg_render_scanline = video_module->vdg_render_cg2; + break; + case 15: default: + if (DRAGON.video_artifact_mode) { + vdg_render_scanline = video_module->vdg_render_cg2; + } else { + vdg_render_scanline = video_module->vdg_render_rg6; + } + break; + } +} diff --git a/src/core/vdg.h b/src/core/vdg.h new file mode 100644 index 0000000..b85b4de --- /dev/null +++ b/src/core/vdg.h @@ -0,0 +1,48 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * See COPYING.GPL for redistribution conditions. */ + +#ifndef __VDG_H__ +#define __VDG_H__ + +#include "types.h" + +#define VDG_CYCLES(c) ((c) * 4) + +#define VDG_tFP VDG_CYCLES(7.0) +#define VDG_tBP VDG_CYCLES(17.5) +#define VDG_tLB VDG_CYCLES(29.5) +#define VDG_tRB VDG_CYCLES(28.0) +#define VDG_tWHS VDG_CYCLES(17.5) +#define VDG_tAV VDG_CYCLES(128) + +#define VDG_LEFT_BORDER_UNSEEN (VDG_tLB - VDG_CYCLES(16)) + +/* All horizontal timings shall remain relative to the HS pulse falling edge */ +#define VDG_HS_FALLING_EDGE (0) +#define VDG_HS_RISING_EDGE (VDG_HS_FALLING_EDGE + VDG_tWHS) +#define VDG_LEFT_BORDER_START (VDG_HS_RISING_EDGE + VDG_tBP) +#define VDG_ACTIVE_LINE_START (VDG_LEFT_BORDER_START + VDG_tLB) +#define VDG_RIGHT_BORDER_START (VDG_ACTIVE_LINE_START + VDG_tAV) +#define VDG_RIGHT_BORDER_END (VDG_RIGHT_BORDER_START + VDG_tRB) +#define VDG_LINE_DURATION VDG_CYCLES(228) +#define VDG_PAL_PADDING_LINE VDG_LINE_DURATION + +#define VDG_VBLANK_START (0) +#define VDG_TOP_BORDER_START (VDG_VBLANK_START + 13) +#define VDG_ACTIVE_AREA_START (VDG_TOP_BORDER_START + 25) +#define VDG_ACTIVE_AREA_END (VDG_ACTIVE_AREA_START + 192) +#define VDG_BOTTOM_BORDER_END (VDG_ACTIVE_AREA_END + 26) +#define VDG_VRETRACE_END (VDG_BOTTOM_BORDER_END + 6) +#define VDG_FRAME_DURATION (262) + +extern const unsigned int vdg_alpha[768]; +extern Cycle scanline_start; +extern int beam_pos; + +void vdg_init(void); +void vdg_reset(void); +void vdg_set_mode(void); + +#endif /* __VDG_H__ */ diff --git a/src/core/vdg_bitmaps.c b/src/core/vdg_bitmaps.c new file mode 100644 index 0000000..d06b90f --- /dev/null +++ b/src/core/vdg_bitmaps.c @@ -0,0 +1,69 @@ +/* This file was automatically generated + * by tools/font2c from ./vdgfont.png */ + +const unsigned int vdg_alpha[768] = { + 0x00, 0x00, 0x00, 0x1c, 0x22, 0x02, 0x1a, 0x2a, 0x2a, 0x1c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x14, 0x22, 0x22, 0x3e, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3c, 0x12, 0x12, 0x1c, 0x12, 0x12, 0x3c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1c, 0x22, 0x20, 0x20, 0x20, 0x22, 0x1c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3c, 0x12, 0x12, 0x12, 0x12, 0x12, 0x3c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3e, 0x20, 0x20, 0x3c, 0x20, 0x20, 0x3e, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3e, 0x20, 0x20, 0x3c, 0x20, 0x20, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1e, 0x20, 0x20, 0x26, 0x22, 0x22, 0x1e, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x3e, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1c, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x22, 0x22, 0x1c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x22, 0x24, 0x28, 0x30, 0x28, 0x24, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3e, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x22, 0x36, 0x2a, 0x2a, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x22, 0x32, 0x2a, 0x26, 0x22, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3e, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3e, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3c, 0x22, 0x22, 0x3c, 0x20, 0x20, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1c, 0x22, 0x22, 0x22, 0x2a, 0x24, 0x1a, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3c, 0x22, 0x22, 0x3c, 0x28, 0x24, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1c, 0x22, 0x10, 0x08, 0x04, 0x22, 0x1c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x2a, 0x2a, 0x36, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x22, 0x22, 0x14, 0x08, 0x14, 0x22, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x22, 0x22, 0x14, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3e, 0x02, 0x04, 0x08, 0x10, 0x20, 0x3e, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x38, 0x20, 0x20, 0x20, 0x20, 0x20, 0x38, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x20, 0x20, 0x10, 0x08, 0x04, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0e, 0x02, 0x02, 0x02, 0x02, 0x02, 0x0e, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x1c, 0x2a, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x10, 0x3e, 0x10, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x14, 0x14, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x14, 0x14, 0x36, 0x00, 0x36, 0x14, 0x14, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x1e, 0x20, 0x1c, 0x02, 0x3c, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x32, 0x32, 0x04, 0x08, 0x10, 0x26, 0x26, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x28, 0x28, 0x10, 0x2a, 0x24, 0x1a, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x10, 0x20, 0x20, 0x20, 0x10, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x04, 0x02, 0x02, 0x02, 0x04, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x1c, 0x3e, 0x1c, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x3e, 0x08, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x10, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x04, 0x08, 0x10, 0x20, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x24, 0x24, 0x24, 0x24, 0x24, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x18, 0x08, 0x08, 0x08, 0x08, 0x1c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1c, 0x22, 0x02, 0x1c, 0x20, 0x20, 0x3e, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1c, 0x22, 0x02, 0x0c, 0x02, 0x22, 0x1c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x04, 0x0c, 0x14, 0x3e, 0x04, 0x04, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3e, 0x20, 0x3c, 0x02, 0x02, 0x22, 0x1c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1c, 0x20, 0x20, 0x3c, 0x22, 0x22, 0x1c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3e, 0x02, 0x04, 0x08, 0x10, 0x20, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1c, 0x22, 0x22, 0x1c, 0x22, 0x22, 0x1c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1c, 0x22, 0x22, 0x1e, 0x02, 0x02, 0x1c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x08, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x04, 0x08, 0x10, 0x20, 0x10, 0x08, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x08, 0x04, 0x02, 0x04, 0x08, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x24, 0x04, 0x08, 0x08, 0x00, 0x08, 0x00, 0x00 +}; diff --git a/src/core/vdisk.c b/src/core/vdisk.c new file mode 100644 index 0000000..1848c46 --- /dev/null +++ b/src/core/vdisk.c @@ -0,0 +1,467 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * 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 "crc16.h" +#include "fs.h" +#include "logging.h" +#include "module.h" +#include "vdisk.h" +#include "xroar.h" + +#define MAX_SIDES (2) +#define MAX_TRACKS (256) + +static struct vdisk *vdisk_load_vdk(const char *filename); +static struct vdisk *vdisk_load_jvc(const char *filename); +static struct vdisk *vdisk_load_dmk(const char *filename); +static int vdisk_save_dmk(struct vdisk *disk); + +static struct { + int filetype; + struct vdisk *(*load_func)(const char *); + int (*save_func)(struct vdisk *); +} dispatch[] = { + { FILETYPE_VDK, vdisk_load_vdk, NULL }, + { FILETYPE_JVC, vdisk_load_jvc, NULL }, + { FILETYPE_DMK, vdisk_load_dmk, vdisk_save_dmk }, + { -1, NULL, NULL } +}; + +struct vdisk *vdisk_blank_disk(int num_sides, int num_tracks, + int track_length) { + struct vdisk *new; + uint8_t *new_track_data; + unsigned int data_size; + /* Ensure multiples of track_length will stay 16-bit aligned */ + if ((track_length % 2) != 0) + track_length++; + if (num_sides < 1 || num_sides > MAX_SIDES + || num_tracks < 1 || num_tracks > MAX_TRACKS + || track_length < 129 || track_length > 0x2940) { + return NULL; + } + new = malloc(sizeof(struct vdisk)); + if (new == NULL) + return NULL; + data_size = num_tracks * num_sides * track_length; + new_track_data = malloc(data_size); + if (new_track_data == NULL) { + free(new); + return NULL; + } + memset(new_track_data, 0, data_size); + new->filetype = FILETYPE_DMK; + new->filename = NULL; + new->file_write_protect = VDISK_WRITE_PROTECT; + new->write_protect = 0; + new->num_sides = num_sides; + new->num_tracks = num_tracks; + new->track_length = track_length; + new->track_data = new_track_data; + return new; +} + +void vdisk_destroy(struct vdisk *disk) { + if (disk == NULL) return; + if (disk->filename) { + free(disk->filename); + disk->filename = NULL; + } + free(disk->track_data); + free(disk); +} + +struct vdisk *vdisk_load(const char *filename) { + int filetype; + int i; + if (filename == NULL) return NULL; + filetype = xroar_filetype_by_ext(filename); + for (i = 0; dispatch[i].filetype >= 0 && dispatch[i].filetype != filetype; i++); + if (dispatch[i].load_func == NULL) { + LOG_WARN("No reader for virtual disk file type.\n"); + return NULL; + } + return dispatch[i].load_func(filename); +} + +int vdisk_save(struct vdisk *disk, int force) { + char *backup_filename; + int i; + if (disk == NULL) return 1; + if (!force && disk->file_write_protect != VDISK_WRITE_ENABLE) { + LOG_DEBUG(3, "Not saving disk file: file is write protected.\n"); + return 0; + } + if (disk->filename == NULL) { + disk->filename = filereq_module->save_filename(NULL); + if (disk->filename != NULL) { + disk->filetype = xroar_filetype_by_ext(disk->filename); + } + } + for (i = 0; dispatch[i].filetype >= 0 && dispatch[i].filetype != disk->filetype; i++); + if (dispatch[i].save_func == NULL) { + LOG_WARN("No writer for virtual disk file type.\n"); + return 1; + } + { + int bf_len = strlen(disk->filename) + 5; + backup_filename = malloc(bf_len); + /* Rename old file to filename.bak if that .bak file does not + * already exist */ + if (backup_filename != NULL) { + snprintf(backup_filename, bf_len, "%s.bak", disk->filename); + if (fs_size(backup_filename) == -1) { + rename(disk->filename, backup_filename); + } + free(backup_filename); + } + } + return dispatch[i].save_func(disk); +} + +static struct vdisk *vdisk_load_vdk(const char *filename) { + struct vdisk *disk; + ssize_t file_size; + unsigned int header_size; + unsigned int num_tracks; + unsigned int num_sides = 1; + unsigned int num_sectors = 18; + unsigned int ssize_code = 1, ssize; + unsigned int track, sector, side; + uint8_t buf[1024]; + int fd; + if ((file_size = fs_size(filename)) < 0) + return NULL; + if ((fd = fs_open(filename, FS_READ)) < 0) + return NULL; + fs_read(fd, buf, 12); + file_size -= 12; + if (buf[0] != 'd' || buf[1] != 'k') { + fs_close(fd); + return NULL; + } + header_size = (buf[2] | (buf[3]<<8)) - 12; + num_tracks = buf[8]; + num_sides = buf[9]; + if (header_size > 0) + fs_read(fd, buf, header_size); + ssize = 128 << ssize_code; + disk = vdisk_blank_disk(num_sides, num_tracks, VDISK_LENGTH_5_25); + if (disk == NULL) { + fs_close(fd); + return NULL; + } + disk->filetype = FILETYPE_VDK; + disk->filename = strdup(filename); + if (vdisk_format_disk(disk, VDISK_DOUBLE_DENSITY, num_sectors, 1, ssize_code) < 0) { + fs_close(fd); + vdisk_destroy(disk); + return NULL; + } + LOG_DEBUG(2,"Loading VDK virtual disk: %dT %dH %dS (%d-byte)\n", num_tracks, num_sides, num_sectors, ssize); + for (track = 0; track < num_tracks; track++) { + for (side = 0; side < num_sides; side++) { + for (sector = 0; sector < num_sectors; sector++) { + fs_read(fd, buf, ssize); + vdisk_update_sector(disk, side, track, sector + 1, ssize, buf); + } + } + } + fs_close(fd); + return disk; +} + +static struct vdisk *vdisk_load_jvc(const char *filename) { + struct vdisk *disk; + ssize_t file_size = fs_size(filename); + unsigned int header_size; + unsigned int num_tracks; + unsigned int num_sides = 1; + unsigned int num_sectors = 18; + unsigned int ssize_code = 1, ssize; + unsigned int first_sector = 1; + unsigned int sector_attr = 0; + unsigned int track, sector, side; + uint8_t buf[1024]; + int fd; + if (file_size < 0) + return NULL; + header_size = file_size % 256; + file_size -= header_size; + /* Supposedly, we are supposed to default to single sided if there's + * no header information overriding, but I found double sided + * headerless DSK files. */ + if (file_size > 198144) num_sides = 2; + fd = fs_open(filename, FS_READ); + if (fd < 0) + return NULL; + if (header_size > 0) { + fs_read(fd, buf, header_size); + num_sectors = buf[0]; + } + if (header_size > 1) num_sides = buf[1]; + if (header_size > 2) ssize_code = buf[2] & 3; + ssize = 128 << ssize_code; + if (header_size > 3) first_sector = buf[3]; + if (header_size > 4) sector_attr = buf[4] ? 1 : 0; + if (sector_attr == 0) + num_tracks = file_size / (num_sectors * ssize) / num_sides; + else + num_tracks = file_size / (num_sectors * (ssize+1)) / num_sides; + disk = vdisk_blank_disk(num_sides, num_tracks, VDISK_LENGTH_5_25); + if (disk == NULL) { + fs_close(fd); + return NULL; + } + disk->filetype = FILETYPE_JVC; + disk->filename = strdup(filename); + if (vdisk_format_disk(disk, VDISK_DOUBLE_DENSITY, num_sectors, first_sector, ssize_code) < 0) { + fs_close(fd); + vdisk_destroy(disk); + return NULL; + } + LOG_DEBUG(2,"Loading JVC virtual disk: %dT %dH %dS (%d-byte)\n", num_tracks, num_sides, num_sectors, ssize); + for (track = 0; track < num_tracks; track++) { + for (side = 0; side < num_sides; side++) { + for (sector = 0; sector < num_sectors; sector++) { + uint8_t attr; + if (sector_attr) fs_read_byte(fd, &attr); + fs_read(fd, buf, ssize); + vdisk_update_sector(disk, side, track, sector + first_sector, ssize, buf); + } + } + } + fs_close(fd); + return disk; +} + +/* XRoar extension to DMK: byte 11 of header contains actual virtual + * disk write protect status, as value in header[0] is interpreted as the + * write protect status of the file. */ + +static struct vdisk *vdisk_load_dmk(const char *filename) { + struct vdisk *disk; + uint8_t header[16]; + ssize_t file_size = fs_size(filename); + unsigned int num_sides; + unsigned int num_tracks; + unsigned int track_length; + unsigned int track, side; + int fd; + if (file_size < 0) + return NULL; + fd = fs_open(filename, FS_READ); + if (fd < 0) + return NULL; + fs_read(fd, header, 16); + num_tracks = header[1]; + track_length = (header[3] << 8) | header[2]; /* yes, little-endian! */ + num_sides = (header[4] & 0x10) ? 1 : 2; + if (header[4] & 0x40) + LOG_WARN("DMK is flagged single-density only\n"); + if (header[4] & 0x80) + LOG_WARN("DMK is flagged density-agnostic\n"); + file_size -= 16; + disk = vdisk_blank_disk(num_sides, num_tracks, VDISK_LENGTH_5_25); + if (disk == NULL) { + fs_close(fd); + return NULL; + } + LOG_DEBUG(2,"Loading DMK virtual disk: %dT %dH (%d-byte)\n", num_tracks, num_sides, track_length); + disk->filetype = FILETYPE_DMK; + disk->filename = strdup(filename); + disk->file_write_protect = header[0] ? VDISK_WRITE_PROTECT : VDISK_WRITE_ENABLE; + if (header[11] == VDISK_WRITE_ENABLE + || header[11] == VDISK_WRITE_PROTECT) { + disk->write_protect = header[11]; + } else { + disk->write_protect = disk->file_write_protect; + } + for (track = 0; track < num_tracks; track++) { + for (side = 0; side < num_sides; side++) { + uint16_t *idams = vdisk_track_base(disk, side, track); + uint8_t *buf = (uint8_t *)idams; + int i; + if (buf == NULL) continue; + fs_read(fd, buf, 128); + for (i = 0; i < 64; i++) { + /* ensure correct endianness */ + uint16_t tmp = (buf[1] << 8) | buf[0]; + idams[i] = tmp; + buf += 2; + } + fs_read(fd, buf, track_length - 128); + buf += track_length - 128; + } + } + fs_close(fd); + return disk; +} + +static int vdisk_save_dmk(struct vdisk *disk) { + uint8_t header[16]; + unsigned int track, side; + int fd; + if (disk == NULL) + return -1; + fd = fs_open(disk->filename, FS_WRITE); + if (fd < 0) + return -1; + LOG_DEBUG(2,"Writing DMK virtual disk: %dT %dH (%d-byte)\n", disk->num_tracks, disk->num_sides, disk->track_length); + memset(header, 0, sizeof(header)); + if (disk->file_write_protect != VDISK_WRITE_ENABLE) + header[0] = 0xff; + header[1] = disk->num_tracks; + header[2] = disk->track_length & 0xff; + header[3] = (disk->track_length >> 8) & 0xff; + if (disk->num_sides == 1) + header[4] |= 0x10; + header[11] = disk->write_protect; + fs_write(fd, header, 16); + for (track = 0; track < disk->num_tracks; track++) { + for (side = 0; side < disk->num_sides; side++) { + uint16_t *idams = vdisk_track_base(disk, side, track); + uint8_t *buf = (uint8_t *)idams; + int i; + if (buf == NULL) continue; + for (i = 0; i < 64; i++) { + fs_write_byte(fd, idams[i] & 0xff); + fs_write_byte(fd, (idams[i] >> 8) & 0xff); + buf += 2; + } + fs_write(fd, buf, disk->track_length - 128); + buf += disk->track_length - 128; + } + } + fs_close(fd); + return 0; +} + +/* Returns void because track data is manipulated in 8-bit and 16-bit + * chunks. */ +void *vdisk_track_base(struct vdisk *disk, int side, int track) { + if (disk == NULL + || side < 0 || (unsigned)side >= disk->num_sides + || track < 0 || (unsigned)track >= disk->num_tracks) { + return NULL; + } + return disk->track_data + + ((track * disk->num_sides) + side) * disk->track_length; +} + +static unsigned int sect_interleave[18] = + { 0, 9, 1, 10, 2, 11, 3, 12, 4, 13, 5, 14, 6, 15, 7, 16, 8, 17 }; + +#define WRITE_BYTE(v) data[offset++] = (v) + +#define WRITE_BYTE_CRC(v) do { \ + WRITE_BYTE(v); \ + crc16_byte(v); \ + } while (0) + +#define WRITE_CRC() do { \ + uint16_t tmpcrc = crc16_value(); \ + data[offset++] = (tmpcrc & 0xff00) >> 8; \ + data[offset++] = tmpcrc & 0xff; \ + } while (0) + +int vdisk_format_disk(struct vdisk *disk, int density, + int num_sectors, int first_sector, int ssize_code) { + int ssize = 128 << ssize_code; + int side, track, sector, i; + if (disk == NULL) return -1; + if (density != VDISK_DOUBLE_DENSITY) return -1; + for (side = 0; (unsigned)side < disk->num_sides; side++) { + for (track = 0; (unsigned)track < disk->num_tracks; track++) { + uint16_t *idams = vdisk_track_base(disk, side, track); + uint8_t *data = (uint8_t *)idams; + unsigned int offset = 128; + unsigned int idam = 0; + for (i = 0; i < 54; i++) WRITE_BYTE(0x4e); + for (i = 0; i < 9; i++) WRITE_BYTE(0x00); + for (i = 0; i < 3; i++) WRITE_BYTE(0xc2); + WRITE_BYTE(0xfc); + for (i = 0; i < 32; i++) data[offset++] = 0x4e; + for (sector = 0; sector < num_sectors; sector++) { + for (i = 0; i < 8; i++) data[offset++] = 0x00; + crc16_reset(); + for (i = 0; i < 3; i++) WRITE_BYTE_CRC(0xa1); + idams[idam++] = offset | density; + WRITE_BYTE_CRC(0xfe); + WRITE_BYTE_CRC(track); + WRITE_BYTE_CRC(side); + WRITE_BYTE_CRC(sect_interleave[sector] + first_sector); + WRITE_BYTE_CRC(ssize_code); + WRITE_CRC(); + for (i = 0; i < 22; i++) WRITE_BYTE(0x4e); + for (i = 0; i < 12; i++) WRITE_BYTE(0x00); + crc16_reset(); + for (i = 0; i < 3; i++) WRITE_BYTE_CRC(0xa1); + WRITE_BYTE_CRC(0xfb); + for (i = 0; i < ssize; i++) + WRITE_BYTE_CRC(0xe5); + WRITE_CRC(); + for (i = 0; i < 24; i++) WRITE_BYTE(0x4e); + } + while (offset < disk->track_length) { + WRITE_BYTE(0x4e); + } + } + } + return 0; +} + +int vdisk_update_sector(struct vdisk *disk, int side, int track, + int sector, int sector_length, uint8_t *buf) { + uint8_t *data; + uint16_t *idams; + unsigned int offset; + int ssize, i; + if (disk == NULL) return -1; + idams = vdisk_track_base(disk, side, track); + if (idams == NULL) return -1; + data = (uint8_t *)idams; + for (i = 0; i < 64; i++) { + offset = idams[i] & 0x3fff; + if (data[offset + 1] == track && data[offset + 2] == side + && data[offset + 3] == sector) + break; + } + if (i >= 64) return -1; + ssize = 128 << data[offset + 4]; + offset += 7; + offset += 22; + for (i = 0; i < 12; i++) WRITE_BYTE(0x00); + crc16_reset(); + for (i = 0; i < 3; i++) WRITE_BYTE_CRC(0xa1); + WRITE_BYTE_CRC(0xfb); + for (i = 0; i < sector_length; i++) { + if (i < ssize) + WRITE_BYTE_CRC(buf[i]); + } + for ( ; i < ssize; i++) + WRITE_BYTE_CRC(0x00); + WRITE_CRC(); + WRITE_BYTE(0xfe); + return 0; +} diff --git a/src/core/vdisk.h b/src/core/vdisk.h new file mode 100644 index 0000000..040cf45 --- /dev/null +++ b/src/core/vdisk.h @@ -0,0 +1,44 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * See COPYING.GPL for redistribution conditions. */ + +#ifndef __VDISK_H__ +#define __VDISK_H__ + +#define VDISK_LENGTH_5_25 (0x1900) +#define VDISK_LENGTH_8 (0x2940) + +#define VDISK_DOUBLE_DENSITY (0x8000) +#define VDISK_SINGLE_DENSITY (0x0000) + +#define VDISK_WRITE_PROTECT (0xff) +#define VDISK_WRITE_ENABLE (0) + +struct vdisk { + int filetype; + char *filename; + int file_write_protect; + int write_protect; + unsigned int num_sides; + unsigned int num_tracks; + unsigned int track_length; + uint8_t *track_data; +}; + +struct vdisk *vdisk_blank_disk(int num_sides, int num_tracks, + int track_length); +void vdisk_destroy(struct vdisk *disk); + +struct vdisk *vdisk_load(const char *filename); +int vdisk_save(struct vdisk *disk, int force); + +void vdisk_set_write_protect(struct vdisk *disk, int write_protect); + +void *vdisk_track_base(struct vdisk *disk, int side, int track); +int vdisk_format_disk(struct vdisk *disk, int density, + int num_sectors, int first_sector, int ssize_code); +int vdisk_update_sector(struct vdisk *disk, int side, int track, + int sector, int sector_length, uint8_t *buf); + +#endif /* __VDISK_H__ */ diff --git a/src/core/vdrive.c b/src/core/vdrive.c new file mode 100644 index 0000000..b21740c --- /dev/null +++ b/src/core/vdrive.c @@ -0,0 +1,378 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * 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 "crc16.h" +#include "events.h" +#include "logging.h" +#include "machine.h" +#include "vdisk.h" +#include "vdrive.h" +#include "xroar.h" + +#define BYTE_TIME (OSCILLATOR_RATE / 31250) +#define MAX_DRIVES VDRIVE_MAX_DRIVES +#define MAX_SIDES (2) +#define MAX_TRACKS (256) + +struct drive_data { + int disk_present; + struct vdisk *disk; + unsigned int current_track; +}; + +int vdrive_ready; +int vdrive_tr00; +int vdrive_index_pulse; +int vdrive_write_protect; + +static struct drive_data drives[MAX_DRIVES], *current_drive; +static int cur_direction; +static unsigned int cur_side; +static unsigned int cur_density; +static int head_incr; /* bytes per write - 2 in SD, 1 in DD */ +static uint8_t *track_base; /* updated to point to base addr of cur track */ +static uint16_t *idamptr; /* likewise, but different data size */ +static unsigned int head_pos; /* index into current track for read/write */ + +static void update_signals(void); + +static Cycle last_update_cycle; +static Cycle track_start_cycle; +static event_t *index_pulse_event; +static event_t *reset_index_pulse_event; +static void do_index_pulse(void *context); +static void do_reset_index_pulse(void *context); + +void vdrive_init(void) { + int i; + for (i = 0; i < MAX_DRIVES; i++) { + drives[i].disk_present = 0; + drives[i].disk = NULL; + drives[i].current_track = 0; + } + vdrive_set_density(VDISK_DOUBLE_DENSITY); + vdrive_set_drive(0); + index_pulse_event = event_new(); + index_pulse_event->dispatch = do_index_pulse; + reset_index_pulse_event = event_new(); + reset_index_pulse_event->dispatch = do_reset_index_pulse; +} + +void vdrive_shutdown(void) { + int i; + for (i = 0; i < MAX_DRIVES; i++) { + if (drives[i].disk != NULL && drives[i].disk_present) { + vdrive_eject_disk(i); + } + } +} + +int vdrive_insert_disk(int drive, struct vdisk *disk) { + if (drive < 0 || drive >= MAX_DRIVES) + return -1; + if (drives[drive].disk_present) { + vdrive_eject_disk(drive); + } + if (disk == NULL) + return -1; + drives[drive].disk = disk; + drives[drive].disk_present = 1; + update_signals(); + return 0; +} + +int vdrive_eject_disk(int drive) { + if (drive < 0 || drive >= MAX_DRIVES) + return -1; + if (!drives[drive].disk_present) + return -1; + vdisk_save(drives[drive].disk, 0); + vdisk_destroy(drives[drive].disk); + drives[drive].disk_present = 0; + update_signals(); + return 0; +} + +struct vdisk *vdrive_disk_in_drive(int drive) { + if (drive < 0 || drive >= MAX_DRIVES) + return NULL; + if (!drives[drive].disk_present) + return NULL; + return drives[drive].disk; +} + +unsigned int vdrive_head_pos(void) { + return head_pos; +} + +/* Lines from controller sent to all drives */ +void vdrive_set_direction(int direction) { + cur_direction = (direction > 0) ? 1 : -1; +} + +void vdrive_set_side(int side) { + if (side < 0 || side >= MAX_SIDES) return; + cur_side = side; + update_signals(); +} + +void vdrive_set_density(int density) { + if (density != VDISK_SINGLE_DENSITY + && density != VDISK_DOUBLE_DENSITY) + return; + cur_density = density; + head_incr = (density == VDISK_SINGLE_DENSITY) ? 2 : 1; +} + +static void update_signals(void) { + assert(current_drive); + vdrive_ready = current_drive->disk_present; + vdrive_tr00 = (current_drive->current_track == 0) ? 1 : 0; + if (!vdrive_ready) { + vdrive_write_protect = VDISK_WRITE_ENABLE; + track_base = NULL; + idamptr = NULL; + return; + } + vdrive_write_protect = current_drive->disk->write_protect; + if (cur_side < current_drive->disk->num_sides) { + idamptr = vdisk_track_base(current_drive->disk, cur_side, current_drive->current_track); + } else { + idamptr = NULL; + } + track_base = (uint8_t *)idamptr; + if (!index_pulse_event->queued) { + head_pos = 128; + track_start_cycle = current_cycle; + index_pulse_event->at_cycle = track_start_cycle + (current_drive->disk->track_length - 128) * BYTE_TIME; + event_queue(index_pulse_event); + } +} + +/* Drive select */ +void vdrive_set_drive(int drive) { + if (drive < 0 || drive >= MAX_DRIVES) return; + current_drive = &drives[drive]; + update_signals(); +} + +/* Drive-specific actions */ +void vdrive_step(void) { + if (vdrive_ready) { + if (cur_direction > 0 || current_drive->current_track > 0) + current_drive->current_track += cur_direction; + if (current_drive->current_track >= MAX_TRACKS) + current_drive->current_track = MAX_TRACKS - 1; + } + update_signals(); +} + +/* Compare IDAM pointers - normal int comparison with 0 being a special case + * to come after everything else */ +static int compar_idams(const void *aa, const void *bb) { + uint16_t a = *((const uint16_t *)aa) & 0x3fff; + uint16_t b = *((const uint16_t *)bb) & 0x3fff; + if (a == b) return 0; + if (a == 0) return 1; + if (b == 0) return -1; + if (a < b) return -1; + return 1; +} + +void vdrive_write(unsigned int data) { + int i; + if (!vdrive_ready) return; + if (! idamptr) return; + data &= 0xff; + for (i = head_incr; i; i--) { + int j; + if (head_pos < current_drive->disk->track_length) { + track_base[head_pos] = data; + for (j = 0; j < 64; j++) { + if (head_pos == (unsigned)(idamptr[j] & 0x3fff)) { + idamptr[j] = 0; + qsort(idamptr, 64, sizeof(uint16_t), compar_idams); + } + } + } + head_pos++; + } + crc16_byte(data); + if (head_pos >= current_drive->disk->track_length) { + vdrive_index_pulse = 1; + } +} + +void vdrive_skip(void) { + if (!vdrive_ready) return; + head_pos += head_incr; + if (head_pos >= current_drive->disk->track_length) { + vdrive_index_pulse = 1; + } +} + +unsigned int vdrive_read(void) { + unsigned int ret = 0; + if (!vdrive_ready) return 0; + if (head_pos < current_drive->disk->track_length) { + ret = track_base[head_pos] & 0xff; + } + head_pos += head_incr; + crc16_byte(ret); + if (head_pos >= current_drive->disk->track_length) { + vdrive_index_pulse = 1; + } + return ret; +} + +#define IDAM(i) ((unsigned)(idamptr[i] & 0x3fff)) + +void vdrive_write_idam(void) { + int i; + if ((head_pos+head_incr) < current_drive->disk->track_length) { + /* Write 0xfe and remove old IDAM ptr if it exists */ + for (i = 0; i < 64; i++) { + int j; + for (j = 0; j < head_incr; j++) { + track_base[head_pos + j] = 0xfe; + if (idamptr) { + if ((head_pos + j) == IDAM(j)) { + idamptr[i] = 0; + } + } + } + } + /* Add to end of idam list and sort */ + if (idamptr) { + idamptr[63] = head_pos | cur_density; + qsort(idamptr, 64, sizeof(uint16_t), compar_idams); + } + } + head_pos += head_incr; + crc16_byte(0xfe); + if (head_pos >= current_drive->disk->track_length) { + vdrive_index_pulse = 1; + } +} + +unsigned int vdrive_time_to_next_byte(void) { + Cycle next_cycle = track_start_cycle + (head_pos - 128) * BYTE_TIME; + int to_time = next_cycle - current_cycle; + if (to_time < 0) { + LOG_DEBUG(4,"Negative time to next byte!\n"); + return 1; + } + return to_time + 1; +} + +/* Calculates the number of cycles it would take to get from the current head + * position to the next IDAM or next index pulse, whichever comes first. */ + +unsigned int vdrive_time_to_next_idam(void) { + unsigned int next_head_pos; + unsigned int i, tmp; + Cycle next_cycle; + int to_time; + if (!vdrive_ready) return OSCILLATOR_RATE / 5; + /* Update head_pos based on time elapsed since track start */ + head_pos = 128 + ((current_cycle - track_start_cycle) / BYTE_TIME); + (void)vdrive_new_index_pulse(); + next_head_pos = current_drive->disk->track_length; + if (idamptr) { + for (i = 0; i < 64; i++) { + if ((unsigned)(idamptr[i] & 0x8000) == cur_density) { + tmp = idamptr[i] & 0x3fff; + if (head_pos < tmp && tmp < next_head_pos) + next_head_pos = tmp; + } + } + } + if (next_head_pos >= current_drive->disk->track_length) + return (index_pulse_event->at_cycle - current_cycle) + 1; + next_cycle = track_start_cycle + (next_head_pos - 128) * BYTE_TIME; + to_time = next_cycle - current_cycle; + if (to_time < 0) { + LOG_DEBUG(4,"Negative time to next IDAM!\n"); + return 1; + } + return to_time + 1; +} + +/* Updates head_pos to next IDAM and returns a pointer to it. If no valid + * IDAMs are present, an index pulse is generated and the head left at the + * beginning of the track. */ + +uint8_t *vdrive_next_idam(void) { + unsigned int next_head_pos; + unsigned int i, tmp; + if (!vdrive_ready) return NULL; + next_head_pos = current_drive->disk->track_length; + if (idamptr) { + for (i = 0; i < 64; i++) { + if ((unsigned)(idamptr[i] & 0x8000) == cur_density) { + tmp = idamptr[i] & 0x3fff; + if (head_pos < tmp && tmp < next_head_pos) + next_head_pos = tmp; + } + } + } + if (next_head_pos >= current_drive->disk->track_length) { + vdrive_index_pulse = 1; + return NULL; + } + head_pos = next_head_pos; + return track_base + next_head_pos; +} + +/* Returns 1 on active transition of index pulse */ +int vdrive_new_index_pulse(void) { + static int last_index_pulse = 0; + int last = last_index_pulse; + last_index_pulse = vdrive_index_pulse; + if (!last && vdrive_index_pulse) + return 1; + return 0; +} + +static void do_index_pulse(void *context) { + (void)context; + if (!vdrive_ready) { + vdrive_index_pulse = 0; + return; + } + vdrive_index_pulse = 1; + head_pos = 128; + last_update_cycle = index_pulse_event->at_cycle; + track_start_cycle = index_pulse_event->at_cycle; + index_pulse_event->at_cycle = track_start_cycle + (current_drive->disk->track_length - 128) * BYTE_TIME; + event_queue(index_pulse_event); + reset_index_pulse_event->at_cycle = track_start_cycle + ((current_drive->disk->track_length - 128)/100) * BYTE_TIME; + event_queue(reset_index_pulse_event); +} + +static void do_reset_index_pulse(void *context) { + (void)context; + vdrive_index_pulse = 0; + /* reset latch */ + (void)vdrive_new_index_pulse(); +} diff --git a/src/core/vdrive.h b/src/core/vdrive.h new file mode 100644 index 0000000..d7d5131 --- /dev/null +++ b/src/core/vdrive.h @@ -0,0 +1,58 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * See COPYING.GPL for redistribution conditions. */ + +/* Implements virtual disk in a drive */ + +#ifndef __VDRIVE_H__ +#define __VDRIVE_H__ + +#include "types.h" +#include "vdisk.h" + +#define VDRIVE_MAX_DRIVES (4) + +#define VDRIVE_MOTOR_OFF (0) +#define VDRIVE_MOTOR_ON (1) + +#define VDRIVE_WRITE_CRC16 do { \ + uint16_t tmp_write_crc = crc16_value(); \ + vdrive_write(tmp_write_crc >> 8); \ + vdrive_write(tmp_write_crc & 0xff); \ + } while (0) + +extern int vdrive_ready; +extern int vdrive_tr00; +extern int vdrive_index_pulse; +extern int vdrive_write_protect; + +void vdrive_init(void); +void vdrive_shutdown(void); + +int vdrive_insert_disk(int drive, struct vdisk *disk); +int vdrive_eject_disk(int drive); +struct vdisk *vdrive_disk_in_drive(int drive); + +unsigned int vdrive_head_pos(void); + +/* Lines from controller sent to all drives */ +void vdrive_set_direction(int direction); +void vdrive_set_side(int side); +void vdrive_set_density(int density); + +/* Drive select */ +void vdrive_set_drive(int drive); + +/* Drive-specific actions */ +void vdrive_step(void); +void vdrive_write(unsigned int data); +void vdrive_skip(void); +unsigned int vdrive_read(void); +void vdrive_write_idam(void); +int vdrive_new_index_pulse(void); /* Has there been one? */ +unsigned int vdrive_time_to_next_byte(void); +unsigned int vdrive_time_to_next_idam(void); +uint8_t *vdrive_next_idam(void); + +#endif /* __VDRIVE_H__ */ diff --git a/src/core/video_generic_ops.c b/src/core/video_generic_ops.c new file mode 100644 index 0000000..7a3caba --- /dev/null +++ b/src/core/video_generic_ops.c @@ -0,0 +1,396 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * See COPYING.GPL for redistribution conditions. */ + +/* This file contains generic scanline rendering routines. It is included + * into various video module source files and makes use of macros defined in + * those files (eg, LOCK_SURFACE and XSTEP) */ + +#include "machine.h" + +/* VDG_tFP here is a kludge - I don't know why it's needed, but without it, + * DragonFire doesn't render correctly. Everything *should* be relative to + * the horizontal sync pulse which occurs *after* the front porch. */ +#define SCAN_OFFSET (VDG_LEFT_BORDER_START - VDG_LEFT_BORDER_UNSEEN + VDG_tFP) + +#ifdef NO_BORDER +#define RENDER_LEFT_BORDER do { \ + while (beam_pos < 32 && beam_pos < beam_to) { \ + beam_pos += 8; \ + } \ + } while (0) +#define RENDER_RIGHT_BORDER do { \ + while (beam_pos >= 288 && beam_pos < 320 && beam_pos < beam_to) { \ + beam_pos += 8; \ + } \ + } while (0) +#else /* NO_BORDER */ +#define RENDER_LEFT_BORDER do { \ + while (beam_pos < 32 && beam_pos < beam_to) { \ + *(pixel) = *(pixel+1*XSTEP) = *(pixel+2*XSTEP) \ + = *(pixel+3*XSTEP) = *(pixel+4*XSTEP) \ + = *(pixel+5*XSTEP) = *(pixel+6*XSTEP) \ + = *(pixel+7*XSTEP) = border_colour; \ + pixel += 8*XSTEP; \ + beam_pos += 8; \ + } \ + } while (0) + +#define RENDER_RIGHT_BORDER do { \ + while (beam_pos >= 288 && beam_pos < 320 && beam_pos < beam_to) { \ + *(pixel) = *(pixel+1*XSTEP) = *(pixel+2*XSTEP) \ + = *(pixel+3*XSTEP) = *(pixel+4*XSTEP) \ + = *(pixel+5*XSTEP) = *(pixel+6*XSTEP) \ + = *(pixel+7*XSTEP) = border_colour; \ + pixel += 8*XSTEP; \ + beam_pos += 8; \ + } \ + } while (0) +#endif /* NO_BORDER */ + +#define ACTIVE_DISPLAY_AREA (beam_pos >= 32 && beam_pos < 288 && beam_pos < beam_to) + +static unsigned int subline; +static Pixel *pixel; +static Pixel darkgreen, black; +static Pixel bg_colour; +static Pixel fg_colour; +static Pixel vdg_colour[16]; +static Pixel *cg_colours; +static Pixel border_colour; +static uint8_t *vram_ptr; + +/* Allocate colours */ +static void alloc_colours(void) { + vdg_colour[0] = MAPCOLOUR(0x00, 0xff, 0x00); + vdg_colour[1] = MAPCOLOUR(0xff, 0xff, 0x00); + vdg_colour[2] = MAPCOLOUR(0x00, 0x00, 0xff); + vdg_colour[3] = MAPCOLOUR(0xff, 0x00, 0x00); + vdg_colour[4] = MAPCOLOUR(0xff, 0xe0, 0xe0); + vdg_colour[5] = MAPCOLOUR(0x00, 0xff, 0xff); + vdg_colour[6] = MAPCOLOUR(0xff, 0x00, 0xff); + vdg_colour[7] = MAPCOLOUR(0xff, 0xa5, 0x00); + vdg_colour[8] = MAPCOLOUR(0x00, 0x00, 0x00); + vdg_colour[9] = MAPCOLOUR(0x00, 0x80, 0xff); + vdg_colour[10] = MAPCOLOUR(0xff, 0x80, 0x00); + vdg_colour[11] = MAPCOLOUR(0xff, 0xff, 0xff); + vdg_colour[12] = MAPCOLOUR(0x00, 0x00, 0x00); + vdg_colour[13] = MAPCOLOUR(0xff, 0x80, 0x00); + vdg_colour[14] = MAPCOLOUR(0x00, 0x80, 0xff); + vdg_colour[15] = MAPCOLOUR(0xff, 0xff, 0xff); + black = MAPCOLOUR(0x00, 0x00, 0x00); + darkgreen = MAPCOLOUR(0x00, 0x20, 0x00); +} + +/* Update graphics mode - change current select colour set */ +static void set_mode(unsigned int mode) { + if (mode & 0x80) { + /* Graphics modes */ + if (((mode & 0x70) == 0x70) && DRAGON.video_artifact_mode) { + cg_colours = &vdg_colour[4 + DRAGON.video_artifact_mode * 4]; + fg_colour = vdg_colour[(mode & 0x08) >> 1]; + } else { + cg_colours = &vdg_colour[(mode & 0x08) >> 1]; + fg_colour = cg_colours[0]; + } + bg_colour = black; + border_colour = fg_colour; + } else { + bg_colour = darkgreen; + border_colour = black; + if (mode & 0x08) + fg_colour = vdg_colour[7]; + else + fg_colour = vdg_colour[0]; + cg_colours = &vdg_colour[(mode & 0x08) >> 1]; + } +} + +/* Renders a line of alphanumeric/semigraphics 4 (mode is selected by data + * line, so need to be handled together) */ +static void render_sg4(void) { + unsigned int octet; + int beam_to = (current_cycle - scanline_start - SCAN_OFFSET) / 2; + if (beam_to < 0) + return; + LOCK_SURFACE; + RENDER_LEFT_BORDER; + while (ACTIVE_DISPLAY_AREA) { + Pixel tmp; + if (beam_pos == 32 || beam_pos == 160) + vram_ptr = (uint8_t *)sam_vram_ptr(sam_vdg_address); + octet = *(vram_ptr++); + if (octet & 0x80) { + tmp = vdg_colour[(octet & 0x70)>>4]; + if (subline < 6) { + *pixel = *(pixel+1*XSTEP) = *(pixel+2*XSTEP) = *(pixel+3*XSTEP) = (octet & 0x08) ? tmp : black; + *(pixel+4*XSTEP) = *(pixel+5*XSTEP) = *(pixel+6*XSTEP) = *(pixel+7*XSTEP) = (octet & 0x04) ? tmp : black; + } else { + *pixel = *(pixel+1*XSTEP) = *(pixel+2*XSTEP) = *(pixel+3*XSTEP) = (octet & 0x02) ? tmp : black; + *(pixel+4*XSTEP) = *(pixel+5*XSTEP) = *(pixel+6*XSTEP) = *(pixel+7*XSTEP) = (octet & 0x01) ? tmp : black; + } + } else { + tmp = vdg_alpha[(octet&0x3f)*12 + subline]; + if (octet & 0x40) + tmp = ~tmp; + *pixel = (tmp & 0x80) ? fg_colour : bg_colour; + *(pixel+1*XSTEP) = (tmp & 0x40) ? fg_colour : bg_colour; + *(pixel+2*XSTEP) = (tmp & 0x20) ? fg_colour : bg_colour; + *(pixel+3*XSTEP) = (tmp & 0x10) ? fg_colour : bg_colour; + *(pixel+4*XSTEP) = (tmp & 0x08) ? fg_colour : bg_colour; + *(pixel+5*XSTEP) = (tmp & 0x04) ? fg_colour : bg_colour; + *(pixel+6*XSTEP) = (tmp & 0x02) ? fg_colour : bg_colour; + *(pixel+7*XSTEP) = (tmp & 0x01) ? fg_colour : bg_colour; + } + pixel += 8*XSTEP; + beam_pos += 8; + if (beam_pos == 160 || beam_pos == 288) + sam_vdg_xstep(16); + } + RENDER_RIGHT_BORDER; + UNLOCK_SURFACE; + if (beam_pos == 320) { + sam_vdg_hsync(10); + pixel += NEXTLINE; + subline++; + if (subline > 11) + subline = 0; + beam_pos++; + } +} + +/* Renders a line of external-alpha/semigraphics 6 (mode is selected by data + * line, so need to be handled together) */ +static void render_sg6(void) { + unsigned int octet; + int beam_to = (current_cycle - scanline_start - SCAN_OFFSET) / 2; + if (beam_to < 0) + return; + LOCK_SURFACE; + RENDER_LEFT_BORDER; + while (ACTIVE_DISPLAY_AREA) { + Pixel tmp; + if (beam_pos == 32 || beam_pos == 160) + vram_ptr = (uint8_t *)sam_vram_ptr(sam_vdg_address); + octet = *(vram_ptr++); + if (octet & 0x80) { + tmp = cg_colours[(octet & 0xc0)>>6]; + if (subline < 4) { + *pixel = *(pixel+1*XSTEP) = *(pixel+2*XSTEP) = *(pixel+3*XSTEP) = (octet & 0x20) ? tmp : black; + *(pixel+4*XSTEP) = *(pixel+5*XSTEP) = *(pixel+6*XSTEP) = *(pixel+7*XSTEP) = (octet & 0x10) ? tmp : black; + } else if (subline < 8) { + *pixel = *(pixel+1*XSTEP) = *(pixel+2*XSTEP) = *(pixel+3*XSTEP) = (octet & 0x08) ? tmp : black; + *(pixel+4*XSTEP) = *(pixel+5*XSTEP) = *(pixel+6*XSTEP) = *(pixel+7*XSTEP) = (octet & 0x04) ? tmp : black; + } else { + *pixel = *(pixel+1*XSTEP) = *(pixel+2*XSTEP) = *(pixel+3*XSTEP) = (octet & 0x02) ? tmp : black; + *(pixel+4*XSTEP) = *(pixel+5*XSTEP) = *(pixel+6*XSTEP) = *(pixel+7*XSTEP) = (octet & 0x01) ? tmp : black; + } + } else { + tmp = octet; + if (octet & 0x40) + tmp = ~tmp; + *pixel = (tmp & 0x80) ? fg_colour : bg_colour; + //*(pixel+1*XSTEP) = (tmp & 0x40) ? fg_colour : bg_colour; + *(pixel+1*XSTEP) = bg_colour; + *(pixel+2*XSTEP) = (tmp & 0x20) ? fg_colour : bg_colour; + *(pixel+3*XSTEP) = (tmp & 0x10) ? fg_colour : bg_colour; + *(pixel+4*XSTEP) = (tmp & 0x08) ? fg_colour : bg_colour; + *(pixel+5*XSTEP) = (tmp & 0x04) ? fg_colour : bg_colour; + *(pixel+6*XSTEP) = (tmp & 0x02) ? fg_colour : bg_colour; + *(pixel+7*XSTEP) = (tmp & 0x01) ? fg_colour : bg_colour; + } + pixel += 8*XSTEP; + beam_pos += 8; + if (beam_pos == 160 || beam_pos == 288) + sam_vdg_xstep(16); + } + RENDER_RIGHT_BORDER; + UNLOCK_SURFACE; + if (beam_pos == 320) { + sam_vdg_hsync(10); + pixel += NEXTLINE; + subline++; + if (subline > 11) + subline = 0; + beam_pos++; + } +} + +#define RENDER_BYTE_CG1(b) do { \ + *pixel = *(pixel+1*XSTEP) \ + = *(pixel+2*XSTEP) = *(pixel+3*XSTEP) \ + = cg_colours[(b & 0xc0) >> 6]; \ + *(pixel+4*XSTEP) = *(pixel+5*XSTEP) \ + = *(pixel+6*XSTEP) = *(pixel+7*XSTEP) \ + = cg_colours[(b & 0x30) >> 4]; \ + *(pixel+8*XSTEP) = *(pixel+9*XSTEP) \ + = *(pixel+10*XSTEP) = *(pixel+11*XSTEP) \ + = cg_colours[(b & 0x0c) >> 2]; \ + *(pixel+12*XSTEP) = *(pixel+13*XSTEP) \ + = *(pixel+14*XSTEP) = *(pixel+15*XSTEP) \ + = cg_colours[b & 0x03]; \ + pixel += 16 * XSTEP; \ + beam_pos += 16; \ + } while (0); + +/* Render a 16-byte colour graphics line (CG1) */ +static void render_cg1(void) { + unsigned int octet; + int beam_to = (current_cycle - scanline_start - SCAN_OFFSET) / 2; + if (beam_to < 0) + return; + LOCK_SURFACE; + RENDER_LEFT_BORDER; + while (ACTIVE_DISPLAY_AREA) { + if (beam_pos == 32) + vram_ptr = (uint8_t *)sam_vram_ptr(sam_vdg_address); + octet = *(vram_ptr++); + RENDER_BYTE_CG1(octet); + if (beam_pos == 288) + sam_vdg_xstep(16); + } + RENDER_RIGHT_BORDER; + UNLOCK_SURFACE; + if (beam_pos == 320) { + sam_vdg_hsync(6); + pixel += NEXTLINE; + subline++; + if (subline > 11) + subline = 0; + beam_pos++; + } +} + +#define RENDER_BYTE_RG1(b) do { \ + *pixel = *(pixel+1*XSTEP) = (b & 0x80) ? fg_colour : bg_colour; \ + *(pixel+2*XSTEP) = *(pixel+3*XSTEP) = (b & 0x40) ? fg_colour : bg_colour; \ + *(pixel+4*XSTEP) = *(pixel+5*XSTEP) = (b & 0x20) ? fg_colour : bg_colour; \ + *(pixel+6*XSTEP) = *(pixel+7*XSTEP) = (b & 0x10) ? fg_colour : bg_colour; \ + *(pixel+8*XSTEP) = *(pixel+9*XSTEP) = (b & 0x08) ? fg_colour : bg_colour; \ + *(pixel+10*XSTEP) = *(pixel+11*XSTEP) = (b & 0x04) ? fg_colour : bg_colour; \ + *(pixel+12*XSTEP) = *(pixel+13*XSTEP) = (b & 0x02) ? fg_colour : bg_colour; \ + *(pixel+14*XSTEP) = *(pixel+15*XSTEP) = (b & 0x01) ? fg_colour : bg_colour; \ + pixel += 16 * XSTEP; \ + beam_pos += 16; \ + } while (0) + +/* Render a 16-byte resolution graphics line (RG1,RG2,RG3) */ +static void render_rg1(void) { + unsigned int octet; + int beam_to = (current_cycle - scanline_start - SCAN_OFFSET) / 2; + if (beam_to < 0) + return; + LOCK_SURFACE; + RENDER_LEFT_BORDER; + while (ACTIVE_DISPLAY_AREA) { + if (beam_pos == 32) + vram_ptr = (uint8_t *)sam_vram_ptr(sam_vdg_address); + octet = *(vram_ptr++); + RENDER_BYTE_RG1(octet); + if (beam_pos == 288) + sam_vdg_xstep(16); + } + RENDER_RIGHT_BORDER; + UNLOCK_SURFACE; + if (beam_pos == 320) { + sam_vdg_hsync(6); + pixel += NEXTLINE; + subline++; + if (subline > 11) + subline = 0; + beam_pos++; + } +} + +#define RENDER_BYTE_CG2(o) do { \ + *pixel = *(pixel+1*XSTEP) = cg_colours[(o & 0xc0) >> 6]; \ + *(pixel+2*XSTEP) = *(pixel+3*XSTEP) = cg_colours[(o & 0x30) >> 4]; \ + *(pixel+4*XSTEP) = *(pixel+5*XSTEP) = cg_colours[(o & 0x0c) >> 2]; \ + *(pixel+6*XSTEP) = *(pixel+7*XSTEP) = cg_colours[o & 0x03]; \ + pixel += 8*XSTEP; \ + beam_pos += 8; \ + } while (0) + +/* Render a 32-byte colour graphics line (CG2,CG3,CG6) */ +static void render_cg2(void) { + int beam_to = (current_cycle - scanline_start - SCAN_OFFSET) / 2; + if (beam_to < 0) + return; + LOCK_SURFACE; + RENDER_LEFT_BORDER; + while (ACTIVE_DISPLAY_AREA) { + unsigned int octet; + if (beam_pos == 32 || beam_pos == 160) + vram_ptr = (uint8_t *)sam_vram_ptr(sam_vdg_address); + octet = *(vram_ptr++); + RENDER_BYTE_CG2(octet); + if (beam_pos == 160 || beam_pos == 288) + sam_vdg_xstep(16); + } + RENDER_RIGHT_BORDER; + UNLOCK_SURFACE; + if (beam_pos == 320) { + sam_vdg_hsync(10); + pixel += NEXTLINE; + subline++; + if (subline > 11) + subline = 0; + beam_pos++; + } +} + +#define RENDER_BYTE_RG6(o) do { \ + *pixel = (o & 0x80) ? fg_colour : bg_colour; \ + *(pixel+1*XSTEP) = (o & 0x40) ? fg_colour : bg_colour; \ + *(pixel+2*XSTEP) = (o & 0x20) ? fg_colour : bg_colour; \ + *(pixel+3*XSTEP) = (o & 0x10) ? fg_colour : bg_colour; \ + *(pixel+4*XSTEP) = (o & 0x08) ? fg_colour : bg_colour; \ + *(pixel+5*XSTEP) = (o & 0x04) ? fg_colour : bg_colour; \ + *(pixel+6*XSTEP) = (o & 0x02) ? fg_colour : bg_colour; \ + *(pixel+7*XSTEP) = (o & 0x01) ? fg_colour : bg_colour; \ + } while (0) + +/* Render a 32-byte resolution graphics line (RG6) */ +static void render_rg6(void) { + unsigned int octet; + int beam_to = (current_cycle - scanline_start - SCAN_OFFSET) / 2; + if (beam_to < 0) + return; + LOCK_SURFACE; + RENDER_LEFT_BORDER; + while (ACTIVE_DISPLAY_AREA) { + if (beam_pos == 32 || beam_pos == 160) + vram_ptr = (uint8_t *)sam_vram_ptr(sam_vdg_address); + octet = *(vram_ptr++); + RENDER_BYTE_RG6(octet); + pixel += 8*XSTEP; + beam_pos += 8; + if (beam_pos == 160 || beam_pos == 288) + sam_vdg_xstep(16); + } + RENDER_RIGHT_BORDER; + UNLOCK_SURFACE; + if (beam_pos == 320) { + sam_vdg_hsync(10); + pixel += NEXTLINE; + subline++; + if (subline > 11) + subline = 0; + beam_pos++; + } +} + +/* Render a line of border (top/bottom) */ +static void render_border(void) { +#ifndef NO_BORDER + unsigned int i; + LOCK_SURFACE; + for (i = 320; i; i--) { + *pixel = border_colour; + pixel += XSTEP; + } + UNLOCK_SURFACE; + pixel += NEXTLINE; +#endif +} diff --git a/src/core/video_sdl.c b/src/core/video_sdl.c new file mode 100644 index 0000000..f8c1a74 --- /dev/null +++ b/src/core/video_sdl.c @@ -0,0 +1,158 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * 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 "logging.h" +#include "module.h" +#include "sam.h" +#include "ui_sdl.h" +#include "vdg.h" +#include "xroar.h" +#ifdef WINDOWS32 +#include "common_windows32.h" +#endif +#include "global.h" +#include "psp_sdl.h" + +static int init(int argc, char **argv); +static void shutdown(void); +static int set_fullscreen(int fullscreen); +static void vsync(void); +static void set_mode(unsigned int mode); +static void render_sg4(void); +static void render_sg6(void); +static void render_cg1(void); +static void render_rg1(void); +static void render_cg2(void); +static void render_rg6(void); +static void render_border(void); +static void alloc_colours(void); + +VideoModule video_sdl_module = { + { "sdl", "Standard SDL surface", + init, 0, shutdown, NULL }, + NULL, set_fullscreen, 0, + vsync, set_mode, + render_sg4, render_sg6, render_cg1, + render_rg1, render_cg2, render_rg6, + render_border +}; + +# if 0 //LUDO: +typedef Uint8 Pixel; +# else +typedef Uint16 Pixel; +# endif +#define MAPCOLOUR(r,g,b) SDL_MapRGB(screen->format, r, g, b) +#define VIDEO_SCREENBASE ((Pixel *)screen->pixels) +#define XSTEP 1 +#define NEXTLINE 0 +#define VIDEO_TOPLEFT (VIDEO_SCREENBASE) +#define VIDEO_VIEWPORT_YOFFSET (0) +#define LOCK_SURFACE SDL_LockSurface(screen) +#define UNLOCK_SURFACE SDL_UnlockSurface(screen) + +static SDL_Surface *screen; + +#include "video_generic_ops.c" + +static int init(int argc, char **argv) +{ +# if 0 //LUDO: + (void)argc; + (void)argv; + LOG_DEBUG(2,"Initialising SDL video driver\n"); +#ifdef WINDOWS32 + if (!getenv("SDL_VIDEODRIVER")) + putenv("SDL_VIDEODRIVER=windib"); +#endif + if (!SDL_WasInit(SDL_INIT_NOPARACHUTE)) { + if (SDL_Init(SDL_INIT_NOPARACHUTE) < 0) { + LOG_ERROR("Failed to initialiase SDL: %s\n", SDL_GetError()); + return 1; + } + } + if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) { + LOG_ERROR("Failed to initialiase SDL video driver: %s\n", SDL_GetError()); + return 1; + } + if (set_fullscreen(sdl_video_want_fullscreen)) + return 1; + alloc_colours(); +#ifdef WINDOWS32 + { + SDL_version sdlver; + SDL_SysWMinfo sdlinfo; + SDL_VERSION(&sdlver); + sdlinfo.version = sdlver; + SDL_GetWMInfo(&sdlinfo); + windows32_main_hwnd = sdlinfo.window; + } +#endif +# else + screen = blit_surface; + alloc_colours(); +# endif + return 0; +} + +static void shutdown(void) { + LOG_DEBUG(2,"Shutting down SDL video driver\n"); + set_fullscreen(0); + /* Should not be freed by caller: SDL_FreeSurface(screen); */ + SDL_QuitSubSystem(SDL_INIT_VIDEO); +} + +SDL_Surface* back_surface = 0; + +static int set_fullscreen(int fullscreen) +{ +# if 0 //LUDO: DISABLE +# ifdef PSP + back_surface = SDL_SetVideoMode(480, 272, 16, SDL_HWSURFACE|SDL_ANYFORMAT|SDL_DOUBLEBUF); + screen = SDL_CreateRGBSurface(SDL_SWSURFACE, + 320, 240, 8, 1 << 3, 1 << 2, 1 << 3, 0); +# else + screen = SDL_SetVideoMode(320, 240, 8, SDL_SWSURFACE|(fullscreen?SDL_FULLSCREEN:0)); +fprintf(stdout, "%x %x %x\n", screen->format->Rmask, screen->format->Gmask, screen->format->Bmask); +# endif + if (screen == NULL) { + LOG_ERROR("Failed to allocate SDL surface for display\n"); + return 1; + } + if (fullscreen) + SDL_ShowCursor(SDL_DISABLE); + else + SDL_ShowCursor(SDL_ENABLE); + video_sdl_module.is_fullscreen = fullscreen; +# endif + return 0; +} + +static void +vsync(void) +{ + psp_sdl_render(); + + pixel = VIDEO_TOPLEFT + VIDEO_VIEWPORT_YOFFSET; + subline = 0; +} diff --git a/src/core/wd279x.c b/src/core/wd279x.c new file mode 100644 index 0000000..6143410 --- /dev/null +++ b/src/core/wd279x.c @@ -0,0 +1,866 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * 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 + */ + +/* Sources: + * WD279x: + * http://www.swtpc.com/mholley/DC_5/TMS279X_DataSheet.pdf + */ + +#include +#include + +#include "types.h" +#include "crc16.h" +#include "events.h" +#include "logging.h" +#include "machine.h" +#include "vdrive.h" +#include "wd279x.h" +#include "xroar.h" + +#define STATUS_NOT_READY (1<<7) +#define STATUS_WRITE_PROTECT (1<<6) +#define STATUS_HEAD_LOADED (1<<5) +#define STATUS_RECORD_TYPE (1<<5) +#define STATUS_SEEK_ERROR (1<<4) +#define STATUS_RNF (1<<4) +#define STATUS_CRC_ERROR (1<<3) +#define STATUS_TRACK_0 (1<<2) +#define STATUS_LOST_DATA (1<<2) +#define STATUS_INDEX_PULSE (1<<1) +#define STATUS_DRQ (1<<1) +#define STATUS_BUSY (1<<0) + +#define W_MILLISEC(ms) ((OSCILLATOR_RATE/1000)*(ms)) +#define W_MICROSEC(us) ((OSCILLATOR_RATE*(us))/1000000) + +#define W_BYTE_TIME (OSCILLATOR_RATE / 31250) + +#define SET_DRQ do { \ + status_register |= STATUS_DRQ; \ + if (wd279x_set_drq_handler) \ + wd279x_set_drq_handler(); \ + } while (0) +#define RESET_DRQ do { \ + status_register &= ~(STATUS_DRQ); \ + if (wd279x_reset_drq_handler) \ + wd279x_reset_drq_handler(); \ + } while (0) +#define SET_INTRQ do { \ + if (wd279x_set_intrq_handler) \ + wd279x_set_intrq_handler(); \ + } while (0) +#define RESET_INTRQ do { \ + if (wd279x_reset_intrq_handler) \ + wd279x_reset_intrq_handler(); \ + } while (0) + +#define NEXT_STATE(f,t) do { \ + state_event->dispatch = f; \ + state_event->at_cycle = current_cycle + t; \ + event_queue(state_event); \ + } while (0) + +#define SINGLE_DENSITY (0) +#define DOUBLE_DENSITY (1) + +#define IS_DOUBLE_DENSITY (density == DOUBLE_DENSITY) +#define IS_SINGLE_DENSITY (density == SINGLE_DENSITY) + +#define SET_DIRECTION do { direction = 1; vdrive_set_direction(1); } while (0) +#define RESET_DIRECTION do { \ + direction = -1; vdrive_set_direction(-1); \ + } while (0) +#define SET_SIDE(s) do { side = (s) ? 1 : 0; vdrive_set_side(side); } while (0) + +/* From enum WD279X_type */ +unsigned int wd279x_type; + +#define HAS_SSO (wd279x_type == WD2795 || wd279x_type == WD2797) +#define HAS_LENGTH_FLAG (wd279x_type == WD2795 || wd279x_type == WD2797) +#define INVERTED_DATA (wd279x_type == WD2791 || wd279x_type == WD2795) + +/* Various signal handlers set by DOS cart reset code: */ +void (*wd279x_set_drq_handler)(void); +void (*wd279x_reset_drq_handler)(void); +void (*wd279x_set_intrq_handler)(void); +void (*wd279x_reset_intrq_handler)(void); + +/* Functions used in state machines: */ +static void type_1_state_1(void *context); +static void type_1_state_2(void *context); +static void type_1_state_3(void *context); +static void verify_track_state_1(void *context); +static void verify_track_state_2(void *context); +static void type_2_state_1(void *context); +static void type_2_state_2(void *context); +static void read_sector_state_1(void *context); +static void read_sector_state_2(void *context); +static void read_sector_state_3(void *context); +static void write_sector_state_1(void *context); +static void write_sector_state_2(void *context); +static void write_sector_state_3(void *context); +static void write_sector_state_4(void *context); +static void write_sector_state_5(void *context); +static void write_sector_state_6(void *context); +static void type_3_state_1(void *context); +static void read_address_state_1(void *context); +static void read_address_state_2(void *context); +static void read_address_state_3(void *context); +static void write_track_state_1(void *context); +static void write_track_state_2(void *context); +static void write_track_state_2b(void *context); +static void write_track_state_3(void *context); + +static event_t *state_event; + +/* WD279X registers */ +static unsigned int status_register; +static unsigned int track_register; +static unsigned int sector_register; +static unsigned int data_register; + +/* WD279X internal state */ +static unsigned int cmd_copy; +static unsigned int is_step_cmd; +static int direction; +static unsigned int side; +static unsigned int step_delay; +static unsigned int index_holes_count; +static unsigned int bytes_left; +static unsigned int dam; +static unsigned int track_register_tmp; + +static unsigned int stepping_rate[4] = { 6, 12, 20, 30 }; +static unsigned int sector_size[2][4] = { + { 256, 512, 1024, 128 }, + { 128, 256, 512, 1024 } +}; + +static unsigned int density; + +void wd279x_init(void) { + wd279x_type = WD2797; + wd279x_set_drq_handler = NULL; + wd279x_reset_drq_handler = NULL; + wd279x_set_intrq_handler = NULL; + wd279x_reset_intrq_handler = NULL; + state_event = event_new(); +} + +void wd279x_reset(void) { + event_dequeue(state_event); + status_register = 0; + track_register = 0; + sector_register = 0; + data_register = 0; + cmd_copy = 0; + RESET_DIRECTION; + SET_SIDE(0); +} + +void wd279x_set_density(unsigned int d) { + /* DDEN# is active-low */ + density = d ? SINGLE_DENSITY : DOUBLE_DENSITY; + vdrive_set_density(d ? VDISK_SINGLE_DENSITY : VDISK_DOUBLE_DENSITY); +} + +void wd279x_track_register_write(unsigned int octet) { + if (INVERTED_DATA) + octet = ~octet; + track_register = octet & 0xff; +} + +unsigned int wd279x_track_register_read(void) { + if (INVERTED_DATA) + return ~track_register & 0xff; + return track_register & 0xff; +} + +void wd279x_sector_register_write(unsigned int octet) { + if (INVERTED_DATA) + octet = ~octet; + sector_register = octet & 0xff; +} + +unsigned int wd279x_sector_register_read(void) { + if (INVERTED_DATA) + return ~sector_register & 0xff; + return sector_register & 0xff; +} + +void wd279x_data_register_write(unsigned int octet) { + RESET_DRQ; + if (INVERTED_DATA) + octet = ~octet; + data_register = octet & 0xff; +} + +unsigned int wd279x_data_register_read(void) { + RESET_DRQ; + if (INVERTED_DATA) + return ~data_register & 0xff; + return data_register & 0xff; +} + +unsigned int wd279x_status_read(void) { + RESET_INTRQ; + if (vdrive_ready) + status_register &= ~STATUS_NOT_READY; + else + status_register |= STATUS_NOT_READY; + if ((cmd_copy & 0xf0) == 0xd0 || (cmd_copy & 0x80) == 0x00) { + if (vdrive_tr00) + status_register |= STATUS_TRACK_0; + else + status_register &= ~STATUS_TRACK_0; + if (vdrive_index_pulse) + status_register |= STATUS_INDEX_PULSE; + else + status_register &= ~STATUS_INDEX_PULSE; + } + if (INVERTED_DATA) + return ~status_register & 0xff; + return status_register & 0xff; +} + +void wd279x_command_write(unsigned int cmd) { + RESET_INTRQ; + if (INVERTED_DATA) + cmd = ~cmd; + cmd &= 0xff; + cmd_copy = cmd; + /* FORCE INTERRUPT */ + if ((cmd & 0xf0) == 0xd0) { + LOG_DEBUG(4,"WD279X: CMD: Force interrupt (%01x)\n",cmd&0x0f); + if ((cmd & 0x0f) == 0) { + event_dequeue(state_event); + status_register &= ~(STATUS_BUSY); + return; + } + if (cmd & 0x08) { + event_dequeue(state_event); + status_register &= ~(STATUS_BUSY); + SET_INTRQ; + return; + } + return; + } + /* Ignore any other command if busy */ + if (status_register & STATUS_BUSY) { + LOG_DEBUG(4,"WD279X: Command received while busy!\n"); + return; + } + /* 0xxxxxxx = RESTORE / SEEK / STEP / STEP-IN / STEP-OUT */ + if ((cmd & 0x80) == 0x00) { + status_register |= STATUS_BUSY; + status_register &= ~(STATUS_CRC_ERROR|STATUS_SEEK_ERROR); + RESET_DRQ; + step_delay = stepping_rate[cmd & 3]; + is_step_cmd = 0; + if ((cmd & 0xe0) == 0x20) { + LOG_DEBUG(4, "WD279X: CMD: Step\n"); + is_step_cmd = 1; + } else if ((cmd & 0xe0) == 0x40) { + LOG_DEBUG(4, "WD279X: CMD: Step-in\n"); + is_step_cmd = 1; + SET_DIRECTION; + } else if ((cmd & 0xe0) == 0x60) { + LOG_DEBUG(4, "WD279X: CMD: Step-out\n"); + is_step_cmd = 1; + RESET_DIRECTION; + } + if (is_step_cmd) { + if (cmd & 0x10) + type_1_state_2(NULL); + else + type_1_state_3(NULL); + return; + } + if ((cmd & 0xf0) == 0x00) { + track_register = 0xff; + data_register = 0x00; + LOG_DEBUG(4, "WD279X: CMD: Restore\n"); + } else { + LOG_DEBUG(4, "WD279X: CMD: Seek (TR=%d)\n", data_register); + } + type_1_state_1(NULL); + return; + } + /* 10xxxxxx = READ/WRITE SECTOR */ + if ((cmd & 0xc0) == 0x80) { + if ((cmd & 0xe0) == 0x80) { + LOG_DEBUG(4, "WD279X: CMD: Read sector (Tr %d, Side %d, Sector %d)\n", track_register, side, sector_register); + } else { + LOG_DEBUG(4, "WD279X: CMD: Write sector\n"); + } + status_register |= STATUS_BUSY; + status_register &= ~(STATUS_LOST_DATA|STATUS_RNF|(1<<5)|(1<<6)); + RESET_DRQ; + if (!vdrive_ready) { + status_register &= ~(STATUS_BUSY); + SET_INTRQ; + return; + } + if (HAS_SSO) + SET_SIDE(cmd & 0x02); /* 'U' */ + if (cmd & 0x04) { /* 'E' set */ + NEXT_STATE(type_2_state_1, W_MILLISEC(30)); + return; + } + type_2_state_1(NULL); + return; + } + /* 11000xx0 = READ ADDRESS */ + /* 11100xx0 = READ TRACK */ + /* 11110xx0 = WRITE TRACK */ + if (((cmd & 0xf9) == 0xc0) + || ((cmd & 0xf9) == 0xe0) + || ((cmd & 0xf9) == 0xf0)) { + status_register |= STATUS_BUSY; + status_register &= ~(STATUS_LOST_DATA|(1<<4)|(1<<5)); + if ((cmd & 0xf0) == 0xf0) + RESET_DRQ; + if (!vdrive_ready) { + status_register &= ~(STATUS_BUSY); + SET_INTRQ; + return; + } + if (HAS_SSO) + SET_SIDE(cmd & 0x02); /* 'U' */ + if (cmd & 0x04) { /* 'E' set */ + NEXT_STATE(type_3_state_1, W_MILLISEC(30)); + return; + } + return type_3_state_1(NULL); + } + LOG_WARN("WD279X: CMD: Unknown command %02x\n", cmd); +} + +static void type_1_state_1(void *context) { + (void)context; + LOG_DEBUG(5, " type_1_state_1()\n"); + if (data_register == track_register) { + verify_track_state_1(context); + return; + } + if (data_register > track_register) + SET_DIRECTION; + else + RESET_DIRECTION; + type_1_state_2(context); +} + +static void type_1_state_2(void *context) { + (void)context; + LOG_DEBUG(5, " type_1_state_2()\n"); + track_register += direction; + type_1_state_3(context); +} + +static void type_1_state_3(void *context) { + (void)context; + LOG_DEBUG(5, " type_1_state_3()\n"); + if (vdrive_tr00 && direction == -1) { + LOG_DEBUG(4,"WD279X: TR00!\n"); + track_register = 0; + verify_track_state_1(context); + return; + } + vdrive_step(); + if (is_step_cmd) { + NEXT_STATE(verify_track_state_1, W_MILLISEC(step_delay)); + return; + } + NEXT_STATE(type_1_state_1, W_MILLISEC(step_delay)); +} + +static void verify_track_state_1(void *context) { + (void)context; + LOG_DEBUG(5, " verify_track_state_1()\n"); + if (!(cmd_copy & 0x04)) { + status_register &= ~(STATUS_BUSY); + SET_INTRQ; + return; + } + index_holes_count = 0; + NEXT_STATE(verify_track_state_2, vdrive_time_to_next_idam()); +} + +static void verify_track_state_2(void *context) { + uint8_t *idam; + int i; + (void)context; + LOG_DEBUG(5, " verify_track_state_2()\n"); + idam = vdrive_next_idam(); + if (vdrive_new_index_pulse()) { + index_holes_count++; + if (index_holes_count >= 5) { + LOG_DEBUG(5, "index_holes_count >= 5: seek error\n"); + status_register &= ~(STATUS_BUSY); + status_register |= STATUS_SEEK_ERROR; + SET_INTRQ; + return; + } + } + if (idam == NULL) { + LOG_DEBUG(5, "null IDAM: -> verify_track_state_2\n"); + NEXT_STATE(verify_track_state_2, vdrive_time_to_next_idam()); + return; + } + crc16_reset(); + if (IS_DOUBLE_DENSITY) { + crc16_byte(0xa1); + crc16_byte(0xa1); + crc16_byte(0xa1); + } + (void)vdrive_read(); /* Include IDAM in CRC */ + if (track_register != vdrive_read()) { + LOG_DEBUG(5, "track_register != idam[1]: -> verify_track_state_2\n"); + NEXT_STATE(verify_track_state_2, vdrive_time_to_next_idam()); + return; + } + /* Include rest of ID field - should result in computed CRC = 0 */ + for (i = 0; i < 5; i++) + (void)vdrive_read(); + if (crc16_value() != 0) { + LOG_DEBUG(3, "Verify track %d CRC16 error: $%04x != 0\n", track_register, crc16_value()); + status_register |= STATUS_CRC_ERROR; + NEXT_STATE(verify_track_state_2, vdrive_time_to_next_idam()); + return; + } + LOG_DEBUG(5, "finished.\n"); + status_register &= ~(STATUS_CRC_ERROR|STATUS_BUSY); + SET_INTRQ; +} + +static void type_2_state_1(void *context) { + (void)context; + LOG_DEBUG(5, " type_2_state_1()\n"); + if ((cmd_copy & 0x20) && vdrive_write_protect) { + status_register &= ~(STATUS_BUSY); + status_register |= STATUS_WRITE_PROTECT; + SET_INTRQ; + return; + } + index_holes_count = 0; + NEXT_STATE(type_2_state_2, vdrive_time_to_next_idam()); +} + +static void type_2_state_2(void *context) { + uint8_t *idam; + int i; + (void)context; + LOG_DEBUG(5, " type_2_state_2()\n"); + idam = vdrive_next_idam(); + if (vdrive_new_index_pulse()) { + index_holes_count++; + if (index_holes_count >= 5) { + status_register &= ~(STATUS_BUSY); + status_register |= STATUS_RNF; + SET_INTRQ; + return; + } + } + if (idam == NULL) { + NEXT_STATE(type_2_state_2, vdrive_time_to_next_idam()); + return; + } + crc16_reset(); + if (IS_DOUBLE_DENSITY) { + crc16_byte(0xa1); + crc16_byte(0xa1); + crc16_byte(0xa1); + } + (void)vdrive_read(); /* Include IDAM in CRC */ + if (track_register != vdrive_read()) { + NEXT_STATE(type_2_state_2, vdrive_time_to_next_idam()); + return; + } + if (side != vdrive_read()) { + NEXT_STATE(type_2_state_2, vdrive_time_to_next_idam()); + return; + } + if (sector_register != vdrive_read()) { + NEXT_STATE(type_2_state_2, vdrive_time_to_next_idam()); + return; + } + i = vdrive_read(); + if (HAS_LENGTH_FLAG) + bytes_left = sector_size[(cmd_copy & 0x08)?1:0][i&3]; + else + bytes_left = sector_size[1][i&3]; + /* Including CRC bytes should result in computed CRC = 0 */ + (void)vdrive_read(); + (void)vdrive_read(); + if (crc16_value() != 0) { + status_register |= STATUS_CRC_ERROR; + LOG_DEBUG(3, "Type 2 tr %d se %d CRC16 error: $%04x != 0\n", track_register, sector_register, crc16_value()); + NEXT_STATE(type_2_state_2, vdrive_time_to_next_idam()); + return; + } + + if ((cmd_copy & 0x20) == 0) { + unsigned int bytes_to_scan, j, tmp; + if (IS_SINGLE_DENSITY) + bytes_to_scan = 30; + else + bytes_to_scan = 43; + j = 0; + dam = 0; + do { + crc16_reset(); + if (IS_DOUBLE_DENSITY) { + crc16_byte(0xa1); + crc16_byte(0xa1); + crc16_byte(0xa1); + } + tmp = vdrive_read(); + if (tmp == 0xfb || tmp == 0xf8) + dam = tmp; + j++; + } while (j < bytes_to_scan && dam == 0); + if (dam == 0) { + NEXT_STATE(type_2_state_2, vdrive_time_to_next_byte()); + return; + } + NEXT_STATE(read_sector_state_1, vdrive_time_to_next_byte()); + return; + } + vdrive_skip(); + vdrive_skip(); + NEXT_STATE(write_sector_state_1, vdrive_time_to_next_byte()); +} + +static void read_sector_state_1(void *context) { + (void)context; + LOG_DEBUG(5, " read_sector_state_1()\n"); + LOG_DEBUG(4,"Reading %d-byte sector (Tr %d, Se %d) from head_pos=%04x\n", bytes_left, track_register, sector_register, vdrive_head_pos()); + status_register |= ((~dam & 1) << 5); + data_register = vdrive_read(); + bytes_left--; + SET_DRQ; + NEXT_STATE(read_sector_state_2, vdrive_time_to_next_byte()); +} + +static void read_sector_state_2(void *context) { + (void)context; + LOG_DEBUG(5, " read_sector_state_2()\n"); + if (status_register & STATUS_DRQ) { + status_register |= STATUS_LOST_DATA; + //RESET_DRQ; // XXX + } + if (bytes_left > 0) { + data_register = vdrive_read(); + bytes_left--; + SET_DRQ; + NEXT_STATE(read_sector_state_2, vdrive_time_to_next_byte()); + return; + } + /* Including CRC bytes should result in computed CRC = 0 */ + (void)vdrive_read(); + (void)vdrive_read(); + NEXT_STATE(read_sector_state_3, vdrive_time_to_next_byte()); +} + +static void read_sector_state_3(void *context) { + (void)context; + LOG_DEBUG(5, " read_sector_state_3()\n"); + if (crc16_value() != 0) { + LOG_DEBUG(3, "Read sector data tr %d se %d CRC16 error: $%04x != 0\n", track_register, sector_register, crc16_value()); + status_register |= STATUS_CRC_ERROR; + } + /* TODO: M == 1 */ + if (cmd_copy & 0x10) { + LOG_DEBUG(2, "WD279X: TODO: multi-sector read will fail.\n"); + } + status_register &= ~(STATUS_BUSY); + SET_INTRQ; +} + +static void write_sector_state_1(void *context) { + unsigned int i; + (void)context; + LOG_DEBUG(5, " write_sector_state_1()\n"); + SET_DRQ; + for (i = 0; i < 8; i++) + vdrive_skip(); + NEXT_STATE(write_sector_state_2, vdrive_time_to_next_byte()); +} + +static void write_sector_state_2(void *context) { + (void)context; + LOG_DEBUG(5, " write_sector_state_2()\n"); + if (status_register & STATUS_DRQ) { + status_register &= ~(STATUS_BUSY); + RESET_DRQ; // XXX + status_register |= STATUS_LOST_DATA; + SET_INTRQ; + return; + } + vdrive_skip(); + NEXT_STATE(write_sector_state_3, vdrive_time_to_next_byte()); +} + +static void write_sector_state_3(void *context) { + unsigned int i; + (void)context; + LOG_DEBUG(5, " write_sector_state_3()\n"); + if (IS_DOUBLE_DENSITY) { + for (i = 0; i < 11; i++) + vdrive_skip(); + for (i = 0; i < 12; i++) + vdrive_write(0); + NEXT_STATE(write_sector_state_4, vdrive_time_to_next_byte()); + return; + } + for (i = 0; i < 6; i++) + vdrive_write(0); + NEXT_STATE(write_sector_state_4, vdrive_time_to_next_byte()); +} + +static void write_sector_state_4(void *context) { + (void)context; + LOG_DEBUG(5, " write_sector_state_4()\n"); + crc16_reset(); + if (IS_DOUBLE_DENSITY) { + crc16_byte(0xa1); + crc16_byte(0xa1); + crc16_byte(0xa1); + } + if (cmd_copy & 1) + vdrive_write(0xf8); + else + vdrive_write(0xfb); + NEXT_STATE(write_sector_state_5, vdrive_time_to_next_byte()); +} + +static void write_sector_state_5(void *context) { + unsigned int data = data_register; + (void)context; + LOG_DEBUG(5, " write_sector_state_5()\n"); + if (status_register & STATUS_DRQ) { + data = 0; + status_register |= STATUS_LOST_DATA; + RESET_DRQ; // XXX + } + vdrive_write(data_register); + bytes_left--; + if (bytes_left > 0) { + SET_DRQ; + NEXT_STATE(write_sector_state_5, vdrive_time_to_next_byte()); + return; + } + VDRIVE_WRITE_CRC16; + NEXT_STATE(write_sector_state_6, vdrive_time_to_next_byte() + W_MICROSEC(20)); +} + +static void write_sector_state_6(void *context) { + (void)context; + LOG_DEBUG(5, " write_sector_state_6()\n"); + vdrive_write(0xfe); + /* TODO: M = 1 */ + status_register &= ~(STATUS_BUSY); + SET_INTRQ; +} + +static void type_3_state_1(void *context) { + (void)context; + LOG_DEBUG(5, " type_3_state_1()\n"); + switch (cmd_copy & 0xf0) { + case 0xc0: + index_holes_count = 0; + NEXT_STATE(read_address_state_1, vdrive_time_to_next_idam()); + return; + case 0xe0: + LOG_WARN("WD279X: CMD: Read track not implemented\n"); + SET_INTRQ; + break; + case 0xf0: + LOG_DEBUG(4, "WD279X: CMD: Write track (Tr %d)\n", track_register); + return write_track_state_1(context); + default: + LOG_WARN("WD279X: CMD: Unknown command %02x\n", cmd_copy); + break; + } +} + +static void read_address_state_1(void *context) { + uint8_t *idam; + (void)context; + LOG_DEBUG(5, " read_address_state_1()\n"); + idam = vdrive_next_idam(); + if (vdrive_new_index_pulse()) { + index_holes_count++; + if (index_holes_count >= 6) { + status_register &= ~(STATUS_BUSY); + status_register |= STATUS_RNF; + SET_INTRQ; + return; + } + } + if (idam == NULL) { + NEXT_STATE(read_address_state_1, vdrive_time_to_next_idam()); + return; + } + crc16_reset(); + if (IS_DOUBLE_DENSITY) { + crc16_byte(0xa1); + crc16_byte(0xa1); + crc16_byte(0xa1); + } + (void)vdrive_read(); + NEXT_STATE(read_address_state_2, vdrive_time_to_next_byte()); +} + +static void read_address_state_2(void *context) { + (void)context; + bytes_left = 5; + data_register = vdrive_read(); + /* At end of command, this is transferred to the sector register: */ + track_register_tmp = data_register; + SET_DRQ; + NEXT_STATE(read_address_state_3, vdrive_time_to_next_byte()); +} + +static void read_address_state_3(void *context) { + (void)context; + /* Lost data not mentioned in data sheet, so not checking + for now */ + if (bytes_left > 0) { + data_register = vdrive_read(); + bytes_left--; + SET_DRQ; + NEXT_STATE(read_address_state_3, vdrive_time_to_next_byte()); + return; + } + sector_register = track_register_tmp; + if (crc16_value() != 0) { + status_register |= STATUS_CRC_ERROR; + } + status_register &= ~(STATUS_BUSY); + SET_INTRQ; +} + +static void write_track_state_1(void *context) { + (void)context; + LOG_DEBUG(5, " write_track_state_1()\n"); + if (vdrive_write_protect) { + status_register &= ~(STATUS_BUSY); + status_register |= STATUS_WRITE_PROTECT; + SET_INTRQ; + return; + } + SET_DRQ; + NEXT_STATE(write_track_state_2, 3 * W_BYTE_TIME); +} + +static void write_track_state_2(void *context) { + (void)context; + LOG_DEBUG(5, " write_track_state_2()\n"); + if (status_register & STATUS_DRQ) { + RESET_DRQ; // XXX + status_register |= STATUS_LOST_DATA; + status_register &= ~(STATUS_BUSY); + SET_INTRQ; + return; + } + NEXT_STATE(write_track_state_2b, vdrive_time_to_next_idam()); +} + +static void write_track_state_2b(void *context) { + (void)context; + LOG_DEBUG(5, " write_track_state_2b()\n"); + if (!vdrive_new_index_pulse()) { + LOG_DEBUG(4,"Waiting for index pulse, head_pos=%04x\n", vdrive_head_pos()); + NEXT_STATE(write_track_state_2b, vdrive_time_to_next_idam()); + return; + } + LOG_DEBUG(4,"Writing track from head_pos=%04x\n", vdrive_head_pos()); + return write_track_state_3(context); +} + +static void write_track_state_3(void *context) { + unsigned int data = data_register; + (void)context; + LOG_DEBUG(5, " write_track_state_3()\n"); + if (vdrive_new_index_pulse()) { + LOG_DEBUG(4,"Finished writing track at head_pos=%04x\n", vdrive_head_pos()); + RESET_DRQ; // XXX + status_register &= ~(STATUS_BUSY); + SET_INTRQ; + return; + } + if (status_register & STATUS_DRQ) { + data = 0; + status_register |= STATUS_LOST_DATA; + } + SET_DRQ; + if (IS_SINGLE_DENSITY) { + /* Single density */ + if (data == 0xf5 || data == 0xf6) { + LOG_DEBUG(4, "Illegal value in single-density track write: %02x\n", data); + } + if (data == 0xf7) { + VDRIVE_WRITE_CRC16; + NEXT_STATE(write_track_state_3, vdrive_time_to_next_byte()); + return; + } + if (data >= 0xf8 && data <= 0xfb) { + crc16_reset(); + vdrive_write(data); + NEXT_STATE(write_track_state_3, vdrive_time_to_next_byte()); + return; + } + if (data == 0xfe) { + LOG_DEBUG(4,"IDAM at head_pos=%04x\n", vdrive_head_pos()); + crc16_reset(); + vdrive_write_idam(); + NEXT_STATE(write_track_state_3, vdrive_time_to_next_byte()); + return; + } + vdrive_write(data); + NEXT_STATE(write_track_state_3, vdrive_time_to_next_byte()); + return; + } + /* Double density */ + if (data == 0xf7) { + VDRIVE_WRITE_CRC16; + NEXT_STATE(write_track_state_3, vdrive_time_to_next_byte()); + return; + } + if (data == 0xfe) { + LOG_DEBUG(4,"IDAM at head_pos=%04x\n", vdrive_head_pos()); + vdrive_write_idam(); + NEXT_STATE(write_track_state_3, vdrive_time_to_next_byte()); + return; + } + if (data == 0xf5) { + crc16_reset(); + crc16_byte(0xa1); + crc16_byte(0xa1); + vdrive_write(0xa1); + NEXT_STATE(write_track_state_3, vdrive_time_to_next_byte()); + return; + } + if (data == 0xf6) { + data = 0xc2; + } + vdrive_write(data); + NEXT_STATE(write_track_state_3, vdrive_time_to_next_byte()); +} diff --git a/src/core/wd279x.h b/src/core/wd279x.h new file mode 100644 index 0000000..9e55376 --- /dev/null +++ b/src/core/wd279x.h @@ -0,0 +1,33 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * See COPYING.GPL for redistribution conditions. */ + +#ifndef __WD279X_H__ +#define __WD279X_H__ + +#include "types.h" + +enum WD279X_type { + WD2791, WD2793, WD2795, WD2797 +}; + +extern unsigned int wd279x_type; +extern void (*wd279x_set_drq_handler)(void); +extern void (*wd279x_reset_drq_handler)(void); +extern void (*wd279x_set_intrq_handler)(void); +extern void (*wd279x_reset_intrq_handler)(void); + +void wd279x_init(void); +void wd279x_reset(void); +void wd279x_set_density(unsigned int d); /* 0 = Double, else Single */ +void wd279x_command_write(unsigned int octet); +void wd279x_track_register_write(unsigned int octet); +void wd279x_sector_register_write(unsigned int octet); +void wd279x_data_register_write(unsigned int octet); +unsigned int wd279x_status_read(void); +unsigned int wd279x_track_register_read(void); +unsigned int wd279x_sector_register_read(void); +unsigned int wd279x_data_register_read(void); + +#endif /* __WD279X_H__ */ diff --git a/src/core/xroar.c b/src/core/xroar.c new file mode 100644 index 0000000..c0cc50e --- /dev/null +++ b/src/core/xroar.c @@ -0,0 +1,239 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * 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 "logging.h" +#include "cart.h" +#include "events.h" +#include "fs.h" +#include "m6809.h" +#include "machine.h" +#include "module.h" +#include "pia.h" +#include "sam.h" +#include "snapshot.h" +#include "xroar.h" + +int requested_frameskip; +int frameskip; +int noratelimit = 0; + +#ifdef TRACE +int trace = 0; +#endif + +static const char *snapshot_load = NULL; + +static struct { + const char *ext; + int filetype; +} filetypes[] = { + { "VDK", FILETYPE_VDK }, + { "JVC", FILETYPE_JVC }, + { "DSK", FILETYPE_JVC }, + { "DMK", FILETYPE_DMK }, + { "BIN", FILETYPE_BIN }, + { "HEX", FILETYPE_HEX }, + { "CAS", FILETYPE_CAS }, + { "WAV", FILETYPE_WAV }, + { "SN", FILETYPE_SNA }, + { NULL, FILETYPE_UNKNOWN } +}; + +static void xroar_helptext(void) { + puts(" -ui MODULE specify user-interface module (-ui help for a list)"); + puts(" -vo MODULE specify video module (-vo help for a list)"); + puts(" -ao MODULE specify audio module (-ao help for a list)"); + puts(" -fskip FRAMES specify frameskip (default: 0)"); + puts(" -snap FILENAME load snapshot after initialising"); +#ifdef TRACE + puts(" -trace start with trace mode on"); +#endif + puts(" -h, --help display this help and exit"); + puts(" --version output version information and exit"); +} + +int xroar_init(int argc, char **argv) +{ + int i; +#ifdef TRACE + trace = 0; +#endif + requested_frameskip = 0; + + /* Select a UI module then, possibly using lists specified in that + * module, select all other modules */ +# if 0 //LUDO: + ui_module = (UIModule *)module_select_by_arg((Module **)ui_module_list, "-ui", argc, argv); + /* Select file requester module */ + if (ui_module->filereq_module_list != NULL) + filereq_module_list = ui_module->filereq_module_list; + filereq_module = (FileReqModule *)module_select_by_arg((Module **)filereq_module_list, "-filereq", argc, argv); + /* Select video module */ + if (ui_module->video_module_list != NULL) + video_module_list = ui_module->video_module_list; + video_module = (VideoModule *)module_select_by_arg((Module **)video_module_list, "-vo", argc, argv); + /* Select sound module */ + if (ui_module->sound_module_list != NULL) + sound_module_list = ui_module->sound_module_list; + sound_module = (SoundModule *)module_select_by_arg((Module **)sound_module_list, "-ao", argc, argv); + /* Select keyboard module */ + if (ui_module->keyboard_module_list != NULL) + keyboard_module_list = ui_module->keyboard_module_list; + keyboard_module = NULL; + /* Select joystick module */ + if (ui_module->joystick_module_list != NULL) + joystick_module_list = ui_module->joystick_module_list; + joystick_module = NULL; + /* Look for other relevant command-line options */ + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-fskip") && i+1= argc) break; + snapshot_load = argv[i]; +#ifdef TRACE + } else if (!strcmp(argv[i], "-trace")) { + trace = 1; +#endif + } else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help") + || !strcmp(argv[i], "-help")) { + printf("Usage: xroar [OPTION]...\n\n"); + module_helptext((Module *)ui_module); + module_helptext((Module *)filereq_module); + module_helptext((Module *)video_module); + module_helptext((Module *)sound_module); + module_helptext((Module *)keyboard_module); + module_helptext((Module *)joystick_module); + machine_helptext(); + cart_helptext(); + xroar_helptext(); + exit(0); + } else + if (!strcmp(argv[i], "--version")) { +# if 0 + printf("XRoar " VERSION "\n"); +# endif + puts("Copyright (C) 2007 Ciaran Anscomb"); + puts("This is free software. You may redistribute copies of it under the terms of"); + puts("the GNU General Public License ."); + puts("There is NO WARRANTY, to the extent permitted by law."); + exit(0); + } + } + machine_getargs(argc, argv); + cart_getargs(argc, argv); +# else + ui_module = (Module **)ui_module_list[0]; + /* Select file requester module */ + if (ui_module->filereq_module_list != NULL) + filereq_module_list = ui_module->filereq_module_list; + filereq_module = filereq_module_list[0]; + /* Select video module */ + if (ui_module->video_module_list != NULL) + video_module_list = ui_module->video_module_list; + video_module = video_module_list[0]; + /* Select sound module */ + if (ui_module->sound_module_list != NULL) + sound_module_list = ui_module->sound_module_list; + sound_module = sound_module_list[0]; + /* Select keyboard module */ + if (ui_module->keyboard_module_list != NULL) + keyboard_module_list = ui_module->keyboard_module_list; + keyboard_module = NULL; + /* Select joystick module */ + if (ui_module->joystick_module_list != NULL) + joystick_module_list = ui_module->joystick_module_list; + joystick_module = NULL; +# endif + + /* Initialise everything */ + current_cycle = 0; + /* ... modules */ + module_init((Module *)ui_module, argc, argv); + filereq_module = (FileReqModule *)module_init_from_list((Module **)filereq_module_list, (Module *)filereq_module, argc, argv); + if (filereq_module == NULL && filereq_module_list != NULL) { + LOG_WARN("No file requester module initialised.\n"); + } + video_module = (VideoModule *)module_init_from_list((Module **)video_module_list, (Module *)video_module, argc, argv); + if (video_module == NULL && video_module_list != NULL) { + LOG_ERROR("No video module initialised.\n"); + return 1; + } + sound_module = (SoundModule *)module_init_from_list((Module **)sound_module_list, (Module *)sound_module, argc, argv); + if (sound_module == NULL && sound_module_list != NULL) { + LOG_ERROR("No sound module initialised.\n"); + return 1; + } + keyboard_module = (KeyboardModule *)module_init_from_list((Module **)keyboard_module_list, (Module *)keyboard_module, argc, argv); + if (keyboard_module == NULL && keyboard_module_list != NULL) { + LOG_WARN("No keyboard module initialised.\n"); + } + joystick_module = (JoystickModule *)module_init_from_list((Module **)joystick_module_list, (Module *)joystick_module, argc, argv); + if (joystick_module == NULL && joystick_module_list != NULL) { + LOG_WARN("No joystick module initialised.\n"); + } + /* ... subsystems */ + keyboard_init(); + joystick_init(); + machine_init(); + /* Reset everything */ + machine_reset(RESET_HARD); + if (snapshot_load) + read_snapshot(snapshot_load); + return 0; +} + +void xroar_shutdown(void) { + machine_shutdown(); + module_shutdown((Module *)joystick_module); + module_shutdown((Module *)keyboard_module); + module_shutdown((Module *)sound_module); + module_shutdown((Module *)video_module); + module_shutdown((Module *)filereq_module); + module_shutdown((Module *)ui_module); +} + +void xroar_mainloop(void) { + while (1) { + while (EVENT_PENDING) + DISPATCH_NEXT_EVENT; + m6809_cycle(event_list->at_cycle); + } +} + +int xroar_filetype_by_ext(const char *filename) { + char *ext; + int i; + ext = strrchr(filename, '.'); + if (ext == NULL) + return FILETYPE_UNKNOWN; + ext++; + for (i = 0; filetypes[i].ext; i++) { + if (!strncasecmp(ext, filetypes[i].ext, strlen(filetypes[i].ext))) + return filetypes[i].filetype; + } + return FILETYPE_UNKNOWN; +} diff --git a/src/core/xroar.h b/src/core/xroar.h new file mode 100644 index 0000000..61399ae --- /dev/null +++ b/src/core/xroar.h @@ -0,0 +1,42 @@ +/* XRoar - a Dragon/Tandy Coco emulator + * Copyright (C) 2003-2007 Ciaran Anscomb + * + * See COPYING.GPL for redistribution conditions. */ + +#ifndef __XROAR_H__ +#define __XROAR_H__ + +#include "types.h" + +#define RESET_SOFT 0 +#define RESET_HARD 1 + +#define FILETYPE_UNKNOWN (0) +#define FILETYPE_VDK (1) +#define FILETYPE_JVC (2) +#define FILETYPE_DMK (3) +#define FILETYPE_BIN (4) +#define FILETYPE_HEX (5) +#define FILETYPE_CAS (6) +#define FILETYPE_WAV (7) +#define FILETYPE_SNA (8) + +extern int requested_frameskip; +extern int frameskip; +extern int noratelimit; + +#ifdef TRACE +extern int trace; +# define IF_TRACE(s) if (trace) { s; } +#else +# define trace 0 +# define IF_TRACE(s) +#endif + +void xroar_getargs(int argc, char **argv); +int xroar_init(int argc, char **argv); +void xroar_shutdown(void); +void xroar_mainloop(void); +int xroar_filetype_by_ext(const char *filename); + +#endif /* __XROAR_H__ */ diff --git a/src/cpulcd.c b/src/cpulcd.c new file mode 100644 index 0000000..8497d3b --- /dev/null +++ b/src/cpulcd.c @@ -0,0 +1,293 @@ +/* EnergySaver for GP2X + + File: cpulcd.c + + Copyright (C) 2006 Kounch + Parts Copyright (c) 2005 Rlyeh + Parts Copyright (c) 2006 Hermes/PS2Reality + + 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + + +#include //Entrada/Salida basica +#include //Para poder hacer mmap y acceder a los registros de memoria +#include +#include //Para poder tener acceso al dispositivo /dev/mem y /dev/fb + +#include "cpulcd.h" + +/* Declaracion de variables globales */ +#define SYS_CLK_FREQ 7372800 + +/*Registros del sistema*/ +#ifndef __HAYMINIMAL__ + unsigned long gp2x_dev[8]={0,0,0,0,0,0,0,0}; + volatile unsigned short *gp2x_memregs; + volatile unsigned long *gp2x_memregl; +#else + extern unsigned long gp2x_dev[8]; + extern volatile unsigned short *gp2x_memregs; + extern volatile unsigned long *gp2x_memregl; +#endif +volatile unsigned short *MEM_REG; + +static struct +{ + unsigned short SYSCLKENREG,SYSCSETREG,FPLLVSETREG,DUALINT920,DUALINT940,DUALCTRL940; +} +system_reg; + + +/*Funcion que abre los dispositivos (basada en Minimal Lib)*/ +void cpulcd_init() +{ + /*Inicialización para acceder a la memoria de la consola */ +#ifndef __HAYMINIMAL__ + if(!gp2x_dev[2]) gp2x_dev[2] = open("/dev/mem", O_RDWR); + gp2x_memregl=(unsigned long *)mmap(0, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, gp2x_dev[2], 0xc0000000); + gp2x_memregs=(unsigned short *)gp2x_memregl; +#endif + MEM_REG=&gp2x_memregs[0]; +} + +/*Funcion que cierra todos los dispositivos que se hayan abierto (basada en Minimal Lib)*/ +int cpulcd_deinit(void) +{ +#ifndef __HAYMINIMAL__ + if(gp2x_dev[2]) close(gp2x_dev[2]); +#endif + return (0); +} + +/* Guardar registros de la GP2X (basado en cpuctrl de Hermes/PS2R)*/ +void save_system_regs() +{ + system_reg.SYSCSETREG=MEM_REG[0x91c>>1]; + system_reg.FPLLVSETREG=MEM_REG[0x912>>1]; + system_reg.SYSCLKENREG=MEM_REG[0x904>>1]; + system_reg.DUALINT920=MEM_REG[0x3B40>>1]; + system_reg.DUALINT940=MEM_REG[0x3B42>>1]; + system_reg.DUALCTRL940=MEM_REG[0x3B48>>1]; +} + +/* Recuperar registros de la GP2X (basado en cpuctrl de Hermes/PS2R)*/ +void load_system_regs() +{ + MEM_REG[0x91c>>1]=system_reg.SYSCSETREG; + MEM_REG[0x910>>1]=system_reg.FPLLVSETREG; + MEM_REG[0x3B40>>1]=system_reg.DUALINT920; + MEM_REG[0x3B42>>1]=system_reg.DUALINT940; + MEM_REG[0x3B48>>1]=system_reg.DUALCTRL940; + MEM_REG[0x904>>1]=system_reg.SYSCLKENREG; +} + +/* Obtener la frecuencia del reloj (basado en cpu_speed de Hermes/PS2R)*/ +unsigned get_speed_clock(void) +{ + int n,m; + unsigned sysfreq=0; + unsigned lfreq[25]={60,66,80,100,120,133,150,166,200,210,220,240,250,266,270,275,280,285,290,295,300,305,310,315,320}; // por encima de 220 MHz solo funciona con div 1 + + sysfreq=get_freq_920_CLK(); + sysfreq*=get_920_Div()+1; + m=(sysfreq/1000000)+4; + n=0; + + for(n=24;n>1;n--) + { + if(lfreq[n]63) div=63; + + set_display_clock_div(64 | div /*(16+8*(gp2x_speed>=220))*/); + set_FCLK(gp2x_speed); + set_DCLK_Div(0); + set_920_Div(0); + + return; +} + +/* (basado en cpuctrl de Hermes/PS2R)*/ +unsigned get_freq_920_CLK() +{ + unsigned i; + unsigned reg,mdiv,pdiv,scale; + reg=MEM_REG[0x912>>1]; + mdiv = ((reg & 0xff00) >> 8) + 8; + pdiv = ((reg & 0xfc) >> 2) + 2; + scale = reg & 3; + + i = (MEM_REG[0x91c>>1] & 7)+1; + return ((SYS_CLK_FREQ * mdiv) / (pdiv << scale))/i; +} + +/* (basado en cpuctrl de Hermes/PS2R)*/ +unsigned short get_920_Div() +{ + return (MEM_REG[0x91c>>1] & 0x7); +} + +/* (basado en cpuctrl de Hermes/PS2R)*/ +unsigned get_display_clock_div() +{ +return ((MEM_REG[0x924>>1]>>8)); +} + +/* (basado en cpuctrl de Hermes/PS2R)*/ +void set_display_clock_div(unsigned div) +{ + div=(div & 0xff)<<8; + MEM_REG[0x924>>1]=(MEM_REG[0x924>>1] & ~(255<<8)) | div; +} + +/* (basado en cpuctrl de Hermes/PS2R)*/ +void set_FCLK(unsigned MHZ) +{ + unsigned v; + unsigned mdiv,pdiv=3,scale=0; + + MHZ*=1000000; + mdiv=(MHZ*pdiv)/SYS_CLK_FREQ; + + mdiv=((mdiv-8)<<8) & 0xff00; + pdiv=((pdiv-2)<<2) & 0xfc; + scale&=3; + v=mdiv | pdiv | scale; + MEM_REG[0x910>>1]=v; +} + +/* (basado en cpuctrl de Hermes/PS2R)*/ +void set_DCLK_Div( unsigned short div ) +{ + unsigned short v; + v = (unsigned short)( MEM_REG[0x91c>>1] & (~(0x7 << 6)) ); + MEM_REG[0x91c>>1] = ((div & 0x7) << 6) | v; +} + +/* (basado en cpuctrl de Hermes/PS2R)*/ +void set_920_Div(unsigned short div) +{ + unsigned short v; + v = MEM_REG[0x91c>>1] & (~0x3); + MEM_REG[0x91c>>1] = (div & 0x7) | v; +} + +/* (basado en cpuctrl de Hermes/PS2R)*/ +void Disable_940() +{ +Disable_Int_940(); +MEM_REG[0x3B48>>1]|= (1 << 7); +MEM_REG[0x904>>1]&=0xfffe; +} + +/* (basado en cpuctrl de Hermes/PS2R)*/ +unsigned short Disable_Int_940() +{ +unsigned short ret; +ret=MEM_REG[0x3B42>>1]; +MEM_REG[0x3B42>>1]=0; +MEM_REG[0x3B46>>1]=0xffff; +return ret; +} + +/* (basado en cpuctrl de Hermes/PS2R)*/ +unsigned get_status_UCLK() +{ +unsigned i; + +i = MEM_REG[0x900>>1]; +i = ((i >> 7) & 1) ; +if(i) return 0; +return 1; +} + +/* (basado en cpuctrl de Hermes/PS2R)*/ +unsigned get_status_ACLK() +{ +unsigned i; + +i = MEM_REG[0x900>>1]; +i = ((i >> 8) & 1) ; +if(i) return 0; + +return 1; +} + +/* (basado en cpuctrl de Hermes/PS2R)*/ +void set_status_UCLK(unsigned s) +{ +if(s==0) MEM_REG[0x900>>1]|=128; +else MEM_REG[0x900>>1]&=~128; +} + +/* (basado en cpuctrl de Hermes/PS2R)*/ +void set_status_ACLK(unsigned s) +{ + +if(s==0) MEM_REG[0x900>>1]|=256; +else MEM_REG[0x900>>1]&=~256; +} + +/* Apagar o encender LCD (basado en cpuctrl de Hermes/PS2R)*/ +void set_display(int mode) // 1- display on 0-display off +{ + int i; + if(mode) + { + + MEM_REG[0x1062>>1] &= ~0x0c; + MEM_REG[0x1066>>1] &= ~0x10; + MEM_REG[0x106E>>1] &= ~0x06; + MEM_REG[0x106E>>1] |= 0x08; + + MEM_REG[0x280A>>1]= 0xffff; + MEM_REG[0x280C>>1]= 0xffff; + MEM_REG[0x280E>>1]= 0xffff; + MEM_REG[0x2802>>1]= 0; + MEM_REG[0x2800>>1]|= 0x0001; + + for (i=0;i<1000000;i++); + + MEM_REG[0x1062>>1] |= 0x0c; + MEM_REG[0x1066>>1] |= 0x10; + MEM_REG[0x106E>>1] &= ~0x08; + MEM_REG[0x106E>>1] |= 0x06; + + } + else + { + MEM_REG[0x106E>>1]&=~4; + MEM_REG[0x280A>>1]= 0; + MEM_REG[0x280C>>1]= 0; + MEM_REG[0x280E>>1]= 0; + MEM_REG[0x2800>>1]= 4; + MEM_REG[0x2802>>1]= 1; + } +} + diff --git a/src/cpulcd.h b/src/cpulcd.h new file mode 100644 index 0000000..54e57f4 --- /dev/null +++ b/src/cpulcd.h @@ -0,0 +1,81 @@ +/* EnergySaver for GP2X + + File: cpulcd.h + + Copyright (C) 2006 Kounch + Parts Copyright (c) 2005 Rlyeh + Parts Copyright (c) 2006 Hermes/PS2Reality + + 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef __CPULCD_H__ +#define __CPULCD_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/*Definicion de funciones*/ + +void cpulcd_init(void); + +int cpulcd_deinit(void); + +void save_system_regs(void); + +void load_system_regs(void); + +unsigned get_speed_clock(void); + +void set_speed_clock(int gp2x_speed); + +unsigned get_freq_920_CLK(); + +unsigned short get_920_Div(); + +unsigned get_display_clock_div(); + +void set_display_clock_div(unsigned div); + +void set_FCLK(unsigned MHZ); + +void set_DCLK_Div( unsigned short div ); + +void set_920_Div(unsigned short div); + +void Disable_940(void); + +unsigned short Disable_Int_940(void); + +unsigned get_status_UCLK(); + +unsigned get_status_ACLK(); + +void set_status_UCLK(unsigned s); + +void set_status_ACLK(unsigned s); + +void set_display(int mode); + + +/* Frecuencia de reloj de sistema */ +#define SYS_CLK_FREQ 7372800 + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/global.c b/src/global.c new file mode 100644 index 0000000..505a0d7 --- /dev/null +++ b/src/global.c @@ -0,0 +1,670 @@ +/* + * Copyright (C) 2006 Ludovic Jacomme (ludovic.jacomme@gmail.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include "global.h" +#include "psp_dragon.h" +#include "psp_fmgr.h" +#include "psp_sdl.h" +#include "core/keyboard_psp.h" + +//LUDO: + DRAGON_t DRAGON; + +void +dragon_set_video_artifact(int mode) +{ + DRAGON.video_artifact_mode = mode; + if (DRAGON.emu_started) { + vdg_set_mode(); + } +} + +void +dragon_default_settings() +{ + DRAGON.dragon_snd_enable = 1; + DRAGON.psp_reverse_analog = 0; + DRAGON.dragon_render_mode = DRAGON_RENDER_FIT; + DRAGON.dragon_model = 3; + DRAGON.dragon_joystick = 0; + DRAGON.dragon_joystick_step = 3; + DRAGON.dragon_joystick_anal = 1; + DRAGON.psp_cpu_clock = 220; + DRAGON.psp_display_lr = 0; + DRAGON.dragon_speed_limiter = 50; + DRAGON.dragon_auto_fire = 0; + dragon_set_video_artifact(2); + DRAGON.dragon_auto_fire_period = 6; + DRAGON.dragon_auto_fire_pressed = 0; + DRAGON.dragon_view_fps = 0; + DRAGON.psp_screenshot_id = 0; + + gp2xPowerSetClockFrequency(DRAGON.psp_cpu_clock); +} + +int +loc_dragon_save_settings(char *chFileName) +{ + FILE* FileDesc; + int error = 0; + + FileDesc = fopen(chFileName, "w"); + if (FileDesc != (FILE *)0 ) { + + fprintf(FileDesc, "psp_cpu_clock=%d\n" , DRAGON.psp_cpu_clock); + fprintf(FileDesc, "psp_reverse_analog=%d\n" , DRAGON.psp_reverse_analog); + fprintf(FileDesc, "psp_skip_max_frame=%d\n" , DRAGON.psp_skip_max_frame); + fprintf(FileDesc, "psp_display_lr=%d\n" , DRAGON.psp_display_lr); + fprintf(FileDesc, "dragon_snd_enable=%d\n" , DRAGON.dragon_snd_enable); + fprintf(FileDesc, "dragon_render_mode=%d\n" , DRAGON.dragon_render_mode); + fprintf(FileDesc, "dragon_model=%d\n" , DRAGON.dragon_model); + fprintf(FileDesc, "dragon_joystick=%d\n" , DRAGON.dragon_joystick); + fprintf(FileDesc, "dragon_joystick_step=%d\n" , DRAGON.dragon_joystick_step); + fprintf(FileDesc, "dragon_joystick_anal=%d\n" , DRAGON.dragon_joystick_anal); + fprintf(FileDesc, "dragon_speed_limiter=%d\n" , DRAGON.dragon_speed_limiter); + fprintf(FileDesc, "dragon_auto_fire_period=%d\n", DRAGON.dragon_auto_fire_period); + fprintf(FileDesc, "video_artifact_mode=%d\n" , DRAGON.video_artifact_mode); + fprintf(FileDesc, "dragon_view_fps=%d\n" , DRAGON.dragon_view_fps); + + fclose(FileDesc); + + } else { + error = 1; + } + + return error; +} + +int +dragon_save_settings(void) +{ + char FileName[MAX_PATH+1]; + int error; + + error = 1; + + snprintf(FileName, MAX_PATH, "%s/set/%s.set", DRAGON.dragon_home_dir, DRAGON.dragon_save_name); + error = loc_dragon_save_settings(FileName); + + return error; +} + +static int +loc_dragon_load_settings(char *chFileName) +{ + char Buffer[512]; + char *Scan; + unsigned int Value; + FILE* FileDesc; + + FileDesc = fopen(chFileName, "r"); + if (FileDesc == (FILE *)0 ) return 0; + + while (fgets(Buffer,512, FileDesc) != (char *)0) { + + Scan = strchr(Buffer,'\n'); + if (Scan) *Scan = '\0'; + /* For this #@$% of windows ! */ + Scan = strchr(Buffer,'\r'); + if (Scan) *Scan = '\0'; + if (Buffer[0] == '#') continue; + + Scan = strchr(Buffer,'='); + if (! Scan) continue; + + *Scan = '\0'; + Value = atoi(Scan+1); + + if (!strcasecmp(Buffer,"psp_cpu_clock")) DRAGON.psp_cpu_clock = Value; + else + if (!strcasecmp(Buffer,"psp_reverse_analog")) DRAGON.psp_reverse_analog = Value; + else + if (!strcasecmp(Buffer,"psp_skip_max_frame")) DRAGON.psp_skip_max_frame = Value; + else + if (!strcasecmp(Buffer,"psp_display_lr")) DRAGON.psp_display_lr = Value; + else + if (!strcasecmp(Buffer,"dragon_snd_enable")) DRAGON.dragon_snd_enable = Value; + else + if (!strcasecmp(Buffer,"dragon_render_mode")) DRAGON.dragon_render_mode = Value; + else + if (!strcasecmp(Buffer,"dragon_model")) DRAGON.dragon_model = Value; + else + if (!strcasecmp(Buffer,"dragon_joystick")) DRAGON.dragon_joystick = Value; + else + if (!strcasecmp(Buffer,"dragon_joystick_step")) DRAGON.dragon_joystick_step = Value; + else + if (!strcasecmp(Buffer,"dragon_joystick_anal")) DRAGON.dragon_joystick_anal = Value; + else + if (!strcasecmp(Buffer,"dragon_speed_limiter")) DRAGON.dragon_speed_limiter = Value; + else + if (!strcasecmp(Buffer,"dragon_auto_fire_period")) DRAGON.dragon_auto_fire_period = Value; + else + if (!strcasecmp(Buffer,"video_artifact_mode")) DRAGON.video_artifact_mode = Value; + else + if (!strcasecmp(Buffer,"dragon_view_fps")) DRAGON.dragon_view_fps = Value; + } + + fclose(FileDesc); + + gp2xPowerSetClockFrequency(DRAGON.psp_cpu_clock); + + return 0; +} + +int +dragon_load_settings() +{ + char FileName[MAX_PATH+1]; + int error; + + error = 1; + + snprintf(FileName, MAX_PATH, "%s/set/%s.set", DRAGON.dragon_home_dir, DRAGON.dragon_save_name); + error = loc_dragon_load_settings(FileName); + + return error; +} + +int +dragon_load_file_settings(char *FileName) +{ + return loc_dragon_load_settings(FileName); +} + +static int +loc_load_rom(char *TmpName) +{ + int error; + error = cart_insert(TmpName, 1); + return error; +} + +static int +loc_load_disk(char *TmpName) +{ + int error; + vdrive_eject_disk(0); + error = vdrive_insert_disk(0, vdisk_load( TmpName )); + return error; +} + +static int +loc_load_tape(char *TmpName) +{ + int error; + error = tape_open_reading(TmpName); + return error; +} + +void +update_save_name(char *Name) +{ + char TmpFileName[MAX_PATH]; + struct stat aStat; + int index; + char *SaveName; + char *Scan1; + char *Scan2; + + SaveName = strrchr(Name,'/'); + if (SaveName != (char *)0) SaveName++; + else SaveName = Name; + + if (!strncasecmp(SaveName, "sav_", 4)) { + Scan1 = SaveName + 4; + Scan2 = strrchr(Scan1, '_'); + if (Scan2 && (Scan2[1] >= '0') && (Scan2[1] <= '5')) { + strncpy(DRAGON.dragon_save_name, Scan1, MAX_PATH); + DRAGON.dragon_save_name[Scan2 - Scan1] = '\0'; + } else { + strncpy(DRAGON.dragon_save_name, SaveName, MAX_PATH); + } + } else { + strncpy(DRAGON.dragon_save_name, SaveName, MAX_PATH); + } + + if (DRAGON.dragon_save_name[0] == '\0') { + strcpy(DRAGON.dragon_save_name,"default"); + } + + for (index = 0; index < DRAGON_MAX_SAVE_STATE; index++) { + DRAGON.dragon_save_state[index].used = 0; + memset(&DRAGON.dragon_save_state[index].date, 0, sizeof(time_t)); + DRAGON.dragon_save_state[index].thumb = 0; + + snprintf(TmpFileName, MAX_PATH, "%s/save/sav_%s_%d.sna", DRAGON.dragon_home_dir, DRAGON.dragon_save_name, index); + if (! stat(TmpFileName, &aStat)) { + DRAGON.dragon_save_state[index].used = 1; + DRAGON.dragon_save_state[index].date = aStat.st_mtime; + snprintf(TmpFileName, MAX_PATH, "%s/save/sav_%s_%d.png", DRAGON.dragon_home_dir, DRAGON.dragon_save_name, index); + if (! stat(TmpFileName, &aStat)) { + if (psp_sdl_load_thumb_png(DRAGON.dragon_save_state[index].surface, TmpFileName)) { + DRAGON.dragon_save_state[index].thumb = 1; + } + } + } + } +} + +typedef struct thumb_list { + struct thumb_list *next; + char *name; + char *thumb; +} thumb_list; + +static thumb_list* loc_head_thumb = 0; + +static void +loc_del_thumb_list() +{ + while (loc_head_thumb != 0) { + thumb_list *del_elem = loc_head_thumb; + loc_head_thumb = loc_head_thumb->next; + if (del_elem->name) free( del_elem->name ); + if (del_elem->thumb) free( del_elem->thumb ); + free(del_elem); + } +} + +static void +loc_add_thumb_list(char* filename) +{ + thumb_list *new_elem; + char tmp_filename[MAX_PATH]; + + strcpy(tmp_filename, filename); + char* save_name = tmp_filename; + + /* .png extention */ + char* Scan = strrchr(save_name, '.'); + if ((! Scan) || (strcasecmp(Scan, ".png"))) return; + *Scan = 0; + + if (strncasecmp(save_name, "sav_", 4)) return; + save_name += 4; + + Scan = strrchr(save_name, '_'); + if (! Scan) return; + *Scan = 0; + + /* only one png for a give save name */ + new_elem = loc_head_thumb; + while (new_elem != 0) { + if (! strcasecmp(new_elem->name, save_name)) return; + new_elem = new_elem->next; + } + + new_elem = (thumb_list *)malloc( sizeof( thumb_list ) ); + new_elem->next = loc_head_thumb; + loc_head_thumb = new_elem; + new_elem->name = strdup( save_name ); + new_elem->thumb = strdup( filename ); +} + +void +load_thumb_list() +{ + char SaveDirName[MAX_PATH]; + DIR* fd = 0; + + loc_del_thumb_list(); + + snprintf(SaveDirName, MAX_PATH, "%s/save", DRAGON.dragon_home_dir); + + fd = opendir(SaveDirName); + if (!fd) return; + + struct dirent *a_dirent; + while ((a_dirent = readdir(fd)) != 0) { + if(a_dirent->d_name[0] == '.') continue; + if (a_dirent->d_type != DT_DIR) + { + loc_add_thumb_list( a_dirent->d_name ); + } + } + closedir(fd); +} + +int +load_thumb_if_exists(char *Name) +{ + char FileName[MAX_PATH]; + char ThumbFileName[MAX_PATH]; + struct stat aStat; + char *SaveName; + char *Scan; + + strcpy(FileName, Name); + SaveName = strrchr(FileName,'/'); + if (SaveName != (char *)0) SaveName++; + else SaveName = FileName; + + Scan = strrchr(SaveName,'.'); + if (Scan) *Scan = '\0'; + + if (!SaveName[0]) return 0; + + thumb_list *scan_list = loc_head_thumb; + while (scan_list != 0) { + if (! strcasecmp( SaveName, scan_list->name)) { + snprintf(ThumbFileName, MAX_PATH, "%s/save/%s", DRAGON.dragon_home_dir, scan_list->thumb); + if (! stat(ThumbFileName, &aStat)) + { + if (psp_sdl_load_thumb_png(save_surface, ThumbFileName)) { + return 1; + } + } + } + scan_list = scan_list->next; + } + return 0; +} + + + + +void +reset_save_name() +{ + update_save_name(""); +} + +void +dragon_reset_computer() +{ + machine_reset(1); +} + +void +dragon_kbd_load(void) +{ + char TmpFileName[MAX_PATH + 1]; + struct stat aStat; + + snprintf(TmpFileName, MAX_PATH, "%s/kbd/%s.kbd", DRAGON.dragon_home_dir, DRAGON.dragon_save_name ); + if (! stat(TmpFileName, &aStat)) { + psp_kbd_load_mapping(TmpFileName); + } +} + +int +dragon_kbd_save(void) +{ + char TmpFileName[MAX_PATH + 1]; + snprintf(TmpFileName, MAX_PATH, "%s/kbd/%s.kbd", DRAGON.dragon_home_dir, DRAGON.dragon_save_name ); + return( psp_kbd_save_mapping(TmpFileName) ); +} + + +int +dragon_load_rom(char *FileName) +{ + char *scan; + char SaveName[MAX_PATH+1]; + int error; + + strncpy(SaveName,FileName,MAX_PATH); + scan = strrchr(SaveName,'.'); + if (scan) *scan = '\0'; + update_save_name(SaveName); + error = loc_load_rom(FileName); + + if (! error ) { + dragon_reset_computer(); + dragon_kbd_load(); + dragon_load_settings(); + } + + return error; +} + +int +dragon_load_tape(char *FileName) +{ + char *scan; + char SaveName[MAX_PATH+1]; + int error; + + error = 1; + + strncpy(SaveName,FileName,MAX_PATH); + scan = strrchr(SaveName,'.'); + if (scan) *scan = '\0'; + update_save_name(SaveName); + error = loc_load_tape(FileName); + + if (! error ) { + dragon_reset_computer(); + dragon_kbd_load(); + dragon_load_settings(); + } + + return error; +} + +void +dragon_eject_rom() +{ + cart_remove(); + machine_reset(1); + reset_save_name(); +} + +int +dragon_load_disk(char *FileName) +{ + char *scan; + char SaveName[MAX_PATH+1]; + int error; + + error = 1; + + strncpy(SaveName,FileName,MAX_PATH); + scan = strrchr(SaveName,'.'); + if (scan) *scan = '\0'; + update_save_name(SaveName); + error = loc_load_disk(FileName); + + if (! error ) { + dragon_kbd_load(); + dragon_load_settings(); + } + + return error; +} + +static int +loc_load_state(char *filename) +{ + int error; + error = read_snapshot(filename); + return error; +} + +int +dragon_load_state(char *FileName) +{ + char *scan; + char SaveName[MAX_PATH+1]; + int error; + + error = 1; + + strncpy(SaveName,FileName,MAX_PATH); + scan = strrchr(SaveName,'.'); + if (scan) *scan = '\0'; + update_save_name(SaveName); + error = read_snapshot(FileName); + + if (! error ) { + dragon_kbd_load(); + dragon_load_settings(); + } + + return error; +} + +int +dragon_snapshot_save_slot(int save_id) +{ + char FileName[MAX_PATH+1]; + struct stat aStat; + int error; + + error = 1; + + if (save_id < DRAGON_MAX_SAVE_STATE) { + snprintf(FileName, MAX_PATH, "%s/save/sav_%s_%d.sna", DRAGON.dragon_home_dir, DRAGON.dragon_save_name, save_id); + error = write_snapshot(FileName); + if (! error) { + if (! stat(FileName, &aStat)) { + DRAGON.dragon_save_state[save_id].used = 1; + DRAGON.dragon_save_state[save_id].thumb = 0; + DRAGON.dragon_save_state[save_id].date = aStat.st_mtime; + snprintf(FileName, MAX_PATH, "%s/save/sav_%s_%d.png", DRAGON.dragon_home_dir, DRAGON.dragon_save_name, save_id); + if (psp_sdl_save_thumb_png(DRAGON.dragon_save_state[save_id].surface, FileName)) { + DRAGON.dragon_save_state[save_id].thumb = 1; + } + } + } + } + + return error; +} + +int +dragon_snapshot_load_slot(int load_id) +{ + char FileName[MAX_PATH+1]; + int error; + + error = 1; + + if (load_id < DRAGON_MAX_SAVE_STATE) { + snprintf(FileName, MAX_PATH, "%s/save/sav_%s_%d.sna", DRAGON.dragon_home_dir, DRAGON.dragon_save_name, load_id); + error = loc_load_state(FileName); + } + return error; +} + +int +dragon_snapshot_del_slot(int save_id) +{ + char FileName[MAX_PATH+1]; + struct stat aStat; + int error; + + error = 1; + + if (save_id < DRAGON_MAX_SAVE_STATE) { + snprintf(FileName, MAX_PATH, "%s/save/sav_%s_%d.sna", DRAGON.dragon_home_dir, DRAGON.dragon_save_name, save_id); + error = remove(FileName); + if (! error) { + DRAGON.dragon_save_state[save_id].used = 0; + DRAGON.dragon_save_state[save_id].thumb = 0; + memset(&DRAGON.dragon_save_state[save_id].date, 0, sizeof(time_t)); + + /* We keep always thumbnail with id 0, to have something to display in the file requester */ + if (save_id != 0) { + snprintf(FileName, MAX_PATH, "%s/save/sav_%s_%d.png", DRAGON.dragon_home_dir, DRAGON.dragon_save_name, save_id); + if (! stat(FileName, &aStat)) { + remove(FileName); + } + } + } + } + + return error; +} + +void +dragon_audio_pause() +{ + SDL_PauseAudio(1); +} + +void +dragon_audio_resume() +{ + SDL_PauseAudio(0); +} + +void +dragon_global_init() +{ + memset(&DRAGON, 0, sizeof(DRAGON_t)); + getcwd(DRAGON.dragon_home_dir,MAX_PATH); + + psp_sdl_init(); + + dragon_default_settings(); + update_save_name(""); + dragon_load_settings(); + + gp2xPowerSetClockFrequency(DRAGON.psp_cpu_clock); +} + +void +dragon_treat_command_key(int dragon_idx) +{ + int new_render; + + switch (dragon_idx) + { + case CMD_FPS: DRAGON.dragon_view_fps = ! DRAGON.dragon_view_fps; + break; + case CMD_JOY: DRAGON.psp_reverse_analog = ! DRAGON.psp_reverse_analog; + break; + case CMD_RENDER: + psp_sdl_black_screen(); + new_render = DRAGON.dragon_render_mode + 1; + if (new_render > DRAGON_LAST_RENDER) new_render = 0; + DRAGON.dragon_render_mode = new_render; + break; + case CMD_LOAD: psp_main_menu_load_current(); + break; + case CMD_SAVE: psp_main_menu_save_current(); + break; + case CMD_RESET: + psp_sdl_black_screen(); + dragon_reset_computer(0); + reset_save_name(); + break; + case CMD_AUTOFIRE: + kbd_change_auto_fire(! DRAGON.dragon_auto_fire); + break; + case CMD_DECFIRE: + if (DRAGON.dragon_auto_fire_period > 0) DRAGON.dragon_auto_fire_period--; + break; + case CMD_INCFIRE: + if (DRAGON.dragon_auto_fire_period < 19) DRAGON.dragon_auto_fire_period++; + break; + case CMD_SCREEN: psp_screenshot_mode = 10; + break; + } +} + diff --git a/src/global.h b/src/global.h new file mode 100644 index 0000000..69587f6 --- /dev/null +++ b/src/global.h @@ -0,0 +1,95 @@ +#ifndef _GLOBAL_H_ +#define _GLOBAL_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; + +#include "gp2x_psp.h" +#include + +# ifndef CLK_TCK +# define CLK_TCK CLOCKS_PER_SEC +# endif + +#define CPU_CLOCK_IDLE 60 +#define CPU_CLOCK_STD 200 + + +# define DRAGON_RENDER_NORMAL 0 +# define DRAGON_RENDER_FIT 1 +# define DRAGON_LAST_RENDER 1 + +# define MAX_PATH 256 +# define DRAGON_MAX_SAVE_STATE 5 + +# define DRAGON_LOAD_K7_MODE 0 +# define DRAGON_LOAD_DISK_MODE 1 +# define DRAGON_MAX_LOAD_MODE 1 + +# define DRAGON_SCREEN_W 320 +# define DRAGON_SCREEN_H 240 + +#define REAL_DRAGON_W 256 +#define REAL_DRAGON_H 192 + +# define DRAGON_HEIGHT DRAGON_SCREEN_H +# define DRAGON_WIDTH DRAGON_SCREEN_W +# define SNAP_WIDTH (REAL_DRAGON_W/2) +# define SNAP_HEIGHT (REAL_DRAGON_H/2) + +#include + + typedef struct DRAGON_save_t { + + SDL_Surface *surface; + char used; + char thumb; + time_t date; + + } DRAGON_save_t; + + typedef struct DRAGON_t { + + DRAGON_save_t dragon_save_state[DRAGON_MAX_SAVE_STATE]; + char dragon_save_name[MAX_PATH]; + char dragon_home_dir[MAX_PATH]; + int dragon_speed_limiter; + int psp_screenshot_id; + int psp_cpu_clock; + int psp_reverse_analog; + int dragon_snd_enable; + int dragon_render_mode; + int dragon_view_fps; + int dragon_current_fps; + int dragon_joystick; + int dragon_joystick_step; + int dragon_joystick_anal; + int dragon_model; + int psp_display_lr; + int psp_skip_max_frame; + int psp_skip_cur_frame; + int dragon_auto_fire; + int dragon_auto_fire_pressed; + int dragon_auto_fire_period; + int video_artifact_mode; + int emu_started; + + } DRAGON_t; + + extern int dragon_in_menu; + + extern DRAGON_t DRAGON; + + extern void dragon_global_init(); + +//END_LUDO: +#ifdef __cplusplus +} +#endif + +# endif diff --git a/src/gp2x_psp.c b/src/gp2x_psp.c new file mode 100644 index 0000000..ceb70be --- /dev/null +++ b/src/gp2x_psp.c @@ -0,0 +1,248 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "global.h" +#include "gp2x_psp.h" + +/* For internal use only */ +# define GP2X_CTRL_UPRIGHT 0x10000 +# define GP2X_CTRL_UPLEFT 0x20000 +# define GP2X_CTRL_DOWNRIGHT 0x40000 +# define GP2X_CTRL_DOWNLEFT 0x80000 + + +static int loc_Volume = 50; + +static int loc_LastEventMask = 0; +static int loc_CurrEventMask = 0; +static int loc_CurrEventButtons = 0; +static u32 loc_LastTimeStamp = 0; +static u32 loc_CurrTimeStamp = 0; + +static int loc_VolumeButtons = 0; +static int loc_VolumePress = 0; +static u32 loc_LastVolumeTimeStamp = 0; + +# define GP2X_MIN_TIME_VOLUME 300000 +# define GP2X_MIN_TIME_REPEAT 100000 + +static inline int +gp2xConvertMaskToButtons(int Mask) +{ + int Buttons = Mask & GP2X_CTRL_MASK; + if (Mask & GP2X_CTRL_UPLEFT ) Buttons |= GP2X_CTRL_UP|GP2X_CTRL_LEFT; + if (Mask & GP2X_CTRL_UPRIGHT) Buttons |= GP2X_CTRL_UP|GP2X_CTRL_RIGHT; + if (Mask & GP2X_CTRL_DOWNLEFT ) Buttons |= GP2X_CTRL_DOWN|GP2X_CTRL_LEFT; + if (Mask & GP2X_CTRL_DOWNRIGHT) Buttons |= GP2X_CTRL_DOWN|GP2X_CTRL_RIGHT; + return Buttons; +} + +void +gp2xTreatVolume(gp2xCtrlData* c) +{ + if (c->Buttons & GP2X_CTRL_VOLDOWN) { + /* Already down ? */ + if (! (loc_VolumeButtons & GP2X_CTRL_VOLDOWN)) { + loc_LastVolumeTimeStamp = loc_CurrTimeStamp; + loc_VolumeButtons |= GP2X_CTRL_VOLDOWN; + loc_VolumePress = 1; + gp2xDecreaseVolume(); + } else + if ((((loc_CurrTimeStamp - loc_LastVolumeTimeStamp) > GP2X_MIN_TIME_VOLUME) && (loc_VolumePress == 1)) || + (((loc_CurrTimeStamp - loc_LastVolumeTimeStamp) > GP2X_MIN_TIME_REPEAT) && (loc_VolumePress > 1))) { + loc_LastVolumeTimeStamp = loc_CurrTimeStamp; + loc_VolumePress++; + gp2xDecreaseVolume(); + } + + } else + if (c->Buttons & GP2X_CTRL_VOLUP) { + /* Already down ? */ + if (! (loc_VolumeButtons & GP2X_CTRL_VOLUP)) { + loc_LastVolumeTimeStamp = loc_CurrTimeStamp; + loc_VolumeButtons |= GP2X_CTRL_VOLUP; + loc_VolumePress = 1; + gp2xIncreaseVolume(); + } else + if ((((loc_CurrTimeStamp - loc_LastVolumeTimeStamp) > GP2X_MIN_TIME_VOLUME) && (loc_VolumePress == 1)) || + (((loc_CurrTimeStamp - loc_LastVolumeTimeStamp) > GP2X_MIN_TIME_REPEAT) && (loc_VolumePress > 1))) { + loc_LastVolumeTimeStamp = loc_CurrTimeStamp; + loc_VolumePress++; + gp2xIncreaseVolume(); + } + + } else { + loc_VolumeButtons = 0; + } +} + +int +gp2xCtrlPeekBufferPositive(gp2xCtrlData* c, int v) +{ + SDL_Event SDLEvent; + + int Event = 0; + int ButtonPress = 0; + int ButtonRelease = 0; + int Mask = 0; + + memset(c, 0x0, sizeof(gp2xCtrlData)); + loc_CurrTimeStamp = SDL_GetTicks() * 1000; + + if (SDL_PollEvent(&SDLEvent)) { +#if defined(MIYOO_MODE) + Event=((SDL_KeyboardEvent*)(&SDLEvent))->keysym.scancode; + if (SDLEvent.type==SDL_KEYDOWN) { + ButtonPress = 1; + } else + if (SDLEvent.type==SDL_KEYUP) { + ButtonRelease = 1; + } +#else +#ifdef GP2X_MODE + Event = SDLEvent.jbutton.button; + if (SDLEvent.type==SDL_JOYBUTTONDOWN) ButtonPress = 1; + else + if (SDLEvent.type==SDL_JOYBUTTONUP) ButtonRelease = 1; +#else + Event=((SDL_KeyboardEvent*)(&SDLEvent))->keysym.scancode; + if (SDLEvent.type==SDL_KEYDOWN) { + ButtonPress = 1; + } else + if (SDLEvent.type==SDL_KEYUP) { + ButtonRelease = 1; + } +#endif +#endif // MIYOO_MODE + switch (Event) { + case GP2X_UP : Mask = GP2X_CTRL_UP; + break; + case GP2X_DOWN : Mask = GP2X_CTRL_DOWN; + break; + case GP2X_LEFT : Mask = GP2X_CTRL_LEFT; + break; + case GP2X_RIGHT : Mask = GP2X_CTRL_RIGHT; + break; + case GP2X_UPLEFT : Mask = GP2X_CTRL_UPLEFT; + break; + case GP2X_UPRIGHT : Mask = GP2X_CTRL_UPRIGHT; + break; + case GP2X_DOWNLEFT : Mask = GP2X_CTRL_DOWNLEFT; + break; + case GP2X_DOWNRIGHT : Mask = GP2X_CTRL_DOWNRIGHT; + break; + case GP2X_A : Mask = GP2X_CTRL_SQUARE; + break; + case GP2X_B : Mask = GP2X_CTRL_CIRCLE; + break; + case GP2X_X : Mask = GP2X_CTRL_CROSS; + break; + case GP2X_Y : Mask = GP2X_CTRL_TRIANGLE; + break; + case GP2X_L : Mask = GP2X_CTRL_LTRIGGER; + break; + case GP2X_R : Mask = GP2X_CTRL_RTRIGGER; + break; + case GP2X_FIRE : Mask = GP2X_CTRL_FIRE; + break; + case GP2X_START : Mask = GP2X_CTRL_START; + break; + case GP2X_SELECT : Mask = GP2X_CTRL_SELECT; + break; + case GP2X_VOLUP : Mask = GP2X_CTRL_VOLUP; + break; + case GP2X_VOLDOWN : Mask = GP2X_CTRL_VOLDOWN; + break; + } + loc_LastEventMask = loc_CurrEventMask; + if (ButtonPress) { + loc_CurrEventMask |= Mask; + } else + if (ButtonRelease) { + loc_CurrEventMask &= ~Mask; + } + loc_CurrEventButtons = gp2xConvertMaskToButtons(loc_CurrEventMask); + c->Buttons = loc_CurrEventButtons; + c->TimeStamp = loc_CurrTimeStamp; + + loc_LastTimeStamp = loc_CurrTimeStamp; + + } else { + c->Buttons = loc_CurrEventButtons; + c->TimeStamp = loc_CurrTimeStamp; + } + + gp2xTreatVolume(c); + + return (c->Buttons != 0); +} + +int +gp2xCtrlReadBufferPositive(gp2xCtrlData* c, int v) +{ + while (! gp2xCtrlPeekBufferPositive(c, v)); + return 1; +} + + +extern void set_speed_clock(int freq); +void +gp2xPowerSetClockFrequency(int freq) +{ +# ifdef GP2X_MODE + if ((freq >= 33) && (freq <= 266)) { + set_speed_clock(freq); + } +# endif +} + +int +gp2xGetSoundVolume() +{ + return loc_Volume; +} + +void +gp2xDecreaseVolume() +{ + loc_Volume -= 2; + if (loc_Volume < 0) loc_Volume = 0; +} + +void +gp2xIncreaseVolume() +{ + loc_Volume += 2; + if (loc_Volume > 100) loc_Volume = 100; +} + +int +gp2xInsmodMMUhack(void) +{ +# ifdef GP2X_MMU_HACK + int mmufd = open("/dev/mmuhack", O_RDWR); + if(mmufd < 0) { + system("/sbin/insmod ./drivers/mmuhack.o"); + mmufd = open("/dev/mmuhack", O_RDWR); + } + if(mmufd < 0) return 0; + close(mmufd); +# endif + return 1; +} + +int +gp2xRmmodMMUhack(void) +{ +# ifdef GP2X_MMU_HACK + system("/sbin/rmmod mmuhack"); +# endif + return 0; +} diff --git a/src/gp2x_psp.h b/src/gp2x_psp.h new file mode 100644 index 0000000..a9c6d67 --- /dev/null +++ b/src/gp2x_psp.h @@ -0,0 +1,137 @@ +# ifndef __SDL_JOY_H__ +# define __SDL_JOY_H__ + +# ifdef __cplusplus +extern "C" { +# endif + +#include "global.h" + +typedef struct gp2xCtrlData { + + int Buttons; + u32 TimeStamp; + +} gp2xCtrlData; + + +# define GP2X_CTRL_UP 0x00001 +# define GP2X_CTRL_RIGHT 0x00002 +# define GP2X_CTRL_DOWN 0x00004 +# define GP2X_CTRL_LEFT 0x00008 +# define GP2X_CTRL_TRIANGLE 0x00010 +# define GP2X_CTRL_CIRCLE 0x00020 +# define GP2X_CTRL_CROSS 0x00040 +# define GP2X_CTRL_SQUARE 0x00080 +# define GP2X_CTRL_SELECT 0x00100 +# define GP2X_CTRL_START 0x00200 +# define GP2X_CTRL_LTRIGGER 0x00400 +# define GP2X_CTRL_RTRIGGER 0x00800 +# define GP2X_CTRL_FIRE 0x01000 +# define GP2X_CTRL_VOLUP 0x02000 +# define GP2X_CTRL_VOLDOWN 0x04000 +# define GP2X_CTRL_MASK 0x07fff + + +#ifdef MIYOO_MODE + +#define GP2X_UP (103) +#define GP2X_DOWN (108) +#define GP2X_LEFT (105) +#define GP2X_RIGHT (106) + +#define GP2X_A (57) +#define GP2X_X (29) +#define GP2X_B (56) +#define GP2X_Y (42) +#define GP2X_L (15) +#define GP2X_R (14) +#define GP2X_SELECT (97) // Select 1 +#define GP2X_START (1) // Start 28 +#define GP2X_FIRE (28) // Reset 97 +#define GP2X_VOLDOWN (-7) +#define GP2X_VOLUP (-8) + +#define GP2X_UPLEFT (-3) +#define GP2X_UPRIGHT (-4) +#define GP2X_DOWNLEFT (-5) +#define GP2X_DOWNRIGHT (-6) + +#else //MIYOO_MODE + +#ifdef GP2X_MODE + +//gp2x buttons codes + +#define GP2X_UP (0) +#define GP2X_DOWN (4) +#define GP2X_LEFT (2) +#define GP2X_RIGHT (6) +#define GP2X_UPLEFT (1) +#define GP2X_UPRIGHT (7) +#define GP2X_DOWNLEFT (3) +#define GP2X_DOWNRIGHT (5) + +#define GP2X_A (12) +#define GP2X_B (13) +#define GP2X_X (14) +#define GP2X_Y (15) +#define GP2X_L (10) +#define GP2X_R (11) +#define GP2X_FIRE (18) +#define GP2X_START (8) +#define GP2X_SELECT (9) +#define GP2X_VOLUP (16) +#define GP2X_VOLDOWN (17) + +#else + +//some keys of the keyboard to emulate gp2x + +#define GP2X_UPLEFT 79 //SDLK_KP7 +#define GP2X_UP 80 //SDLK_KP8 +#define GP2X_UPRIGHT 81 //SDLK_KP9 + +#define GP2X_LEFT 83 //SDLK_KP4 +#define GP2X_RIGHT 85 //SDLK_KP6 + +#define GP2X_DOWNLEFT 87 //SDLK_KP1 +#define GP2X_DOWN 88 //SDLK_KP2 +#define GP2X_DOWNRIGHT 89 //SDLK_KP3 + +#define GP2X_A 38 //SDLK_a +#define GP2X_B 56 //SDLK_b +#define GP2X_X 53 //SDLK_x +#define GP2X_Y 29 //SDLK_y +#define GP2X_L 46 //SDLK_l +#define GP2X_R 27 //SDLK_r +#define GP2X_FIRE 65 //SDLK_SPACE +#define GP2X_START 36 //SDLK_RETURN +#define GP2X_SELECT 39 //SDLK_s +#define GP2X_VOLUP 86 //SDLK_KP_PLUS +#define GP2X_VOLDOWN 82 //SDLK_KP_MINUS + +#endif +#endif //MIYOO_MODE + +#define GP2X_NOEVENT -1 + +#define DELAY_KEY_FIRST 250 +#define DELAY_KEY_REPEAT 80 + + extern int gp2xCtrlReadBufferPositive(gp2xCtrlData* c, int v); + extern int gp2xCtrlPeekBufferPositive(gp2xCtrlData* c, int v); + extern void gp2xPowerSetClockFrequency(int freq); + + extern int gp2xGetSoundVolume(); + extern void gp2xDecreaseVolume(); + extern void gp2xIncreaseVolume(); + + extern int gp2xInsmodMMUhack(); + extern int gp2xRmmodMMUhack(); + +# ifdef __cplusplus +} +# endif + +# endif diff --git a/src/gp2xdragon.gpe b/src/gp2xdragon.gpe new file mode 100755 index 0000000..5c720fd Binary files /dev/null and b/src/gp2xdragon.gpe differ diff --git a/src/psp_danzeff.c b/src/psp_danzeff.c new file mode 100644 index 0000000..b93379c --- /dev/null +++ b/src/psp_danzeff.c @@ -0,0 +1,342 @@ +#include +#include +#include +#include "global.h" +#include "psp_danzeff.h" +#include "SDL_image.h" + +#define false 0 +#define true 1 + + int psp_kbd_last_skin = 0; + int psp_kbd_skin = -1; + char *psp_kbd_skin_dir[PSP_KBD_MAX_SKIN]; + int psp_kbd_skin_first = 1; + +static /*bool*/ int holding = false; //user is holding a button +static /*bool*/ int dirty = true; //keyboard needs redrawing +static /*bool*/ int shifted = false; //user is holding shift +static int mode = 0; //charset selected. (0 - letters or 1 - numbers) +static /*bool*/ int initialized = false; //keyboard is initialized + +//Position on the 3-3 grid the user has selected (range 0-2) +static int selected_x = 1; +static int selected_y = 1; + +//Variable describing where each of the images is +#define guiStringsSize 12 /* size of guistrings array */ +#define PICS_BASEDIR "./graphics/" + +static char *guiStrings[] = +{ + "keys.png", "keys_t.png", "keys_s.png", + "keys_c.png", "keys_c_t.png", "keys_s_c.png", + "nums.png", "nums_t.png", "nums_s.png", + "nums_c.png", "nums_c_t.png", "nums_s_c.png" +}; + +//amount of modes (non shifted), each of these should have a corresponding shifted mode. +#define MODE_COUNT 2 +//this is the layout of the keyboard +static char modeChar[MODE_COUNT*2][3][3][5] = +{ + { //standard letters + { ",ABC", ".DEF","!GHI" }, + { "-JKL", { DANZEFF_DEL, 'M', ' ', 'N', '\0' }, "?OPQ" }, + { "(RST", ":UVW",")XYZ" } + }, + + { //capital letters + { "^ABC", "@DEF","*GHI" }, + { "_JKL", { DANZEFF_DEL, 'M', ' ', 'N', '\0' }, "\"OPQ" }, + { "=RST", ";UVW","/XYZ" } + }, + + { //numbers + { { DANZEFF_TAB , DANZEFF_CONTROL, DANZEFF_SHIFT , '1', '\0' }, + { 0 , DANZEFF_SUPPR , DANZEFF_CAPSLOCK , '2', '\0' }, + { DANZEFF_SUPPR , DANZEFF_ALT , DANZEFF_SHIFT , '3', '\0' }}, + + { { 0 , DANZEFF_SHIFT, 0 , '4', '\0' }, + { DANZEFF_DEL , 0 , ' ', '5', '\0' }, + { DANZEFF_ENTER , 0 , 0 , '6', '\0' }}, + + { { 0 , DANZEFF_ENTER, 0 , '7', '\0' }, + { 0 , 0 , 0 , '8', '\0' }, + { 0 , 0 , '0', '9', '\0' }}, + }, + + { //special characters + { ",(.)", "\"<'>","-[_]" }, + { "!{?}","\010\0 \0", "+\\=/" }, + { ":@;#", "~$`%","*^|&" } + } +}; + +int +danzeff_isinitialized() +{ + return initialized; +} + +int +danzeff_dirty() +{ + return dirty; +} + +/** Attempts to read a character from the controller +* If no character is pressed then we return 0 +* Other special values: 1 = move left, 2 = move right, 3 = select, 4 = start +* Every other value should be a standard ascii value. +* An unsigned int is returned so in the future we can support unicode input +*/ +unsigned int +danzeff_readInput(gp2xCtrlData pspctrl) +{ + //Work out where the analog stick is selecting + int x = 1; + int y = 1; + if (pspctrl.Buttons & GP2X_CTRL_LEFT ) x -= 1; + if (pspctrl.Buttons & GP2X_CTRL_RIGHT) x += 1; + if (pspctrl.Buttons & GP2X_CTRL_UP ) y -= 1; + if (pspctrl.Buttons & GP2X_CTRL_DOWN) y += 1; + + if (selected_x != x || selected_y != y) //If they've moved, update dirty + { + dirty = true; + selected_x = x; + selected_y = y; + } + #ifndef MIYOO_MODE + //if they are changing shift then that makes it dirty too + if ((!shifted && (pspctrl.Buttons & GP2X_CTRL_RTRIGGER)) || (shifted && !(pspctrl.Buttons & GP2X_CTRL_RTRIGGER))) + dirty = true; + #endif + + unsigned int pressed = 0; //character they have entered, 0 as that means 'nothing' + #ifndef MIYOO_MODE + shifted = (pspctrl.Buttons & GP2X_CTRL_RTRIGGER)?true:false; + #endif + + if (!holding) + { + if (pspctrl.Buttons& (GP2X_CTRL_CROSS|GP2X_CTRL_CIRCLE|GP2X_CTRL_TRIANGLE|GP2X_CTRL_SQUARE)) //pressing a char select button + { + int innerChoice = 0; + if (pspctrl.Buttons & GP2X_CTRL_TRIANGLE) + innerChoice = 0; + else if (pspctrl.Buttons & GP2X_CTRL_SQUARE) + innerChoice = 1; + else if (pspctrl.Buttons & GP2X_CTRL_CROSS) + innerChoice = 2; + else //if (pspctrl.Buttons & GP2X_CTRL_CIRCLE) + innerChoice = 3; + + //Now grab the value out of the array + pressed = modeChar[ mode*2 + shifted][y][x][innerChoice]; + } + #ifdef MIYOO_MODE + else if (pspctrl.Buttons& GP2X_CTRL_FIRE) //toggle mode + #else + else if (pspctrl.Buttons& GP2X_CTRL_LTRIGGER) //toggle mode + #endif + { + dirty = true; + #ifdef MIYOO_MODE + if(shifted) + { + #endif + mode++; + mode %= MODE_COUNT; + #ifdef MIYOO_MODE + } + shifted = !shifted; + #endif + } + else if (pspctrl.Buttons& GP2X_CTRL_SELECT) + { + pressed = DANZEFF_SELECT; //SELECT + } + else if (pspctrl.Buttons& GP2X_CTRL_START) + { + pressed = DANZEFF_START; //START + } + } + +//RTRIGGER doesn't set holding + holding = pspctrl.Buttons & ~(GP2X_CTRL_RTRIGGER|GP2X_CTRL_UP|GP2X_CTRL_DOWN|GP2X_CTRL_LEFT|GP2X_CTRL_RIGHT); + + return pressed; +} + +///----------------------------------------------------------- +///These are specific to the implementation, they should have the same behaviour across implementations. +///----------------------------------------------------------- + + +///This is the original SDL implementation +#ifdef DANZEFF_SDL + +static SDL_Surface* keyBits[guiStringsSize]; +static int moved_x = 0, moved_y = 0; // location that we are moved to + +///variable needed for rendering in SDL, the screen surface to draw to, and a function to set it! +static SDL_Surface* danzeff_screen; +static SDL_Rect danzeff_screen_rect; + +void +danzeff_set_screen(SDL_Surface* screen) +{ + danzeff_screen = screen; + danzeff_screen_rect.x = 0; + danzeff_screen_rect.y = 0; + danzeff_screen_rect.h = screen->h; + danzeff_screen_rect.w = screen->w; + + moved_x = danzeff_screen->w - 150; + moved_y = danzeff_screen->h - 150; +} + + +///Internal function to draw a surface internally offset +//Render the given surface at the current screen position offset by screenX, screenY +//the surface will be internally offset by offsetX,offsetY. And the size of it to be drawn will be intWidth,intHeight +void +surface_draw_offset(SDL_Surface* pixels, int screenX, int screenY, int offsetX, int offsetY, int intWidth, int intHeight) +{ + //move the draw position + danzeff_screen_rect.x = moved_x + screenX; + danzeff_screen_rect.y = moved_y + screenY; + + //Set up the rectangle + SDL_Rect pixels_rect; + pixels_rect.x = offsetX; + pixels_rect.y = offsetY; + pixels_rect.w = intWidth; + pixels_rect.h = intHeight; + + SDL_BlitSurface(pixels, &pixels_rect, danzeff_screen, &danzeff_screen_rect); +} + +///Draw a surface at the current moved_x, moved_y +void +surface_draw(SDL_Surface* pixels) +{ + surface_draw_offset(pixels, 0, 0, 0, 0, pixels->w, pixels->h); +} + +void +danzeff_init_skin() +{ + int index; + + psp_kbd_last_skin = psp_fmgr_get_dir_list(PICS_BASEDIR, PSP_KBD_MAX_SKIN, psp_kbd_skin_dir) - 1; + + /* Should not happen ! */ + if (psp_kbd_last_skin < 0) { + fprintf(stdout, "no keyboard skin in %s directory !\n", PICS_BASEDIR); + exit(1); + } + + if ((psp_kbd_skin == -1) || (psp_kbd_skin > psp_kbd_last_skin)) { + psp_kbd_skin_first = 0; + for (psp_kbd_skin = 0; psp_kbd_skin <= psp_kbd_last_skin; psp_kbd_skin++) { + if (!strcasecmp(psp_kbd_skin_dir[psp_kbd_skin], "default/")) break; + } + if (psp_kbd_skin > psp_kbd_last_skin) psp_kbd_skin = 0; + } +} + +/* load all the guibits that make up the OSK */ +int +danzeff_load() +{ + char tmp_filename[128]; + + if (initialized) return 1; + + if (psp_kbd_skin_first) { + danzeff_init_skin(); + } + int a; + for (a = 0; a < guiStringsSize; a++) + { + strcpy(tmp_filename, PICS_BASEDIR); + strcat(tmp_filename, psp_kbd_skin_dir[psp_kbd_skin] ); + strcat(tmp_filename, guiStrings[a] ); + keyBits[a] = IMG_Load(tmp_filename); + if (keyBits[a] == NULL) + { + //ERROR! out of memory. + //free all previously created surfaces and set initialized to false + int b; + for (b = 0; b < a; b++) + { + SDL_FreeSurface(keyBits[b]); + keyBits[b] = NULL; + } + initialized = false; + fprintf(stdout, "can't load image %s\n", tmp_filename); + exit(1); + } + } + initialized = true; + return 1; +} + +/* remove all the guibits from memory */ +void +danzeff_free() +{ + if (!initialized) return; + + int a; + for (a = 0; a < guiStringsSize; a++) + { + SDL_FreeSurface(keyBits[a]); + keyBits[a] = NULL; + } + initialized = false; +} + +/* draw the keyboard at the current position */ +void +danzeff_render() +{ + dirty = false; + + ///Draw the background for the selected keyboard either transparent or opaque + ///this is the whole background image, not including the special highlighted area + //if center is selected then draw the whole thing opaque + if (selected_x == 1 && selected_y == 1) + surface_draw(keyBits[6*mode + shifted*3]); + else + surface_draw(keyBits[6*mode + shifted*3 + 1]); + + ///Draw the current Highlighted Selector (orange bit) + surface_draw_offset(keyBits[6*mode + shifted*3 + 2], + //Offset from the current draw position to render at + selected_x*43, selected_y*43, + //internal offset of the image + selected_x*64,selected_y*64, + //size to render (always the same) + 64, 64); +} + +/* move the position the keyboard is currently drawn at */ +void +danzeff_moveTo(const int newX, const int newY) +{ + moved_x = danzeff_screen->w - 150 + newX; + moved_y = danzeff_screen->h - 150 + newY; +} + +void +danzeff_change_skin() +{ + danzeff_free(); + danzeff_load(); +} + +#endif //DANZEFF_SDL diff --git a/src/psp_danzeff.h b/src/psp_danzeff.h new file mode 100644 index 0000000..3c77563 --- /dev/null +++ b/src/psp_danzeff.h @@ -0,0 +1,73 @@ +#ifndef INCLUDED_KEYBOARDS_DANZEFF_H +#define INCLUDED_KEYBOARDS_DANZEFF_H + +//danzeff is BSD licensed, if you do make a new renderer then please share it back and I can add it +//to the original distribution. + +//Set which renderer target to build for (currently only SDL is available) +#define DANZEFF_SDL +//TODO #define DANZEFF_SCEGU etc etc ;) + +#define DANZEFF_SELECT 1 +#define DANZEFF_START 2 + +#define DANZEFF_CONTROL 3 +#define DANZEFF_ALT 4 +#define DANZEFF_CAPSLOCK 5 +#define DANZEFF_SHIFT 6 +#define DANZEFF_SUPPR 7 + +#define DANZEFF_DEL 8 +#define DANZEFF_TAB 9 +#define DANZEFF_ENTER 10 + + + +#ifdef __cplusplus +extern "C" { +#endif + +# define PSP_KBD_MAX_SKIN 128 + + extern int psp_kbd_skin; + extern int psp_kbd_last_skin; + extern char *psp_kbd_skin_dir[PSP_KBD_MAX_SKIN]; + +//Initialization and de-init of the keyboard, provided as the keyboard uses alot of images, so if you aren't going to use it for a while, I'd recommend unloading it. +extern int danzeff_load(void); +extern void danzeff_free(void); + +//returns true if the keyboard is initialized +extern int danzeff_isinitialized(void); + +/** Attempts to read a character from the controller +* If no character is pressed then we return 0 +* Other special values: 1 = move left, 2 = move right, 3 = select, 4 = start +* Every other value should be a standard ascii value. +* An unsigned int is returned so in the future we can support unicode input +*/ +extern unsigned int danzeff_readInput(gp2xCtrlData pspctrl); + +//Move the area the keyboard is rendered at to here +extern void danzeff_moveTo(const int newX, const int newY); + +//Returns true if the keyboard that would be rendered now is different to the last time +//that the keyboard was drawn, this is altered by readInput/render. +extern int danzeff_dirty(); + +//draw the keyboard to the screen +extern void danzeff_render(); + +///Functions only for particular renderers: + +#include +//set the screen surface for rendering on. +extern void danzeff_set_screen(SDL_Surface* screen); + +extern void danzeff_change_skin(); + +#ifdef __cplusplus +} +#endif + +#endif //INCLUDED_KEYBOARDS_DANZEFF_H diff --git a/src/psp_dragon.c b/src/psp_dragon.c new file mode 100644 index 0000000..faa1863 --- /dev/null +++ b/src/psp_dragon.c @@ -0,0 +1,183 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include + +#include "global.h" +#include "psp_sdl.h" +#include "psp_danzeff.h" +#include "psp_dragon.h" + +int psp_screenshot_mode = 0; +int dragon_in_menu = 0; + +static void +dragon_render_normal() +{ + int x; + int y; + u16 *src_pixel = (u16*)blit_surface->pixels; + u16 *dst_pixel = (u16*)back_surface->pixels; + dst_pixel += (320 - DRAGON_SCREEN_W) / 2; + dst_pixel += ((240 - DRAGON_SCREEN_H) * PSP_LINE_SIZE / 2); + for (y = 0; y < DRAGON_SCREEN_H; ++y) { + for (x = 0; x < DRAGON_SCREEN_W; ++x) { + dst_pixel[x] = src_pixel[x]; + } + dst_pixel += PSP_LINE_SIZE; + src_pixel += DRAGON_SCREEN_W; + } +} + +/* + + LUDO: 16-bit HiColor (565 format) + see http://www.compuphase.com/graphic/scale3.htm + + */ +static inline u16 loc_coloraverage(u16 a, u16 b) +{ + return (u16)(((a ^ b) & 0xf7deU) >> 1) + (a & b); +} + +static inline void +dragon_X125_pixel(u16 *dist, const u16 *src) +{ + dist[0] = src[0]; + dist[1] = loc_coloraverage(src[0], src[1]); + dist[2] = src[1]; + dist[3] = src[2]; + dist[4] = src[3]; +} + +static void +dragon_render_fit() +{ + u16 *src_pixel = (u16*)blit_surface->pixels; + u16 *dst_pixel = (u16*)back_surface->pixels; + + int src_y; + int dst_x; + int dst_y; + int count; + + src_pixel += DRAGON_SCREEN_W * 24; + + for (dst_y = 0; dst_y < 240; dst_y++) { + src_y = (dst_y * REAL_DRAGON_H) / 240; + u16* src_line = &src_pixel[(src_y * DRAGON_SCREEN_W)]; + u16* dst_line = dst_pixel; + + src_line += 32; + count = 256; + while (count > 0) { + dragon_X125_pixel(dst_line, src_line); + src_line += 4; + dst_line += 5; + count -= 4; + } + dst_pixel += PSP_LINE_SIZE; + } +} + +static void +dragon_synchronize(void) +{ + static u32 nextclock = 1; + + if (DRAGON.dragon_speed_limiter) { + + if (nextclock) { + u32 curclock; + do { + curclock = SDL_GetTicks(); + } while (curclock < nextclock); + + nextclock = curclock + (u32)( 1000 / DRAGON.dragon_speed_limiter); + } + } +} + +void +dragon_update_fps() +{ + static u32 next_sec_clock = 0; + static u32 cur_num_frame = 0; + cur_num_frame++; + u32 curclock = SDL_GetTicks(); + if (curclock > next_sec_clock) { + next_sec_clock = curclock + 1000; + DRAGON.dragon_current_fps = cur_num_frame; + cur_num_frame = 0; + } +} + +extern int psp_in_dragon_menu; + +void +psp_sdl_render() +{ + if (dragon_in_menu) return; + + if (DRAGON.psp_skip_cur_frame <= 0) { + + DRAGON.psp_skip_cur_frame = DRAGON.psp_skip_max_frame; + + if (DRAGON.dragon_render_mode == DRAGON_RENDER_NORMAL) dragon_render_normal(); + else + if (DRAGON.dragon_render_mode == DRAGON_RENDER_FIT) dragon_render_fit(); + + if (psp_kbd_is_danzeff_mode()) { + + danzeff_moveTo(-50, -50); + danzeff_render(); + } + + if (DRAGON.dragon_view_fps) { + char buffer[32]; + sprintf(buffer, "%3d", (int)DRAGON.dragon_current_fps); + psp_sdl_fill_print(0, 0, buffer, 0xffffff, 0 ); + } + + if (DRAGON.psp_display_lr) { + psp_kbd_display_active_mapping(); + } + psp_sdl_flip(); + + if (psp_screenshot_mode) { + psp_screenshot_mode--; + if (psp_screenshot_mode <= 0) { + psp_sdl_save_screenshot(); + psp_screenshot_mode = 0; + } + } + + } else if (DRAGON.psp_skip_max_frame) { + DRAGON.psp_skip_cur_frame--; + } + + if (DRAGON.dragon_speed_limiter) { + dragon_synchronize(); + } + + if (DRAGON.dragon_view_fps) { + dragon_update_fps(); + } +} diff --git a/src/psp_dragon.h b/src/psp_dragon.h new file mode 100644 index 0000000..d64c077 --- /dev/null +++ b/src/psp_dragon.h @@ -0,0 +1,31 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +# ifndef _PSP_TI99_H_ +# define _PSP_TI99_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + void psp_sdl_render(void); + + extern int psp_screenshot_mode; + +#ifdef __cplusplus +} +#endif +# endif diff --git a/src/psp_fmgr.c b/src/psp_fmgr.c new file mode 100644 index 0000000..a54f48a --- /dev/null +++ b/src/psp_fmgr.c @@ -0,0 +1,605 @@ +/* + * Copyright (C) 2006 Ludovic Jacomme (ludovic.jacomme@gmail.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "global.h" +#include "psp_sdl.h" +#include "psp_menu.h" +#include "psp_danzeff.h" + +#include "psp_kbd.h" +#include "psp_sdl.h" +#include "psp_fmgr.h" + +# define PSP_FMGR_MIN_TIME 150000 + +static struct dirent files[PSP_FMGR_MAX_ENTRY]; +static struct dirent *sortfiles[PSP_FMGR_MAX_ENTRY]; +static int nfiles; +static int user_file_format = 0; + +static void +SJISCopy(struct dirent *a, char *file) +{ + unsigned char ca; + int i; + int len = strlen(a->d_name); + + for(i=0;i<=len;i++){ + ca = a->d_name[i]; + if (((0x81 <= ca)&&(ca <= 0x9f)) + || ((0xe0 <= ca)&&(ca <= 0xef))){ + file[i++] = ca; + file[i] = a->d_name[i]; + } + else{ + if(ca>='a' && ca<='z') ca-=0x20; + file[i] = ca; + } + } +} + +static int +cmpFile(struct dirent *a, struct dirent *b) +{ + char file1[0x108]; + char file2[0x108]; + char ca, cb; + int i, n, ret; + + if(a->d_type == b->d_type){ + SJISCopy(a, file1); + SJISCopy(b, file2); + n=strlen(file1); + for(i=0; i<=n; i++){ + ca=file1[i]; cb=file2[i]; + ret = ca-cb; + if(ret!=0) return ret; + } + return 0; + } + + if(a->d_type == DT_DIR) return -1; + else return 1; +} + +static void +sort(struct dirent **a, int left, int right) +{ + struct dirent *tmp, *pivot; + int i, p; + + if (left < right) { + pivot = a[left]; + p = left; + for (i=left+1; i<=right; i++) { + if (cmpFile(a[i],pivot)<0){ + p=p+1; + tmp=a[p]; + a[p]=a[i]; + a[i]=tmp; + } + } + a[left] = a[p]; + a[p] = pivot; + sort(a, left, p-1); + sort(a, p+1, right); + } +} + +static void +my_sort(struct dirent **a, int left, int right) +{ + struct dirent* swap_elem; + int length = right - left; + int index; + /* LUDO: Better like this ... + quicksort is o(n2) when the list is already sorted !! + */ + for (index = 0; index < length; index++) { + int perm = rand() % length; + swap_elem = a[perm]; + a[perm] = a[index]; + a[index] = swap_elem; + } + sort(a, left, right); +} + +int +psp_fmgr_getExtId(const char *szFilePath) +{ + char *pszExt; + if((pszExt = strrchr(szFilePath, '.'))) { + if ((!strcasecmp(pszExt, ".rom")) || + (!strcasecmp(pszExt, ".ccc"))) { + return FMGR_FORMAT_ROM; + } else + if (!strcasecmp(pszExt, ".sna")) { + return FMGR_FORMAT_STATE; + } else + if ((!strcasecmp(pszExt, ".dmk")) || + (!strcasecmp(pszExt, ".dsk")) || + (!strcasecmp(pszExt, ".vdk")) || + (!strcasecmp(pszExt, ".jvc"))) { + return FMGR_FORMAT_DISK; + } else + if (!strcasecmp(pszExt, ".kbd")) { + return FMGR_FORMAT_KBD; + } else + if (!strcasecmp(pszExt, ".set")) { + return FMGR_FORMAT_SET; + } else + if (!strcasecmp(pszExt, ".zip")) { + return FMGR_FORMAT_ZIP; + } + } + return 0; +} + + +static void +getDir(const char *path) +{ + DIR* fd; + int b=0; + int format = 0; + + nfiles = 0; + + if(strcmp(path,"./")){ + strcpy(files[nfiles].d_name,".."); + files[nfiles].d_type = DT_DIR; + sortfiles[nfiles] = files + nfiles; + nfiles++; + b=1; + } + fd = opendir(path); + if (! fd) return; + while(nfilesd_name); + } + return nfiles; +} + +static void +psp_display_screen_fmgr(void) +{ + psp_sdl_blit_background(); +} + + +static int +psp_fmgr_ask_confirm(void) +{ + gp2xCtrlData c; + int confirm = 0; + + psp_sdl_back2_print(190, 70, "Delete a file", + PSP_MENU_WARNING_COLOR); +# if defined(MIYOO_MODE) + psp_sdl_back2_print(170, 80, "press B to confirm !", + PSP_MENU_WARNING_COLOR); +# else + psp_sdl_back2_print(170, 80, "press X to confirm !", + PSP_MENU_WARNING_COLOR); +#endif + psp_sdl_flip(); + + psp_kbd_wait_no_button(); + + do + { + gp2xCtrlReadBufferPositive(&c, 1); + c.Buttons &= PSP_ALL_BUTTON_MASK; + + if (c.Buttons & GP2X_CTRL_CROSS) { confirm = 1; break; } + + } while (c.Buttons == 0); + + psp_kbd_wait_no_button(); + + return confirm; +} + +int +psp_file_find_first(char first_char) +{ + int index; + first_char = toupper(first_char); + for (index = 0; index < nfiles; index++) { + char test_char = toupper(sortfiles[index]->d_name[0]); + if (test_char == first_char) { + return index; + } + } + return -1; +} + +int +psp_file_request(char *out, char *pszStartPath) +{ +static int sel=0; + + gp2xCtrlData c; + int display_screen; + int last_time; + int tmp; + + long color; + int top, rows=20, x, y, i, up=0; + char path[PSP_FMGR_MAX_PATH]; + char oldDir[PSP_FMGR_MAX_NAME]; + char buffer[PSP_FMGR_MAX_NAME]; + char *p; + long new_pad; + long old_pad; + int file_selected; + int display_thumb; + int check_thumb; + + int danzeff_mode; + int danzeff_key; + + danzeff_key = 0; + danzeff_mode = 0; + + + memset(files, 0x00, sizeof(struct dirent) * PSP_FMGR_MAX_ENTRY); + memset(sortfiles, 0x00, sizeof(struct dirent *) * PSP_FMGR_MAX_ENTRY); + nfiles = 0; + + strcpy(path, pszStartPath); + getDir(path); + + last_time = 0; + old_pad = 0; + top = 0; + file_selected = 0; + + if (sel >= nfiles) sel = 0; + + display_thumb = 0; + check_thumb = 1; + + load_thumb_list(); + + for(;;) + { + x = 0; y = 15; + + if (check_thumb) { + check_thumb = 0; + display_thumb = 0; + if (sortfiles[sel]->d_type != DT_DIR) { + if (load_thumb_if_exists(sortfiles[sel]->d_name)) { + display_thumb = 1; + } + } + } + + psp_display_screen_fmgr(); + psp_sdl_back2_rectangle(x, y, 190, rows * 10); + if (display_thumb) { + psp_sdl_blit_thumb(180, 40, save_surface); + } + + for(i=0; i= nfiles) break; + if(top+i == sel) color = PSP_MENU_SEL_COLOR; + else color = PSP_MENU_TEXT_COLOR; + strncpy(buffer, sortfiles[top+i]->d_name, 30); + string_fill_with_space(buffer, 30); + psp_sdl_back2_print(x, y, buffer, color); + y += 10; + } + + if (danzeff_mode) { + danzeff_moveTo(-10, -50); + danzeff_render(); + } + psp_sdl_flip(); + + while (1) { + + gp2xCtrlPeekBufferPositive(&c, 1); + c.Buttons &= PSP_ALL_BUTTON_MASK; + + new_pad = c.Buttons; + + if ((old_pad != new_pad) || ((c.TimeStamp - last_time) > PSP_FMGR_MIN_TIME)) { + last_time = c.TimeStamp; + old_pad = new_pad; + break; + } + } + + if (danzeff_mode) { + + danzeff_key = danzeff_readInput(c); + + if (danzeff_key > DANZEFF_START) { + if (danzeff_key > ' ') { + int new_sel = psp_file_find_first(danzeff_key); + if (new_sel >= 0) { + sel = new_sel; + check_thumb = 1; + goto lab_end; + } + } + + } else + if ((danzeff_key == DANZEFF_START ) || + (danzeff_key == DANZEFF_SELECT)) + { + danzeff_mode = 0; + old_pad = new_pad = 0; + + psp_kbd_wait_no_button(); + } + + if (danzeff_key >= -1) { + continue; + } + + } + + if (new_pad & GP2X_CTRL_START) { + danzeff_mode = 1; + } else + if ((new_pad & GP2X_CTRL_CROSS) || + (new_pad & GP2X_CTRL_CIRCLE)) { + + if (sortfiles[sel]->d_type == DT_DIR) { + if(!strcmp(sortfiles[sel]->d_name,"..")){ + up=1; + } else { + strcat(path,sortfiles[sel]->d_name); + getDir(path); + sel=0; + check_thumb = 1; + } + }else{ + strcpy(out, path); + strcat(out, sortfiles[sel]->d_name); + strcpy(pszStartPath,path); + file_selected = 1; + break; + } + } else if(new_pad & GP2X_CTRL_TRIANGLE){ + up=1; + } else if((new_pad & GP2X_CTRL_SQUARE) || (new_pad & GP2X_CTRL_SELECT)) { + /* Cancel */ + file_selected = 0; + break; + } else if(new_pad & GP2X_CTRL_UP){ + sel--; + check_thumb = 1; + } else if(new_pad & GP2X_CTRL_DOWN){ + sel++; + check_thumb = 1; + } else if(new_pad & GP2X_CTRL_LEFT){ + sel-=10; + check_thumb = 1; + } else if(new_pad & GP2X_CTRL_RIGHT){ + sel+=10; + check_thumb = 1; + } else if(new_pad & GP2X_CTRL_RTRIGGER){ + + if (sortfiles[sel]->d_type != DT_DIR) { + strcpy(out, path); + strcat(out, sortfiles[sel]->d_name); + strcpy(pszStartPath,path); + if (psp_fmgr_ask_confirm()) { + for (tmp = sel; tmp < (nfiles - 1); tmp++) { + sortfiles[tmp] = sortfiles[tmp + 1]; + } + nfiles--; + remove(out); + } + check_thumb = 1; + } + } + + if(up) { + check_thumb = 1; + + if(strcmp(path,"./")) { + p=strrchr(path,'/'); + *p=0; + p=strrchr(path,'/'); + p++; + strcpy(oldDir,p); + strcat(oldDir,"/"); + *p=0; + getDir(path); + sel=0; + for(i=0; id_name)) { + sel=i; + top=sel-3; + break; + } + } + } + up=0; + } +lab_end: + + if(top > nfiles-rows) top=nfiles-rows; + if(top < 0) top=0; + if(sel >= nfiles) sel=nfiles-1; + if(sel < 0) sel=0; + if(sel >= top+rows) top=sel-rows+1; + if(sel < top) top=sel; + } + + return file_selected; +} + +static char user_filedir_kbd[PSP_FMGR_MAX_NAME]; +static char user_filedir_set[PSP_FMGR_MAX_NAME]; +static char user_filedir_rom[PSP_FMGR_MAX_NAME]; +static char user_filedir_disk[PSP_FMGR_MAX_NAME]; +static char user_filedir_snap[PSP_FMGR_MAX_NAME]; + +int +psp_fmgr_menu(int format) +{ + static int first = 1; + + char *user_filedir; + char user_filename[PSP_FMGR_MAX_NAME]; + struct stat aStat; + int file_format; + int error; + int ret; + + user_file_format = format; + ret = 0; + + if (first) { + first = 0; + getcwd(user_filedir_kbd , PSP_FMGR_MAX_NAME); + strcpy(user_filedir_set , user_filedir_kbd); + strcpy(user_filedir_rom , user_filedir_kbd); + strcpy(user_filedir_disk, user_filedir_kbd); + strcpy(user_filedir_snap, user_filedir_kbd); + strcat(user_filedir_kbd , "/kbd/"); + strcat(user_filedir_set , "/set/"); + strcat(user_filedir_rom , "/rom/"); + strcat(user_filedir_disk, "/disk/"); + strcat(user_filedir_snap, "/snap/"); + } + + if (format == FMGR_FORMAT_KBD) user_filedir = user_filedir_kbd; + else + if (format == FMGR_FORMAT_SET) user_filedir = user_filedir_set; + else + if (format == FMGR_FORMAT_ROM) user_filedir = user_filedir_rom; + else + if (format == FMGR_FORMAT_STATE) user_filedir = user_filedir_snap; + else + /* if (format == FMGR_FORMAT_DISK) */ user_filedir = user_filedir_disk; + + psp_kbd_wait_no_button(); + + if (psp_file_request(user_filename, user_filedir)) { + error = 0; + if (stat(user_filename, &aStat)) error = 1; + else + { + file_format = psp_fmgr_getExtId(user_filename); + + if (file_format == FMGR_FORMAT_ZIP) { + /* NONE */ + error = 1; + } + else + { + if (file_format == FMGR_FORMAT_STATE) /* State */ error = dragon_load_state(user_filename); + else + if (file_format == FMGR_FORMAT_ROM) /* ROM */ error = dragon_load_rom(user_filename); + else + if (file_format == FMGR_FORMAT_DISK) /* Disk */ error = dragon_load_disk(user_filename); + else + if (file_format == FMGR_FORMAT_KBD) /* Kbd */ error = psp_kbd_load_mapping(user_filename); + else + if (file_format == FMGR_FORMAT_SET) /* Settings */ error = dragon_load_file_settings(user_filename); + } + } + + if (error) ret = -1; + else ret = 1; + } + + psp_kbd_wait_no_button(); + + return ret; +} diff --git a/src/psp_fmgr.h b/src/psp_fmgr.h new file mode 100644 index 0000000..a2a7032 --- /dev/null +++ b/src/psp_fmgr.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2006 Ludovic Jacomme (ludovic.jacomme@gmail.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +# ifndef _PSP_FMGR_H_ +# define _PSP_FMGR_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +# define PSP_FMGR_MAX_PATH 512 +# define PSP_FMGR_MAX_NAME 256 +# define PSP_FMGR_MAX_ENTRY 2048 + +# define FMGR_FORMAT_ROM 1 +# define FMGR_FORMAT_STATE 2 +# define FMGR_FORMAT_DISK 3 +# define FMGR_FORMAT_KBD 4 +# define FMGR_FORMAT_ZIP 5 +# define FMGR_FORMAT_SET 6 + + extern int psp_fmgr_menu(int format); + +#ifdef __cplusplus +} +#endif + +# endif diff --git a/src/psp_font.c b/src/psp_font.c new file mode 100644 index 0000000..e2d1e71 --- /dev/null +++ b/src/psp_font.c @@ -0,0 +1,535 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +unsigned char psp_font_8x8[]={ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7E, 0x81, 0xA5, 0x81, 0xBD, 0x99, 0x81, 0x7E, + 0x7E, 0xFF, 0xDB, 0xFF, 0xC3, 0xE7, 0xFF, 0x7E, + 0x6C, 0xFE, 0xFE, 0xFE, 0x7C, 0x38, 0x10, 0x00, + 0x10, 0x38, 0x7C, 0xFE, 0x7C, 0x38, 0x10, 0x00, + 0x38, 0x7C, 0x38, 0xFE, 0xFE, 0x92, 0x10, 0x7C, + 0x00, 0x10, 0x38, 0x7C, 0xFE, 0x7C, 0x38, 0x7C, + 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, + 0x0F, 0x07, 0x0F, 0x7D, 0xCC, 0xCC, 0xCC, 0x78, + 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x7E, 0x18, + 0x3F, 0x33, 0x3F, 0x30, 0x30, 0x70, 0xF0, 0xE0, + 0x7F, 0x63, 0x7F, 0x63, 0x63, 0x67, 0xE6, 0xC0, + 0x99, 0x5A, 0x3C, 0xE7, 0xE7, 0x3C, 0x5A, 0x99, + 0x80, 0xE0, 0xF8, 0xFE, 0xF8, 0xE0, 0x80, 0x00, + 0x02, 0x0E, 0x3E, 0xFE, 0x3E, 0x0E, 0x02, 0x00, + 0x18, 0x3C, 0x7E, 0x18, 0x18, 0x7E, 0x3C, 0x18, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00, + 0x7F, 0xDB, 0xDB, 0x7B, 0x1B, 0x1B, 0x1B, 0x00, + 0x3E, 0x63, 0x38, 0x6C, 0x6C, 0x38, 0x86, 0xFC, + 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, 0x0C, 0xFE, 0x0C, 0x18, 0x00, 0x00, + 0x00, 0x30, 0x60, 0xFE, 0x60, 0x30, 0x00, 0x00, + 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xFE, 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, + 0x18, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x18, 0x00, + 0x6C, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6C, 0x6C, 0xFE, 0x6C, 0xFE, 0x6C, 0x6C, 0x00, + 0x18, 0x7E, 0xC0, 0x7C, 0x06, 0xFC, 0x18, 0x00, + 0x00, 0xC6, 0xCC, 0x18, 0x30, 0x66, 0xC6, 0x00, + 0x38, 0x6C, 0x38, 0x76, 0xDC, 0xCC, 0x76, 0x00, + 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00, + 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00, + 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, + 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, + 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x80, 0x00, + 0x7C, 0xCE, 0xDE, 0xF6, 0xE6, 0xC6, 0x7C, 0x00, + 0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xFC, 0x00, + 0x78, 0xCC, 0x0C, 0x38, 0x60, 0xCC, 0xFC, 0x00, + 0x78, 0xCC, 0x0C, 0x38, 0x0C, 0xCC, 0x78, 0x00, + 0x1C, 0x3C, 0x6C, 0xCC, 0xFE, 0x0C, 0x1E, 0x00, + 0xFC, 0xC0, 0xF8, 0x0C, 0x0C, 0xCC, 0x78, 0x00, + 0x38, 0x60, 0xC0, 0xF8, 0xCC, 0xCC, 0x78, 0x00, + 0xFC, 0xCC, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x00, + 0x78, 0xCC, 0xCC, 0x78, 0xCC, 0xCC, 0x78, 0x00, + 0x78, 0xCC, 0xCC, 0x7C, 0x0C, 0x18, 0x70, 0x00, + 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, + 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x30, + 0x18, 0x30, 0x60, 0xC0, 0x60, 0x30, 0x18, 0x00, + 0x00, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x00, 0x00, + 0x60, 0x30, 0x18, 0x0C, 0x18, 0x30, 0x60, 0x00, + 0x3C, 0x66, 0x0C, 0x18, 0x18, 0x00, 0x18, 0x00, + 0x7C, 0xC6, 0xDE, 0xDE, 0xDC, 0xC0, 0x7C, 0x00, + 0x30, 0x78, 0xCC, 0xCC, 0xFC, 0xCC, 0xCC, 0x00, + 0xFC, 0x66, 0x66, 0x7C, 0x66, 0x66, 0xFC, 0x00, + 0x3C, 0x66, 0xC0, 0xC0, 0xC0, 0x66, 0x3C, 0x00, + 0xF8, 0x6C, 0x66, 0x66, 0x66, 0x6C, 0xF8, 0x00, + 0xFE, 0x62, 0x68, 0x78, 0x68, 0x62, 0xFE, 0x00, + 0xFE, 0x62, 0x68, 0x78, 0x68, 0x60, 0xF0, 0x00, + 0x3C, 0x66, 0xC0, 0xC0, 0xCE, 0x66, 0x3A, 0x00, + 0xCC, 0xCC, 0xCC, 0xFC, 0xCC, 0xCC, 0xCC, 0x00, + 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x1E, 0x0C, 0x0C, 0x0C, 0xCC, 0xCC, 0x78, 0x00, + 0xE6, 0x66, 0x6C, 0x78, 0x6C, 0x66, 0xE6, 0x00, + 0xF0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xFE, 0x00, + 0xC6, 0xEE, 0xFE, 0xFE, 0xD6, 0xC6, 0xC6, 0x00, + 0xC6, 0xE6, 0xF6, 0xDE, 0xCE, 0xC6, 0xC6, 0x00, + 0x38, 0x6C, 0xC6, 0xC6, 0xC6, 0x6C, 0x38, 0x00, + 0xFC, 0x66, 0x66, 0x7C, 0x60, 0x60, 0xF0, 0x00, + 0x7C, 0xC6, 0xC6, 0xC6, 0xD6, 0x7C, 0x0E, 0x00, + 0xFC, 0x66, 0x66, 0x7C, 0x6C, 0x66, 0xE6, 0x00, + 0x7C, 0xC6, 0xE0, 0x78, 0x0E, 0xC6, 0x7C, 0x00, + 0xFC, 0xB4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, + 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xFC, 0x00, + 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x78, 0x30, 0x00, + 0xC6, 0xC6, 0xC6, 0xC6, 0xD6, 0xFE, 0x6C, 0x00, + 0xC6, 0xC6, 0x6C, 0x38, 0x6C, 0xC6, 0xC6, 0x00, + 0xCC, 0xCC, 0xCC, 0x78, 0x30, 0x30, 0x78, 0x00, + 0xFE, 0xC6, 0x8C, 0x18, 0x32, 0x66, 0xFE, 0x00, + 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00, + 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x02, 0x00, + 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00, + 0x10, 0x38, 0x6C, 0xC6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00, + 0xE0, 0x60, 0x60, 0x7C, 0x66, 0x66, 0xDC, 0x00, + 0x00, 0x00, 0x78, 0xCC, 0xC0, 0xCC, 0x78, 0x00, + 0x1C, 0x0C, 0x0C, 0x7C, 0xCC, 0xCC, 0x76, 0x00, + 0x00, 0x00, 0x78, 0xCC, 0xFC, 0xC0, 0x78, 0x00, + 0x38, 0x6C, 0x64, 0xF0, 0x60, 0x60, 0xF0, 0x00, + 0x00, 0x00, 0x76, 0xCC, 0xCC, 0x7C, 0x0C, 0xF8, + 0xE0, 0x60, 0x6C, 0x76, 0x66, 0x66, 0xE6, 0x00, + 0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x0C, 0x00, 0x1C, 0x0C, 0x0C, 0xCC, 0xCC, 0x78, + 0xE0, 0x60, 0x66, 0x6C, 0x78, 0x6C, 0xE6, 0x00, + 0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x00, 0x00, 0xCC, 0xFE, 0xFE, 0xD6, 0xD6, 0x00, + 0x00, 0x00, 0xB8, 0xCC, 0xCC, 0xCC, 0xCC, 0x00, + 0x00, 0x00, 0x78, 0xCC, 0xCC, 0xCC, 0x78, 0x00, + 0x00, 0x00, 0xDC, 0x66, 0x66, 0x7C, 0x60, 0xF0, + 0x00, 0x00, 0x76, 0xCC, 0xCC, 0x7C, 0x0C, 0x1E, + 0x00, 0x00, 0xDC, 0x76, 0x62, 0x60, 0xF0, 0x00, + 0x00, 0x00, 0x7C, 0xC0, 0x70, 0x1C, 0xF8, 0x00, + 0x10, 0x30, 0xFC, 0x30, 0x30, 0x34, 0x18, 0x00, + 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, + 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0x78, 0x30, 0x00, + 0x00, 0x00, 0xC6, 0xC6, 0xD6, 0xFE, 0x6C, 0x00, + 0x00, 0x00, 0xC6, 0x6C, 0x38, 0x6C, 0xC6, 0x00, + 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0x7C, 0x0C, 0xF8, + 0x00, 0x00, 0xFC, 0x98, 0x30, 0x64, 0xFC, 0x00, + 0x1C, 0x30, 0x30, 0xE0, 0x30, 0x30, 0x1C, 0x00, + 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00, + 0xE0, 0x30, 0x30, 0x1C, 0x30, 0x30, 0xE0, 0x00, + 0x76, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6C, 0xC6, 0xC6, 0xFE, 0x00, + 0x7C, 0xC6, 0xC0, 0xC6, 0x7C, 0x0C, 0x06, 0x7C, + 0x00, 0xCC, 0x00, 0xCC, 0xCC, 0xCC, 0x76, 0x00, + 0x1C, 0x00, 0x78, 0xCC, 0xFC, 0xC0, 0x78, 0x00, + 0x7E, 0x81, 0x3C, 0x06, 0x3E, 0x66, 0x3B, 0x00, + 0xCC, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00, + 0xE0, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00, + 0x30, 0x30, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00, + 0x00, 0x00, 0x7C, 0xC6, 0xC0, 0x78, 0x0C, 0x38, + 0x7E, 0x81, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00, + 0xCC, 0x00, 0x78, 0xCC, 0xFC, 0xC0, 0x78, 0x00, + 0xE0, 0x00, 0x78, 0xCC, 0xFC, 0xC0, 0x78, 0x00, + 0xCC, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x7C, 0x82, 0x38, 0x18, 0x18, 0x18, 0x3C, 0x00, + 0xE0, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, + 0xC6, 0x10, 0x7C, 0xC6, 0xFE, 0xC6, 0xC6, 0x00, + 0x30, 0x30, 0x00, 0x78, 0xCC, 0xFC, 0xCC, 0x00, + 0x1C, 0x00, 0xFC, 0x60, 0x78, 0x60, 0xFC, 0x00, + 0x00, 0x00, 0x7F, 0x0C, 0x7F, 0xCC, 0x7F, 0x00, + 0x3E, 0x6C, 0xCC, 0xFE, 0xCC, 0xCC, 0xCE, 0x00, + 0x78, 0x84, 0x00, 0x78, 0xCC, 0xCC, 0x78, 0x00, + 0x00, 0xCC, 0x00, 0x78, 0xCC, 0xCC, 0x78, 0x00, + 0x00, 0xE0, 0x00, 0x78, 0xCC, 0xCC, 0x78, 0x00, + 0x78, 0x84, 0x00, 0xCC, 0xCC, 0xCC, 0x76, 0x00, + 0x00, 0xE0, 0x00, 0xCC, 0xCC, 0xCC, 0x76, 0x00, + 0x00, 0xCC, 0x00, 0xCC, 0xCC, 0x7C, 0x0C, 0xF8, + 0xC3, 0x18, 0x3C, 0x66, 0x66, 0x3C, 0x18, 0x00, + 0xCC, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0x78, 0x00, + 0x18, 0x18, 0x7E, 0xC0, 0xC0, 0x7E, 0x18, 0x18, + 0x38, 0x6C, 0x64, 0xF0, 0x60, 0xE6, 0xFC, 0x00, + 0xCC, 0xCC, 0x78, 0x30, 0xFC, 0x30, 0xFC, 0x30, + 0xF8, 0xCC, 0xCC, 0xFA, 0xC6, 0xCF, 0xC6, 0xC3, + 0x0E, 0x1B, 0x18, 0x3C, 0x18, 0x18, 0xD8, 0x70, + 0x1C, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00, + 0x38, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x00, 0x1C, 0x00, 0x78, 0xCC, 0xCC, 0x78, 0x00, + 0x00, 0x1C, 0x00, 0xCC, 0xCC, 0xCC, 0x76, 0x00, + 0x00, 0xF8, 0x00, 0xB8, 0xCC, 0xCC, 0xCC, 0x00, + 0xFC, 0x00, 0xCC, 0xEC, 0xFC, 0xDC, 0xCC, 0x00, + 0x3C, 0x6C, 0x6C, 0x3E, 0x00, 0x7E, 0x00, 0x00, + 0x38, 0x6C, 0x6C, 0x38, 0x00, 0x7C, 0x00, 0x00, + 0x18, 0x00, 0x18, 0x18, 0x30, 0x66, 0x3C, 0x00, + 0x00, 0x00, 0x00, 0xFC, 0xC0, 0xC0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFC, 0x0C, 0x0C, 0x00, 0x00, + 0xC6, 0xCC, 0xD8, 0x36, 0x6B, 0xC2, 0x84, 0x0F, + 0xC3, 0xC6, 0xCC, 0xDB, 0x37, 0x6D, 0xCF, 0x03, + 0x18, 0x00, 0x18, 0x18, 0x3C, 0x3C, 0x18, 0x00, + 0x00, 0x33, 0x66, 0xCC, 0x66, 0x33, 0x00, 0x00, + 0x00, 0xCC, 0x66, 0x33, 0x66, 0xCC, 0x00, 0x00, + 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, + 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, + 0xDB, 0xF6, 0xDB, 0x6F, 0xDB, 0x7E, 0xD7, 0xED, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0xF8, 0x18, 0x18, 0x18, + 0x18, 0x18, 0xF8, 0x18, 0xF8, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0xF6, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0xFE, 0x36, 0x36, 0x36, + 0x00, 0x00, 0xF8, 0x18, 0xF8, 0x18, 0x18, 0x18, + 0x36, 0x36, 0xF6, 0x06, 0xF6, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0xFE, 0x06, 0xF6, 0x36, 0x36, 0x36, + 0x36, 0x36, 0xF6, 0x06, 0xFE, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0xFE, 0x00, 0x00, 0x00, + 0x18, 0x18, 0xF8, 0x18, 0xF8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xF8, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x1F, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x1F, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0xFF, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x1F, 0x18, 0x1F, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x37, 0x30, 0x3F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3F, 0x30, 0x37, 0x36, 0x36, 0x36, + 0x36, 0x36, 0xF7, 0x00, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0x00, 0xF7, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, + 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, + 0x36, 0x36, 0xF7, 0x00, 0xF7, 0x36, 0x36, 0x36, + 0x18, 0x18, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x3F, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x1F, 0x18, 0x1F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1F, 0x18, 0x1F, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x3F, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0xFF, 0x36, 0x36, 0x36, + 0x18, 0x18, 0xFF, 0x18, 0xFF, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0xF8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1F, 0x18, 0x18, 0x18, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x76, 0xDC, 0xC8, 0xDC, 0x76, 0x00, + 0x00, 0x78, 0xCC, 0xF8, 0xCC, 0xF8, 0xC0, 0xC0, + 0x00, 0xFC, 0xCC, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, + 0x00, 0x00, 0xFE, 0x6C, 0x6C, 0x6C, 0x6C, 0x00, + 0xFC, 0xCC, 0x60, 0x30, 0x60, 0xCC, 0xFC, 0x00, + 0x00, 0x00, 0x7E, 0xD8, 0xD8, 0xD8, 0x70, 0x00, + 0x00, 0x66, 0x66, 0x66, 0x66, 0x7C, 0x60, 0xC0, + 0x00, 0x76, 0xDC, 0x18, 0x18, 0x18, 0x18, 0x00, + 0xFC, 0x30, 0x78, 0xCC, 0xCC, 0x78, 0x30, 0xFC, + 0x38, 0x6C, 0xC6, 0xFE, 0xC6, 0x6C, 0x38, 0x00, + 0x38, 0x6C, 0xC6, 0xC6, 0x6C, 0x6C, 0xEE, 0x00, + 0x1C, 0x30, 0x18, 0x7C, 0xCC, 0xCC, 0x78, 0x00, + 0x00, 0x00, 0x7E, 0xDB, 0xDB, 0x7E, 0x00, 0x00, + 0x06, 0x0C, 0x7E, 0xDB, 0xDB, 0x7E, 0x60, 0xC0, + 0x38, 0x60, 0xC0, 0xF8, 0xC0, 0x60, 0x38, 0x00, + 0x78, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x00, + 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x00, + 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x7E, 0x00, + 0x60, 0x30, 0x18, 0x30, 0x60, 0x00, 0xFC, 0x00, + 0x18, 0x30, 0x60, 0x30, 0x18, 0x00, 0xFC, 0x00, + 0x0E, 0x1B, 0x1B, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xD8, 0xD8, 0x70, + 0x18, 0x18, 0x00, 0x7E, 0x00, 0x18, 0x18, 0x00, + 0x00, 0x76, 0xDC, 0x00, 0x76, 0xDC, 0x00, 0x00, + 0x38, 0x6C, 0x6C, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x0F, 0x0C, 0x0C, 0x0C, 0xEC, 0x6C, 0x3C, 0x1C, + 0x58, 0x6C, 0x6C, 0x6C, 0x6C, 0x00, 0x00, 0x00, + 0x70, 0x98, 0x30, 0x60, 0xF8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x3C, 0x3C, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + + +/* The font is generated from Xorg 6x10-L1.bdf */ +unsigned char psp_font_6x10[] = { + 0x00>>2, 0xA8>>2, 0x00>>2, 0x88>>2, 0x00>>2, 0x88>>2, 0x00>>2, 0xA8>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0x20>>2, 0x70>>2, 0xF8>>2, 0x70>>2, 0x20>>2, 0x00>>2, 0x00>>2, 0x00>>2, + 0xA8>>2, 0x54>>2, 0xA8>>2, 0x54>>2, 0xA8>>2, 0x54>>2, 0xA8>>2, 0x54>>2, 0xA8>>2, 0x54>>2, + 0x00>>2, 0x90>>2, 0x90>>2, 0xF0>>2, 0x90>>2, 0x90>>2, 0x78>>2, 0x10>>2, 0x10>>2, 0x10>>2, + 0x00>>2, 0xE0>>2, 0x80>>2, 0xC0>>2, 0x80>>2, 0xB8>>2, 0x20>>2, 0x30>>2, 0x20>>2, 0x20>>2, + 0x00>>2, 0x70>>2, 0x80>>2, 0x80>>2, 0x70>>2, 0x70>>2, 0x48>>2, 0x70>>2, 0x48>>2, 0x48>>2, + 0x00>>2, 0x80>>2, 0x80>>2, 0x80>>2, 0xF0>>2, 0x78>>2, 0x40>>2, 0x70>>2, 0x40>>2, 0x40>>2, + 0x00>>2, 0x20>>2, 0x50>>2, 0x20>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0x20>>2, 0x20>>2, 0xF8>>2, 0x20>>2, 0x20>>2, 0xF8>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x90>>2, 0xD0>>2, 0xD0>>2, 0xB0>>2, 0x90>>2, 0x40>>2, 0x40>>2, 0x40>>2, 0x78>>2, + 0x00>>2, 0x90>>2, 0x90>>2, 0x60>>2, 0x40>>2, 0x78>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x10>>2, + 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0xE0>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0xE0>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, + 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x3C>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, + 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x3C>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, + 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0xFC>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, + 0xFC>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0xFC>>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, 0xFF>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0xFC>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0xFC>>2, + 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x3C>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, + 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0xE0>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, + 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0xFC>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0xFC>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, + 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, + 0x00>>2, 0x18>>2, 0x60>>2, 0x80>>2, 0x60>>2, 0x18>>2, 0x00>>2, 0xF8>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0xC0>>2, 0x30>>2, 0x08>>2, 0x30>>2, 0xC0>>2, 0x00>>2, 0xF8>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0x00>>2, 0xF8>>2, 0x50>>2, 0x50>>2, 0x50>>2, 0x50>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x08>>2, 0x10>>2, 0xF8>>2, 0x20>>2, 0xF8>>2, 0x40>>2, 0x80>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x30>>2, 0x48>>2, 0x40>>2, 0xE0>>2, 0x40>>2, 0x48>>2, 0xB0>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x20>>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, 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x00>>2, 0x20>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x50>>2, 0x50>>2, 0x50>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x50>>2, 0x50>>2, 0xF8>>2, 0x50>>2, 0xF8>>2, 0x50>>2, 0x50>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x20>>2, 0x70>>2, 0xA0>>2, 0x70>>2, 0x28>>2, 0x70>>2, 0x20>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x48>>2, 0xA8>>2, 0x50>>2, 0x20>>2, 0x50>>2, 0xA8>>2, 0x90>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x40>>2, 0xA0>>2, 0xA0>>2, 0x40>>2, 0xA8>>2, 0x90>>2, 0x68>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x10>>2, 0x20>>2, 0x40>>2, 0x40>>2, 0x40>>2, 0x20>>2, 0x10>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x40>>2, 0x20>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x20>>2, 0x40>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0x88>>2, 0x50>>2, 0xF8>>2, 0x50>>2, 0x88>>2, 0x00>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0x20>>2, 0x20>>2, 0xF8>>2, 0x20>>2, 0x20>>2, 0x00>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x30>>2, 0x20>>2, 0x40>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0xF8>>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, 0x70>>2, 0x20>>2, 0x00>>2, + 0x00>>2, 0x08>>2, 0x08>>2, 0x10>>2, 0x20>>2, 0x40>>2, 0x80>>2, 0x80>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x20>>2, 0x50>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x50>>2, 0x20>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x20>>2, 0x60>>2, 0xA0>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0xF8>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x70>>2, 0x88>>2, 0x08>>2, 0x30>>2, 0x40>>2, 0x80>>2, 0xF8>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0xF8>>2, 0x08>>2, 0x10>>2, 0x30>>2, 0x08>>2, 0x88>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x10>>2, 0x30>>2, 0x50>>2, 0x90>>2, 0xF8>>2, 0x10>>2, 0x10>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0xF8>>2, 0x80>>2, 0xB0>>2, 0xC8>>2, 0x08>>2, 0x88>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x30>>2, 0x40>>2, 0x80>>2, 0xB0>>2, 0xC8>>2, 0x88>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0xF8>>2, 0x08>>2, 0x10>>2, 0x10>>2, 0x20>>2, 0x40>>2, 0x40>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x70>>2, 0x88>>2, 0x88>>2, 0x70>>2, 0x88>>2, 0x88>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x70>>2, 0x88>>2, 0x98>>2, 0x68>>2, 0x08>>2, 0x10>>2, 0x60>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0x20>>2, 0x70>>2, 0x20>>2, 0x00>>2, 0x20>>2, 0x70>>2, 0x20>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0x20>>2, 0x70>>2, 0x20>>2, 0x00>>2, 0x30>>2, 0x20>>2, 0x40>>2, 0x00>>2, + 0x00>>2, 0x08>>2, 0x10>>2, 0x20>>2, 0x40>>2, 0x20>>2, 0x10>>2, 0x08>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0x00>>2, 0xF8>>2, 0x00>>2, 0xF8>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x40>>2, 0x20>>2, 0x10>>2, 0x08>>2, 0x10>>2, 0x20>>2, 0x40>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x70>>2, 0x88>>2, 0x10>>2, 0x20>>2, 0x20>>2, 0x00>>2, 0x20>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x70>>2, 0x88>>2, 0x98>>2, 0xA8>>2, 0xB0>>2, 0x80>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x20>>2, 0x50>>2, 0x88>>2, 0x88>>2, 0xF8>>2, 0x88>>2, 0x88>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0xF0>>2, 0x48>>2, 0x48>>2, 0x70>>2, 0x48>>2, 0x48>>2, 0xF0>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x70>>2, 0x88>>2, 0x80>>2, 0x80>>2, 0x80>>2, 0x88>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0xF0>>2, 0x48>>2, 0x48>>2, 0x48>>2, 0x48>>2, 0x48>>2, 0xF0>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0xF8>>2, 0x80>>2, 0x80>>2, 0xF0>>2, 0x80>>2, 0x80>>2, 0xF8>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0xF8>>2, 0x80>>2, 0x80>>2, 0xF0>>2, 0x80>>2, 0x80>>2, 0x80>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x70>>2, 0x88>>2, 0x80>>2, 0x80>>2, 0x98>>2, 0x88>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0xF8>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x70>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x38>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x90>>2, 0x60>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x88>>2, 0x90>>2, 0xA0>>2, 0xC0>>2, 0xA0>>2, 0x90>>2, 0x88>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x80>>2, 0x80>>2, 0x80>>2, 0x80>>2, 0x80>>2, 0x80>>2, 0xF8>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x88>>2, 0x88>>2, 0xD8>>2, 0xA8>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x88>>2, 0x88>>2, 0xC8>>2, 0xA8>>2, 0x98>>2, 0x88>>2, 0x88>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x70>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0xF0>>2, 0x88>>2, 0x88>>2, 0xF0>>2, 0x80>>2, 0x80>>2, 0x80>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x70>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0xA8>>2, 0x70>>2, 0x08>>2, 0x00>>2, + 0x00>>2, 0xF0>>2, 0x88>>2, 0x88>>2, 0xF0>>2, 0xA0>>2, 0x90>>2, 0x88>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x70>>2, 0x88>>2, 0x80>>2, 0x70>>2, 0x08>>2, 0x88>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0xF8>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x50>>2, 0x50>>2, 0x50>>2, 0x20>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0xA8>>2, 0xA8>>2, 0xD8>>2, 0x88>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x88>>2, 0x88>>2, 0x50>>2, 0x20>>2, 0x50>>2, 0x88>>2, 0x88>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x88>>2, 0x88>>2, 0x50>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0xF8>>2, 0x08>>2, 0x10>>2, 0x20>>2, 0x40>>2, 0x80>>2, 0xF8>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x70>>2, 0x40>>2, 0x40>>2, 0x40>>2, 0x40>>2, 0x40>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x80>>2, 0x80>>2, 0x40>>2, 0x20>>2, 0x10>>2, 0x08>>2, 0x08>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x70>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x20>>2, 0x50>>2, 0x88>>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, 0xF8>>2, 0x00>>2, + 0x20>>2, 0x10>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0x00>>2, 0x70>>2, 0x08>>2, 0x78>>2, 0x88>>2, 0x78>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x80>>2, 0x80>>2, 0xB0>>2, 0xC8>>2, 0x88>>2, 0xC8>>2, 0xB0>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0x00>>2, 0x70>>2, 0x88>>2, 0x80>>2, 0x88>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x08>>2, 0x08>>2, 0x68>>2, 0x98>>2, 0x88>>2, 0x98>>2, 0x68>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0x00>>2, 0x70>>2, 0x88>>2, 0xF8>>2, 0x80>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x30>>2, 0x48>>2, 0x40>>2, 0xF0>>2, 0x40>>2, 0x40>>2, 0x40>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0x00>>2, 0x78>>2, 0x88>>2, 0x88>>2, 0x78>>2, 0x08>>2, 0x88>>2, 0x70>>2, + 0x00>>2, 0x80>>2, 0x80>>2, 0xB0>>2, 0xC8>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x20>>2, 0x00>>2, 0x60>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x08>>2, 0x00>>2, 0x18>>2, 0x08>>2, 0x08>>2, 0x08>>2, 0x48>>2, 0x48>>2, 0x30>>2, + 0x00>>2, 0x80>>2, 0x80>>2, 0x88>>2, 0x90>>2, 0xE0>>2, 0x90>>2, 0x88>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x60>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0x00>>2, 0xD0>>2, 0xA8>>2, 0xA8>>2, 0xA8>>2, 0x88>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0x00>>2, 0xB0>>2, 0xC8>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0x00>>2, 0x70>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0x00>>2, 0xB0>>2, 0xC8>>2, 0x88>>2, 0xC8>>2, 0xB0>>2, 0x80>>2, 0x80>>2, + 0x00>>2, 0x00>>2, 0x00>>2, 0x68>>2, 0x98>>2, 0x88>>2, 0x98>>2, 0x68>>2, 0x08>>2, 0x08>>2, + 0x00>>2, 0x00>>2, 0x00>>2, 0xB0>>2, 0xC8>>2, 0x80>>2, 0x80>>2, 0x80>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0x00>>2, 0x70>>2, 0x80>>2, 0x70>>2, 0x08>>2, 0xF0>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x40>>2, 0x40>>2, 0xF0>>2, 0x40>>2, 0x40>>2, 0x48>>2, 0x30>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0x00>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x98>>2, 0x68>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0x00>>2, 0x88>>2, 0x88>>2, 0x50>>2, 0x50>>2, 0x20>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0x00>>2, 0x88>>2, 0x88>>2, 0xA8>>2, 0xA8>>2, 0x50>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0x00>>2, 0x88>>2, 0x50>>2, 0x20>>2, 0x50>>2, 0x88>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0x00>>2, 0x88>>2, 0x88>>2, 0x98>>2, 0x68>>2, 0x08>>2, 0x88>>2, 0x70>>2, + 0x00>>2, 0x00>>2, 0x00>>2, 0xF8>>2, 0x10>>2, 0x20>>2, 0x40>>2, 0xF8>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x18>>2, 0x20>>2, 0x10>>2, 0x60>>2, 0x10>>2, 0x20>>2, 0x18>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x60>>2, 0x10>>2, 0x20>>2, 0x18>>2, 0x20>>2, 0x10>>2, 0x60>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x48>>2, 0xA8>>2, 0x90>>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, 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, 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, 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, + 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, + 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, 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, 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, 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, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x20>>2, 0x00>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0x20>>2, 0x78>>2, 0xA0>>2, 0xA0>>2, 0xA0>>2, 0x78>>2, 0x20>>2, 0x00>>2, + 0x00>>2, 0x30>>2, 0x48>>2, 0x40>>2, 0xE0>>2, 0x40>>2, 0x48>>2, 0xB0>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0x00>>2, 0x88>>2, 0x70>>2, 0x50>>2, 0x70>>2, 0x88>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x88>>2, 0x88>>2, 0x50>>2, 0x20>>2, 0xF8>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x00>>2, + 0x00>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x00>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x70>>2, 0x80>>2, 0xE0>>2, 0x90>>2, 0x48>>2, 0x38>>2, 0x08>>2, 0x70>>2, 0x00>>2, + 0x50>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x70>>2, 0x88>>2, 0xA8>>2, 0xC8>>2, 0xA8>>2, 0x88>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x38>>2, 0x48>>2, 0x58>>2, 0x28>>2, 0x00>>2, 0x78>>2, 0x00>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0x00>>2, 0x24>>2, 0x48>>2, 0x90>>2, 0x48>>2, 0x24>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x78>>2, 0x08>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x78>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x70>>2, 0x88>>2, 0xE8>>2, 0xC8>>2, 0xC8>>2, 0x88>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0xF8>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x20>>2, 0x50>>2, 0x20>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0x20>>2, 0x20>>2, 0xF8>>2, 0x20>>2, 0x20>>2, 0xF8>>2, 0x00>>2, 0x00>>2, + 0x30>>2, 0x48>>2, 0x10>>2, 0x20>>2, 0x78>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, + 0x70>>2, 0x08>>2, 0x30>>2, 0x08>>2, 0x70>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, + 0x10>>2, 0x20>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0x00>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0xC8>>2, 0xB0>>2, 0x80>>2, 0x00>>2, + 0x00>>2, 0x78>>2, 0xE8>>2, 0xE8>>2, 0x68>>2, 0x28>>2, 0x28>>2, 0x28>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x20>>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, 0x10>>2, 0x20>>2, + 0x20>>2, 0x60>>2, 0x20>>2, 0x20>>2, 0x70>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x30>>2, 0x48>>2, 0x48>>2, 0x30>>2, 0x00>>2, 0x78>>2, 0x00>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0x00>>2, 0x90>>2, 0x48>>2, 0x24>>2, 0x48>>2, 0x90>>2, 0x00>>2, 0x00>>2, + 0x40>>2, 0xC0>>2, 0x40>>2, 0x40>>2, 0xE4>>2, 0x0C>>2, 0x14>>2, 0x3C>>2, 0x04>>2, 0x00>>2, + 0x40>>2, 0xC0>>2, 0x40>>2, 0x40>>2, 0xE8>>2, 0x14>>2, 0x04>>2, 0x08>>2, 0x1C>>2, 0x00>>2, + 0xC0>>2, 0x20>>2, 0x40>>2, 0x20>>2, 0xC8>>2, 0x18>>2, 0x28>>2, 0x78>>2, 0x08>>2, 0x00>>2, + 0x00>>2, 0x20>>2, 0x00>>2, 0x20>>2, 0x20>>2, 0x40>>2, 0x88>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x40>>2, 0x20>>2, 0x70>>2, 0x88>>2, 0x88>>2, 0xF8>>2, 0x88>>2, 0x88>>2, 0x00>>2, 0x00>>2, + 0x10>>2, 0x20>>2, 0x70>>2, 0x88>>2, 0x88>>2, 0xF8>>2, 0x88>>2, 0x88>>2, 0x00>>2, 0x00>>2, + 0x20>>2, 0x50>>2, 0x70>>2, 0x88>>2, 0x88>>2, 0xF8>>2, 0x88>>2, 0x88>>2, 0x00>>2, 0x00>>2, + 0x48>>2, 0xB0>>2, 0x70>>2, 0x88>>2, 0x88>>2, 0xF8>>2, 0x88>>2, 0x88>>2, 0x00>>2, 0x00>>2, + 0x50>>2, 0x00>>2, 0x70>>2, 0x88>>2, 0x88>>2, 0xF8>>2, 0x88>>2, 0x88>>2, 0x00>>2, 0x00>>2, + 0x20>>2, 0x50>>2, 0x70>>2, 0x88>>2, 0x88>>2, 0xF8>>2, 0x88>>2, 0x88>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x3C>>2, 0x50>>2, 0x90>>2, 0x9C>>2, 0xF0>>2, 0x90>>2, 0x9C>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x70>>2, 0x88>>2, 0x80>>2, 0x80>>2, 0x80>>2, 0x88>>2, 0x70>>2, 0x20>>2, 0x40>>2, + 0x40>>2, 0xF8>>2, 0x80>>2, 0x80>>2, 0xF0>>2, 0x80>>2, 0x80>>2, 0xF8>>2, 0x00>>2, 0x00>>2, + 0x10>>2, 0xF8>>2, 0x80>>2, 0x80>>2, 0xF0>>2, 0x80>>2, 0x80>>2, 0xF8>>2, 0x00>>2, 0x00>>2, + 0x20>>2, 0xF8>>2, 0x80>>2, 0x80>>2, 0xF0>>2, 0x80>>2, 0x80>>2, 0xF8>>2, 0x00>>2, 0x00>>2, + 0x50>>2, 0xF8>>2, 0x80>>2, 0x80>>2, 0xF0>>2, 0x80>>2, 0x80>>2, 0xF8>>2, 0x00>>2, 0x00>>2, + 0x40>>2, 0x20>>2, 0x70>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x10>>2, 0x20>>2, 0x70>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x20>>2, 0x50>>2, 0x70>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x50>>2, 0x00>>2, 0x70>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0xF0>>2, 0x48>>2, 0x48>>2, 0xE8>>2, 0x48>>2, 0x48>>2, 0xF0>>2, 0x00>>2, 0x00>>2, + 0x28>>2, 0x50>>2, 0x88>>2, 0xC8>>2, 0xA8>>2, 0x98>>2, 0x88>>2, 0x88>>2, 0x00>>2, 0x00>>2, + 0x40>>2, 0x20>>2, 0x70>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x10>>2, 0x20>>2, 0x70>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x20>>2, 0x50>>2, 0x70>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x28>>2, 0x50>>2, 0x70>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x50>>2, 0x00>>2, 0x70>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0x00>>2, 0x88>>2, 0x50>>2, 0x20>>2, 0x50>>2, 0x88>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x70>>2, 0x98>>2, 0x98>>2, 0xA8>>2, 0xC8>>2, 0xC8>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x40>>2, 0x20>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x10>>2, 0x20>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x20>>2, 0x50>>2, 0x00>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x50>>2, 0x00>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x10>>2, 0x20>>2, 0x88>>2, 0x88>>2, 0x50>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x80>>2, 0xF0>>2, 0x88>>2, 0xF0>>2, 0x80>>2, 0x80>>2, 0x80>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x70>>2, 0x88>>2, 0x90>>2, 0xA0>>2, 0x90>>2, 0x88>>2, 0xB0>>2, 0x00>>2, 0x00>>2, + 0x40>>2, 0x20>>2, 0x00>>2, 0x70>>2, 0x08>>2, 0x78>>2, 0x88>>2, 0x78>>2, 0x00>>2, 0x00>>2, + 0x10>>2, 0x20>>2, 0x00>>2, 0x70>>2, 0x08>>2, 0x78>>2, 0x88>>2, 0x78>>2, 0x00>>2, 0x00>>2, + 0x20>>2, 0x50>>2, 0x00>>2, 0x70>>2, 0x08>>2, 0x78>>2, 0x88>>2, 0x78>>2, 0x00>>2, 0x00>>2, + 0x28>>2, 0x50>>2, 0x00>>2, 0x70>>2, 0x08>>2, 0x78>>2, 0x88>>2, 0x78>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x50>>2, 0x00>>2, 0x70>>2, 0x08>>2, 0x78>>2, 0x88>>2, 0x78>>2, 0x00>>2, 0x00>>2, + 0x20>>2, 0x50>>2, 0x20>>2, 0x70>>2, 0x08>>2, 0x78>>2, 0x88>>2, 0x78>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0x00>>2, 0x78>>2, 0x14>>2, 0x7C>>2, 0x90>>2, 0x7C>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0x00>>2, 0x70>>2, 0x88>>2, 0x80>>2, 0x88>>2, 0x70>>2, 0x20>>2, 0x40>>2, + 0x40>>2, 0x20>>2, 0x00>>2, 0x70>>2, 0x88>>2, 0xF8>>2, 0x80>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x10>>2, 0x20>>2, 0x00>>2, 0x70>>2, 0x88>>2, 0xF8>>2, 0x80>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x20>>2, 0x50>>2, 0x00>>2, 0x70>>2, 0x88>>2, 0xF8>>2, 0x80>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x50>>2, 0x00>>2, 0x70>>2, 0x88>>2, 0xF8>>2, 0x80>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x40>>2, 0x20>>2, 0x00>>2, 0x60>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x20>>2, 0x40>>2, 0x00>>2, 0x60>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x20>>2, 0x50>>2, 0x00>>2, 0x60>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x50>>2, 0x00>>2, 0x60>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0xC0>>2, 0x30>>2, 0x70>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x28>>2, 0x50>>2, 0x00>>2, 0xB0>>2, 0xC8>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x00>>2, 0x00>>2, + 0x40>>2, 0x20>>2, 0x00>>2, 0x70>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x10>>2, 0x20>>2, 0x00>>2, 0x70>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x20>>2, 0x50>>2, 0x00>>2, 0x70>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x28>>2, 0x50>>2, 0x00>>2, 0x70>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x50>>2, 0x00>>2, 0x70>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x70>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0x20>>2, 0x00>>2, 0xF8>>2, 0x00>>2, 0x20>>2, 0x00>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x00>>2, 0x00>>2, 0x78>>2, 0x98>>2, 0xA8>>2, 0xC8>>2, 0xF0>>2, 0x00>>2, 0x00>>2, + 0x40>>2, 0x20>>2, 0x00>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x98>>2, 0x68>>2, 0x00>>2, 0x00>>2, + 0x10>>2, 0x20>>2, 0x00>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x98>>2, 0x68>>2, 0x00>>2, 0x00>>2, + 0x20>>2, 0x50>>2, 0x00>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x98>>2, 0x68>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x50>>2, 0x00>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0x98>>2, 0x68>>2, 0x00>>2, 0x00>>2, + 0x00>>2, 0x10>>2, 0x20>>2, 0x88>>2, 0x88>>2, 0x98>>2, 0x68>>2, 0x08>>2, 0x88>>2, 0x70>>2, + 0x00>>2, 0x00>>2, 0x80>>2, 0xF0>>2, 0x88>>2, 0x88>>2, 0x88>>2, 0xF0>>2, 0x80>>2, 0x80>>2, + 0x00>>2, 0x50>>2, 0x00>>2, 0x88>>2, 0x88>>2, 0x98>>2, 0x68>>2, 0x08>>2, 0x88>>2, 0x70>>2, +}; diff --git a/src/psp_kbd.c b/src/psp_kbd.c new file mode 100644 index 0000000..0dc83fb --- /dev/null +++ b/src/psp_kbd.c @@ -0,0 +1,1063 @@ +/* + * Copyright (C) 2006 Ludovic Jacomme (ludovic.jacomme@gmail.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "global.h" +#include "psp_kbd.h" +#include "psp_menu.h" +#include "psp_sdl.h" +#include "psp_danzeff.h" + +# define KBD_MIN_ANALOG_TIME 150000 +# define KBD_MIN_START_TIME 1000000 +# define KBD_MAX_EVENT_TIME 500000 +# define KBD_MIN_PENDING_TIME 300000 +# define KBD_MIN_DANZEFF_TIME 150000 +# define KBD_MIN_COMMAND_TIME 100000 +# define KBD_MIN_BATTCHECK_TIME 90000000 +# define KBD_MIN_AUTOFIRE_TIME 1000000 + + static gp2xCtrlData loc_button_data; + static unsigned int loc_last_event_time = 0; + static unsigned int loc_last_analog_time = 0; + static long first_time_stamp = -1; + static long first_time_auto_stamp = -1; + static char loc_button_press[ KBD_MAX_BUTTONS ]; + static char loc_button_release[ KBD_MAX_BUTTONS ]; + static unsigned int loc_button_mask[ KBD_MAX_BUTTONS ] = + { + GP2X_CTRL_UP , /* KBD_UP */ + GP2X_CTRL_RIGHT , /* KBD_RIGHT */ + GP2X_CTRL_DOWN , /* KBD_DOWN */ + GP2X_CTRL_LEFT , /* KBD_LEFT */ + GP2X_CTRL_TRIANGLE , /* KBD_TRIANGLE */ + GP2X_CTRL_CIRCLE , /* KBD_CIRCLE */ + GP2X_CTRL_CROSS , /* KBD_CROSS */ + GP2X_CTRL_SQUARE , /* KBD_SQUARE */ + GP2X_CTRL_SELECT , /* KBD_SELECT */ + GP2X_CTRL_START , /* KBD_START */ + GP2X_CTRL_LTRIGGER , /* KBD_LTRIGGER */ + GP2X_CTRL_RTRIGGER , /* KBD_RTRIGGER */ + GP2X_CTRL_FIRE, /* KBD_FIRE */ + }; + + static char loc_button_name[ KBD_ALL_BUTTONS ][20] = + { + "UP", + "RIGHT", + "DOWN", + "LEFT", + "Y", // Triangle + "B", // Circle + "X", // Cross + "A", // Square + "SELECT", + "START", + "LTRIGGER", + "RTRIGGER", + "JOY_FIRE", + "JOY_UP", + "JOY_RIGHT", + "JOY_DOWN", + "JOY_LEFT" + }; + + static char loc_button_name_L[ KBD_ALL_BUTTONS ][20] = + { + "L_UP", + "L_RIGHT", + "L_DOWN", + "L_LEFT", + "L_Y", // Triangle + "L_B", // Circle + "L_X", // Cross + "L_A", // Square + "L_SELECT", + "L_START", + "L_LTRIGGER", + "L_RTRIGGER", + "L_JOY_FIRE", + "L_JOY_UP", + "L_JOY_RIGHT", + "L_JOY_DOWN", + "L_JOY_LEFT" + }; + + static char loc_button_name_R[ KBD_ALL_BUTTONS ][20] = + { + "R_UP", + "R_RIGHT", + "R_DOWN", + "R_LEFT", + "R_Y", // Triangle + "R_B", // Circle + "R_X", // Cross + "R_A", // Square + "R_SELECT", + "R_START", + "R_LTRIGGER", + "R_RTRIGGER", + "R_JOY_FIRE", + "R_JOY_UP", + "R_JOY_RIGHT", + "R_JOY_DOWN", + "R_JOY_LEFT" + }; + + struct dragon_key_trans psp_dragon_key_info[DRAGONK_MAX_KEY]= + { + // DRAGONK NAME + { DRAGONK_0, "0" }, + { DRAGONK_1, "1" }, + { DRAGONK_2, "2" }, + { DRAGONK_3, "3" }, + { DRAGONK_4, "4" }, + { DRAGONK_5, "5" }, + { DRAGONK_6, "6" }, + { DRAGONK_7, "7" }, + { DRAGONK_8, "8" }, + { DRAGONK_9, "9" }, + { DRAGONK_A, "A" }, + { DRAGONK_B, "B" }, + { DRAGONK_C, "C" }, + { DRAGONK_D, "D" }, + { DRAGONK_E, "E" }, + { DRAGONK_F, "F" }, + { DRAGONK_G, "G" }, + { DRAGONK_H, "H" }, + { DRAGONK_I, "I" }, + { DRAGONK_J, "J" }, + { DRAGONK_K, "K" }, + { DRAGONK_L, "L" }, + { DRAGONK_M, "M" }, + { DRAGONK_N, "N" }, + { DRAGONK_O, "O" }, + { DRAGONK_P, "P" }, + { DRAGONK_Q, "Q" }, + { DRAGONK_R, "R" }, + { DRAGONK_S, "S" }, + { DRAGONK_T, "T" }, + { DRAGONK_U, "U" }, + { DRAGONK_V, "V" }, + { DRAGONK_W, "W" }, + { DRAGONK_X, "X" }, + { DRAGONK_Y, "Y" }, + { DRAGONK_Z, "Z" }, + { DRAGONK_a, "a" }, + { DRAGONK_b, "b" }, + { DRAGONK_c, "c" }, + { DRAGONK_d, "d" }, + { DRAGONK_e, "e" }, + { DRAGONK_f, "f" }, + { DRAGONK_g, "g" }, + { DRAGONK_h, "h" }, + { DRAGONK_i, "i" }, + { DRAGONK_j, "j" }, + { DRAGONK_k, "k" }, + { DRAGONK_l, "l" }, + { DRAGONK_m, "m" }, + { DRAGONK_n, "n" }, + { DRAGONK_o, "o" }, + { DRAGONK_p, "p" }, + { DRAGONK_q, "q" }, + { DRAGONK_r, "r" }, + { DRAGONK_s, "s" }, + { DRAGONK_t, "t" }, + { DRAGONK_u, "u" }, + { DRAGONK_v, "v" }, + { DRAGONK_w, "w" }, + { DRAGONK_x, "x" }, + { DRAGONK_y, "y" }, + { DRAGONK_z, "z" }, + { DRAGONK_QUOTE, "\'" }, + { DRAGONK_COMMA, "," }, + { DRAGONK_LESS, "<" }, + { DRAGONK_PERIOD, "." }, + { DRAGONK_GREATER, ">" }, + { DRAGONK_SEMICOLON, ";" }, + { DRAGONK_COLON, ":" }, + { DRAGONK_UNDERSCORE, "_" }, + { DRAGONK_PIPE, "|" }, + { DRAGONK_EQUAL, "=" }, + { DRAGONK_PLUS, "+" }, + { DRAGONK_TILDA , "~" }, + { DRAGONK_DBLQUOTE, "\"" }, + { DRAGONK_QUESTION, "?" }, + { DRAGONK_SLASH, "/" }, + { DRAGONK_MINUS, "-" }, + { DRAGONK_LBRACKET, "[" }, + { DRAGONK_RBRACKET, "]" }, + { DRAGONK_LCBRACE, "{" }, + { DRAGONK_RCBRACE, "}" }, + { DRAGONK_SPACE, "SPACE" }, + { DRAGONK_EXCLAMATN, "!" }, + { DRAGONK_AT, "@" }, + { DRAGONK_HASH, "#" }, + { DRAGONK_DOLLAR, "$" }, + { DRAGONK_PERCENT, "%" }, + { DRAGONK_POWER, "^" }, + { DRAGONK_AMPERSAND, "&" }, + { DRAGONK_ASTERISK, "*" }, + { DRAGONK_LPAREN, "(" }, + { DRAGONK_RPAREN, ")" }, + { DRAGONK_BACKSLASH, "\\" }, + { DRAGONK_BACKQUOTE, "`" }, + { DRAGONK_TAB, "TAB" }, + { DRAGONK_BACKSPACE, "BACKSPACE" }, + { DRAGONK_LEFT, "LEFT" }, + { DRAGONK_RIGHT, "RIGHT" }, + { DRAGONK_UP, "UP" }, + { DRAGONK_DOWN, "DOWN" }, + { DRAGONK_DELETE, "DELETE" }, + { DRAGONK_RETURN, "RETURN" }, + { DRAGONK_SHIFT, "SHIFT" }, + { DRAGONK_JOY_UP, "JOY_UP" }, + { DRAGONK_JOY_DOWN, "JOY_DOWN" }, + { DRAGONK_JOY_LEFT, "JOY_LEFT" }, + { DRAGONK_JOY_RIGHT, "JOY_RIGHT" }, + { DRAGONK_JOY_FIRE, "JOY_FIRE" }, + { CMD_FPS, "CMD_FPS" }, + { CMD_JOY, "CMD_JOY" }, + { CMD_RENDER, "CMD_RENDER" }, + { CMD_LOAD, "CMD_LOAD" }, + { CMD_SAVE, "CMD_SAVE" }, + { CMD_RESET, "CMD_RESET" }, + { CMD_AUTOFIRE, "CMD_AUTOFIRE" }, + { CMD_INCFIRE, "CMD_INCFIRE" }, + { CMD_DECFIRE, "CMD_DECFIRE" }, + { CMD_SCREEN, "CMD_SCREEN" } + }; + + static int loc_default_mapping[ KBD_ALL_BUTTONS ] = { + DRAGONK_UP , /* KBD_UP */ + DRAGONK_RIGHT , /* KBD_RIGHT */ + DRAGONK_DOWN , /* KBD_DOWN */ + DRAGONK_LEFT , /* KBD_LEFT */ + DRAGONK_RETURN , /* KBD_TRIANGLE */ + DRAGONK_1 , /* KBD_CIRCLE */ + DRAGONK_JOY_FIRE , /* KBD_CROSS */ + DRAGONK_2 , /* KBD_SQUARE */ + -1 , /* KBD_SELECT */ + -1 , /* KBD_START */ + KBD_LTRIGGER_MAPPING , /* KBD_LTRIGGER */ + KBD_RTRIGGER_MAPPING , /* KBD_RTRIGGER */ + DRAGONK_JOY_FIRE , /* KBD_JOY_FIRE */ + DRAGONK_JOY_UP , /* KBD_JOY_UP */ + DRAGONK_JOY_RIGHT , /* KBD_JOY_RIGHT */ + DRAGONK_JOY_DOWN , /* KBD_JOY_DOWN */ + DRAGONK_JOY_LEFT /* KBD_JOY_LEFT */ + }; + + static int loc_default_mapping_L[ KBD_ALL_BUTTONS ] = { + DRAGONK_UP , /* KBD_UP */ + DRAGONK_RIGHT , /* KBD_RIGHT */ + DRAGONK_DOWN , /* KBD_DOWN */ + DRAGONK_LEFT , /* KBD_LEFT */ + CMD_LOAD , /* KBD_TRIANGLE */ + CMD_RENDER , /* KBD_CIRCLE */ + CMD_SAVE , /* KBD_CROSS */ + CMD_FPS , /* KBD_SQUARE */ + -1 , /* KBD_SELECT */ + -1 , /* KBD_START */ + KBD_LTRIGGER_MAPPING , /* KBD_LTRIGGER */ + KBD_RTRIGGER_MAPPING , /* KBD_RTRIGGER */ + DRAGONK_JOY_FIRE , /* KBD_JOY_FIRE */ + DRAGONK_JOY_UP , /* KBD_JOY_UP */ + DRAGONK_JOY_RIGHT , /* KBD_JOY_RIGHT */ + DRAGONK_JOY_DOWN , /* KBD_JOY_DOWN */ + DRAGONK_JOY_LEFT /* KBD_JOY_LEFT */ + }; + + static int loc_default_mapping_R[ KBD_ALL_BUTTONS ] = { + DRAGONK_UP , /* KBD_UP */ + CMD_INCFIRE , /* KBD_RIGHT */ + DRAGONK_DOWN , /* KBD_DOWN */ + CMD_DECFIRE , /* KBD_LEFT */ + DRAGONK_RETURN , /* KBD_TRIANGLE */ + DRAGONK_4 , /* KBD_CIRCLE */ + CMD_AUTOFIRE , /* KBD_CROSS */ + DRAGONK_SPACE , /* KBD_SQUARE */ + -1 , /* KBD_SELECT */ + -1 , /* KBD_START */ + KBD_LTRIGGER_MAPPING , /* KBD_LTRIGGER */ + KBD_RTRIGGER_MAPPING , /* KBD_RTRIGGER */ + DRAGONK_JOY_FIRE , /* KBD_JOY_FIRE */ + DRAGONK_JOY_UP , /* KBD_JOY_UP */ + DRAGONK_JOY_RIGHT , /* KBD_JOY_RIGHT */ + DRAGONK_JOY_DOWN , /* KBD_JOY_DOWN */ + DRAGONK_JOY_LEFT /* KBD_JOY_LEFT */ + }; + +# define KBD_MAX_ENTRIES 104 + + int kbd_layout[KBD_MAX_ENTRIES][2] = { + /* Key Ascii */ + { DRAGONK_0, '0' }, + { DRAGONK_1, '1' }, + { DRAGONK_2, '2' }, + { DRAGONK_3, '3' }, + { DRAGONK_4, '4' }, + { DRAGONK_5, '5' }, + { DRAGONK_6, '6' }, + { DRAGONK_7, '7' }, + { DRAGONK_8, '8' }, + { DRAGONK_9, '9' }, + { DRAGONK_A, 'A' }, + { DRAGONK_B, 'B' }, + { DRAGONK_C, 'C' }, + { DRAGONK_D, 'D' }, + { DRAGONK_E, 'E' }, + { DRAGONK_F, 'F' }, + { DRAGONK_G, 'G' }, + { DRAGONK_H, 'H' }, + { DRAGONK_I, 'I' }, + { DRAGONK_J, 'J' }, + { DRAGONK_K, 'K' }, + { DRAGONK_L, 'L' }, + { DRAGONK_M, 'M' }, + { DRAGONK_N, 'N' }, + { DRAGONK_O, 'O' }, + { DRAGONK_P, 'P' }, + { DRAGONK_Q, 'Q' }, + { DRAGONK_R, 'R' }, + { DRAGONK_S, 'S' }, + { DRAGONK_T, 'T' }, + { DRAGONK_U, 'U' }, + { DRAGONK_V, 'V' }, + { DRAGONK_W, 'W' }, + { DRAGONK_X, 'X' }, + { DRAGONK_Y, 'Y' }, + { DRAGONK_Z, 'Z' }, + { DRAGONK_a, 'a' }, + { DRAGONK_b, 'b' }, + { DRAGONK_c, 'c' }, + { DRAGONK_d, 'd' }, + { DRAGONK_e, 'e' }, + { DRAGONK_f, 'f' }, + { DRAGONK_g, 'g' }, + { DRAGONK_h, 'h' }, + { DRAGONK_i, 'i' }, + { DRAGONK_j, 'j' }, + { DRAGONK_k, 'k' }, + { DRAGONK_l, 'l' }, + { DRAGONK_m, 'm' }, + { DRAGONK_n, 'n' }, + { DRAGONK_o, 'o' }, + { DRAGONK_p, 'p' }, + { DRAGONK_q, 'q' }, + { DRAGONK_r, 'r' }, + { DRAGONK_s, 's' }, + { DRAGONK_t, 't' }, + { DRAGONK_u, 'u' }, + { DRAGONK_v, 'v' }, + { DRAGONK_w, 'w' }, + { DRAGONK_x, 'x' }, + { DRAGONK_y, 'y' }, + { DRAGONK_z, 'z' }, + { DRAGONK_QUOTE, '\'' }, + { DRAGONK_COMMA, ',' }, + { DRAGONK_LESS, '<' }, + { DRAGONK_PERIOD, '.' }, + { DRAGONK_GREATER, '>' }, + { DRAGONK_SEMICOLON, ';' }, + { DRAGONK_COLON, ':' }, + { DRAGONK_UNDERSCORE, '_' }, + { DRAGONK_PIPE, '|' }, + { DRAGONK_EQUAL, '=' }, + { DRAGONK_PLUS, '+' }, + { DRAGONK_TILDA , '~' }, + { DRAGONK_DBLQUOTE, '"' }, + { DRAGONK_QUESTION, '?' }, + { DRAGONK_SLASH, '/' }, + { DRAGONK_MINUS, '-' }, + { DRAGONK_LBRACKET, '[' }, + { DRAGONK_RBRACKET, ']' }, + { DRAGONK_LCBRACE, '{' }, + { DRAGONK_RCBRACE, '}' }, + { DRAGONK_SPACE, ' ' }, + { DRAGONK_EXCLAMATN, '!' }, + { DRAGONK_AT, '@' }, + { DRAGONK_HASH, '#' }, + { DRAGONK_DOLLAR, '$' }, + { DRAGONK_PERCENT, '%' }, + { DRAGONK_POWER, '^' }, + { DRAGONK_AMPERSAND, '&' }, + { DRAGONK_ASTERISK, '*' }, + { DRAGONK_LPAREN, '(' }, + { DRAGONK_RPAREN, ')' }, + { DRAGONK_BACKSLASH, '\\' }, + { DRAGONK_BACKQUOTE, '`' }, + { DRAGONK_TAB, DANZEFF_TAB }, + { DRAGONK_BACKSPACE, DANZEFF_DEL }, + { DRAGONK_LEFT, -1 }, + { DRAGONK_RIGHT, -1 }, + { DRAGONK_UP, -1 }, + { DRAGONK_DOWN, -1 }, + { DRAGONK_DELETE, DANZEFF_SUPPR }, + { DRAGONK_RETURN, DANZEFF_ENTER }, + { DRAGONK_SHIFT, DANZEFF_SHIFT } + }; + + int psp_kbd_mapping[ KBD_ALL_BUTTONS ]; + int psp_kbd_mapping_L[ KBD_ALL_BUTTONS ]; + int psp_kbd_mapping_R[ KBD_ALL_BUTTONS ]; + int psp_kbd_presses[ KBD_ALL_BUTTONS ]; + int kbd_ltrigger_mapping_active; + int kbd_rtrigger_mapping_active; + + static int danzeff_dragon_key = 0; + static int danzeff_dragon_pending = 0; + static int danzeff_mode = 0; + + + char command_keys[ 128 ]; + static int command_mode = 0; + static int command_index = 0; + static int command_size = 0; + static int command_dragon_pending = 0; + static int command_dragon_key = 0; + +int +dragon_key_event(int dragon_idx, int press) +{ + if (dragon_idx < CMD_FPS) { + if ((dragon_idx >= 0) && + (dragon_idx < DRAGONK_MAX_KEY )) { + + if (press) { + dragon_key_press(dragon_idx); + } else { + dragon_key_release(dragon_idx); + } + } + } else { + if (press) { + dragon_treat_command_key(dragon_idx); + } + } + return 0; +} + +int +dragon_kbd_reset() +{ + dragon_reset_keyboard(); + return 0; +} + +int +dragon_get_key_from_ascii(int key_ascii) +{ + int index; + for (index = 0; index < KBD_MAX_ENTRIES; index++) { + if (kbd_layout[index][1] == key_ascii) return kbd_layout[index][0]; + } + return -1; +} + +void +psp_kbd_run_command(char *Command) +{ + strncpy(command_keys, Command, 128); + command_size = strlen(Command); + command_index = 0; + + command_dragon_key = 0; + command_dragon_pending = 0; + command_mode = 1; +} + +int +psp_kbd_reset_mapping(void) +{ + memcpy(psp_kbd_mapping , loc_default_mapping, sizeof(loc_default_mapping)); + memcpy(psp_kbd_mapping_L, loc_default_mapping_L, sizeof(loc_default_mapping_L)); + memcpy(psp_kbd_mapping_R, loc_default_mapping_R, sizeof(loc_default_mapping_R)); + return 0; +} + +int +psp_kbd_reset_hotkeys(void) +{ + int index; + int key_id; + for (index = 0; index < KBD_ALL_BUTTONS; index++) { + key_id = loc_default_mapping[index]; + if ((key_id >= CMD_FPS) && (key_id <= CMD_SCREEN)) { + psp_kbd_mapping[index] = key_id; + } + key_id = loc_default_mapping_L[index]; + if ((key_id >= CMD_FPS) && (key_id <= CMD_SCREEN)) { + psp_kbd_mapping_L[index] = key_id; + } + key_id = loc_default_mapping_R[index]; + if ((key_id >= CMD_FPS) && (key_id <= CMD_SCREEN)) { + psp_kbd_mapping_R[index] = key_id; + } + } + return 0; +} + +int +psp_kbd_load_mapping_file(FILE *KbdFile) +{ + char Buffer[512]; + char *Scan; + int tmp_mapping[KBD_ALL_BUTTONS]; + int tmp_mapping_L[KBD_ALL_BUTTONS]; + int tmp_mapping_R[KBD_ALL_BUTTONS]; + int dragon_key_id = 0; + int kbd_id = 0; + + memcpy(tmp_mapping , loc_default_mapping , sizeof(loc_default_mapping)); + memcpy(tmp_mapping_L, loc_default_mapping_L, sizeof(loc_default_mapping_R)); + memcpy(tmp_mapping_R, loc_default_mapping_R, sizeof(loc_default_mapping_R)); + + while (fgets(Buffer,512,KbdFile) != (char *)0) { + + Scan = strchr(Buffer,'\n'); + if (Scan) *Scan = '\0'; + /* For this #@$% of windows ! */ + Scan = strchr(Buffer,'\r'); + if (Scan) *Scan = '\0'; + if (Buffer[0] == '#') continue; + + Scan = strchr(Buffer,'='); + if (! Scan) continue; + + *Scan = '\0'; + dragon_key_id = atoi(Scan + 1); + + for (kbd_id = 0; kbd_id < KBD_ALL_BUTTONS; kbd_id++) { + if (!strcasecmp(Buffer,loc_button_name[kbd_id])) { + tmp_mapping[kbd_id] = dragon_key_id; + //break; + } + } + for (kbd_id = 0; kbd_id < KBD_ALL_BUTTONS; kbd_id++) { + if (!strcasecmp(Buffer,loc_button_name_L[kbd_id])) { + tmp_mapping_L[kbd_id] = dragon_key_id; + //break; + } + } + for (kbd_id = 0; kbd_id < KBD_ALL_BUTTONS; kbd_id++) { + if (!strcasecmp(Buffer,loc_button_name_R[kbd_id])) { + tmp_mapping_R[kbd_id] = dragon_key_id; + //break; + } + } + } + + memcpy(psp_kbd_mapping, tmp_mapping, sizeof(psp_kbd_mapping)); + memcpy(psp_kbd_mapping_L, tmp_mapping_L, sizeof(psp_kbd_mapping_L)); + memcpy(psp_kbd_mapping_R, tmp_mapping_R, sizeof(psp_kbd_mapping_R)); + + return 0; +} + +int +psp_kbd_load_mapping(char *kbd_filename) +{ + FILE *KbdFile; + int error = 0; + + KbdFile = fopen(kbd_filename, "r"); + error = 1; + + if (KbdFile != (FILE*)0) { + psp_kbd_load_mapping_file(KbdFile); + error = 0; + fclose(KbdFile); + } + + kbd_ltrigger_mapping_active = 0; + kbd_rtrigger_mapping_active = 0; + + return error; +} + +int +psp_kbd_save_mapping(char *kbd_filename) +{ + FILE *KbdFile; + int kbd_id = 0; + int error = 0; + + KbdFile = fopen(kbd_filename, "w"); + error = 1; + + if (KbdFile != (FILE*)0) { + + for (kbd_id = 0; kbd_id < KBD_ALL_BUTTONS; kbd_id++) + { + fprintf(KbdFile, "%s=%d\n", loc_button_name[kbd_id], psp_kbd_mapping[kbd_id]); + } + for (kbd_id = 0; kbd_id < KBD_ALL_BUTTONS; kbd_id++) + { + fprintf(KbdFile, "%s=%d\n", loc_button_name_L[kbd_id], psp_kbd_mapping_L[kbd_id]); + } + for (kbd_id = 0; kbd_id < KBD_ALL_BUTTONS; kbd_id++) + { + fprintf(KbdFile, "%s=%d\n", loc_button_name_R[kbd_id], psp_kbd_mapping_R[kbd_id]); + } + error = 0; + fclose(KbdFile); + } + + return error; +} + +int +psp_kbd_enter_command() +{ + gp2xCtrlData c; + + unsigned int command_key = 0; + int dragon_key = 0; + int key_event = 0; + + gp2xCtrlPeekBufferPositive(&c, 1); + + if (command_dragon_pending) + { + if ((c.TimeStamp - loc_last_event_time) > KBD_MIN_COMMAND_TIME) { + loc_last_event_time = c.TimeStamp; + command_dragon_pending = 0; + dragon_key_event(command_dragon_key, 0); + } + + return 0; + } + + if ((c.TimeStamp - loc_last_event_time) > KBD_MIN_COMMAND_TIME) { + loc_last_event_time = c.TimeStamp; + + if (command_index >= command_size) { + + command_mode = 0; + command_index = 0; + command_size = 0; + + command_dragon_pending = 0; + command_dragon_key = 0; + + return 0; + } + + command_key = command_keys[command_index++]; + dragon_key = dragon_get_key_from_ascii(command_key); + + if (dragon_key != -1) { + command_dragon_key = dragon_key; + command_dragon_pending = 1; + dragon_key_event(command_dragon_key, 1); + } + + return 1; + } + + return 0; +} + +int +psp_kbd_is_danzeff_mode() +{ + return danzeff_mode; +} + +int +psp_kbd_enter_danzeff() +{ + unsigned int danzeff_key = 0; + int dragon_key = 0; + int key_event = 0; + gp2xCtrlData c; + + if (! danzeff_mode) { + psp_init_keyboard(); + danzeff_mode = 1; + } + + gp2xCtrlPeekBufferPositive(&c, 1); + + if (danzeff_dragon_pending) + { + if ((c.TimeStamp - loc_last_event_time) > KBD_MIN_PENDING_TIME) { + loc_last_event_time = c.TimeStamp; + danzeff_dragon_pending = 0; + dragon_key_event(danzeff_dragon_key, 0); + } + + return 0; + } + + if ((c.TimeStamp - loc_last_event_time) > KBD_MIN_DANZEFF_TIME) { + loc_last_event_time = c.TimeStamp; + + gp2xCtrlPeekBufferPositive(&c, 1); + danzeff_key = danzeff_readInput(c); + } + + if (danzeff_key > DANZEFF_START) { + dragon_key = dragon_get_key_from_ascii(danzeff_key); + + if (dragon_key != -1) { + danzeff_dragon_key = dragon_key; + danzeff_dragon_pending = 1; + dragon_key_event(danzeff_dragon_key, 1); + } + + return 1; + + } else if (danzeff_key == DANZEFF_START) { + danzeff_mode = 0; + danzeff_dragon_pending = 0; + danzeff_dragon_key = 0; + + psp_kbd_wait_no_button(); + + } else if (danzeff_key == DANZEFF_SELECT) { + danzeff_mode = 0; + danzeff_dragon_pending = 0; + danzeff_dragon_key = 0; + psp_main_menu(); + psp_init_keyboard(); + + psp_kbd_wait_no_button(); + } + + return 0; +} + +void +psp_kbd_display_active_mapping() +{ + if (kbd_ltrigger_mapping_active) { + psp_sdl_fill_rectangle(0, 0, 10, 3, psp_sdl_rgb(0x0, 0x0, 0xff), 0); + } else { + psp_sdl_fill_rectangle(0, 0, 10, 3, 0x0, 0); + } + + if (kbd_rtrigger_mapping_active) { + psp_sdl_fill_rectangle(309, 0, 10, 3, psp_sdl_rgb(0x0, 0x0, 0xff), 0); + } else { + psp_sdl_fill_rectangle(309, 0, 10, 3, 0x0, 0); + } +} + +int +dragon_decode_key(int psp_b, int button_pressed) +{ + int wake = 0; + int reverse_analog = ! DRAGON.psp_reverse_analog; + + if (reverse_analog) { + if ((psp_b >= KBD_JOY_UP ) && + (psp_b <= KBD_JOY_LEFT)) { + psp_b = psp_b - KBD_JOY_UP + KBD_UP; + } else + if ((psp_b >= KBD_UP ) && + (psp_b <= KBD_LEFT)) { + psp_b = psp_b - KBD_UP + KBD_JOY_UP; + } + } + + if (psp_b == KBD_START) { + if (button_pressed) psp_kbd_enter_danzeff(); + } else + if (psp_b == KBD_SELECT) { + if (button_pressed) { + psp_main_menu(); + psp_init_keyboard(); + } + } else { +#ifndef MIYOO_MODE + if (psp_b == KBD_FIRE) { + kbd_home_button_released = !button_pressed; + } else { +#endif + + if (psp_kbd_mapping[psp_b] >= 0) { + wake = 1; + if (button_pressed) { + // Determine which buton to press first (ie which mapping is currently active) + if (kbd_ltrigger_mapping_active) { + // Use ltrigger mapping + psp_kbd_presses[psp_b] = psp_kbd_mapping_L[psp_b]; + dragon_key_event(psp_kbd_presses[psp_b], button_pressed); + } else + if (kbd_rtrigger_mapping_active) { + // Use rtrigger mapping + psp_kbd_presses[psp_b] = psp_kbd_mapping_R[psp_b]; + dragon_key_event(psp_kbd_presses[psp_b], button_pressed); + } else { + // Use standard mapping + psp_kbd_presses[psp_b] = psp_kbd_mapping[psp_b]; + dragon_key_event(psp_kbd_presses[psp_b], button_pressed); + } + } else { + // Determine which button to release (ie what was pressed before) + dragon_key_event(psp_kbd_presses[psp_b], button_pressed); + } + + } else { + if (psp_kbd_mapping[psp_b] == KBD_LTRIGGER_MAPPING) { + kbd_ltrigger_mapping_active = button_pressed; + kbd_rtrigger_mapping_active = 0; + } else + if (psp_kbd_mapping[psp_b] == KBD_RTRIGGER_MAPPING) { + kbd_rtrigger_mapping_active = button_pressed; + kbd_ltrigger_mapping_active = 0; + } + } +#ifndef MIYOO_MODE + } +#endif + } + return 0; +} + +int +dragon_decode_joystick(int psp_b) +{ + int reverse_analog = DRAGON.psp_reverse_analog; + + if (reverse_analog) { + if ((psp_b >= KBD_JOY_UP ) && + (psp_b <= KBD_JOY_LEFT)) { + psp_b = psp_b - KBD_JOY_UP + KBD_UP; + } else + if ((psp_b >= KBD_UP ) && + (psp_b <= KBD_LEFT)) { + psp_b = psp_b - KBD_UP + KBD_JOY_UP; + } + } + + if (psp_kbd_mapping[psp_b] != -1) { + dragon_joystick_event(psp_kbd_mapping[psp_b]); + } + return 0; +} + + +void +kbd_change_auto_fire(int auto_fire) +{ + DRAGON.dragon_auto_fire = auto_fire; + if (DRAGON.dragon_auto_fire_pressed) { + dragon_key_event(DRAGONK_JOY_FIRE, 0); + DRAGON.dragon_auto_fire_pressed = 0; + } +} + + +static int +kbd_reset_button_status(void) +{ + int b = 0; + /* Reset Button status */ + for (b = 0; b < KBD_MAX_BUTTONS; b++) { + loc_button_press[b] = 0; + loc_button_release[b] = 0; + } + psp_init_keyboard(); + return 0; +} + +int +kbd_scan_keyboard(void) +{ + gp2xCtrlData c; + long delta_stamp; + int event; + int b; + + int new_Lx; + int new_Ly; + int old_Lx; + int old_Ly; + + event = 0; + gp2xCtrlPeekBufferPositive( &c, 1 ); + + if ((c.Buttons & (GP2X_CTRL_LTRIGGER|GP2X_CTRL_RTRIGGER|GP2X_CTRL_START)) == + (GP2X_CTRL_LTRIGGER|GP2X_CTRL_RTRIGGER|GP2X_CTRL_START)) { + /* Exit ! */ + psp_sdl_exit(0); + } + + if (DRAGON.dragon_auto_fire) { + delta_stamp = c.TimeStamp - first_time_auto_stamp; + if ((delta_stamp < 0) || + (delta_stamp > (KBD_MIN_AUTOFIRE_TIME / (1 + DRAGON.dragon_auto_fire_period)))) { + first_time_auto_stamp = c.TimeStamp; + dragon_key_event(DRAGONK_JOY_FIRE, DRAGON.dragon_auto_fire_pressed); + DRAGON.dragon_auto_fire_pressed = ! DRAGON.dragon_auto_fire_pressed; + } + } + + for (b = 0; b < KBD_MAX_BUTTONS; b++) + { + if (c.Buttons & loc_button_mask[b]) { + + dragon_decode_joystick(b); + + if (!(loc_button_data.Buttons & loc_button_mask[b])) { + loc_button_press[b] = 1; + event = 1; + } + } else { + if (loc_button_data.Buttons & loc_button_mask[b]) { + loc_button_release[b] = 1; + loc_button_press[b] = 0; + event = 1; + } + } + } + memcpy(&loc_button_data,&c,sizeof(gp2xCtrlData)); + + return event; +} + +void +psp_kbd_wait_start(void) +{ + while (1) + { + gp2xCtrlData c; + gp2xCtrlReadBufferPositive(&c, 1); + if (c.Buttons & GP2X_CTRL_START) break; + } + psp_kbd_wait_no_button(); +} + +void +psp_init_keyboard(void) +{ + dragon_kbd_reset(); + kbd_ltrigger_mapping_active = 0; + kbd_rtrigger_mapping_active = 0; +} + +void +psp_kbd_wait_no_button(void) +{ + gp2xCtrlData c; + + do { + gp2xCtrlPeekBufferPositive(&c, 1); + } while (c.Buttons != 0); +} + +void +psp_kbd_wait_button(void) +{ + gp2xCtrlData c; + + do { + gp2xCtrlReadBufferPositive(&c, 1); + } while (c.Buttons == 0); +} + +int +psp_update_keys(void) +{ + int b; + + static char first_time = 1; + static int release_pending = 0; + + if (first_time) { + + memcpy(psp_kbd_mapping, loc_default_mapping, sizeof(loc_default_mapping)); + memcpy(psp_kbd_mapping_L, loc_default_mapping_L, sizeof(loc_default_mapping_L)); + memcpy(psp_kbd_mapping_R, loc_default_mapping_R, sizeof(loc_default_mapping_R)); + + gp2xCtrlData c; + gp2xCtrlPeekBufferPositive(&c, 1); + + if (first_time_stamp == -1) first_time_stamp = c.TimeStamp; + if ((! c.Buttons) && ((c.TimeStamp - first_time_stamp) < KBD_MIN_START_TIME)) return 0; + + dragon_kbd_load(); + + first_time = 0; + release_pending = 0; + + for (b = 0; b < KBD_MAX_BUTTONS; b++) { + loc_button_release[b] = 0; + loc_button_press[b] = 0; + } + gp2xCtrlPeekBufferPositive(&loc_button_data, 1); + + psp_main_menu(); + psp_init_keyboard(); + + return 0; + } + + if (command_mode) { + return psp_kbd_enter_command(); + } + + if (danzeff_mode) { + return psp_kbd_enter_danzeff(); + } + + if (release_pending) + { + release_pending = 0; + for (b = 0; b < KBD_MAX_BUTTONS; b++) { + if (loc_button_release[b]) { + loc_button_release[b] = 0; + loc_button_press[b] = 0; + dragon_decode_key(b, 0); + } + } + } + + kbd_scan_keyboard(); + + /* check press event */ + for (b = 0; b < KBD_MAX_BUTTONS; b++) { + if (loc_button_press[b]) { + loc_button_press[b] = 0; + release_pending = 0; + dragon_decode_key(b, 1); + } + } + /* check release event */ + for (b = 0; b < KBD_MAX_BUTTONS; b++) { + if (loc_button_release[b]) { + release_pending = 1; + break; + } + } + + return 0; +} diff --git a/src/psp_kbd.h b/src/psp_kbd.h new file mode 100644 index 0000000..7d702cd --- /dev/null +++ b/src/psp_kbd.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2006 Ludovic Jacomme (ludovic.jacomme@gmail.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +# ifndef _KBD_H_ +# define _KBD_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +# define PSP_ALL_BUTTON_MASK 0xFFFF + +#include "core/keyboard_psp.h" + +# define KBD_UP 0 +# define KBD_RIGHT 1 +# define KBD_DOWN 2 +# define KBD_LEFT 3 +# define KBD_TRIANGLE 4 +# define KBD_CIRCLE 5 +# define KBD_CROSS 6 +# define KBD_SQUARE 7 +# define KBD_SELECT 8 +# define KBD_START 9 +# define KBD_LTRIGGER 10 +# define KBD_RTRIGGER 11 +# define KBD_FIRE 12 + +# define KBD_MAX_BUTTONS 13 + +# define KBD_JOY_UP 13 +# define KBD_JOY_RIGHT 14 +# define KBD_JOY_DOWN 15 +# define KBD_JOY_LEFT 16 + +# define KBD_ALL_BUTTONS 17 + +# define KBD_UNASSIGNED -1 + +# define KBD_LTRIGGER_MAPPING -2 +# define KBD_RTRIGGER_MAPPING -3 +# define KBD_NORMAL_MAPPING -1 + + struct dragon_key_trans { + int dragon_id; + char name[20]; + }; + + + extern int psp_screenshot_mode; + extern int psp_kbd_mapping[ KBD_ALL_BUTTONS ]; + extern int psp_kbd_mapping_L[ KBD_ALL_BUTTONS ]; + extern int psp_kbd_mapping_R[ KBD_ALL_BUTTONS ]; + extern int psp_kbd_presses[ KBD_ALL_BUTTONS ]; + extern int kbd_ltrigger_mapping_active; + extern int kbd_rtrigger_mapping_active; + + extern struct dragon_key_trans psp_dragon_key_info[DRAGONK_MAX_KEY]; + + extern int psp_update_keys(void); + extern void kbd_wait_start(void); + extern void psp_init_keyboard(void); + extern void psp_kbd_wait_no_button(void); + extern int psp_kbd_is_danzeff_mode(void); + extern void psp_kbd_display_active_mapping(void); + extern int psp_kbd_load_mapping(char *kbd_filename); +#ifdef __cplusplus +} +#endif +# endif diff --git a/src/psp_main.c b/src/psp_main.c new file mode 100755 index 0000000..6bacaf5 --- /dev/null +++ b/src/psp_main.c @@ -0,0 +1,84 @@ +#include +#include +#include "SDL.h" + +#include +#include +#include "cpulcd.h" +#include "gp2x_psp.h" + +#define STDOUT_FILE "stdout.txt" +#define STDERR_FILE "stderr.txt" + +extern int a_main(int argc, char *argv[]); + +static void cleanup_output(void); + +/* Remove the output files if there was no output written */ +static void cleanup_output(void) +{ +#ifndef NO_STDIO_REDIRECT + FILE *file; + int empty; +#endif + + /* Flush the output in case anything is queued */ + fclose(stdout); + fclose(stderr); + +#ifndef NO_STDIO_REDIRECT + /* See if the files have any output in them */ + file = fopen(STDOUT_FILE, "rb"); + if ( file ) { + empty = (fgetc(file) == EOF) ? 1 : 0; + fclose(file); + if ( empty ) { + remove(STDOUT_FILE); + } + } + file = fopen(STDERR_FILE, "rb"); + if ( file ) { + empty = (fgetc(file) == EOF) ? 1 : 0; + fclose(file); + if ( empty ) { + remove(STDERR_FILE); + } + } +#endif +#ifdef GP2X_MODE + gp2xRmmodMMUhack(); + set_speed_clock(CPU_CLOCK_STD); + cpulcd_deinit(); + sync(); + chdir("/usr/gp2x"); + execl("/usr/gp2x/gp2xmenu", "/usr/gp2x/gp2xmenu", NULL); +#endif +} + +int +main(int argc, char *argv[]) +{ +#ifdef GP2X_MODE + cpulcd_init(); +#endif + +#ifndef NO_STDIO_REDIRECT + /* Redirect standard output and standard error. */ + /* TODO: Error checking. */ + freopen(STDOUT_FILE, "w", stdout); + freopen(STDERR_FILE, "w", stderr); + setvbuf(stdout, NULL, _IOLBF, BUFSIZ); /* Line buffered */ + setbuf(stderr, NULL); /* No buffering */ +#endif /* NO_STDIO_REDIRECT */ + + atexit(cleanup_output); + +#ifdef GP2X_MODE + gp2xInsmodMMUhack(); +#endif + + char *a_arg_v[1] = { "dragon" }; + a_main(1, a_arg_v); + + return 0; +} diff --git a/src/psp_menu.c b/src/psp_menu.c new file mode 100644 index 0000000..f41ebe2 --- /dev/null +++ b/src/psp_menu.c @@ -0,0 +1,723 @@ +/* + * Copyright (C) 2006 Ludovic Jacomme (ludovic.jacomme@gmail.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "global.h" +#include "psp_sdl.h" +#include "psp_kbd.h" +#include "psp_menu.h" +#include "psp_fmgr.h" +#include "psp_run.h" +#include "psp_menu_kbd.h" +#include "psp_menu_set.h" +#include "psp_menu_help.h" +#include "gp2x_psp.h" + +extern SDL_Surface *back_surface; +static int psp_menu_dirty = 1; + +# define MENU_SCREENSHOT 0 +# define MENU_VOLUME 1 + +# define MENU_LOAD_SLOT 2 +# define MENU_SAVE_SLOT 3 +# define MENU_DEL_SLOT 4 + +# define MENU_HELP 5 +# define MENU_COMMAND 6 + +# define MENU_LOAD_ROM 7 +# define MENU_LOAD_DISK 8 +# define MENU_LOAD_SNA 9 +# define MENU_EJECT_ROM 10 + +# define MENU_KEYBOARD 11 +# define MENU_SETTINGS 12 + +# define MENU_HARD_RESET 13 +# define MENU_BACK 14 +# define MENU_EXIT 15 + +# define MAX_MENU_ITEM (MENU_EXIT + 1) + + static menu_item_t menu_list[] = + { + "Save Screenshot :", + "Volume :", + + "Load Slot", + "Save Slot", + "Delete Slot", + + "Help", + "Command : ", + + "Load Rom", + "Load Disk", + "Load Snapshot", + "Eject Rom", + + + "Keyboard", + "Settings", + + "Hard Reset", + "Back to DRAGON", + + "Exit" + }; + + static int cur_menu_id = MENU_LOAD_ROM; + static int cur_slot = 0; + + static int cur_command = 0; + +#define MAX_COMMAND 4 + + static char* command_name[MAX_COMMAND] = { + "LOADM", "RUN", "EXEC", "DIR" + }; + +void +psp_menu_display_save_name() +{ + char buffer[128]; + int Length; + + snprintf(buffer, 30, "Game: %s", DRAGON.dragon_save_name); + Length = strlen(buffer); + psp_sdl_back2_print(300 - (6*Length), 5, buffer, PSP_MENU_TEXT2_COLOR); +} + +void +string_fill_with_space(char *buffer, int size) +{ + int length = strlen(buffer); + int index; + + for (index = length; index < size; index++) { + buffer[index] = ' '; + } + buffer[size] = 0; +} + +static void +psp_display_screen_menu(void) +{ + char buffer[64]; + int menu_id = 0; + int slot_id = 0; + int color = 0; + int x = 0; + int y = 0; + int y_step = 0; + + if (psp_menu_dirty) + { + psp_sdl_blit_background(); + //psp_menu_dirty = 0; + } + + x = 10; + y = 5; + y_step = 10; + + for (menu_id = 0; menu_id < MAX_MENU_ITEM; menu_id++) { + color = PSP_MENU_TEXT_COLOR; + if (cur_menu_id == menu_id) color = PSP_MENU_SEL_COLOR; + else + if (menu_id == MENU_EXIT) color = PSP_MENU_WARNING_COLOR; + else + if (menu_id == MENU_HELP) color = PSP_MENU_GREEN_COLOR; + + if (menu_id == MENU_COMMAND) { + strcpy(buffer, menu_list[menu_id].title); + strcat(buffer, command_name[cur_command]); + string_fill_with_space(buffer, 25); + psp_sdl_back2_print(x, y, buffer, color); + } else { + psp_sdl_back2_print(x, y, menu_list[menu_id].title, color); + } + + if (menu_id == MENU_SCREENSHOT) { + sprintf(buffer,"%d", DRAGON.psp_screenshot_id); + string_fill_with_space(buffer, 4); + psp_sdl_back2_print(120, y, buffer, color); + } else + if (menu_id == MENU_VOLUME) { + sprintf(buffer,"%d", gp2xGetSoundVolume()); + string_fill_with_space(buffer, 4); + psp_sdl_back2_print(120, y, buffer, color); + y += y_step; + } else + if (menu_id == MENU_DEL_SLOT) { + y += y_step; + } else + if (menu_id == MENU_SETTINGS) { + y += y_step; + } else + if (menu_id == MENU_BACK) { + y += y_step; + } else + if (menu_id == MENU_EJECT_ROM) { + y += y_step; + } else + if (menu_id == MENU_COMMAND) { + y += y_step; + } else + if (menu_id == MENU_HELP) { + y += y_step; + } + + y += y_step; + } + y_step = 10; + y = 35; + + for (slot_id = 0; slot_id < DRAGON_MAX_SAVE_STATE; slot_id++) { + if (slot_id == cur_slot) color = PSP_MENU_SEL2_COLOR; + else color = PSP_MENU_TEXT_COLOR; + + if (DRAGON.dragon_save_state[slot_id].used) { + sprintf(buffer, "- used"); + } else { + sprintf(buffer, "- empty"); + } + string_fill_with_space(buffer, 32); + psp_sdl_back2_print(100, y, buffer, color); + + y += y_step; + } + + if (DRAGON.dragon_save_state[cur_slot].thumb) { + psp_sdl_blit_thumb(180, 40, DRAGON.dragon_save_state[cur_slot].surface); + #ifndef MIYOO_MODE + } else { + psp_sdl_blit_thumb(180, 40, thumb_surface); + #endif + } + psp_menu_display_save_name(); +} + +static void +psp_main_menu_soft_reset() +{ + /* Reset ! */ + psp_display_screen_menu(); + psp_sdl_back2_print(120, 135, "Soft Reset Dragon !", + PSP_MENU_WARNING_COLOR); + psp_menu_dirty = 1; + psp_sdl_flip(); + dragon_reset_computer(0); + sleep(1); +} + +static void +psp_main_menu_hard_reset() +{ + /* Reset ! */ + psp_display_screen_menu(); + psp_sdl_back2_print(120, 135, "Hard Reset Dragon !", + PSP_MENU_WARNING_COLOR); + psp_menu_dirty = 1; + psp_sdl_flip(); + dragon_reset_computer(1); + sleep(1); +} + +static void +psp_main_menu_cmd_loadm(void) +{ + char *RunName; + RunName = psp_run_search(DRAGON.dragon_save_name); + if (RunName != (char *)0 ) { + psp_kbd_run_command(RunName); + } else { + psp_kbd_run_command("LOADM\""); + } +} + +static void +psp_main_menu_cmd_dir(void) +{ + psp_kbd_run_command("DIR\n"); +} + +static void +psp_main_menu_cmd_exec(void) +{ + psp_kbd_run_command("EXEC\n"); +} + +static void +psp_main_menu_cmd_run(void) +{ + char *RunName; + RunName = psp_run_search(DRAGON.dragon_save_name); + if (RunName != (char *)0 ) { + psp_kbd_run_command(RunName); + } else { + psp_kbd_run_command("RUN\""); + } +} + +static int +psp_main_menu_load(int format) +{ + int ret; + + ret = psp_fmgr_menu(format); + if (ret == 1) /* load OK */ + { + psp_menu_dirty = 1; + psp_display_screen_menu(); + psp_sdl_back2_print(120, 135, "File loaded !", + PSP_MENU_NOTE_COLOR); + psp_menu_dirty = 1; + psp_sdl_flip(); + sleep(1); + return 1; + } + else + if (ret == -1) /* Load Error */ + { + psp_menu_dirty = 1; + psp_display_screen_menu(); + psp_sdl_back2_print(120, 135, "Can't load file !", + PSP_MENU_WARNING_COLOR); + psp_menu_dirty = 1; + psp_sdl_flip(); + sleep(1); + } + return 0; +} + +/* used by DRAGON keys */ +int +psp_main_menu_load_current() +{ + if (DRAGON.dragon_save_state[cur_slot].used) { + return dragon_snapshot_load_slot(cur_slot); + } + return -1; +} + +int +psp_main_menu_save_current() +{ + return dragon_snapshot_save_slot(cur_slot); +} + +int +psp_main_menu_load_slot() +{ + int error; + + if (! DRAGON.dragon_save_state[cur_slot].used) { + + psp_display_screen_menu(); + psp_sdl_back2_print(120, 135, "Slot is empty !", + PSP_MENU_NOTE_COLOR); + psp_menu_dirty = 1; + psp_sdl_flip(); + sleep(1); + + return 0; + } + + error = dragon_snapshot_load_slot(cur_slot); + + if (! error) /* save OK */ + { + psp_display_screen_menu(); + psp_sdl_back2_print(120, 135, "File loaded !", + PSP_MENU_NOTE_COLOR); + psp_menu_dirty = 1; + psp_sdl_flip(); + sleep(1); + + return 1; + } + else + { + psp_display_screen_menu(); + psp_sdl_back2_print(120, 135, "Can't load file !", + PSP_MENU_WARNING_COLOR); + psp_menu_dirty = 1; + psp_sdl_flip(); + sleep(1); + } + return 0; +} + +static int +psp_main_menu_eject_rom() +{ + dragon_eject_rom(); + + psp_display_screen_menu(); + psp_sdl_back2_print(120, 135, "Rom ejected !", + PSP_MENU_NOTE_COLOR); + psp_menu_dirty = 1; + psp_sdl_flip(); + sleep(1); + + return 0; +} + +static void +psp_main_menu_save_slot() +{ + int error; + error = dragon_snapshot_save_slot(cur_slot); + + if (! error) /* save OK */ + { + psp_display_screen_menu(); + psp_sdl_back2_print(120, 135, "File saved !", + PSP_MENU_NOTE_COLOR); + psp_menu_dirty = 1; + psp_sdl_flip(); + sleep(1); + } + else + { + psp_display_screen_menu(); + psp_sdl_back2_print(120, 135, "Can't save file !", + PSP_MENU_WARNING_COLOR); + psp_menu_dirty = 1; + psp_sdl_flip(); + sleep(1); + } +} + +static void +psp_main_menu_del_slot() +{ + int error; + + if (! DRAGON.dragon_save_state[cur_slot].used) { + + psp_display_screen_menu(); + psp_sdl_back2_print(120, 135, "Slot is empty !", + PSP_MENU_NOTE_COLOR); + psp_menu_dirty = 1; + psp_sdl_flip(); + sleep(1); + + return; + } + + error = dragon_snapshot_del_slot(cur_slot); + + if (! error) /* save OK */ + { + psp_display_screen_menu(); + psp_sdl_back2_print(120, 135, "File deleted !", + PSP_MENU_NOTE_COLOR); + psp_menu_dirty = 1; + psp_sdl_flip(); + sleep(1); + } + else + { + psp_display_screen_menu(); + psp_sdl_back2_print(120, 135, "Can't delete file !", + PSP_MENU_WARNING_COLOR); + psp_menu_dirty = 1; + psp_sdl_flip(); + sleep(1); + } +} + +static void +psp_main_menu_cur_slot(int step) +{ + if (step == 1) { + cur_slot++; if (cur_slot >= DRAGON_MAX_SAVE_STATE) cur_slot = 0; + } else if (step == -1) { + cur_slot--; if (cur_slot < 0) cur_slot = DRAGON_MAX_SAVE_STATE - 1; + } +} + +static void +psp_main_menu_screenshot(void) +{ + psp_screenshot_mode = 10; +} + +static void +psp_main_menu_volume(int step) +{ + if (step == 1) { + gp2xIncreaseVolume(); + } else if (step == -1) { + gp2xDecreaseVolume(); + } +} + +int +psp_main_menu_exit(void) +{ + gp2xCtrlData c; + + psp_display_screen_menu(); + #ifdef MIYOO_MODE + psp_sdl_back2_print(120, 135, "press B to confirm !", PSP_MENU_WARNING_COLOR); + #else + psp_sdl_back2_print(120, 135, "press X to confirm !", PSP_MENU_WARNING_COLOR); + #endif + psp_menu_dirty = 1; + psp_sdl_flip(); + + psp_kbd_wait_no_button(); + + do + { + gp2xCtrlReadBufferPositive(&c, 1); + c.Buttons &= PSP_ALL_BUTTON_MASK; + + if (c.Buttons & GP2X_CTRL_CROSS) { + psp_sdl_clear_screen(0); + psp_sdl_flip(); + psp_sdl_clear_screen(0); + psp_sdl_flip(); + psp_sdl_exit(0); + } + + } while (c.Buttons == 0); + + psp_kbd_wait_no_button(); + + return 0; +} + +void +psp_main_menu_cmd(void) +{ + if (cur_command == 0) { + psp_main_menu_cmd_loadm(); + } else + if (cur_command == 1) { + psp_main_menu_cmd_run(); + } else + if (cur_command == 2) { + psp_main_menu_cmd_exec(); + } else + if (cur_command == 3) { + psp_main_menu_cmd_dir(); + } +} + +void +psp_main_menu_cur_cmd(int step) +{ + if (step == 1) { + cur_command++; if (cur_command >= MAX_COMMAND) cur_command = 0; + } else if (step == -1) { + cur_command--; if (cur_command < 0) cur_command = MAX_COMMAND-1; + } +} + +int +psp_main_menu(void) +{ + gp2xCtrlData c; + long new_pad; + long old_pad; + int last_time; + int end_menu; + + dragon_audio_pause(); + + dragon_in_menu = 1; + + psp_kbd_wait_no_button(); + + old_pad = 0; + last_time = 0; + end_menu = 0; + + psp_menu_dirty = 1; + + while (! end_menu) + { + psp_display_screen_menu(); + psp_sdl_flip(); + + while (1) + { + gp2xCtrlPeekBufferPositive(&c, 1); + c.Buttons &= PSP_ALL_BUTTON_MASK; + + if (c.Buttons) break; + } + + new_pad = c.Buttons; + + if ((old_pad != new_pad) || ((c.TimeStamp - last_time) > PSP_MENU_MIN_TIME)) { + last_time = c.TimeStamp; + old_pad = new_pad; + + } else continue; + + if ((c.Buttons & (GP2X_CTRL_LTRIGGER|GP2X_CTRL_RTRIGGER|GP2X_CTRL_START)) == + (GP2X_CTRL_LTRIGGER|GP2X_CTRL_RTRIGGER|GP2X_CTRL_START)) { + /* Exit ! */ + psp_sdl_exit(0); + } else + if ((c.Buttons & GP2X_CTRL_RTRIGGER) == GP2X_CTRL_RTRIGGER) { + psp_main_menu_soft_reset(); + end_menu = 1; + } else + if ((new_pad & GP2X_CTRL_CROSS ) || + (new_pad & GP2X_CTRL_CIRCLE) || + (new_pad & GP2X_CTRL_TRIANGLE)) + { + int step; + + if (new_pad & GP2X_CTRL_CROSS) step = -1; + else + if (new_pad & GP2X_CTRL_CIRCLE) step = 1; + else step = 0; + + switch (cur_menu_id ) + { + case MENU_LOAD_SLOT : if (step) psp_main_menu_cur_slot(step); + else if (psp_main_menu_load_slot()) end_menu = 1; + break; + case MENU_SAVE_SLOT : if (step) psp_main_menu_cur_slot(step); + else psp_main_menu_save_slot(); + break; + case MENU_DEL_SLOT : if (step) psp_main_menu_cur_slot(step); + else psp_main_menu_del_slot(); + break; + + case MENU_LOAD_ROM : psp_menu_dirty = 1; + if (psp_main_menu_load(FMGR_FORMAT_ROM)) end_menu = 1; + old_pad = new_pad = 0; + psp_menu_dirty = 1; + break; + case MENU_EJECT_ROM : psp_menu_dirty = 1; + psp_main_menu_eject_rom(); + end_menu = 1; + old_pad = new_pad = 0; + psp_menu_dirty = 1; + break; + case MENU_LOAD_SNA : psp_menu_dirty = 1; + if (psp_main_menu_load(FMGR_FORMAT_STATE)) end_menu = 1; + old_pad = new_pad = 0; + psp_menu_dirty = 1; + break; + case MENU_LOAD_DISK : psp_menu_dirty = 1; + if (psp_main_menu_load(FMGR_FORMAT_DISK)) end_menu = 1; + old_pad = new_pad = 0; + psp_menu_dirty = 1; + break; + case MENU_KEYBOARD : psp_keyboard_menu(); + psp_menu_dirty = 1; + old_pad = new_pad = 0; + break; + case MENU_COMMAND : if (step) psp_main_menu_cur_cmd(step); + else { + psp_main_menu_cmd(); + end_menu = 1; + } + break; + case MENU_SETTINGS : psp_menu_dirty = 1; + psp_settings_menu(); + psp_menu_dirty = 1; + old_pad = new_pad = 0; + break; + + case MENU_SCREENSHOT : psp_main_menu_screenshot(); + end_menu = 1; + break; + case MENU_VOLUME : psp_main_menu_volume(step); + psp_menu_dirty = 1; + old_pad = new_pad = 0; + break; + case MENU_HARD_RESET : psp_main_menu_hard_reset(); + end_menu = 1; + break; + + case MENU_BACK : end_menu = 1; + break; + + case MENU_EXIT : psp_main_menu_exit(); + break; + + case MENU_HELP : psp_menu_dirty = 1; + psp_help_menu(); + old_pad = new_pad = 0; + psp_menu_dirty = 1; + break; + } + + } else + if(new_pad & GP2X_CTRL_UP) { + + if (cur_menu_id > 0) cur_menu_id--; + else cur_menu_id = MAX_MENU_ITEM-1; + + } else + if(new_pad & GP2X_CTRL_DOWN) { + + if (cur_menu_id < (MAX_MENU_ITEM-1)) cur_menu_id++; + else cur_menu_id = 0; + + } else + if(new_pad & GP2X_CTRL_SQUARE) { + /* Cancel */ + end_menu = -1; + } else + if(new_pad & GP2X_CTRL_SELECT) { + /* Back to DRAGON */ + end_menu = 1; + } + } + + psp_kbd_wait_no_button(); + + psp_sdl_clear_screen( PSP_MENU_BLACK_COLOR ); + psp_sdl_flip(); + psp_sdl_clear_screen( PSP_MENU_BLACK_COLOR ); + psp_sdl_flip(); + + psp_sdl_clear_blit(0); + + dragon_audio_resume(); + + dragon_in_menu = 0; + + return 1; +} + diff --git a/src/psp_menu.h b/src/psp_menu.h new file mode 100644 index 0000000..8628cb5 --- /dev/null +++ b/src/psp_menu.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2006 Ludovic Jacomme (ludovic.jacomme@gmail.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +# ifndef _PSP_MENU_H_ +# define _PSP_MENU_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +# define PSP_MENU_BORDER_COLOR psp_sdl_rgb(0x80,0x80,0xF0) +# define PSP_MENU_WARNING_COLOR psp_sdl_rgb(0xFF,0x00,0x00) +# define PSP_MENU_NOTE_COLOR psp_sdl_rgb(0xFF,0xFF,0x00) +# define PSP_MENU_BACKGROUND_COLOR psp_sdl_rgb(0x00,0x00,0x00) +# define PSP_MENU_BLACK_COLOR psp_sdl_rgb(0x00,0x00,0x00) +# define PSP_MENU_AUTHOR_COLOR psp_sdl_rgb(0x00,0x00,0xFF) +# define PSP_MENU_BLUE_COLOR psp_sdl_rgb(0x00,0x00,0xFF) +# define PSP_MENU_GREEN_COLOR psp_sdl_rgb(0x00,0xFF,0x00) +# define PSP_MENU_RED_COLOR psp_sdl_rgb(0xFF,0x00,0x00) + +# define PSP_MENU_TEXT_COLOR psp_sdl_rgb(0x80,0x80,0x80) +# define PSP_MENU_TEXT2_COLOR psp_sdl_rgb(0xff,0xff,0xff) +# define PSP_MENU_SEL_COLOR psp_sdl_rgb(0x00,0xff,0xff) +# define PSP_MENU_SEL2_COLOR psp_sdl_rgb(0xFF,0x00,0x80) + +# define PSP_MENU_MIN_TIME 150000 + + typedef struct menu_item_t { + char *title; + } menu_item_t; + + + extern int psp_main_menu(void); + extern void psp_display_screen_menu_battery(void); + extern void string_fill_with_space(char *buffer, int size); + extern void psp_menu_display_save_name(); + +#ifdef __cplusplus +} +#endif + +# endif diff --git a/src/psp_menu_help.c b/src/psp_menu_help.c new file mode 100644 index 0000000..75d16a5 --- /dev/null +++ b/src/psp_menu_help.c @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2006 Ludovic Jacomme (ludovic.jacomme@gmail.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "SDL.h" + +#include "global.h" +#include "psp_kbd.h" +#include "psp_sdl.h" +#include "psp_menu.h" +#include "psp_menu_help.h" + + static int psp_menu_dirty = 1; + +# define MAX_HELP_LINE 4096 + +# define HELP_LINE_BY_PAGE 24 +# define HELP_CHAR_BY_LINE 53 + + static char* psp_help[MAX_HELP_LINE]; + static int psp_help_size = -1; + static int psp_help_current = 0; + +static void +psp_initialize_help(void) +{ + char FileName[MAX_PATH+1]; + + char Buffer[512]; + char *Scan; + FILE* FileDesc; + + /* Already done ? */ + if (psp_help_size > 0) return; + + strcpy(FileName, "./help.txt"); + FileDesc = fopen(FileName, "r"); + + psp_help_current = 0; + + if (FileDesc == (FILE *)0 ) { + psp_help[0] = strdup( "no help file found !"); + psp_help_size = 1; + return; + } + + psp_help_size = 0; + while (fgets(Buffer,512, FileDesc) != (char *)0) { + + Scan = strchr(Buffer,'\n'); + if (Scan) *Scan = '\0'; + /* For this #@$% of windows ! */ + Scan = strchr(Buffer,'\r'); + if (Scan) *Scan = '\0'; + + psp_help[psp_help_size++] = strdup(Buffer); + if (psp_help_size >= MAX_HELP_LINE) break; + } + fclose(FileDesc); +} + +static void +psp_display_screen_help(void) +{ + char buffer[512]; + + int help_line = 0; + int index = 0; + + int x = 0; + int y = 0; + int y_step = 0; + + if (psp_menu_dirty) + { + psp_sdl_blit_background(); + //psp_menu_dirty = 0; + } + + x = 0; + y = 0; + y_step = 10; + + help_line = psp_help_current; + index = 0; + + while ((index < HELP_LINE_BY_PAGE) && (help_line < psp_help_size)) { + strcpy(buffer, psp_help[help_line]); + string_fill_with_space(buffer, HELP_CHAR_BY_LINE); + psp_sdl_back2_print(x, y, buffer, PSP_MENU_SEL_COLOR); + y += y_step; + index++; + help_line++; + } + + if (index != HELP_LINE_BY_PAGE) { + buffer[0]=0; + string_fill_with_space(buffer, HELP_CHAR_BY_LINE); + while (index < HELP_LINE_BY_PAGE) { + psp_sdl_back2_print(x, y, buffer, PSP_MENU_SEL_COLOR); + y += y_step; + index++; + } + } +} + +int +psp_help_menu() +{ + gp2xCtrlData c; + long new_pad; + long old_pad; + int last_time; + int end_menu; + + psp_kbd_wait_no_button(); + + psp_initialize_help(); + + old_pad = 0; + last_time = 0; + end_menu = 0; + + psp_menu_dirty = 1; + + while (! end_menu) + { + psp_display_screen_help(); + psp_sdl_flip(); + + while (1) + { + gp2xCtrlReadBufferPositive(&c, 1); + c.Buttons &= PSP_ALL_BUTTON_MASK; + + if (c.Buttons) break; + } + + new_pad = c.Buttons; + + if ((old_pad != new_pad) || ((c.TimeStamp - last_time) > PSP_MENU_MIN_TIME)) { + last_time = c.TimeStamp; + old_pad = new_pad; + + } else continue; + + if(new_pad & GP2X_CTRL_SELECT) { + /* Back to Main menu */ + end_menu = 1; + } else + if(new_pad & GP2X_CTRL_UP) { + if (psp_help_current > 0) psp_help_current--; + } else + if(new_pad & GP2X_CTRL_DOWN) { + if ((psp_help_current + 1) < psp_help_size) psp_help_current++; + } else + if(new_pad & GP2X_CTRL_LEFT) { + if (psp_help_current > HELP_LINE_BY_PAGE) psp_help_current -= HELP_LINE_BY_PAGE; + else psp_help_current = 0; + } else + if(new_pad & GP2X_CTRL_RIGHT) { + if ((psp_help_current + HELP_LINE_BY_PAGE + 1) < psp_help_size) psp_help_current += HELP_LINE_BY_PAGE; + } + } + + psp_kbd_wait_no_button(); + + return 1; +} + diff --git a/src/psp_menu_help.h b/src/psp_menu_help.h new file mode 100644 index 0000000..b75d2bb --- /dev/null +++ b/src/psp_menu_help.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2006 Ludovic Jacomme (ludovic.jacomme@gmail.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +# ifndef _PSP_MENU_HELP_H_ +# define _PSP_MENU_HELP_H_ + + extern int psp_help_menu(); + +# endif diff --git a/src/psp_menu_kbd.c b/src/psp_menu_kbd.c new file mode 100644 index 0000000..1fdb522 --- /dev/null +++ b/src/psp_menu_kbd.c @@ -0,0 +1,567 @@ +/* + * Copyright (C) 2006 Ludovic Jacomme (ludovic.jacomme@gmail.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "global.h" +#include "psp_sdl.h" +#include "psp_kbd.h" +#include "psp_menu.h" +#include "psp_fmgr.h" +#include "psp_menu_kbd.h" +#include "psp_danzeff.h" + +# define MENU_KBD_SKIN 0 +# define MENU_KBD_KBD_SELECT 1 +# define MENU_KBD_UP 2 +# define MENU_KBD_DOWN 3 +# define MENU_KBD_LEFT 4 +# define MENU_KBD_RIGHT 5 +# define MENU_KBD_CROSS 6 +# define MENU_KBD_SQUARE 7 +# define MENU_KBD_TRIANGLE 8 +# define MENU_KBD_CIRCLE 9 +# define MENU_KBD_LTRIGGER 10 +# define MENU_KBD_RTRIGGER 11 +# define MENU_KBD_FIRE 12 +# define MENU_KBD_JOY_UP 13 +# define MENU_KBD_JOY_DOWN 14 +# define MENU_KBD_JOY_LEFT 15 +# define MENU_KBD_JOY_RIGHT 16 + +# define MENU_KBD_LOAD 17 +# define MENU_KBD_SAVE 18 +# define MENU_KBD_HOTKEYS 19 +# define MENU_KBD_RESET 20 + +# define MENU_KBD_BACK 21 + +# define MAX_MENU_KBD_ITEM (MENU_KBD_BACK + 1) + + static menu_item_t menu_list[] = + { + "Skin :", + "Mapping :", + + "Up :", + "Down :", + "Left :", + "Right :", +# if defined(MIYOO_MODE) + "B :", + "Y :", + "X :", + "A :", +# else + "X :", + "A :", + "Y :", + "B :", +# endif + "LTrigger :", + "RTrigger :", +# if defined(MIYOO_MODE) + "Start :", +# else + "JoyFire :", +# endif + "JoyUp :", + "JoyDown :", + "JoyLeft :", + "JoyRight :", + + "Load Keyboard", + "Save Keyboard", + "Set Hotkeys", + "Reset Keyboard", + "Back to Menu" + }; + + static int cur_menu_id = MENU_KBD_LOAD; + + static int loc_kbd_mapping[ KBD_ALL_BUTTONS ]; + static int loc_kbd_mapping_L[ KBD_ALL_BUTTONS ]; + static int loc_kbd_mapping_R[ KBD_ALL_BUTTONS ]; + + static int psp_menu_dirty = 1; + + int menu_kbd_selected = -1; + +static int +psp_kbd_menu_id_to_key_id(int menu_id) +{ + int kbd_id = 0; + + switch ( menu_id ) + { + case MENU_KBD_UP : kbd_id = KBD_UP; break; + case MENU_KBD_DOWN : kbd_id = KBD_DOWN; break; + case MENU_KBD_LEFT : kbd_id = KBD_LEFT; break; + case MENU_KBD_RIGHT : kbd_id = KBD_RIGHT; break; + case MENU_KBD_TRIANGLE : kbd_id = KBD_TRIANGLE; break; + case MENU_KBD_CROSS : kbd_id = KBD_CROSS; break; + case MENU_KBD_SQUARE : kbd_id = KBD_SQUARE; break; + case MENU_KBD_CIRCLE : kbd_id = KBD_CIRCLE; break; + case MENU_KBD_LTRIGGER : kbd_id = KBD_LTRIGGER; break; + case MENU_KBD_RTRIGGER : kbd_id = KBD_RTRIGGER; break; + case MENU_KBD_FIRE : kbd_id = KBD_FIRE; break; + case MENU_KBD_JOY_UP : kbd_id = KBD_JOY_UP; break; + case MENU_KBD_JOY_DOWN : kbd_id = KBD_JOY_DOWN; break; + case MENU_KBD_JOY_LEFT : kbd_id = KBD_JOY_LEFT; break; + case MENU_KBD_JOY_RIGHT : kbd_id = KBD_JOY_RIGHT; break; + } + return kbd_id; +} + +static void +psp_display_screen_kbd_menu(void) +{ + char buffer[32]; + char *scan; + int menu_id = 0; + int kbd_id = 0; + int dragon_key = 0; + int color = 0; + int x = 0; + int y = 0; + int y_step = 0; + + if (psp_menu_dirty) + { + psp_sdl_blit_background(); + //psp_menu_dirty = 0; + } + + x = 10; + y = 5; + y_step = 10; + + for (menu_id = 0; menu_id < MAX_MENU_KBD_ITEM; menu_id++) + { + if (cur_menu_id == menu_id) color = PSP_MENU_SEL_COLOR; + else + if (menu_id == MENU_KBD_KBD_SELECT) color = PSP_MENU_NOTE_COLOR; + else color = PSP_MENU_TEXT_COLOR; + + psp_sdl_back2_print(x, y, menu_list[menu_id].title, color); + + if (menu_id == MENU_KBD_SKIN) { + snprintf(buffer, 30, psp_kbd_skin_dir[psp_kbd_skin]); + scan = strchr(buffer, '/'); + if (scan) *scan = 0; + psp_sdl_back2_print(80, y, buffer, color); + } else + if (menu_id == MENU_KBD_KBD_SELECT) { + + if (menu_kbd_selected == -1) sprintf(buffer, "standard"); + else + if (menu_kbd_selected == KBD_LTRIGGER_MAPPING) sprintf(buffer, "left"); + else + if (menu_kbd_selected == KBD_RTRIGGER_MAPPING) sprintf(buffer, "right"); + + string_fill_with_space(buffer, 20); + psp_sdl_back2_print(80, y, buffer, color); + + } else + if ((menu_id >= MENU_KBD_UP ) && + (menu_id <= MENU_KBD_JOY_RIGHT)) + { + kbd_id = psp_kbd_menu_id_to_key_id(menu_id); + + if (menu_kbd_selected == KBD_NORMAL_MAPPING ) dragon_key = loc_kbd_mapping[kbd_id]; + else + if (menu_kbd_selected == KBD_LTRIGGER_MAPPING) dragon_key = loc_kbd_mapping_L[kbd_id]; + else + if (menu_kbd_selected == KBD_RTRIGGER_MAPPING) dragon_key = loc_kbd_mapping_R[kbd_id]; + + if ((dragon_key >= 0) && (dragon_key < DRAGONK_MAX_KEY)) { + strcpy(buffer, psp_dragon_key_info[dragon_key].name); + } else + if (dragon_key == KBD_UNASSIGNED) { + sprintf(buffer, "UNASSIGNED"); + } else + if (dragon_key == KBD_LTRIGGER_MAPPING) { + sprintf(buffer, "L MAPPING"); + } else + if (dragon_key == KBD_RTRIGGER_MAPPING) { + sprintf(buffer, "R MAPPING"); + } else { + sprintf(buffer, "KEY %d", dragon_key); + } + string_fill_with_space(buffer, 12); + psp_sdl_back2_print(80, y, buffer, color); + + if (menu_id == MENU_KBD_JOY_RIGHT) { + y += y_step; + } + } + y += y_step; + } + + psp_menu_display_save_name(); +} + +static void +psp_keyboard_menu_update_lr(void) +{ + int kbd_id; + + for (kbd_id = 0; kbd_id < KBD_ALL_BUTTONS; kbd_id++) { + if (loc_kbd_mapping[kbd_id] == KBD_LTRIGGER_MAPPING) { + loc_kbd_mapping_L[kbd_id] = KBD_LTRIGGER_MAPPING; + loc_kbd_mapping_R[kbd_id] = KBD_LTRIGGER_MAPPING; + } else + if (loc_kbd_mapping[kbd_id] == KBD_RTRIGGER_MAPPING) { + loc_kbd_mapping_L[kbd_id] = KBD_RTRIGGER_MAPPING; + loc_kbd_mapping_R[kbd_id] = KBD_RTRIGGER_MAPPING; + } + } +} + +static void +psp_keyboard_menu_reset_kbd(void) +{ + psp_display_screen_kbd_menu(); + psp_sdl_back2_print(180, 80, "Reset Keyboard !", + PSP_MENU_WARNING_COLOR); + psp_menu_dirty = 1; + psp_sdl_flip(); + psp_kbd_reset_mapping(); + sleep(1); + + memcpy(loc_kbd_mapping, psp_kbd_mapping, sizeof(psp_kbd_mapping)); + memcpy(loc_kbd_mapping_L, psp_kbd_mapping_L, sizeof(psp_kbd_mapping_L)); + memcpy(loc_kbd_mapping_R, psp_kbd_mapping_R, sizeof(psp_kbd_mapping_R)); +} + +static void +psp_keyboard_menu_hotkeys(void) +{ + psp_display_screen_kbd_menu(); + psp_sdl_back2_print(180, 80, "Set Hotkeys !", + PSP_MENU_WARNING_COLOR); + psp_menu_dirty = 1; + psp_sdl_flip(); + psp_kbd_reset_hotkeys(); + sleep(1); + + memcpy(loc_kbd_mapping, psp_kbd_mapping, sizeof(psp_kbd_mapping)); + memcpy(loc_kbd_mapping_L, psp_kbd_mapping_L, sizeof(psp_kbd_mapping_L)); + memcpy(loc_kbd_mapping_R, psp_kbd_mapping_R, sizeof(psp_kbd_mapping_R)); +} + +static void +psp_keyboard_menu_load() +{ + int ret; + + ret = psp_fmgr_menu(FMGR_FORMAT_KBD); + if (ret == 1) /* load OK */ + { + psp_display_screen_kbd_menu(); + psp_sdl_back2_print(180, 80, "File loaded !", + PSP_MENU_NOTE_COLOR); + psp_menu_dirty = 1; + psp_sdl_flip(); + sleep(1); + } + else + if (ret == -1) /* Load Error */ + { + psp_display_screen_kbd_menu(); + psp_sdl_back2_print(180, 80, "Can't load file !", + PSP_MENU_WARNING_COLOR); + psp_menu_dirty = 1; + psp_sdl_flip(); + sleep(1); + } + + memcpy(loc_kbd_mapping , psp_kbd_mapping, sizeof(psp_kbd_mapping)); + memcpy(loc_kbd_mapping_L, psp_kbd_mapping_L, sizeof(psp_kbd_mapping_L)); + memcpy(loc_kbd_mapping_R, psp_kbd_mapping_R, sizeof(psp_kbd_mapping_R)); +} + +static void +psp_keyboard_menu_mapping(int kbd_id, int step) +{ + int *ploc_kbd_mapping; + + ploc_kbd_mapping = loc_kbd_mapping; + + if (menu_kbd_selected == KBD_LTRIGGER_MAPPING) { + ploc_kbd_mapping = loc_kbd_mapping_L; + } else + if (menu_kbd_selected == KBD_RTRIGGER_MAPPING) { + ploc_kbd_mapping = loc_kbd_mapping_R; + } + + if (step < 0) ploc_kbd_mapping[kbd_id]--; + else + if (step > 0) ploc_kbd_mapping[kbd_id]++; + + if (ploc_kbd_mapping[kbd_id] < -3) ploc_kbd_mapping[kbd_id] = DRAGONK_MAX_KEY-1; + else + if (ploc_kbd_mapping[kbd_id] >= DRAGONK_MAX_KEY) ploc_kbd_mapping[kbd_id] = -3; +} + +static void +psp_keyboard_menu_save() +{ + int error; + + psp_keyboard_menu_update_lr(); + + memcpy(psp_kbd_mapping , loc_kbd_mapping , sizeof(psp_kbd_mapping)); + memcpy(psp_kbd_mapping_L, loc_kbd_mapping_L, sizeof(psp_kbd_mapping_L)); + memcpy(psp_kbd_mapping_R, loc_kbd_mapping_R, sizeof(psp_kbd_mapping_R)); + + error = dragon_kbd_save(); + + if (! error) /* save OK */ + { + psp_display_screen_kbd_menu(); + psp_sdl_back2_print(180, 80, "File saved !", + PSP_MENU_NOTE_COLOR); + psp_menu_dirty = 1; + psp_sdl_flip(); + sleep(1); + } + else + { + psp_display_screen_kbd_menu(); + psp_sdl_back2_print(180, 80, "Can't save file !", + PSP_MENU_WARNING_COLOR); + psp_menu_dirty = 1; + psp_sdl_flip(); + sleep(1); + } +} + +static void +psp_keyboard_select_change(int step) +{ + if (step > 0) { + if (menu_kbd_selected == KBD_RTRIGGER_MAPPING) menu_kbd_selected = -1; + else menu_kbd_selected--; + } else { + if (menu_kbd_selected == -1) menu_kbd_selected = KBD_RTRIGGER_MAPPING; + else menu_kbd_selected++; + } + psp_keyboard_menu_update_lr(); +} + +static void +psp_keyboard_menu_skin(int step) +{ + if (step > 0) { + if (psp_kbd_skin < psp_kbd_last_skin) psp_kbd_skin++; + else psp_kbd_skin = 0; + } else { + if (psp_kbd_skin > 0) psp_kbd_skin--; + else psp_kbd_skin = psp_kbd_last_skin; + } + + danzeff_change_skin(); +} + +void +psp_keyboard_menu(void) +{ + long new_pad; + long old_pad; + int last_time; + int end_menu; + int kbd_id; + int dragon_key; + int danzeff_mode; + int danzeff_key; + + psp_menu_dirty = 1; + + psp_kbd_wait_no_button(); + + old_pad = 0; + last_time = 0; + end_menu = 0; + kbd_id = 0; + + danzeff_key = 0; + danzeff_mode = 0; + + memcpy(loc_kbd_mapping , psp_kbd_mapping , sizeof(psp_kbd_mapping)); + memcpy(loc_kbd_mapping_L, psp_kbd_mapping_L, sizeof(psp_kbd_mapping_L)); + memcpy(loc_kbd_mapping_R, psp_kbd_mapping_R, sizeof(psp_kbd_mapping_R)); + + while (! end_menu) + { + psp_display_screen_kbd_menu(); + + if (danzeff_mode) { + danzeff_moveTo(-10, -50); + danzeff_render(); + } + psp_sdl_flip(); + + gp2xCtrlData c; + gp2xCtrlPeekBufferPositive(&c, 1); + c.Buttons &= PSP_ALL_BUTTON_MASK; + + new_pad = c.Buttons; + + if ((old_pad != new_pad) || ((c.TimeStamp - last_time) > PSP_MENU_KBD_MIN_TIME)) { + last_time = c.TimeStamp; + old_pad = new_pad; + + } else continue; + + if (danzeff_mode) { + + danzeff_key = danzeff_readInput(c); + + if (danzeff_key > DANZEFF_START) { + dragon_key = dragon_get_key_from_ascii(danzeff_key); + + if (dragon_key != -1) { + if ((cur_menu_id >= MENU_KBD_UP ) && + (cur_menu_id <= MENU_KBD_JOY_RIGHT)) + { + kbd_id = psp_kbd_menu_id_to_key_id(cur_menu_id); + + if (menu_kbd_selected == -1) loc_kbd_mapping[kbd_id] = dragon_key; + else + if (menu_kbd_selected == KBD_LTRIGGER_MAPPING) loc_kbd_mapping_L[kbd_id] = dragon_key; + else + if (menu_kbd_selected == KBD_RTRIGGER_MAPPING) loc_kbd_mapping_R[kbd_id] = dragon_key; + } + } + + } else + if ((danzeff_key == DANZEFF_START ) || + (danzeff_key == DANZEFF_SELECT)) + { + danzeff_mode = 0; + old_pad = new_pad = 0; + + psp_kbd_wait_no_button(); + } + + continue; + } + + if (new_pad & GP2X_CTRL_LTRIGGER) { + psp_keyboard_select_change(-1); + } else + if (new_pad & GP2X_CTRL_RTRIGGER) { + psp_keyboard_select_change(+1); + } else + if ((new_pad & GP2X_CTRL_CROSS ) || + (new_pad & GP2X_CTRL_CIRCLE)) + { + int step; + + if (new_pad & GP2X_CTRL_CROSS) step = -1; + else + if (new_pad & GP2X_CTRL_CIRCLE) step = 1; + else step = 0; + + if ((cur_menu_id >= MENU_KBD_UP ) && + (cur_menu_id <= MENU_KBD_JOY_RIGHT)) + { + kbd_id = psp_kbd_menu_id_to_key_id(cur_menu_id); + psp_keyboard_menu_mapping(kbd_id, step); + } + else + { + switch (cur_menu_id ) + { + case MENU_KBD_SKIN : psp_keyboard_menu_skin(step); + break; + case MENU_KBD_KBD_SELECT : psp_keyboard_select_change(step); + break; + case MENU_KBD_LOAD : psp_menu_dirty = 1; + psp_keyboard_menu_load(); + psp_menu_dirty = 1; + old_pad = new_pad = 0; + menu_kbd_selected = -1; + break; + case MENU_KBD_SAVE : psp_keyboard_menu_save(); + break; + case MENU_KBD_HOTKEYS : psp_keyboard_menu_hotkeys(); + break; + case MENU_KBD_RESET : psp_keyboard_menu_reset_kbd(); + break; + + case MENU_KBD_BACK : end_menu = 1; + break; + } + } + + } else + if(new_pad & GP2X_CTRL_UP) { + + if (cur_menu_id > 0) cur_menu_id--; + else cur_menu_id = MAX_MENU_KBD_ITEM-1; + + } else + if(new_pad & GP2X_CTRL_DOWN) { + + if (cur_menu_id < (MAX_MENU_KBD_ITEM-1)) cur_menu_id++; + else cur_menu_id = 0; + + } else + if(new_pad & GP2X_CTRL_SQUARE) { + /* Cancel */ + end_menu = -1; + } else + if ((new_pad & GP2X_CTRL_SELECT) == GP2X_CTRL_SELECT) { + /* Back to Main Menu */ + end_menu = 1; + } else + if ((new_pad & GP2X_CTRL_START) == GP2X_CTRL_START) { + if ((cur_menu_id < MENU_KBD_UP ) || + (cur_menu_id > MENU_KBD_JOY_RIGHT)) { + cur_menu_id = MENU_KBD_UP; + } + danzeff_mode = 1; + } + } + + if (end_menu > 0) { + /* Validate */ + psp_keyboard_menu_update_lr(); + + memcpy(psp_kbd_mapping , loc_kbd_mapping , sizeof(psp_kbd_mapping)); + memcpy(psp_kbd_mapping_L, loc_kbd_mapping_L, sizeof(psp_kbd_mapping_L)); + memcpy(psp_kbd_mapping_R, loc_kbd_mapping_R, sizeof(psp_kbd_mapping_R)); + } + + psp_kbd_wait_no_button(); +} + diff --git a/src/psp_menu_kbd.h b/src/psp_menu_kbd.h new file mode 100644 index 0000000..e280019 --- /dev/null +++ b/src/psp_menu_kbd.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2006 Ludovic Jacomme (ludovic.jacomme@gmail.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +# ifndef _PSP_MENU_KBD_H_ +# define _PSP_MENU_KBD_H_ + +# define PSP_MENU_KBD_MIN_TIME 100000 + + extern void psp_keyboard_menu(void); + +# endif diff --git a/src/psp_menu_set.c b/src/psp_menu_set.c new file mode 100644 index 0000000..5461a34 --- /dev/null +++ b/src/psp_menu_set.c @@ -0,0 +1,649 @@ +/* + * Copyright (C) 2006 Ludovic Jacomme (ludovic.jacomme@gmail.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "global.h" +#include "psp_sdl.h" +#include "psp_kbd.h" +#include "psp_menu.h" +#include "psp_fmgr.h" +#include "psp_menu_kbd.h" +#include "psp_menu_set.h" + +extern SDL_Surface *back_surface; +static int psp_menu_dirty = 1; + +# define MENU_SET_SOUND 0 +# define MENU_SET_SPEED_LIMIT 1 +# define MENU_SET_SKIP_FPS 2 +# define MENU_SET_VIEW_FPS 3 +# define MENU_SET_RENDER 4 +# define MENU_SET_ARTIFACT 5 +# define MENU_SET_ANALOG 6 +# define MENU_SET_AUTOFIRE 7 +# define MENU_SET_DISPLAY_LR 8 +# define MENU_SET_JOYSTICK 9 +# define MENU_SET_JOYSTICK_ANAL 10 +# define MENU_SET_JOYSTICK_STEP 11 +# define MENU_SET_MODEL 12 +# define MENU_SET_CLOCK 13 + +# define MENU_SET_LOAD 14 +# define MENU_SET_SAVE 15 +# define MENU_SET_RESET 16 + +# define MENU_SET_BACK 17 + +# define MAX_MENU_SET_ITEM (MENU_SET_BACK + 1) + + static menu_item_t menu_list[] = + { + { "Sound enable :"}, + { "Speed limiter :"}, + { "Skip frame :"}, + { "Display fps :"}, + { "Render mode :"}, + { "Artifact mode :"}, + { "Swap Analog/Cursor :"}, + { "Auto fire period :"}, + { "Display LR led :"}, + { "Joystick :"}, + { "Joystick analog :"}, + { "Joystick step :"}, + { "Dragon model :"}, + { "Clock frequency :"}, + + { "Load settings" }, + { "Save settings" }, + { "Reset settings" }, + { "Back to Menu" } + }; + + static int cur_menu_id = MENU_SET_LOAD; + + static int dragon_snd_enable = 0; + static int dragon_view_fps = 0; + static int dragon_speed_limiter = 50; + static int dragon_render_mode = 0; + static int psp_reverse_analog = 0; + static int dragon_model = 0; + static int dragon_joystick = 0; + static int dragon_joystick_step = 3; + static int dragon_joystick_anal = 1; + static int dragon_auto_fire_period = 0; + static int video_artifact_mode = 2; + static int psp_cpu_clock = 220; + static int psp_display_lr = 0; + static int dragon_skip_fps = 0; + +static void +psp_settings_menu_reset(void); + +static void +psp_display_screen_settings_menu(void) +{ + char buffer[64]; + int menu_id = 0; + int color = 0; + int x = 0; + int y = 0; + int y_step = 0; + + if (psp_menu_dirty) + { + psp_sdl_blit_background(); + //psp_menu_dirty = 0; + } + + x = 10; + y = 15; + y_step = 10; + + for (menu_id = 0; menu_id < MAX_MENU_SET_ITEM; menu_id++) { + color = PSP_MENU_TEXT_COLOR; + if (cur_menu_id == menu_id) color = PSP_MENU_SEL_COLOR; + + psp_sdl_back2_print(x, y, menu_list[menu_id].title, color); + + if (menu_id == MENU_SET_SOUND) { + if (dragon_snd_enable) strcpy(buffer,"yes"); + else strcpy(buffer,"no "); + string_fill_with_space(buffer, 4); + psp_sdl_back2_print(140, y, buffer, color); + } else + if (menu_id == MENU_SET_VIEW_FPS) { + if (dragon_view_fps) strcpy(buffer,"yes"); + else strcpy(buffer,"no "); + string_fill_with_space(buffer, 4); + psp_sdl_back2_print(140, y, buffer, color); + } else + if (menu_id == MENU_SET_ANALOG) { + if (psp_reverse_analog) strcpy(buffer,"yes"); + else strcpy(buffer,"no "); + string_fill_with_space(buffer, 4); + psp_sdl_back2_print(140, y, buffer, color); + } else + if (menu_id == MENU_SET_AUTOFIRE) { + sprintf(buffer,"%d", dragon_auto_fire_period+1); + string_fill_with_space(buffer, 7); + psp_sdl_back2_print(140, y, buffer, color); + } else + if (menu_id == MENU_SET_ARTIFACT) { + sprintf(buffer,"%d", video_artifact_mode); + string_fill_with_space(buffer, 4); + psp_sdl_back2_print(140, y, buffer, color); + } else + if (menu_id == MENU_SET_DISPLAY_LR) { + if (psp_display_lr) strcpy(buffer,"yes"); + else strcpy(buffer,"no "); + string_fill_with_space(buffer, 4); + psp_sdl_back2_print(140, y, buffer, color); + } else + if (menu_id == MENU_SET_SKIP_FPS) { + sprintf(buffer,"%d", dragon_skip_fps); + string_fill_with_space(buffer, 4); + psp_sdl_back2_print(140, y, buffer, color); + } else + if (menu_id == MENU_SET_SPEED_LIMIT) { + if (dragon_speed_limiter == 0) strcpy(buffer, "no"); + else sprintf(buffer, "%d fps", dragon_speed_limiter); + string_fill_with_space(buffer, 7); + psp_sdl_back2_print(140, y, buffer, color); + } else + if (menu_id == MENU_SET_JOYSTICK) { + if (dragon_joystick) strcpy(buffer, "right"); + else strcpy(buffer, "left"); + string_fill_with_space(buffer, 10); + psp_sdl_back2_print(140, y, buffer, color); + } else + if (menu_id == MENU_SET_JOYSTICK_STEP) { + sprintf(buffer,"%d", dragon_joystick_step); + string_fill_with_space(buffer, 4); + psp_sdl_back2_print(140, y, buffer, color); + } else + if (menu_id == MENU_SET_JOYSTICK_ANAL) { + if (dragon_joystick_anal) strcpy(buffer,"yes"); + else strcpy(buffer,"no "); + string_fill_with_space(buffer, 4); + psp_sdl_back2_print(140, y, buffer, color); + } else + if (menu_id == MENU_SET_MODEL) { + if (dragon_model == 0) strcpy(buffer, "dragon32"); + else + if (dragon_model == 1) strcpy(buffer, "dragon64"); + else + if (dragon_model == 2) strcpy(buffer, "tano"); + else + if (dragon_model == 3) strcpy(buffer, "coco"); + else + if (dragon_model == 4) strcpy(buffer, "cocous"); + + string_fill_with_space(buffer, 10); + psp_sdl_back2_print(140, y, buffer, color); + } else + if (menu_id == MENU_SET_RENDER) { + + if (dragon_render_mode == DRAGON_RENDER_NORMAL) strcpy(buffer, "normal"); + else strcpy(buffer, "fit"); + + string_fill_with_space(buffer, 13); + psp_sdl_back2_print(140, y, buffer, color); + } else + if (menu_id == MENU_SET_CLOCK) { + sprintf(buffer,"%d", psp_cpu_clock); + string_fill_with_space(buffer, 4); + psp_sdl_back2_print(140, y, buffer, color); + y += y_step; + } else + if (menu_id == MENU_SET_RESET) { + y += y_step; + } + + y += y_step; + } + + psp_menu_display_save_name(); +} + +static void +psp_settings_menu_clock(int step) +{ + if (step > 0) { + psp_cpu_clock += 10; + if (psp_cpu_clock > 260) psp_cpu_clock = 260; + } else { + psp_cpu_clock -= 10; + if (psp_cpu_clock < 60) psp_cpu_clock = 60; + } +} + +static void +psp_settings_menu_skip_fps(int step) +{ + if (step > 0) { + if (dragon_skip_fps < 25) dragon_skip_fps++; + } else { + if (dragon_skip_fps > 0) dragon_skip_fps--; + } +} + +static void +psp_settings_menu_autofire(int step) +{ + if (step > 0) { + if (dragon_auto_fire_period < 19) dragon_auto_fire_period++; + } else { + if (dragon_auto_fire_period > 0) dragon_auto_fire_period--; + } +} + +static void +psp_settings_menu_artifact(int step) +{ + if (step > 0) { + if (video_artifact_mode < 2) video_artifact_mode++; + } else { + if (video_artifact_mode > 0) video_artifact_mode--; + } +} + +void +psp_settings_menu_limiter(int step) +{ + if (step > 0) { + if (dragon_speed_limiter < 60) dragon_speed_limiter++; + else dragon_speed_limiter = 0; + } else { + if (dragon_speed_limiter > 0) dragon_speed_limiter--; + else dragon_speed_limiter = 60; + } +} + +static void +psp_settings_menu_joystick_step(int step) +{ + if (step > 0) { + if (dragon_joystick_step < 20) dragon_joystick_step++; + else dragon_joystick_step = 1; + } else { + if (dragon_joystick_step > 1) dragon_joystick_step--; + else dragon_joystick_step = 20; + } +} + +static void +psp_settings_menu_model(int step) +{ + if (step > 0) { + if (dragon_model < 4) dragon_model++; + else dragon_model = 0; + } else { + if (dragon_model > 0) dragon_model--; + else dragon_model = 4; + } +} + +static void +psp_settings_menu_render(int step) +{ + if (step > 0) { + if (dragon_render_mode < DRAGON_LAST_RENDER) dragon_render_mode++; + else dragon_render_mode = 0; + } else { + if (dragon_render_mode > 0) dragon_render_mode--; + else dragon_render_mode = DRAGON_LAST_RENDER; + } +} + +static void +psp_settings_menu_init(void) +{ + dragon_snd_enable = DRAGON.dragon_snd_enable; + dragon_render_mode = DRAGON.dragon_render_mode; + dragon_joystick = DRAGON.dragon_joystick; + dragon_joystick_step = DRAGON.dragon_joystick_step; + dragon_joystick_anal = DRAGON.dragon_joystick_anal; + dragon_model = DRAGON.dragon_model; + dragon_speed_limiter = DRAGON.dragon_speed_limiter; + dragon_skip_fps = DRAGON.psp_skip_max_frame; + dragon_view_fps = DRAGON.dragon_view_fps; + dragon_auto_fire_period = DRAGON.dragon_auto_fire_period; + video_artifact_mode = DRAGON.video_artifact_mode ; + psp_cpu_clock = DRAGON.psp_cpu_clock; + psp_reverse_analog = DRAGON.psp_reverse_analog; + psp_display_lr = DRAGON.psp_display_lr; +} + +static void +psp_settings_menu_load(int format) +{ + int ret; + + ret = psp_fmgr_menu(format); + if (ret == 1) /* load OK */ + { + psp_display_screen_settings_menu(); + psp_sdl_back2_print(170, 110, "File loaded !", + PSP_MENU_NOTE_COLOR); + psp_menu_dirty = 1; + psp_sdl_flip(); + sleep(1); + psp_settings_menu_init(); + } + else + if (ret == -1) /* Load Error */ + { + psp_display_screen_settings_menu(); + psp_sdl_back2_print(170, 110, "Can't load file !", + PSP_MENU_WARNING_COLOR); + psp_menu_dirty = 1; + psp_sdl_flip(); + sleep(1); + } +} + +void +psp_display_reboot_needed(void) +{ + psp_kbd_wait_no_button(); + psp_display_screen_settings_menu(); + psp_sdl_back2_print(170, 70, "DRAGON Version Change", + PSP_MENU_WARNING_COLOR); + psp_sdl_back2_print(170, 110, "You need to Reboot", + PSP_MENU_WARNING_COLOR); + psp_sdl_flip(); + psp_kbd_wait_button(); +} + +static void +psp_settings_menu_validate(void) +{ + DRAGON.dragon_snd_enable = dragon_snd_enable; + DRAGON.dragon_render_mode = dragon_render_mode; + DRAGON.dragon_joystick = dragon_joystick; + DRAGON.dragon_joystick_step = dragon_joystick_step; + DRAGON.dragon_joystick_anal = dragon_joystick_anal; + if (dragon_model != DRAGON.dragon_model) { + DRAGON.dragon_model = dragon_model; + dragon_reset_computer(1); + } + DRAGON.psp_cpu_clock = psp_cpu_clock; + DRAGON.dragon_speed_limiter = dragon_speed_limiter; + DRAGON.dragon_auto_fire_period = dragon_auto_fire_period; + if (video_artifact_mode != DRAGON.video_artifact_mode) { + dragon_set_video_artifact(video_artifact_mode); + } + DRAGON.psp_reverse_analog = psp_reverse_analog; + + DRAGON.psp_skip_max_frame = dragon_skip_fps; + DRAGON.psp_skip_cur_frame = 0; + + DRAGON.dragon_view_fps = dragon_view_fps; + DRAGON.psp_display_lr = psp_display_lr; + + gp2xPowerSetClockFrequency(DRAGON.psp_cpu_clock); +} + +static void +psp_settings_menu_save_config() +{ + int error; + + psp_settings_menu_validate(); + error = dragon_save_settings(); + + if (! error) /* save OK */ + { + psp_display_screen_settings_menu(); + psp_sdl_back2_print(170, 110, "File saved !", + PSP_MENU_NOTE_COLOR); + psp_menu_dirty = 1; + psp_sdl_flip(); + sleep(1); + } + else + { + psp_display_screen_settings_menu(); + psp_sdl_back2_print(170, 110, "Can't save file !", + PSP_MENU_WARNING_COLOR); + psp_menu_dirty = 1; + psp_sdl_flip(); + sleep(1); + } +} + +int +psp_settings_menu_exit(void) +{ + gp2xCtrlData c; + + psp_display_screen_settings_menu(); + #ifdef MIYOO_MODE + psp_sdl_back2_print(170, 110, "press B to confirm !", PSP_MENU_WARNING_COLOR); + #else + psp_sdl_back2_print(170, 110, "press X to confirm !", PSP_MENU_WARNING_COLOR); + #endif + psp_menu_dirty = 1; + psp_sdl_flip(); + + psp_kbd_wait_no_button(); + + do + { + gp2xCtrlReadBufferPositive(&c, 1); + c.Buttons &= PSP_ALL_BUTTON_MASK; + + if (c.Buttons & GP2X_CTRL_CROSS) psp_sdl_exit(0); + + } while (c.Buttons == 0); + + psp_kbd_wait_no_button(); + + return 0; +} + +static void +psp_settings_menu_save() +{ + int error; + + psp_settings_menu_validate(); + error = dragon_save_settings(); + + if (! error) /* save OK */ + { + psp_display_screen_settings_menu(); + psp_sdl_back2_print(170, 110, "File saved !", + PSP_MENU_NOTE_COLOR); + psp_menu_dirty = 1; + psp_sdl_flip(); + sleep(1); + } + else + { + psp_display_screen_settings_menu(); + psp_sdl_back2_print(170, 110, "Can't save file !", + PSP_MENU_WARNING_COLOR); + psp_menu_dirty = 1; + psp_sdl_flip(); + sleep(1); + } +} + +static void +psp_settings_menu_reset(void) +{ + psp_display_screen_settings_menu(); + psp_sdl_back2_print(170, 110, "Reset Settings !", + PSP_MENU_WARNING_COLOR); + psp_menu_dirty = 1; + psp_sdl_flip(); + dragon_default_settings(); + psp_settings_menu_init(); + sleep(1); +} + +int +psp_settings_menu(void) +{ + gp2xCtrlData c; + long new_pad; + long old_pad; + int last_time; + int end_menu; + + psp_kbd_wait_no_button(); + + old_pad = 0; + last_time = 0; + end_menu = 0; + + psp_settings_menu_init(); + + psp_menu_dirty = 1; + + while (! end_menu) + { + psp_display_screen_settings_menu(); + psp_sdl_flip(); + + while (1) + { + gp2xCtrlReadBufferPositive(&c, 1); + c.Buttons &= PSP_ALL_BUTTON_MASK; + + if (c.Buttons) break; + } + + new_pad = c.Buttons; + + if ((old_pad != new_pad) || ((c.TimeStamp - last_time) > PSP_MENU_MIN_TIME)) { + last_time = c.TimeStamp; + old_pad = new_pad; + + } else continue; + + if ((c.Buttons & GP2X_CTRL_RTRIGGER) == GP2X_CTRL_RTRIGGER) { + psp_settings_menu_reset(); + end_menu = 1; + } else + if ((new_pad & GP2X_CTRL_CROSS ) || + (new_pad & GP2X_CTRL_CIRCLE)) + { + int step; + + if (new_pad & GP2X_CTRL_CROSS) step = -1; + else + if (new_pad & GP2X_CTRL_CIRCLE) step = 1; + else step = 0; + + switch (cur_menu_id ) + { + case MENU_SET_SOUND : dragon_snd_enable = ! dragon_snd_enable; + break; + case MENU_SET_SPEED_LIMIT : psp_settings_menu_limiter( step ); + break; + case MENU_SET_JOYSTICK : dragon_joystick = ! dragon_joystick; + break; + case MENU_SET_JOYSTICK_STEP : psp_settings_menu_joystick_step( step ); + break; + case MENU_SET_JOYSTICK_ANAL : dragon_joystick_anal = ! dragon_joystick_anal; + break; + case MENU_SET_MODEL : psp_settings_menu_model( step ); + break; + case MENU_SET_AUTOFIRE : psp_settings_menu_autofire( step ); + break; + case MENU_SET_VIEW_FPS : dragon_view_fps = ! dragon_view_fps; + break; + case MENU_SET_DISPLAY_LR : psp_display_lr = ! psp_display_lr; + break; + case MENU_SET_ARTIFACT : psp_settings_menu_artifact( step ); + break; + case MENU_SET_SKIP_FPS : psp_settings_menu_skip_fps( step ); + break; + case MENU_SET_ANALOG : psp_reverse_analog = ! psp_reverse_analog; + break; + case MENU_SET_RENDER : psp_settings_menu_render( step ); + break; + case MENU_SET_CLOCK : psp_settings_menu_clock( step ); + break; + case MENU_SET_LOAD : psp_settings_menu_load(FMGR_FORMAT_SET); + psp_menu_dirty = 1; + old_pad = new_pad = 0; + break; + case MENU_SET_SAVE : psp_settings_menu_save(); + psp_menu_dirty = 1; + old_pad = new_pad = 0; + break; + case MENU_SET_RESET : psp_settings_menu_reset(); + break; + + case MENU_SET_BACK : end_menu = 1; + break; + } + + } else + if(new_pad & GP2X_CTRL_UP) { + + if (cur_menu_id > 0) cur_menu_id--; + else cur_menu_id = MAX_MENU_SET_ITEM-1; + + } else + if(new_pad & GP2X_CTRL_DOWN) { + + if (cur_menu_id < (MAX_MENU_SET_ITEM-1)) cur_menu_id++; + else cur_menu_id = 0; + + } else + if(new_pad & GP2X_CTRL_SQUARE) { + /* Cancel */ + end_menu = -1; + } else + if(new_pad & GP2X_CTRL_SELECT) { + /* Back to TI99 */ + end_menu = 1; + } + } + + if (end_menu > 0) { + psp_settings_menu_validate(); + } + + psp_kbd_wait_no_button(); + + psp_sdl_clear_screen( PSP_MENU_BLACK_COLOR ); + psp_sdl_flip(); + psp_sdl_clear_screen( PSP_MENU_BLACK_COLOR ); + psp_sdl_flip(); + + return 1; +} + diff --git a/src/psp_menu_set.h b/src/psp_menu_set.h new file mode 100644 index 0000000..676027b --- /dev/null +++ b/src/psp_menu_set.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2006 Ludovic Jacomme (ludovic.jacomme@gmail.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +# ifndef _PSP_MENU_H_ +# define _PSP_MENU_H_ + +# define PSP_MENU_BORDER_COLOR psp_sdl_rgb(0x80,0x80,0xF0) +# define PSP_MENU_WARNING_COLOR psp_sdl_rgb(0xFF,0x00,0x00) +# define PSP_MENU_NOTE_COLOR psp_sdl_rgb(0xFF,0xFF,0x00) +# define PSP_MENU_BACKGROUND_COLOR psp_sdl_rgb(0x00,0x00,0x00) +# define PSP_MENU_BLACK_COLOR psp_sdl_rgb(0x00,0x00,0x00) +# define PSP_MENU_AUTHOR_COLOR psp_sdl_rgb(0x00,0x00,0xFF) +# define PSP_MENU_GREEN_COLOR psp_sdl_rgb(0x00,0xFF,0x00) +# define PSP_MENU_RED_COLOR psp_sdl_rgb(0xFF,0x00,0x00) + +# define PSP_MENU_TEXT_COLOR psp_sdl_rgb(0x80,0x80,0xFF) +# define PSP_MENU_TEXT2_COLOR psp_sdl_rgb(0xff,0xff,0xff) +# define PSP_MENU_SEL_COLOR psp_sdl_rgb(0x00,0xff,0xff) +# define PSP_MENU_SEL2_COLOR psp_sdl_rgb(0xFF,0x00,0x80) + +# define PSP_MENU_MIN_TIME 150000 + + typedef struct menu_item_t { + char *title; + } menu_item_t; + + + extern int psp_main_menu(void); + +# endif diff --git a/src/psp_run.c b/src/psp_run.c new file mode 100644 index 0000000..b3c7331 --- /dev/null +++ b/src/psp_run.c @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2006 Ludovic Jacomme (ludovic.jacomme@gmail.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "psp_run.h" + + static psp_run_t *psp_run_head = (psp_run_t *)0; + +int +psp_run_load_file(void) +{ + psp_run_t *psp_run_new; + char FileName[512]; + char Buffer[512]; + FILE *RunFile; + char *DiskName; + char *RunName; + char *Scan; + int error = 0; + + strcpy(FileName, "./run.txt"); + + RunFile = fopen(FileName, "r"); + error = 1; + + if (RunFile != (FILE*)0) { + + while (fgets(Buffer,512,RunFile) != (char *)0) { + + Scan = strchr(Buffer,'\n'); + if (Scan) *Scan = '\0'; + /* For this #@$% of windows ! */ + Scan = strchr(Buffer,'\r'); + if (Scan) *Scan = '\0'; + if (Buffer[0] == '#') continue; + + Scan = strchr(Buffer,'='); + if (! Scan) continue; + + *Scan = '\0'; + DiskName = strdup(Buffer); + RunName = strdup(Scan + 1); + + psp_run_new = (psp_run_t *)malloc(sizeof(psp_run_t)); + + psp_run_new->next = psp_run_head; + psp_run_new->disk_name = DiskName; + psp_run_new->run_name = RunName; + psp_run_head = psp_run_new; + } + + error = 0; + fclose(RunFile); + } + + return error; +} + +char * +psp_run_search(char *DiskName) +{ + psp_run_t *Scan; + + if (DiskName != (char *)0 ) + { + for (Scan = psp_run_head; + Scan != (psp_run_t *)0; + Scan = Scan->next) + { + if (!strcasecmp(Scan->disk_name, DiskName)) { + return Scan->run_name; + } + } + } + return NULL; +} diff --git a/src/psp_run.h b/src/psp_run.h new file mode 100644 index 0000000..a0e2488 --- /dev/null +++ b/src/psp_run.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2006 Ludovic Jacomme (ludovic.jacomme@gmail.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +# ifndef _PSP_RUN_H_ +# define _PSP_RUN_H_ + +#ifdef __cplusplus +extern "C" { +# endif + + typedef struct psp_run_t { + struct psp_run_t *next; + char *disk_name; + char *run_name; + } psp_run_t; + + extern int psp_run_load_file(void); + extern char *psp_run_search(char *disk_name); + +#ifdef __cplusplus +} +# endif + +# endif diff --git a/src/psp_sdl.c b/src/psp_sdl.c new file mode 100644 index 0000000..2d0e7b4 --- /dev/null +++ b/src/psp_sdl.c @@ -0,0 +1,696 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include + +#include "global.h" +#include "psp_sdl.h" +#include "psp_danzeff.h" + + extern unsigned char psp_font_8x8[]; + extern unsigned char psp_font_6x10[]; + + unsigned char *psp_font; + int psp_font_width = 8; + int psp_font_height = 8; + + SDL_Surface *back_surface; + SDL_Surface *back2_surface; + SDL_Surface *blit_surface; + SDL_Surface *splash_surface; + SDL_Surface *thumb_surface; + SDL_Surface *save_surface; + +uint +psp_sdl_rgb(uchar R, uchar G, uchar B) +{ + return SDL_MapRGB(back_surface->format, R,G,B); +} + +ushort * +psp_sdl_get_vram_addr(uint x, uint y) +{ + ushort *vram = (ushort *)back_surface->pixels; + return vram + x + (y*PSP_LINE_SIZE); +} + +void +loc_psp_debug(char *file, int line, char *message) +{ + static int current_line = 0; + static int current_col = 10; + + char buffer[128]; + current_line += 10; + if (current_line > 220) + { + if (current_col == 200) { + psp_sdl_clear_screen(psp_sdl_rgb(0, 0, 0xff)); + current_col = 10; + } else { + current_col = 200; + } + + current_line = 10; + } + sprintf(buffer,"%s:%d %s", file, line, message); + psp_sdl_print(current_col, current_line, buffer, psp_sdl_rgb(0xff,0xff,0xff) ); +} + +void +psp_sdl_print(int x,int y, char *str, int color) +{ + int index; + int x0 = x; + + for (index = 0; str[index] != '\0'; index++) { + psp_sdl_put_char(x, y, color, 0, str[index], 1, 0); + x += psp_font_width; + if (x >= (PSP_SDL_SCREEN_WIDTH - psp_font_width)) { + x = x0; y+= psp_font_height; + } + if (y >= (PSP_SDL_SCREEN_HEIGHT - psp_font_width)) break; + } +} + +void +psp_sdl_clear_screen(int color) +{ + int x; int y; + ushort *vram = psp_sdl_get_vram_addr(0,0); + + for (y = 0; y < PSP_SDL_SCREEN_HEIGHT; y++) { + for (x = 0; x < PSP_SDL_SCREEN_WIDTH; x++) { + vram[x + (y*PSP_LINE_SIZE)] = color; + } + } +} + +void +psp_sdl_black_screen() +{ + SDL_FillRect(back_surface,NULL,SDL_MapRGB(back_surface->format,0x0,0x0,0x0)); + SDL_Flip(back_surface); + SDL_FillRect(back_surface,NULL,SDL_MapRGB(back_surface->format,0x0,0x0,0x0)); + SDL_Flip(back_surface); +} + +void +psp_sdl_clear_blit(int color) +{ + if (blit_surface) { + ushort *vram = (ushort *)blit_surface->pixels; + int my_size = blit_surface->h * blit_surface->w; + while (my_size > 0) { + vram[--my_size] = color; + } + } +} + +void +psp_sdl_draw_rectangle(int x, int y, int w, int h, int border, int mode) +{ + ushort *vram = (ushort *)psp_sdl_get_vram_addr(x, y); + int xo, yo; + if (mode == PSP_SDL_XOR) { + for (xo = 0; xo < w; xo++) { + vram[xo] ^= border; + vram[xo + h * PSP_LINE_SIZE] ^= border; + } + for (yo = 0; yo < h; yo++) { + vram[yo * PSP_LINE_SIZE] ^= border; + vram[w + yo * PSP_LINE_SIZE] ^= border; + } + vram[w + h * PSP_LINE_SIZE] ^= border; + } else { + for (xo = 0; xo < w; xo++) { + vram[xo] = border; + vram[xo + h * PSP_LINE_SIZE] = border; + } + for (yo = 0; yo < h; yo++) { + vram[yo * PSP_LINE_SIZE] = border; + vram[w + yo * PSP_LINE_SIZE] = border; + } + vram[w + h * PSP_LINE_SIZE] = border; + } +} + +void +psp_sdl_fill_rectangle(int x, int y, int w, int h, int color, int mode) +{ + ushort *vram = (ushort *)psp_sdl_get_vram_addr(x, y); + int xo, yo; + if (mode == PSP_SDL_XOR) { + for (xo = 0; xo <= w; xo++) { + for (yo = 0; yo <= h; yo++) { + if ( ((xo == 0) && ((yo == 0) || (yo == h))) || + ((xo == w) && ((yo == 0) || (yo == h))) ) { + /* Skip corner */ + } else { + vram[xo + yo * PSP_LINE_SIZE] ^= color; + } + } + } + } else { + for (xo = 0; xo <= w; xo++) { + for (yo = 0; yo <= h; yo++) { + vram[xo + yo * PSP_LINE_SIZE] = color; + } + } + } +} + +static int +psp_sdl_get_back2_color(int x, int y) +{ + uchar *back2 = (uchar *)back2_surface->pixels; + int bytes_per_pixels = back2_surface->format->BytesPerPixel; + int pitch = back2_surface->pitch; + Uint8 r = back2[0 + (y * pitch) + (x * bytes_per_pixels)]; + Uint8 g = back2[1 + (y * pitch) + (x * bytes_per_pixels)]; + Uint8 b = back2[2 + (y * pitch) + (x * bytes_per_pixels)]; + int color = psp_sdl_rgb(r, g, b); + + return color; +} + +void +psp_sdl_back2_rectangle(int x, int y, int w, int h) +{ + if (! back2_surface) { + psp_sdl_fill_rectangle(x, y, w, h, 0x0, 0); + return; + } + + ushort *vram = (ushort *)psp_sdl_get_vram_addr(x, y); + + int xo, yo; + for (xo = 0; xo <= w; xo++) { + for (yo = 0; yo <= h; yo++) { + vram[xo + yo * PSP_LINE_SIZE] = psp_sdl_get_back2_color(x + xo, y + yo); + } + } +} + +void +psp_sdl_put_char(int x, int y, int color, int bgcolor, uchar c, int drawfg, int drawbg) +{ + int cx; + int cy; + int b; + int index; + + ushort *vram = (ushort *)psp_sdl_get_vram_addr(x, y); + index = ((ushort)c) * psp_font_height; + + for (cy=0; cy< psp_font_height; cy++) { + b = 1 << (psp_font_width - 1); + for (cx=0; cx< psp_font_width; cx++) { + if (psp_font[index] & b) { + if (drawfg) vram[cx + cy * PSP_LINE_SIZE] = color; + } else { + if (drawbg) vram[cx + cy * PSP_LINE_SIZE] = bgcolor; + } + b = b >> 1; + } + index++; + } +} + +void +psp_sdl_back2_put_char(int x, int y, int color, uchar c) +{ + int cx; + int cy; + int bmask; + int index; + + if (! back2_surface) { + psp_sdl_put_char(x, y, color, 0x0, c, 1, 1); + return; + } + + ushort *vram = (ushort *)psp_sdl_get_vram_addr(x, y); + + index = ((ushort)c) * psp_font_height; + + for (cy=0; cy< psp_font_height; cy++) { + bmask = 1 << (psp_font_width - 1); + for (cx=0; cx< psp_font_width; cx++) { + if (psp_font[index] & bmask) { + vram[cx + cy * PSP_LINE_SIZE] = color; + } else { + vram[cx + cy * PSP_LINE_SIZE] = psp_sdl_get_back2_color(x + cx, y + cy); + } + bmask = bmask >> 1; + } + index++; + } +} + +unsigned char +psp_convert_utf8_to_iso_8859_1(unsigned char c1, unsigned char c2) +{ + unsigned char res = 0; + if (c1 == 0xc2) res = c2; + else + if (c1 == 0xc3) res = c2 | 0x40; + return res; +} + + +void +psp_sdl_fill_print(int x,int y,const char *str, int color, int bgcolor) +{ + int index; + int x0 = x; + + for (index = 0; str[index] != '\0'; index++) { + uchar c = str[index]; + if ((c == 0xc2) || (c == 0xc3)) { + uchar new_c = psp_convert_utf8_to_iso_8859_1(c, str[index+1]); + if (new_c) { c = new_c; index++; } + } + psp_sdl_put_char(x, y, color, bgcolor, c, 1, 1); + x += psp_font_width; + if (x >= (PSP_SDL_SCREEN_WIDTH - psp_font_width)) { + x = x0; y++; + } + if (y >= (PSP_SDL_SCREEN_HEIGHT - psp_font_width)) break; + } +} + +void +psp_sdl_back2_print(int x,int y,const char *str, int color) +{ + int index; + int x0 = x; + + for (index = 0; str[index] != '\0'; index++) { + uchar c = str[index]; + if ((c == 0xc2) || (c == 0xc3)) { + uchar new_c = psp_convert_utf8_to_iso_8859_1(c, str[index+1]); + if (new_c) { c = new_c; index++; } + } + psp_sdl_back2_put_char(x, y, color, c); + x += psp_font_width; + if (x >= (PSP_SDL_SCREEN_WIDTH - psp_font_width)) { + x = x0; y++; + } + if (y >= (PSP_SDL_SCREEN_HEIGHT - psp_font_width)) break; + } +} + +void +psp_sdl_lock(void) +{ + SDL_LockSurface(back_surface); +} + +void +psp_sdl_load_background() +{ + back2_surface = IMG_Load("./background.png"); + thumb_surface = IMG_Load("./thumb.png"); +} + +void +psp_sdl_blit_background() +{ + static int first = 1; + + if (first && (back2_surface == NULL)) { + psp_sdl_load_background(); + first = 0; + } + if (back2_surface != NULL) { + SDL_BlitSurface(back2_surface, NULL, back_surface, NULL); + } else { + psp_sdl_clear_screen(psp_sdl_rgb(0x00, 0x00, 0x00)); + } +} + +void +psp_sdl_blit_thumb(int dst_x, int dst_y, SDL_Surface* thumb_surface) +{ + SDL_Rect dstRect; + dstRect.x = dst_x; + dstRect.y = dst_y; + dstRect.w = thumb_surface->w; + dstRect.h = thumb_surface->h; + SDL_BlitSurface(thumb_surface, NULL, back_surface, &dstRect); +} + +void +psp_sdl_blit_splash() +{ + if (! splash_surface) { + splash_surface = IMG_Load("./splash.png"); + } + SDL_BlitSurface(splash_surface, NULL, back_surface, NULL); +} + +void +psp_sdl_display_splash() +{ + psp_sdl_blit_splash(); + psp_sdl_flip(); + psp_sdl_blit_splash(); + psp_sdl_flip(); + sleep(2); +} + +void +psp_sdl_unlock(void) +{ + SDL_UnlockSurface(back_surface); +} + +void +psp_sdl_flip(void) +{ + SDL_Flip(back_surface); +} + +#define systemRedShift (back_surface->format->Rshift) +#define systemGreenShift (back_surface->format->Gshift) +#define systemBlueShift (back_surface->format->Bshift) +#define systemRedMask (back_surface->format->Rmask) +#define systemGreenMask (back_surface->format->Gmask) +#define systemBlueMask (back_surface->format->Bmask) + +int +psp_sdl_save_png(SDL_Surface* my_surface, char* filename) +{ + int w = my_surface->w; + int h = my_surface->h; + u8* pix = (u8*)my_surface->pixels; + u8 writeBuffer[512 * 3]; + + FILE *fp = fopen(filename,"wb"); + + if(!fp) return 0; + + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, + NULL, + NULL, + NULL); + if(!png_ptr) { + fclose(fp); + return 0; + } + + png_infop info_ptr = png_create_info_struct(png_ptr); + + if(!info_ptr) { + png_destroy_write_struct(&png_ptr,NULL); + fclose(fp); + return 0; + } + + png_init_io(png_ptr,fp); + + png_set_IHDR(png_ptr, + info_ptr, + w, + h, + 8, + PNG_COLOR_TYPE_RGB, + PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + + png_write_info(png_ptr,info_ptr); + + u8 *b = writeBuffer; + + int sizeX = w; + int sizeY = h; + int y; + int x; + + u16 *p = (u16 *)pix; + for(y = 0; y < sizeY; y++) { + for(x = 0; x < sizeX; x++) { + u16 v = p[x]; + + *b++ = ((v & systemRedMask ) >> systemRedShift ) << 3; // R + *b++ = ((v & systemGreenMask) >> systemGreenShift) << 2; // G + *b++ = ((v & systemBlueMask ) >> systemBlueShift ) << 3; // B + } + p += my_surface->pitch / 2; + png_write_row(png_ptr,writeBuffer); + + b = writeBuffer; + } + + png_write_end(png_ptr, info_ptr); + + png_destroy_write_struct(&png_ptr, &info_ptr); + + fclose(fp); + return 1; +} + +int +psp_sdl_load_png(SDL_Surface* my_surface, char* filename) +{ + int w = my_surface->w; + int h = my_surface->h; + int pitch = my_surface->pitch / 2; + u16* pix = (u16*)my_surface->pixels; + + FILE *fp = fopen(filename,"rb"); + if (!fp) return 0; + + const size_t nSigSize = 8; + u8 signature[nSigSize]; + if (fread(signature, sizeof(u8), nSigSize, fp) != nSigSize) { + fclose(fp); + return 0; + } + + if (!png_check_sig(signature, nSigSize)) { + fclose(fp); + return 0; + } + + png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, + NULL, + NULL, + NULL); + if(!png_ptr) { + fclose(fp); + return 0; + } + + png_infop info_ptr = png_create_info_struct(png_ptr); + + if(!info_ptr) { + png_destroy_read_struct(&png_ptr, NULL, NULL); + fclose(fp); + return 0; + } + + png_init_io(png_ptr, fp); + png_set_sig_bytes(png_ptr, nSigSize); + png_read_png(png_ptr, info_ptr, + PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_PACKING | + PNG_TRANSFORM_EXPAND | PNG_TRANSFORM_BGR , NULL); + + png_uint_32 width = png_get_image_width( png_ptr, info_ptr); //info_ptr->width; + png_uint_32 height = png_get_image_height( png_ptr, info_ptr); //info_ptr->height; + int color_type = png_get_color_type(png_ptr, info_ptr); //info_ptr->color_type; + + if ((width > w) || + (height > h)) { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + fclose(fp); + return 0; + } + + png_byte **pRowTable = png_get_rows(png_ptr, info_ptr); //info_ptr->row_pointers; + unsigned int x, y; + u8 r, g, b; + + for (y=0; y> 3) << systemRedShift ) & systemRedMask) | + (((g >> 2) << systemGreenShift) & systemGreenMask) | + (((b >> 3) << systemBlueShift ) & systemBlueMask); + *pix++= v; + } + pix += pitch - width; + } + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + fclose(fp); + + return 1; +} + +int +psp_sdl_save_screenshot_png(char *filename) +{ + return psp_sdl_save_png(blit_surface, filename); +} + +int +psp_sdl_save_thumb_png(SDL_Surface* my_surface, char* filename) +{ + int x; + int y; + u16* src_pixel = (u16*)blit_surface->pixels; + u16* dst_pixel = (u16*)my_surface->pixels; + + src_pixel += DRAGON_WIDTH * 24; + for (y = 0; y < REAL_DRAGON_H; y += 2) { + for (x = 0; x < REAL_DRAGON_W; x += 2) { + *dst_pixel++ = src_pixel[32 + x]; + } + src_pixel += DRAGON_WIDTH * 2; + } + /* Then save thumb in file */ + return psp_sdl_save_png(my_surface, filename); +} + +int +psp_sdl_load_thumb_png(SDL_Surface* my_surface, char* filename) +{ + return psp_sdl_load_png( my_surface, filename); +} + +void +psp_sdl_save_screenshot(void) +{ + char TmpFileName[MAX_PATH]; + + sprintf(TmpFileName,"%s/scr/screenshot_%d.png", DRAGON.dragon_home_dir, DRAGON.psp_screenshot_id++); + if (DRAGON.psp_screenshot_id >= 10) DRAGON.psp_screenshot_id = 0; + psp_sdl_save_screenshot_png(TmpFileName); +} + +int +psp_sdl_init(void) +{ + if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_JOYSTICK|SDL_INIT_AUDIO) < 0) { + return 0; + } + SDL_JoystickEventState(SDL_ENABLE); + SDL_JoystickOpen(0); + + if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) { + return 0; + } + + psp_sdl_select_font_6x10(); + + back_surface=SDL_SetVideoMode(PSP_SDL_SCREEN_WIDTH,PSP_SDL_SCREEN_HEIGHT, 16, + SDL_DOUBLEBUF|SDL_HWSURFACE|SDL_HWPALETTE); + + if ( !back_surface) { + return 0; + } + + blit_surface = SDL_CreateRGBSurface(SDL_SWSURFACE, + DRAGON_WIDTH, DRAGON_HEIGHT, + back_surface->format->BitsPerPixel, + back_surface->format->Rmask, + back_surface->format->Gmask, + back_surface->format->Bmask, 0); + + SDL_ShowCursor(SDL_DISABLE); + + psp_sdl_display_splash(); + + /* Danzeff Keyboard */ + danzeff_load(); + danzeff_set_screen(back_surface); + + /* Create surface for save state */ + int Index = 0; + for (Index = 0; Index < DRAGON_MAX_SAVE_STATE; Index++) { + DRAGON.dragon_save_state[Index].surface = + SDL_CreateRGBSurface(SDL_SWSURFACE, + SNAP_WIDTH, SNAP_HEIGHT, + blit_surface->format->BitsPerPixel, + blit_surface->format->Rmask, + blit_surface->format->Gmask, + blit_surface->format->Bmask, 0); + } + save_surface = SDL_CreateRGBSurface(SDL_SWSURFACE, + SNAP_WIDTH, SNAP_HEIGHT, + back_surface->format->BitsPerPixel, + back_surface->format->Rmask, + back_surface->format->Gmask, + back_surface->format->Bmask, 0); + + return 1; +} + +void +psp_sdl_exit(int status) +{ + SDL_CloseAudio(); + SDL_Quit(); + exit(status); +} + +void +psp_sdl_select_font_8x8() +{ + psp_font = psp_font_8x8; + psp_font_height = 8; + psp_font_width = 8; +} + +void +psp_sdl_select_font_6x10() +{ + psp_font = psp_font_6x10; + psp_font_height = 10; + psp_font_width = 6; +} + diff --git a/src/psp_sdl.h b/src/psp_sdl.h new file mode 100644 index 0000000..83fb5e8 --- /dev/null +++ b/src/psp_sdl.h @@ -0,0 +1,72 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +# ifndef _PSP_SDL_H_ +# define _PSP_SDL_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +# define psp_debug(m) loc_psp_debug(__FILE__,__LINE__,m) + +# define PSP_SDL_NOP 0 +# define PSP_SDL_XOR 1 + +# define PSP_LINE_SIZE 320 + +# define PSP_SDL_SCREEN_WIDTH 320 +# define PSP_SDL_SCREEN_HEIGHT 240 + + typedef unsigned char uchar; + typedef unsigned int uint; + typedef unsigned short ushort; + + extern SDL_Surface* back_surface; + extern SDL_Surface* blit_surface; + extern SDL_Surface* save_surface; + extern SDL_Surface* thumb_surface; + + extern int psp_load_fonts(void); + extern int psp_print_text(char * str, int colour, int v, int h); + + extern void loc_psp_debug(char *file, int line, char *message); + + /* PG -> SDL function */ + + extern void psp_sdl_print(int x,int y, char *str, int color); + extern void psp_sdl_clear_screen(int color); + extern void psp_sdl_fill_rectangle(int x, int y, int w, int h, int color, int mode); + extern void psp_sdl_draw_rectangle(int x, int y, int w, int h, int border, int mode); + extern void psp_sdl_put_char(int x, int y, int color, int bgcolor, uchar c, int drawfg, int drawbg); + extern void psp_sdl_fill_print(int x,int y,const char *str, int color, int bgcolor); + extern void psp_sdl_flip(void); + + extern void psp_sdl_lock(void); + extern void psp_sdl_unlock(void); + extern int psp_sdl_init(void); + extern void psp_sdl_save_bmp(char *filename); + extern void psp_sdl_blit_background(); + extern void psp_sdl_exit(int status); + extern void psp_sdl_black_screen(); + extern void psp_sdl_select_font_6x10(); + extern void psp_sdl_select_font_8x8(); + +#ifdef __cplusplus +} +#endif +# endif diff --git a/src/pspsdk_linux.c b/src/pspsdk_linux.c new file mode 100644 index 0000000..139d9cc --- /dev/null +++ b/src/pspsdk_linux.c @@ -0,0 +1,165 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pspsdk_linux.h" +#include "psptypes.h" +#include "pspctrl.h" + +/* For internal use only */ +# define PSP_CTRL_UPRIGHT 0x10000 +# define PSP_CTRL_UPLEFT 0x20000 +# define PSP_CTRL_DOWNRIGHT 0x40000 +# define PSP_CTRL_DOWNLEFT 0x80000 + + +static int loc_LastEventMask = 0; +static int loc_CurrEventMask = 0; +static int loc_CurrEventButtons = 0; +static u32 loc_LastTimeStamp = 0; +static u32 loc_CurrTimeStamp = 0; + +# define PSP_MIN_TIME_REPEAT 100000 + +static inline int +sceConvertMaskToButtons(int Mask) +{ + int Buttons = Mask & PSP_CTRL_MASK; + if (Mask & PSP_CTRL_UPLEFT ) Buttons |= PSP_CTRL_UP|PSP_CTRL_LEFT; + if (Mask & PSP_CTRL_UPRIGHT) Buttons |= PSP_CTRL_UP|PSP_CTRL_RIGHT; + if (Mask & PSP_CTRL_DOWNLEFT ) Buttons |= PSP_CTRL_DOWN|PSP_CTRL_LEFT; + if (Mask & PSP_CTRL_DOWNRIGHT) Buttons |= PSP_CTRL_DOWN|PSP_CTRL_RIGHT; + return Buttons; +} + + +int +sceCtrlPeekBufferPositive(SceCtrlData* c, int v) +{ +static int loc_lx = 128; +static int loc_ly = 128; + + SDL_Event SDLEvent; + + int Event = 0; + int ButtonPress = 0; + int ButtonRelease = 0; + int Mask = 0; + + memset(c, 0x0, sizeof(SceCtrlData)); + loc_CurrTimeStamp = SDL_GetTicks() * 1000; + + if (SDL_PollEvent(&SDLEvent)) { + Event=((SDL_KeyboardEvent*)(&SDLEvent))->keysym.scancode; + if (SDLEvent.type==SDL_KEYDOWN) { + ButtonPress = 1; + } else + if (SDLEvent.type==SDL_KEYUP) { + ButtonRelease = 1; + } + switch (Event) { + case PSP_UP : Mask = PSP_CTRL_UP; + break; + case PSP_DOWN : Mask = PSP_CTRL_DOWN; + break; + case PSP_LEFT : Mask = PSP_CTRL_LEFT; + break; + case PSP_RIGHT : Mask = PSP_CTRL_RIGHT; + break; + case PSP_UPLEFT : Mask = PSP_CTRL_UPLEFT; + break; + case PSP_UPRIGHT : Mask = PSP_CTRL_UPRIGHT; + break; + case PSP_DOWNLEFT : Mask = PSP_CTRL_DOWNLEFT; + break; + case PSP_DOWNRIGHT : Mask = PSP_CTRL_DOWNRIGHT; + break; + case PSP_SQUARE : Mask = PSP_CTRL_SQUARE; + break; + case PSP_CIRCLE : Mask = PSP_CTRL_CIRCLE; + break; + case PSP_CROSS : Mask = PSP_CTRL_CROSS; + break; + case PSP_TRIANGLE : Mask = PSP_CTRL_TRIANGLE; + break; + case PSP_L : Mask = PSP_CTRL_LTRIGGER; + break; + case PSP_R : Mask = PSP_CTRL_RTRIGGER; + break; + case PSP_START : Mask = PSP_CTRL_START; + break; + case PSP_SELECT : Mask = PSP_CTRL_SELECT; + break; + case PSP_JOY_UP : if (ButtonPress) loc_ly = 0; + else loc_ly = 128; + break; + case PSP_JOY_DOWN : if (ButtonPress) loc_ly = 255; + else loc_ly = 128; + break; + case PSP_JOY_LEFT : if (ButtonPress) loc_lx = 0; + else loc_lx = 128; + break; + case PSP_JOY_RIGHT : if (ButtonPress) loc_lx = 255; + else loc_lx = 128; + break; + } + loc_LastEventMask = loc_CurrEventMask; + if (ButtonPress) { + loc_CurrEventMask |= Mask; + } else + if (ButtonRelease) { + loc_CurrEventMask &= ~Mask; + } + loc_CurrEventButtons = sceConvertMaskToButtons(loc_CurrEventMask); + c->Buttons = loc_CurrEventButtons; + c->TimeStamp = loc_CurrTimeStamp; + + loc_LastTimeStamp = loc_CurrTimeStamp; + + } else { + c->Buttons = loc_CurrEventButtons; + c->TimeStamp = loc_CurrTimeStamp; + } + + c->Lx = loc_lx; + c->Ly = loc_ly; + + return (c->Buttons != 0); +} + +int +sceCtrlReadBufferPositive(SceCtrlData* c, int v) +{ + while (! sceCtrlPeekBufferPositive(c, v)); + return 1; +} + + +void +scePowerSetClockFrequency(int freq1, int freq2, int freq3) +{ +} + +void +pspDebugScreenInit() +{ +} + +void +sceKernelDelayThread(int uvalue) +{ + usleep(uvalue / 100); +} + +void +sceKernelExitGame(int status) +{ + exit(status); +} diff --git a/src/pspsdk_linux.h b/src/pspsdk_linux.h new file mode 100644 index 0000000..bafde44 --- /dev/null +++ b/src/pspsdk_linux.h @@ -0,0 +1,51 @@ +# ifndef __SDL_JOY_H__ +# define __SDL_JOY_H__ + +# ifdef __cplusplus +extern "C" { +# endif + +#ifdef LINUX_MODE + +//some keys of the keyboard to emulate sce + +#define PSP_UPLEFT 79 //SDLK_KP7 +#define PSP_UP 80 //SDLK_KP8 +#define PSP_UPRIGHT 81 //SDLK_KP9 + +#define PSP_LEFT 83 //SDLK_KP4 +#define PSP_RIGHT 85 //SDLK_KP6 + +#define PSP_DOWNLEFT 87 //SDLK_KP1 +#define PSP_DOWN 88 //SDLK_KP2 +#define PSP_DOWNRIGHT 89 //SDLK_KP3 + +#define PSP_TRIANGLE 25 +#define PSP_SQUARE 38 +#define PSP_CROSS 53 +#define PSP_CIRCLE 40 + +#define PSP_L 46 //SDLK_l +#define PSP_R 27 //SDLK_r +#define PSP_FIRE 65 //SDLK_SPACE +#define PSP_START 36 //SDLK_RETURN +#define PSP_SELECT 39 //SDLK_s + +#define PSP_JOY_UP 98 +#define PSP_JOY_DOWN 104 +#define PSP_JOY_LEFT 100 +#define PSP_JOY_RIGHT 102 + +#endif + +#define PSP_NOEVENT -1 + +#define DELAY_KEY_FIRST 250 +#define DELAY_KEY_REPEAT 80 + + +# ifdef __cplusplus +} +# endif + +# endif