diff options
author | Michael Meissner <gnu@the-meissners.org> | 1995-08-23 21:06:36 +0000 |
---|---|---|
committer | Michael Meissner <gnu@the-meissners.org> | 1995-08-23 21:06:36 +0000 |
commit | cb7a68927ab066fb794ed40ad38f601845516fe4 (patch) | |
tree | 393e8c5129cfacb4fdf20ecbbeb8125c06041a7f /sim | |
parent | 19c3fec4bf74083f6136dd64e4b503426c77b474 (diff) | |
download | gdb-cb7a68927ab066fb794ed40ad38f601845516fe4.zip gdb-cb7a68927ab066fb794ed40ad38f601845516fe4.tar.gz gdb-cb7a68927ab066fb794ed40ad38f601845516fe4.tar.bz2 |
Add PowerPC simulator from Andrew Cagney <cagney@highland.com.au>
Diffstat (limited to 'sim')
36 files changed, 9971 insertions, 20 deletions
diff --git a/sim/.Sanitize b/sim/.Sanitize index 73c9a37..7474973 100644 --- a/sim/.Sanitize +++ b/sim/.Sanitize @@ -30,6 +30,7 @@ configure configure.in h8300 h8500 +ppc sh w65 z8k diff --git a/sim/ChangeLog b/sim/ChangeLog index fcd3f03..a094a41 100644 --- a/sim/ChangeLog +++ b/sim/ChangeLog @@ -1,5 +1,29 @@ +Mon Aug 21 17:53:48 1995 Michael Meissner <meissner@tiktok.cygnus.com> + + * configure.in (powerpc{,le}-*-*): Add psim from Andrew Cagney + <cagney@highland.com.au>. + * configure: Regnerate from configure.in. + +Thu Aug 3 10:45:37 1995 Fred Fish <fnf@cygnus.com> + + * Update all FSF addresses except those in COPYING* files. + +Thu Jul 20 15:17:29 1995 Fred Fish <fnf@cygnus.com> + + * Makefile.in (CC_FOR_BUILD): Define default and arrange to pass + submakes either default or passed in value. + Wed Jul 5 14:32:54 1995 J.T. Conklin <jtc@rtl.cygnus.com> + * Makefile.in (all, clean, distclean, mostlyclean, realclean, + install): Changed targets so that they descend all + subdirectories in $(SUBDIRS). + (*-all, *-clean, *-install): Removed targets. + + * configure.in: Don't bother with target makefile fragments, they + are no longer needed. + * configure: regenerated. + * Makefile.in, configure.in: converted to autoconf. * configure: New file, generated with autconf 2.4. diff --git a/sim/configure b/sim/configure index 4ad651e..a6c0eae 100755 --- a/sim/configure +++ b/sim/configure @@ -1,7 +1,7 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated automatically using autoconf version 2.4 +# Generated automatically using autoconf version 2.3 # Copyright (C) 1992, 1993, 1994 Free Software Foundation, Inc. # # This configure script is free software; the Free Software Foundation @@ -216,7 +216,7 @@ EOF verbose=yes ;; -version | --version | --versio | --versi | --vers) - echo "configure generated by autoconf version 2.4" + echo "configure generated by autoconf version 2.3" exit 0 ;; -with-* | --with-*) @@ -401,7 +401,6 @@ fi ac_aux_dir= for ac_dir in `cd $srcdir;pwd`/.. $srcdir/`cd $srcdir;pwd`/..; do - ac_dir=`cd $ac_dir; pwd` if test -f $ac_dir/install-sh; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install-sh -c" @@ -533,6 +532,8 @@ case "${target}" in h8300*-*-*) sim_target=h8300 ;; h8500-*-*) sim_target=h8500 ;; sh*-*-*) sim_target=sh ;; + powerpc-*-*) sim_target=ppc ;; + powerpcle-*-*) sim_target=ppc ;; w65-*-*) sim_target=w65 ;; z8k*-*-*) sim_target=z8k ;; *) sim_target=none ;; @@ -542,19 +543,6 @@ configdirs=${sim_target} subdirs="$configdirs" -if test ! -f ${srcdir}/${sim_target}/${sim_target}.mt ; then - target_makefile_frag=/dev/null -else - target_makefile_frag=${srcdir}/${sim_target}/${sim_target}.mt -fi - -frags= -if test $target_makefile_frag != /dev/null; then - frags="$frags $target_makefile_frag" -fi - - - trap '' 1 2 15 cat > confcache <<\EOF # This file is a shell script that caches the results of configure @@ -643,7 +631,7 @@ do echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion" exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;; -version | --version | --versio | --versi | --vers | --ver | --ve | --v) - echo "$CONFIG_STATUS generated by autoconf version 2.4" + echo "$CONFIG_STATUS generated by autoconf version 2.3" exit 0 ;; -help | --help | --hel | --he | --h) echo "\$ac_cs_usage"; exit 0 ;; @@ -685,9 +673,6 @@ s%@build_cpu@%$build_cpu%g s%@build_vendor@%$build_vendor%g s%@build_os@%$build_os%g s%@subdirs@%$subdirs%g -/@target_makefile_frag@/r $target_makefile_frag -s%@target_makefile_frag@%%g -s%@frags@%$frags%g CEOF EOF diff --git a/sim/configure.in b/sim/configure.in index 36209c7..36d6a78 100644 --- a/sim/configure.in +++ b/sim/configure.in @@ -16,6 +16,8 @@ case "${target}" in h8300*-*-*) sim_target=h8300 ;; h8500-*-*) sim_target=h8500 ;; sh*-*-*) sim_target=sh ;; + powerpc-*-*) sim_target=ppc ;; + powerpcle-*-*) sim_target=ppc ;; w65-*-*) sim_target=w65 ;; z8k*-*-*) sim_target=z8k ;; *) sim_target=none ;; diff --git a/sim/ppc/.Sanitize b/sim/ppc/.Sanitize new file mode 100644 index 0000000..625fd36 --- /dev/null +++ b/sim/ppc/.Sanitize @@ -0,0 +1,85 @@ +# .Sanitize for devo/sim/ppc. + +# Each directory to survive it's way into a release will need a file +# like this one called "./.Sanitize". All keyword lines must exist, +# and must exist in the order specified by this file. Each directory +# in the tree will be processed, top down, in the following order. + +# Hash started lines like this one are comments and will be deleted +# before anything else is done. Blank lines will also be squashed +# out. + +# The lines between the "Do-first:" line and the "Things-to-keep:" +# line are executed as a /bin/sh shell script before anything else is +# done in this + +Do-first: + +# All files listed between the "Things-to-keep:" line and the +# "Files-to-sed:" line will be kept. All other files will be removed. +# Directories listed in this section will have their own Sanitize +# called. Directories not listed will be removed in their entirety +# with rm -rf. + +Things-to-keep: + +COPYING +COPYING.LIB +ChangeLog +Makefile.in +README.psim +basics.h +bits.c +bits.h +configure +configure.in +core.c +core.h +cpu.c +cpu.h +debug.c +debug.h +device_tree.c +device_tree.h +devices.c +devices.h +double.c +dp-bit.c +events.c +events.h +gen.c +idecode_branch.h +idecode_expression.h +idecode_fields.h +idecode_insn.h +inline.c +inline.h +interrupts.c +interrupts.h +main.c +memory_map.c +memory_map.h +ppc-endian.c +ppc-endian.h +ppc-instructions +ppc-spr-table +ppc.mt +psim.c +psim.h +registers.c +registers.h +sim_callbacks.h +sim_calls.c +std-config.h +system.c +system.h +vm.c +vm.h +words.h + +Things-to-lose: + + +Do-last: + +# End of file. diff --git a/sim/ppc/COPYING b/sim/ppc/COPYING new file mode 100644 index 0000000..a43ea21 --- /dev/null +++ b/sim/ppc/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) 19yy <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/sim/ppc/COPYING.LIB b/sim/ppc/COPYING.LIB new file mode 100644 index 0000000..eb685a5 --- /dev/null +++ b/sim/ppc/COPYING.LIB @@ -0,0 +1,481 @@ + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, 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 library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, 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 companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, 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 library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete 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 distribute a copy of this License along with the +Library. + + 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 Library or any portion +of it, thus forming a work based on the Library, 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) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +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 Library, 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 Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you 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. + + If distribution of 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 satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. 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. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library 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. + + 9. 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 Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +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. + + 11. 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 Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library 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 Library. + +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. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library 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. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library 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 Library +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 Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +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 + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "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 +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. 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 LIBRARY 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 +LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + <one line to give the library's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/sim/ppc/ChangeLog b/sim/ppc/ChangeLog new file mode 100644 index 0000000..5fc6a07 --- /dev/null +++ b/sim/ppc/ChangeLog @@ -0,0 +1,81 @@ +Tue Aug 22 09:31:18 1995 Michael Meissner <meissner@tiktok.cygnus.com> + + * system.c (system_call): Add read support. + + * main.c (main): -t sets trace_device_tree. Correct usage message + to current reality. + + * device_tree.c (update_memory_node_for_section): Make tracing + output line up. If not code or readonly, assume that the section + is a data section and has read/write permissions. Add readonly + support. + + * core.c (create_core_from_addresses): Print end address in traces + and make tracing output line up. + + * Makefile.in: Rewrite from Makefile to work with the Cygnus + environment, and support compiling in a different directory than + the sources reside in. + + * ppc-endian.h: Rename from endian.h so that it doesn't get + confused with /usr/include/sys/endian.h on Linux. Add Linux + endian support. + + * ppc-endian.c: Rename to be consistant with ppc-endian.h. + Include ppc-endian.h, not endian.h. + + * basics.h (sysdep.h): Include sysdep.h that configure makes. + Include ppc-endian.h, not endian.h. + + * std-config.h: Rename from ppc-config. Put #ifndefs around most + configuration macros, so they can be overridden via CFLAGS. By + default, turn off tracing. + + * configure.in: Clone from other simulator targets. + * configure: Generate via autoconf from configure.in. + +Sat Aug 19 09:05:32 1995 Andrew Cagney - aka Noid <cagney@kremvax> + + * ppc-instructions: fix srawi (was geting XER[CA] real wrong). + + * interrupts.c (data_storage_interrupt): allow stack to grow by + upto one MB per increment. + + * ppc-instructions: divw was computing rA / rA not rA / rB + + * main.c (main): really stupid. Wasn't exiting with correct status + +Fri Aug 18 00:38:01 1995 Andrew Cagney - aka Noid <cagney@kremvax> + + * system.c (system_call): add system calls kill(2) and getpid(2). + + * main.c (main): Check/return exit status when simulation + finishes. + +Thu Aug 17 14:29:18 1995 Andrew Cagney <cagney@kremvax> + + * device_tree.c (create_option_device_node): Alignment rules (at + least for the moment) now are for strict alignment only for LE OEA + mode. (Because of compiler problems). + + * system.c (system_call) SYS_exit: Wasn't exiting with correct status. + +Thu Aug 17 01:16:38 1995 Andrew Cagney - aka Noid <cagney@kremvax> + + * vm.c (DEFINE_VM_DATA_MAP_WRITE_N): For miss aligned transfer + forgot to return. + + * system.c (system_call): didn't page align break argument before + determining increment break increment. + + * psim/ppc: Re-arange entire directory structure so that + everything lives in the one directory. While a pain for cleaning, + makes building across multiple architectures much simpler. + + * devices.c, device_tree.c: Added code that provides a simple + illustration of how an interrupt control device could be + implemented. + + * devices.c: Added code so that the dumb console device can read + (from stdin) as well as write to stdout. + diff --git a/sim/ppc/Makefile.in b/sim/ppc/Makefile.in new file mode 100644 index 0000000..8c5865e --- /dev/null +++ b/sim/ppc/Makefile.in @@ -0,0 +1,297 @@ +# +# This file is part of the program psim. +# +# Copyright (C) 1994-1995, Andrew Cagney <cagney@highland.com.au> +# +# 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. +# + +VPATH = @srcdir@ +srcdir = @srcdir@ +srcroot = $(srcdir)/../.. + +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +host_alias = @host_alias@ +target_alias = @target_alias@ +program_transform_name = @program_transform_name@ +bindir = $(exec_prefix)/bin +libdir = $(exec_prefix)/lib +tooldir = $(libdir)/$(target_alias) + +datadir = $(prefix)/lib +mandir = $(prefix)/man +man1dir = $(mandir)/man1 +man2dir = $(mandir)/man2 +man3dir = $(mandir)/man3 +man4dir = $(mandir)/man4 +man5dir = $(mandir)/man5 +man6dir = $(mandir)/man6 +man7dir = $(mandir)/man7 +man8dir = $(mandir)/man8 +man9dir = $(mandir)/man9 +infodir = $(prefix)/info +includedir = $(prefix)/include +docdir = $(datadir)/doc + +SHELL = /bin/sh + +# FIXME: use autoconf's AC_PROG_INSTALL +INSTALL = $(srcroot)/install.sh -c +INSTALL_PROGRAM = $(INSTALL) +INSTALL_DATA = $(INSTALL) +INSTALL_XFORM = $(INSTALL) -t='$(program_transform_name)' +INSTALL_XFORM1= $(INSTALL_XFORM) -b=.1 + +AR = ar +AR_FLAGS = rc +CFLAGS = -g +BISON = bison +MAKEINFO = makeinfo +RANLIB = ranlib + +.NOEXPORT: +MAKEOVERRIDES= + +LIB_INCLUDES = -I$(srcdir)/../../include +BFD_INCLUDES = -I../../bfd -I$(srcdir)/../../bfd +GDB_INCLUDES = -I../../gdb -I$(srcdir)/../../gdb -I$(srcdir)/../../gdb/config -I$(srcdir)/../../mmalloc +INCLUDES = -I. -I$(srcdir) $(LIB_INCLUDES) $(BFD_INCLUDES) $(GDB_INCLUDES) + +CONFIG_FILE = std-config.h + +LIBIBERTY_LIB = ../../libiberty/libiberty.a +BFD_LIB = ../../bfd/libbfd.a + +#### Makefile fragments come in here. +# @host_makefile_frag@ +### + +TARGETLIB = libsim.a + +all: run libsim.a $(GDB_OBJ) + +.c.o: + $(CC) -c $(CFLAGS) $(HDEFINES) $(TDEFINES) $(INCLUDES) $< + + + +BASICS_H = \ + sysdep.h \ + config.h \ + words.h \ + ppc-endian.h \ + debug.h \ + bits.h \ + sim_callbacks.h + +PSIM_H = \ + psim.h \ + $(BASICS_H) + +IDECODE_H = \ + idecode.h \ + idecode_insn.h \ + idecode_expression.h \ + idecode_branch.h \ + idecode_fields.h \ + icache.h + +REGISTERS_H = \ + registers.h \ + spreg.h + +CPU_H = \ + cpu.h \ + $(BASICS_H) \ + $(REGISTERS_H) \ + device_tree.h \ + memory_map.h \ + core.h \ + vm.h \ + events.h \ + interrupts.h \ + psim.h \ + icache.h + + +INLINE = \ + inline.h \ + inline.c + +BUILT_SRC = \ + icache.h \ + idecode.h idecode.c \ + semantics.h semantics.c \ + spreg.h spreg.c \ + config.h + +LIB_SRC = \ + psim.c \ + bits.c \ + ppc-endian.c \ + debug.c \ + memory_map.c \ + vm.c \ + core.c \ + events.c \ + system.c \ + registers.c \ + cpu.c \ + interrupts.c \ + devices.c \ + device_tree.c + +MAIN_SRC = \ + main.c \ + sim_calls.c + + +LIB_OBJ = \ + debug.o \ + bits.o \ + ppc-endian.o \ + system.o \ + registers.o \ + memory_map.o \ + vm.o \ + core.o \ + spreg.o \ + cpu.o \ + interrupts.o \ + events.o \ + devices.o \ + device_tree.o \ + semantics.o \ + idecode.o \ + psim.o + + +GDB_OBJ = sim_calls.o + + +psim: libsim.a main.o $(LIBIBERTY_LIB) $(BFD_LIB) $(LIBS) + $(CC) $(CFLAGS) $(LDFLAGS) -o psim main.o libsim.a $(BFD_LIB) $(LIBIBERTY_LIB) $(LIBS) + +run: psim + rm -f run + ln psim run + +libsim.a: $(BUILT_SRC) $(LIB_OBJ) $(GDB_OBJ) + rm -f $(TARGETLIB) + $(AR) $(AR_FLAGS) $(TARGETLIB) $(LIB_OBJ) $(GDB_OBJ) + $(RANLIB) $(TARGETLIB) + +psim.o: psim.c psim.h $(CPU_H) $(IDECODE_H) $(INLINE) + +bits.o: bits.c bits.h + +debug.o: debug.c $(BASICS_H) + +ppc-endian.o: ppc-endian.c ppc-endian.h \ + config.h words.h sim_callbacks.h + +system.o: system.c system.h $(CPU_H) $(IDECODE_H) + +registers.o: registers.c $(REGISTERS_H) $(BASICS_H) + +cpu.o: cpu.c $(CPU_H) $(IDECODE_H) + +interrupts.o: interrupts.c $(CPU_H) $(IDECODE_H) system.h + +idecode.o: idecode.c $(CPU_H) $(IDECODE_H) semantics.h + +memory_map.o: memory_map.c memory_map.h $(BASICS_H) device_tree.h interrupts.h + +# double.o: double.c dp-bit.c + +vm.o: vm.c vm.h $(BASICS_H) $(REGISTERS_H) \ + device_tree.h memory_map.h core.h interrupts.h + +core.o: core.c core.h $(BASICS_H) \ + device_tree.h memory_map.h + +events.o: events.c events.h $(BASICS_H) + +sim_calls.o: sim_calls.c $(PSIM_H) ../../gdb/tm.h devices.h + +spreg.o: spreg.h spreg.c words.h + +main.o: main.c $(PSIM_H) + +devices.o: devices.c devices.h $(BASICS_H) \ + device_tree.h events.h + +device_tree.o: device_tree.c device_tree.h devices.h $(BASICS_H) + +semantics.o: semantics.c semantics.h $(CPU_H) $(IDECODE_H) + + +# +# Rules to create the built c source code files +# + +config.h: $(CONFIG_FILE) + cp $(srcdir)/$(CONFIG_FILE) config.h + + +tmp-gencode: gen ppc-instructions ppc-spr-table $(srcdir)/../../move-if-change + ./gen -r $(srcdir)/ppc-spr-table \ + -P tmp-spreg.h \ + -p tmp-spreg.c \ + -i $(srcdir)/ppc-instructions \ + -C tmp-icache.h \ + -S tmp-semantics.h \ + -s tmp-semantics.c \ + -D tmp-idecode.h \ + -d tmp-idecode.c + $(srcdir)/../../move-if-change tmp-icache.h icache.h + $(srcdir)/../../move-if-change tmp-idecode.h idecode.h + $(srcdir)/../../move-if-change tmp-idecode.c idecode.c + $(srcdir)/../../move-if-change tmp-semantics.h semantics.h + $(srcdir)/../../move-if-change tmp-semantics.c semantics.c + $(srcdir)/../../move-if-change tmp-spreg.h spreg.h + $(srcdir)/../../move-if-change tmp-spreg.c spreg.c + touch tmp-gencode + +icache.h idecode.h idecode.c semantics.h semantics.c spreg.h spreg.c: tmp-gencode + +gen.o: gen.c config.h + +gen: gen.o config.h $(LIBIBERTY_LIB) $(LIBS) + $(CC) $(CFLAGS) $(LDFLAGS) -o gen gen.o $(LIBIBERTY_LIB) $(LIBS) + +# + +tags etags: TAGS + +TAGS: tmp-gencode config.h + etags $(srcdir)/*.h $(srcdir)/*.c $(BUILT_SRC) + +clean: + rm -f tmp-* *.[oas] core psim run gen + +distclean mostlyclean realclean: clean + rm -f TAGS $(BUILT_SRC) Makefile config.cache config.log config.status + +Makefile: Makefile.in config.status @frags@ + $(SHELL) ./config.status + +config.status: configure + $(SHELL) ./config.status --recheck + +install: + echo Install psim ... diff --git a/sim/ppc/README.psim b/sim/ppc/README.psim new file mode 100644 index 0000000..c76c023 --- /dev/null +++ b/sim/ppc/README.psim @@ -0,0 +1,253 @@ + + PSIM + +Copyright (C) 1994-1995, Andrew Cagney <cagney@highland.com.au> + +This directory contains the program PSIM that models the PowerPC +architecture. It can either be run stand alone (psim) or linked with +GDB. + + +CONTENTS: + + psim-*.tar: + + psim-sim-*.tar.gz simulator source code + + psim-test-*.tar.gz test directory for simulator + + psim-gdb-*.diff.gz patches to integrated psim + into gdb + + gnu-*.tar: + + gnu-gdb-*.diff.gz patches to gdb that may have + already been merged into the + GDB source tree. + + gnu-*-*.diff.gz Other noise + + +BUILDING: + + o Install flex, bison, gnu-make, native gcc and probably also byacc. + + + o First you will need a fairly current copy of GDB (try the ftp site + ftp.cygnus.com:pub). I've built it with a beta version of gdb-4.15. + + Unpack gdb vis: + + $ gunzip < gdb-4.15.tar.gz | tar xf - + + + o Apply any patches that haven't yet been merged into the GDB source + tree. + + $ cd gdb-4.15 + $ gunzip < ../psim-gdb-*.diff.gz | patch -p1 + $ gunzip < ../gnu-gdb-*.diff.gz | patch -p1 + + + o Unpack the psim source code (and optionally the test directory) + + $ cd gdb-4.15 + $ gunzip < ../psim-sim-*.tar.gz | tar xvf - + $ gunzip < ../psim-test-*.tar.gz | tar xvf - + + + o Configure gdb as per normal. I use something along the lines of: + + $ cd gdb-4.15 + $ CC=gcc ./configure --target=powerpcle-unknown-eabi + + + o Build your entire gdb tree as per normal. Something along the + lines of: + + $ cd gdb-4.15 + $ make CC=gcc + . + . + . + + + + o Install it it all as per normal. Something along the lines of: + + $ cd gdb-4.15 + $ make CC=gcc install + + The program sim/ppc/psim is not installed. + + +RUNNING: + + PSIM can either be run as a stand alone program or as part + of gdb. The psim-test archive contains pre-compiled and + linked programs that can be run on PSIM. The notes below + assume that you have unpacked that tar archive. + + To rebuild the archive you will need to obtain a working + version of an ELF compiler/linker for the PowerPC. + + Example of running PSIM: + + Print out the users environment: + + $ sim/ppc/psim sim/ppc/test/envp + + Print out the arguments: + + $ sim/ppc/psim sim/ppc/test/argv a b c + + Check the OEA model: + + $ sim/ppc/psim sim/ppc/test/interrupt + + Check that sbrk works + + $ sim/ppc/psim sim/ppc/test/break + + Try for speed. The program count contains a loop + of two instructions which is looped <arg> times. + See later for how to make PSIM run 10-100 times + faster. + + $ time sim/ppc/sim sim/ppc/test/count 5000000 + $ expr 10 \* 1000 \* 1000 / <seconds> + + + Example of running GDB: + + The most important thing to be aware of is the fact + that before the simulator is used, the user must attach + to it (target sim) and than load the executable (load count). + + $ cd sim/ppc/test + $ powerpc-unknown-eabi-gdb count + (gdb) target sim + (gdb) load count + (gdb) break main + (gdb) run + . + . + . + + +CONFIGURATION: Making it go faster + + See the file sim/ppc/config.h (a.k.a. sim/ppc/data/ppc-config) + for notes. + + +KNOWN FEATURES + + SMP, dual-endian, VEA and OEA models, hardware devices + (console, icu, reset) ... + + +KNOWN PROBLEMS: + + Configuration could be better. + + HTAB (page) code for OEA model untested. Some of the vm code + instructions unimplemented. + + Doesn't detect/handle changing endian bits. In fact they are + ignored. + + Return from interrupt instruction unimplemented. + + Flush instruction cache instructions do nothing. Perhaphs they + should (if there is an instruction cache) flush it. + + PowerOpen VEA model (a.k.a XCOFF a.k.a AIX) broken. It was + working but that is before I changed the create stack frame + code into an ELF version. + + OpenBoot and PR*P interfaces missing. Open boot could be + implemented by putting special instructions at the address + of the OpenBoot callback functions. Those instructions + could than emulate OpenBoot behavour. + + VEA memory read/write performance could be improved by merging + the data sections. + + When reading in a VEA executable, the binaries text and data + sections are not made page aligned. + + Missing or commented out instructions. + + Lack of floating point support. + [workaround: build everything using -msoft-float] + + 64bit untested. + + Event code for pending events from signal handlers not + finished/tested. + + Better and more devices. + + Only two device trees VEA and OEA (clayton) and those hard coded. + Should be possible to specify a file containing a device tree + description as the program to run. At present it a device tree + file is detected causing psim to abort. + + I wonder if I've got my ppc.instructions copyright + notice correct. + + +THANKS: + + Thanks go to the following who each helped in some way. + + Allen Briggs, Bett Koch, David Edelsohn, + Michael Meissner, Bob Mercier, Richard Perini, + Richard Stallman, Mitchele Walker + + +---------------------------------------------------------------- + + +Random notes on performance: + + +$ cd test +time ../psim count `expr 10000000 / 2` +time ../psim volatile-count `expr 10000000 / 7` + +Where 2 and 7 are the number of instructions in the main loop. + + + 611/729 - baseline + +Tests: + + CFLAGS= -c -O2 -m486 -fomit-frame-pointer + + o different first/second level table/switch combinations + + 0 - use a table + 1 - use a simple switch + 2 - use an expanded switch + +i486DX4/100 - AMD + + 1/108/140 - switch=0/0/0,expand=2,inline=2,nia=1,cache=1 + 1/114/140 - switch=0/0/0,expand=2,inline=2,nia=1,cache=1 + 1/137/149 - switch=0/0,expand=2,inline=1,nia=1,cache=1 + 1/144/155 - switch=2/1,expand=2,inline=1,nia=1,cache=1 + 1/153/159 - switch=2/1,expand=0,inline=1,nia=1,cache=1 + 1/185/189 - switch=0/0,expand=0,inline=1,nia=1 + +i486DX2/66 + + 1/572/695 - switch=1/1,expand=0,inline=0 + 1/579/729 - switch=0/0,expand=0,inline=0 + 1/570/682 - switch=2/2,expand=0,inline=0 + 1/431/492 - switch=0/0,expand=0,inline=1,nia=0 + 1/271/292 - switch=2/1,expand=0,inline=1,nia=0 + 1/270/316 - switch=2/2,expand=0,inline=1,nia=0 + 1/271/281 - switch=1/1,expand=0,inline=1,nia=1 + 1/267/274 - switch=2/1,expand=0,inline=1,nia=1 diff --git a/sim/ppc/bits.h b/sim/ppc/bits.h new file mode 100644 index 0000000..e3889b4 --- /dev/null +++ b/sim/ppc/bits.h @@ -0,0 +1,194 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1995, Andrew Cagney <cagney@highland.com.au> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + */ + + +#ifndef _BITS_H_ +#define _BITS_H_ + +/* bit manipulation routines: + + Bit numbering: The bits are numbered according to the PowerPC + convention - the left most (or most significant) is bit 0 while the + right most (least significant) is bit 1. + + Size convention: Each macro is in three forms - <MACRO>32 which + operates in 32bit quantity (bits are numbered 0..31); <MACRO>64 + which operates using 64bit quantites (and bits are numbered 0..64); + and <MACRO> which operates using the bit size of the target + architecture (bits are still numbered 0..63), with 32bit + architectures ignoring the first 32bits having bit 32 as the most + significant. + + BIT*(POS): Quantity with just 1 bit set. + + MASK*(FIRST, LAST): Create a constant bit mask of the specified + size with bits [FIRST .. LAST] set. + + MASKED*(VALUE, FIRST, LAST): Masks out all but bits [FIRST + .. LAST]. + + EXTRACTED*(VALUE, FIRST, LAST): Masks out bits [FIRST .. LAST] but + also right shifts the masked value so that bit LAST becomes the + least significant (right most). + + SHUFFLE*(VALUE, OLD, NEW): Moves things around so that bit pos OLD + is extracted and than moved to bit pos NEW. + + + + IEA_MASKED(SHOULD_MASK, ADDR): Convert the address to the targets + natural size. If in 32bit mode, discard the high 32bits. + + EXTENDED(VALUE): Convert VALUE (32bits of it) to the targets + natural size. If in 64bit mode, sign extend the value. + + */ + +/* Bit operators */ +#define BIT4(POS) (1 << _MAKE_SHIFT(4, POS)) +#define BIT5(POS) (1 << _MAKE_SHIFT(5, POS)) +#define BIT10(POS) (1 << _MAKE_SHIFT(10, POS)) +#define BIT32(POS) _BITn(32, POS) +#define BIT64(POS) _BITn(64, POS) + +#if (WITH_64BIT_TARGET) +#define BIT(POS) BIT64(POS) +#else +#define BIT(POS) (((POS) < 32) ? 0 : _BITn(32, (POS)-32)) +#endif + + +/* multi bit mask */ + +#define MASK32(START, STOP) _MASKn(32, START, STOP) +#define MASK64(START, STOP) _MASKn(64, START, STOP) + +#if (WITH_64BIT_TARGET) +#define MASK(START, STOP) (((START) <= (STOP)) \ + ? _MASKn(64, START, STOP) \ + : (_MASKn(64, 0, STOP) \ + | _MASKn(64, START, 63))) +#else +#define MASK(START, STOP) (((START) <= (STOP)) \ + ? (((STOP) < 32) \ + ? 0 \ + : _MASKn(32, \ + (START) < 32 ? 0 : (START) - 32, \ + (STOP)-32)) \ + : (_MASKn(32, \ + (START) < 32 ? 0 : (START) - 32, \ + 31) \ + | (((STOP) < 32) \ + ? 0 \ + : _MASKn(32, \ + 0, \ + (STOP) - 32)))) +#endif + + +#define MASKED32(WORD, START, STOP) _MASKEDn(32, WORD, START, STOP) +#define MASKED64(WORD, START, STOP) _MASKEDn(64, WORD, START, STOP) +#define MASKED10(WORD, START, STOP) _MASKEDn(10, WORD, START, STOP) +#define EXTRACTED32(WORD, START, STOP) _EXTRACTEDn(32, WORD, START, STOP) +#define EXTRACTED64(WORD, START, STOP) _EXTRACTEDn(64, WORD, START, STOP) +#define EXTRACTED10(WORD, START, STOP) _EXTRACTEDn(10, WORD, START, STOP) + +#define MASKED(WORD, START, STOP) ((natural_word)(WORD) & MASK(START, STOP)) +#if (WITH_64BITS_TARGET) +#define EXTRACTED(WORD, START, STOP) _EXTRACTEDn(64, WORD, START, STOP) +#else +#define EXTRACTED(WORD, START, STOP) (STOP < 32 \ + ? 0 \ + : (((natural_word)WORD \ + >> (63 - (STOP))) \ + && MASK(START+(63-STOP), 63))) +#endif + + +#define SHUFFLE32(WORD, OLD, NEW) _SHUFFLEn(32, WORD, OLD, NEW) +#define SHUFFLE64(WORD, OLD, NEW) _SHUFFLEn(64, WORD, OLD, NEW) +#define SHUFFLE(WORD, OLD, NEW) _SHUFFLEn(_word, WORD, OLD, NEW) +/* NB: the wierdness (N>O?N-O:0) is to stop a warning from GCC */ +#define _SHUFFLEn(N, WORD, OLD, NEW) \ +((OLD) < (NEW) \ + ? (((unsigned##N)(WORD) \ + >> (((NEW) > (OLD)) ? ((NEW) - (OLD)) : 0)) \ + & MASK32((NEW), (NEW))) \ + : (((unsigned##N)(WORD) \ + << (((OLD) > (NEW)) ? ((OLD) - (NEW)) : 0)) \ + & MASK32((NEW), (NEW)))) + + + +/* depending on MODE return a 64bit or 32bit (sign extended) value */ +#if (WITH_64BIT_TARGET) +#define EXTENDED(X) ((signed64)(signed32)(X)) +#else +#define EXTENDED(X) (X) +#endif + + + + +/* memory alignment macro's */ +#define _ALIGNa(A,X) (((X) + ((A)-1)) & ~((A)-1)) +#define ALIGN_8(X) _ALIGNa(8, X) +#define ALIGN_16(X) _ALIGNa(16, X) +#define ALIGN_PAGE(X) _ALIGNa(0x1000, X) +#define FLOOR_PAGE(X) ((X) & ~(0x1000 - 1)) + +/* bit bliting macro's */ +#define BLIT32(V, POS, BIT) \ +do { \ + if (BIT) \ + V |= BIT32(POS); \ + else \ + V &= ~BIT32(POS); \ +} while (0) +#define MLIT32(V, LO, HI, VAL) \ +do { \ + (V) = (((V) & ~MASK32((LO), (HI))) \ + | ((VAL) << _MAKE_SHIFT(32,HI))); \ +} while (0) + + + +/* Things for creating single bit set values */ +/* MakeBit */ +#define _MAKE_SHIFT(WIDTH, pos) (WIDTH - 1 - (pos)) +#define _BITn(WIDTH, pos) (((natural##WIDTH)(1)) \ + << _MAKE_SHIFT(WIDTH, pos)) +/* MakeBitMask */ +#define _MASKn(WIDTH, START, STOP) (((((unsigned##WIDTH)0) - 1) \ + >> (WIDTH - ((STOP) - (START) + 1))) \ + << (WIDTH - 1 - (STOP))) + + + +/* mask the required bits, leaving them in place */ +#define _MASKEDn(WIDTH, WORD, START, STOP) \ +(((natural##WIDTH)(WORD)) & MASK##WIDTH(START, STOP)) + +/* extract the required bits aligning them with the lsb */ +#define _EXTRACTEDn(WIDTH, WORD, START, STOP) \ +((((natural##WIDTH)(WORD)) >> (WIDTH - (STOP) - 1)) \ + & _MASKn(WIDTH, WIDTH-1+(START)-(STOP), WIDTH-1)) + +#endif /* _BITS_H_ */ diff --git a/sim/ppc/configure.in b/sim/ppc/configure.in new file mode 100644 index 0000000..4cd6559 --- /dev/null +++ b/sim/ppc/configure.in @@ -0,0 +1,32 @@ +dnl Process this file with autoconf to produce a configure script. +AC_PREREQ(2.3)dnl +AC_INIT(Makefile.in) + +AC_CONFIG_AUX_DIR(`cd $srcdir;pwd`/../..) +AC_CANONICAL_SYSTEM +AC_ARG_PROGRAM + +. ${srcdir}/../../bfd/configure.host + +# Set up to make a link between the host's include file and "sysdep.h". +files="../../bfd/hosts/${my_host}.h" +if test ! -f ${srcdir}/${files} ; then + files=../../bfd/hosts/std-host.h + AC_MSG_WARN(z8k sim has no specific support for host ${host} -- using std-host) +fi +AC_LINK_FILES($files, sysdep.h) + +if test -f ${srcdir}/../../bfd/config/${my_host}.mh; then + host_makefile_frag=../../bfd/config/${my_host}.mh +else + host_makefile_frag=/dev/null +fi + +frags= +if test $host_makefile_frag != /dev/null; then + frags="$frags $host_makefile_frag" +fi +AC_SUBST_FILE(host_makefile_frag) +AC_SUBST(frags) + +AC_OUTPUT(Makefile) diff --git a/sim/ppc/core.c b/sim/ppc/core.c new file mode 100644 index 0000000..5e8479f --- /dev/null +++ b/sim/ppc/core.c @@ -0,0 +1,356 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1995, Andrew Cagney <cagney@highland.com.au> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + */ + + +#ifndef _CORE_C_ +#define _CORE_C_ + +#ifndef STATIC_INLINE_CORE +#define STATIC_INLINE_CORE STATIC_INLINE +#endif + +#include "basics.h" +#include "device_tree.h" +#include "memory_map.h" +#include "core.h" + + +struct _core { + /* attached devices */ + device_node *device_tree; + /* different memory maps */ + memory_map *readable; /* really everything */ + memory_map *writeable; + memory_map *executable; + /* VEA model requires additional memory information */ + unsigned_word data_upper_bound; + unsigned_word data_high_water; + unsigned_word stack_upper_bound; + unsigned_word stack_lower_bound; + unsigned_word stack_low_water; + /* misc */ + int trace; +}; + + +STATIC_INLINE_CORE void +create_core_from_addresses(device_node *device, + void *data) +{ + core *memory = (core*)data; + device_address *address; + for (address = device->addresses; + address != NULL; + address = address->next_address) { + switch (device->type) { + case memory_device: + { + void *ram = zalloc(address->size); + TRACE(trace_core, + ("create_core_from_addresses() adding memory at 0x%.8x-0x%.8x, size %8d\n", + address->lower_bound, address->lower_bound + address->size - 1, address->size)); + core_add_raw_memory(memory, + ram, + address->lower_bound, + address->size, + address->access); + } + break; + case sequential_device: + case block_device: + case bus_device: + case other_device: + { + TRACE(trace_core, + ("create_core_from_addresses() adding device at 0x%.8x-0x%.8x, size %8d\n", + address->lower_bound, address->lower_bound + address->size - 1, address->size)); + ASSERT(device->callbacks != NULL); + core_add_callback_memory(memory, + device, + device->callbacks->read_callback, + device->callbacks->write_callback, + address->lower_bound, + address->size, + address->access); + } + break; + default: + TRACE(trace_core, + ("create_core_from_addresses() unknown type %d\n", (int)device->type)); + break; + /* nothing happens here */ + } + } +} + + +INLINE_CORE core * +core_create(device_node *root, + int trace) +{ + core *memory; + + /* Initialize things */ + memory = ZALLOC(core); + memory->trace = trace; + memory->device_tree = root; + + /* allocate space for the separate virtual to physical maps */ + memory->executable = new_memory_map(); + memory->readable = new_memory_map(); + memory->writeable = new_memory_map(); + + /* initial values for the water marks */ + memory->data_high_water = 0; + memory->stack_low_water = memory->data_high_water - sizeof(unsigned_word); + + /* go over the device tree looking for address ranges to add to + memory */ + device_tree_traverse(root, + create_core_from_addresses, + NULL, + memory); + + /* return the created core object */ + return memory; +} + + +STATIC_INLINE_CORE void +zero_core_from_addresses(device_node *device, + void *data) +{ + core *memory = (core*)data; + device_address *address; + + /* for memory nodes, copy or zero any data */ + if (device->type == memory_device) { + for (address = device->addresses; + address != NULL; + address = address->next_address) { + if (memory_map_zero(memory->readable, + address->lower_bound, + address->size) != address->size) + error("init_core_from_addresses() - zero failed\n"); + /* adjust high water mark (sbrk) */ + if (memory->data_upper_bound < address->upper_bound) + memory->data_upper_bound = address->upper_bound; + } + } +} + +STATIC_INLINE_CORE void +load_core_from_addresses(device_node *device, + void *data) +{ + core *memory = (core*)data; + device_address *address; + + /* initialize the address range with the value attached to the + address. Even works for devices! */ + for (address = device->addresses; + address != NULL; + address = address->next_address) { + /* (re)init the address range. I don't want to think about what + this is doing to callback devices! */ + if (address->init) { + if (memory_map_write_buffer(memory->readable, + address->init, + address->lower_bound, + address->size, + raw_transfer) != address->size) + error("init_core_from_addresses() - write failed\n"); + } + } +} + +INLINE_CORE void +core_init(core *memory) +{ + unsigned nr_cleared; + unsigned_word clear_base; + unsigned_word clear_bound; + + /* for vea, several memory break points */ + memory->data_upper_bound = 0; + memory->stack_upper_bound = device_tree_find_int(memory->device_tree, + "/options/stack-pointer");; + memory->stack_lower_bound = memory->stack_upper_bound; + + /* (re) clear all of memory that is specified by memory-address + entries. While we're at it determine the upper bound for memory + areas */ + device_tree_traverse(memory->device_tree, + NULL, + zero_core_from_addresses, + memory); + + /* May have grown the data sectioin (vea model), zero that too if + present */ + clear_base = memory->data_upper_bound; + clear_bound = memory->data_high_water; + if (clear_bound > clear_base) { + while ((nr_cleared = memory_map_zero(memory->readable, + clear_base, + clear_bound - clear_base)) > 0) { + clear_base += nr_cleared; + } + } + + /* clear any part of the stack that was dynamically allocated */ + clear_base = memory->stack_low_water; + clear_bound = memory->stack_upper_bound; + if (clear_bound > clear_base) { + while ((nr_cleared = memory_map_zero(memory->readable, + clear_base, + clear_bound - clear_base)) > 0) { + clear_base += nr_cleared; + } + } + + /* with everything zero'ed, now (re) load any data sections */ + device_tree_traverse(memory->device_tree, + NULL, + load_core_from_addresses, + memory); + +} + + + +INLINE_CORE void +core_add_raw_memory(core *memory, + void *buffer, + unsigned_word base, + unsigned size, + device_access access) +{ + if (access & device_is_readable) + memory_map_add_raw_memory(memory->readable, + buffer, base, size); + if (access & device_is_writeable) + memory_map_add_raw_memory(memory->writeable, + buffer, base, size); + if (access & device_is_executable) + memory_map_add_raw_memory(memory->executable, + buffer, base, size); +} + + +INLINE_CORE void +core_add_callback_memory(core *memory, + device_node *device, + device_reader_callback *reader, + device_writer_callback *writer, + unsigned_word base, + unsigned size, + device_access access) +{ + if (access & device_is_readable) + memory_map_add_callback_memory(memory->readable, + device, reader, writer, + base, size); + if (access & device_is_writeable) + memory_map_add_callback_memory(memory->writeable, + device, reader, writer, + base, size); + if (access & device_is_executable) + memory_map_add_callback_memory(memory->executable, + device, reader, writer, + base, size); +} + + +STATIC_INLINE_CORE void +malloc_core_memory(core *memory, + unsigned_word base, + unsigned size, + device_access access) +{ + void *buffer = (void*)zalloc(size); + core_add_raw_memory(memory, buffer, base, size, access); +} + +INLINE_CORE unsigned_word +core_data_upper_bound(core *memory) +{ + return memory->data_upper_bound; +} + + +INLINE_CORE unsigned_word +core_stack_lower_bound(core *memory) +{ + return memory->stack_lower_bound; +} + +INLINE_CORE unsigned_word +core_stack_size(core *memory) +{ + return (memory->stack_upper_bound - memory->stack_lower_bound); +} + + + +INLINE_CORE void +core_add_data(core *memory, unsigned_word incr) +{ + memory->data_upper_bound += incr; + if (memory->data_upper_bound > memory->data_high_water) { + malloc_core_memory(memory, memory->data_high_water, incr, + device_is_readable | device_is_writeable); + memory->data_high_water = memory->data_upper_bound; + } +} + + +INLINE_CORE void +core_add_stack(core *memory, unsigned_word incr) +{ + memory->stack_lower_bound -= incr; + if (memory->stack_lower_bound < memory->stack_low_water) { + malloc_core_memory(memory, memory->stack_lower_bound, incr, + device_is_readable | device_is_writeable); + memory->stack_low_water = memory->stack_lower_bound; + } +} + + +INLINE_CORE memory_map * +core_readable(core *core) +{ + return core->readable; +} + + +INLINE_CORE memory_map * +core_writeable(core *core) +{ + return core->writeable; +} + + +INLINE_CORE memory_map * +core_executable(core *core) +{ + return core->executable; +} + +#endif /* _CORE_ */ diff --git a/sim/ppc/core.h b/sim/ppc/core.h new file mode 100644 index 0000000..5d7837a --- /dev/null +++ b/sim/ppc/core.h @@ -0,0 +1,103 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1995, Andrew Cagney <cagney@highland.com.au> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + */ + + +#ifndef _CORE_H_ +#define _CORE_H_ + +#ifndef INLINE_CORE +#define INLINE_CORE +#endif + +/* the base type */ + +typedef struct _core core; + + +/* create the hardware's core (memory and devices) from the device + tree */ +INLINE_CORE core *core_create +(device_node *root, + int trace); + + +/* given a created core object, (re)initialize it from the + information provided in it's associated device tree */ + +INLINE_CORE void core_init +(core *memory); + + +/* from this core extract out the three different types of memory - + executable, readable, writeable */ + +INLINE_CORE memory_map *core_readable +(core *memory); + +INLINE_CORE memory_map *core_writeable +(core *memory); + +INLINE_CORE memory_map *core_executable +(core *memory); + + +/* operators to grow memory on the fly */ + +INLINE_CORE void core_add_raw_memory +(core *memory, + void *buffer, + unsigned_word base, + unsigned size, + device_access access); + +INLINE_CORE void core_add_callback_memory +(core *memory, + device_node *device, + device_reader_callback *reader, + device_writer_callback *writer, + unsigned_word base, + unsigned size, + device_access access); + + +/* In the VEA model, memory grow's after it is created. Operators + below grow memory as required. + + FIXME - should this be inside of vm? */ + +INLINE_CORE unsigned_word core_data_upper_bound +(core *memory); + +INLINE_CORE unsigned_word core_stack_lower_bound +(core *memory); + +INLINE_CORE unsigned_word core_stack_size +(core *memory); + +INLINE_CORE void core_add_data +(core *memory, + unsigned_word incr); + +INLINE_CORE void core_add_stack +(core *memory, + unsigned_word incr); + + +#endif /* _CORE_ */ diff --git a/sim/ppc/device_tree.c b/sim/ppc/device_tree.c new file mode 100644 index 0000000..3478320 --- /dev/null +++ b/sim/ppc/device_tree.c @@ -0,0 +1,506 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1995, Andrew Cagney <cagney@highland.com.au> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + */ + + +#ifndef _DEVICE_TREE_C_ +#define _DEVICE_TREE_C_ + +#ifndef STATIC_INLINE_DEVICE_TREE +#define STATIC_INLINE_DEVICE_TREE STATIC_INLINE +#endif + +#include <string.h> + +#include "basics.h" +#include "device_tree.h" +#include "devices.h" + +#include "bfd.h" + +enum { clayton_memory_size = 0x100000 }; + +/* insert the address into the device_nodes sorted list of addresses */ +INLINE_DEVICE_TREE void +device_node_add_address(device_node *node, + unsigned_word lower_bound, + unsigned size, + device_access access, + void *init) +{ + unsigned_word upper_bound = lower_bound + size; + device_address *new_address; + device_address **current_address; + + /* find the insertion point */ + current_address = &node->addresses; + while (*current_address != NULL + && (*current_address)->upper_bound >= upper_bound) { + current_address = &(*current_address)->next_address; + } + + /* insert */ + new_address = ZALLOC(device_address); + new_address->lower_bound = lower_bound; + new_address->upper_bound = lower_bound + size; + new_address->size = size; + new_address->access = access; + new_address->init = init; + new_address->next_address = *current_address; + *current_address = new_address; +} + + +/* create a new device tree optionally making it a child of the parent + node */ + +INLINE_DEVICE_TREE device_node * +device_node_create(device_node *parent, + char *name, + device_type type, + device_callbacks *callbacks, + void *data) +{ + device_node *new_node; + new_node = ZALLOC(device_node); + new_node->parent = parent; + new_node->name = name; + new_node->type = type; + new_node->callbacks = callbacks; + new_node->data = data; + if (parent != NULL) { + new_node->sibling = parent->children; + parent->children = new_node; + } + return new_node; +} + + +/* Binary file: + + The specified file is a binary, assume VEA is required, construct a + fake device tree based on the addresses of the text / data segments + requested by the binary */ + + +/* Update the fake device tree so that memory is allocated for this + section */ +STATIC_INLINE_DEVICE_TREE void +update_memory_node_for_section(bfd *abfd, + asection *the_section, + PTR obj) +{ + unsigned_word section_vma; + unsigned_word section_size; + device_access section_access; + void *section_init; + device_node *memory = (device_node*)obj; + + /* skip the section if no memory to allocate */ + if (! (bfd_get_section_flags(abfd, the_section) & SEC_ALLOC)) + return; + + /* check/ignore any sections of size zero */ + section_size = bfd_get_section_size_before_reloc(the_section); + if (section_size == 0) + return; + + /* find where it is to go */ + section_vma = bfd_get_section_vma(abfd, the_section); + + TRACE(trace_device_tree, + ("name=%-7s, vma=0x%.8x, size=%6d, flags=%3x(%s%s%s%s )\n", + bfd_get_section_name(abfd, the_section), + section_vma, section_size, + bfd_get_section_flags(abfd, the_section), + bfd_get_section_flags(abfd, the_section) & SEC_LOAD ? " LOAD" : "", + bfd_get_section_flags(abfd, the_section) & SEC_CODE ? " CODE" : "", + bfd_get_section_flags(abfd, the_section) & SEC_DATA ? " DATA" : "", + bfd_get_section_flags(abfd, the_section) & SEC_ALLOC ? " ALLOC" : "", + bfd_get_section_flags(abfd, the_section) & SEC_READONLY ? " READONLY" : "" + )); + + if (bfd_get_section_flags(abfd, the_section) & SEC_LOAD) { + section_init = zalloc(section_size); + if (!bfd_get_section_contents(abfd, + the_section, + section_init, 0, + section_size)) { + bfd_perror("core:load_section()"); + error("load of data failed"); + return; + } + } + else { + section_init = NULL; + } + + /* determine the devices access */ + if (bfd_get_section_flags(abfd, the_section) & SEC_CODE) + section_access = (device_is_readable | device_is_executable); + else if (bfd_get_section_flags(abfd, the_section) & SEC_READONLY) + section_access = device_is_readable; + else + section_access = (device_is_readable | device_is_writeable); + + /* find our memory and add this section to its list of addresses */ + device_node_add_address(memory, + section_vma, + section_size, + section_access, + section_init); +} + + +/* construct the device tree from the executable */ + +STATIC_INLINE_DEVICE_TREE device_node * +create_option_device_node(device_node *root, + bfd *image) +{ + device_node *option_node; + + /* the option node and than its members */ + option_node = device_node_create(root, "options", options_device, + NULL, NULL); + + /* which endian are we ? */ + device_node_create(option_node, + "little-endian?", + boolean_type_device, + NULL, + (void*)(image->xvec->byteorder_big_p ? 0 : -1)); + + /* what is the initial entry point */ + device_node_create(option_node, + "program-counter", + integer_type_device, + NULL, + (void*)(bfd_get_start_address(image))); + + /* address of top of boot stack */ + TRACE(trace_tbd, ("create_optioin_device_node() - TBD - NT/OpenBoot?\n")); + device_node_create(option_node, + "stack-pointer", + integer_type_device, + NULL, + (void*)(bfd_get_start_address(image) == 0 + ? clayton_memory_size /* OEA */ + : (image->xvec->flavour == bfd_target_elf_flavour + ? 0xe0000000 /* elf */ + : 0x20000000 /* xcoff */))); + + /* execution environment */ + device_node_create(option_node, + "vea?", + boolean_type_device, + NULL, + (void*)(bfd_get_start_address(image) == 0 + ? 0 + : -1)); + + /* what type of binary */ + TRACE(trace_tbd, ("create_optioin_device_node() - TBD - NT/OpenBoot?\n")); + device_node_create(option_node, + "elf?", + boolean_type_device, + NULL, + (void*)(image->xvec->flavour == bfd_target_elf_flavour + ? -1 /* elf binary */ + : 0 /* probably aix binary */)); + + /* must all memory transfers be naturally aligned? */ + device_node_create(option_node, + "aligned?", + boolean_type_device, + NULL, + (void*)((WITH_ALIGNMENT == NONSTRICT_ALIGNMENT + || image->xvec->byteorder_big_p + || bfd_get_start_address(image) != 0) + ? 0 + : -1)); + + + return option_node; +} + + +/* clatyon is a simple machine that does not require interrupts or any + thing else */ + +STATIC_INLINE_DEVICE_TREE device_node * +create_clayton_device_tree(bfd *image) +{ + device_node *root; + device_node *io_node; + device_node *data_node; + device_node *memory_node; + + /* the root */ + root = ZALLOC(device_node); + + /* memory - clayton has 2mb of RAM at location 0 */ + memory_node = device_node_create(root, + "memory", + memory_device, + NULL, + NULL); + device_node_add_address(memory_node, 0x0, clayton_memory_size, + (device_is_readable + | device_is_writeable + | device_is_executable), + NULL); + + /* io address space */ + io_node = device_node_create(root, "io", bus_device, NULL, NULL); + + /* and IO devices */ + find_device_descriptor("console") + ->creator(io_node, "console@0x400000,0"); + find_device_descriptor("halt") + ->creator(io_node, "halt@0x500000,0"); + find_device_descriptor("icu") + ->creator(io_node, "icu@0x600000,0"); + + /* data to load */ + data_node = device_node_create(root, "image", data_device, NULL, NULL); + bfd_map_over_sections(image, + update_memory_node_for_section, + (PTR)data_node); + + /* options */ + create_option_device_node(root, image); + + return root; +} + + +/* user mode executable build up a device tree that reflects this */ + +STATIC_INLINE_DEVICE_TREE device_node * +create_vea_device_tree(bfd *image) +{ + device_node *root; + device_node *memory_node; + device_node *option_node; + + /* the root */ + root = ZALLOC(device_node); + + /* memory */ + memory_node = device_node_create(root, "memory", memory_device, + NULL, NULL); + bfd_map_over_sections(image, + update_memory_node_for_section, + (PTR)memory_node); + /* options - only endian so far */ + option_node = create_option_device_node(root, image); + + return root; +} + + +/* create a device tree from the specified file */ +INLINE_DEVICE_TREE device_node * +device_tree_create(const char *file_name) +{ + bfd *image; + device_node *tree; + + bfd_init(); /* could be redundant but ... */ + + /* open the file */ + image = bfd_openr(file_name, NULL); + if (image == NULL) { + bfd_perror("open failed:"); + error("nothing loaded\n"); + return NULL; + } + + /* check it is valid */ + if (!bfd_check_format(image, bfd_object)) { + printf_filtered("create_device_tree() - FIXME - should check more bfd bits\n"); + printf_filtered("create_device_tree() - %s not an executable, assume device file\n", file_name); + bfd_close(image); + image = NULL; + } + + /* depending on what was found about the file, load it */ + if (image != NULL) { + if (bfd_get_start_address(image) == 0) { + TRACE(trace_device_tree, ("create_device_tree() - clayton image\n")); + tree = create_clayton_device_tree(image); + } + else if (bfd_get_start_address(image) > 0) { + TRACE(trace_device_tree, ("create_device_tree() - vea image\n")); + tree = create_vea_device_tree(image); + } + bfd_close(image); + } + else { + error("TBD - create_device_tree() text file defining device tree\n"); + tree = NULL; + } + + return tree; +} + + +/* traverse a device tree applying prefix/postfix functions to it */ + +INLINE_DEVICE_TREE void +device_tree_traverse(device_node *root, + device_tree_traverse_function *prefix, + device_tree_traverse_function *postfix, + void *data) +{ + device_node *child; + if (prefix != NULL) + prefix(root, data); + for (child = root->children; child != NULL; child = child->sibling) { + device_tree_traverse(child, prefix, postfix, data); + } + if (postfix != NULL) + postfix(root, data); +} + + +/* query the device tree */ + +INLINE_DEVICE_TREE device_node * +device_tree_find_node(device_node *root, + const char *path) +{ + char *chp; + int name_len; + device_node *child; + + /* strip off any leading `/', `../' or `./' */ + while (1) { + if (strncmp(path, "/", strlen("/")) == 0) { + while (root->parent != NULL) + root = root->parent; + path += strlen("/"); + } + else if (strncmp(path, "./", strlen("./")) == 0) { + root = root; + path += strlen("./"); + } + else if (strncmp(path, "../", strlen("../")) == 0) { + if (root->parent != NULL) + root = root->parent; + path += strlen("../"); + } + else { + break; + } + } + + /* find the qualified (with @) and unqualified names in the path */ + chp = strchr(path, '/'); + name_len = (chp == NULL + ? strlen(path) + : chp - path); + + /* search through children for a match */ + for (child = root->children; + child != NULL; + child = child->sibling) { + if (strncmp(path, child->name, name_len) == 0 + && (strlen(child->name) == name_len + || strchr(child->name, '@') == child->name + name_len)) { + if (path[name_len] == '\0') + return child; + else + return device_tree_find_node(child, path + name_len + 1); + } + } + return NULL; +} + +INLINE_DEVICE_TREE device_node *device_tree_find_next_node +(device_node *root, + const char *path, + device_node *last); + +INLINE_DEVICE_TREE signed_word +device_tree_find_int(device_node *root, + const char *path) +{ + device_node *int_node = device_tree_find_node(root, path); + if (int_node == NULL) { + error("device_tree_find_int() - node %s does not exist\n", path); + return 0; + } + else if (int_node->type != integer_type_device) { + error("device_tree_find_int() - node %s is not an int\n", path); + return 0; + } + else { + return (signed_word)(int_node->data); + } +} + + +INLINE_DEVICE_TREE const char *device_tree_find_string +(device_node *root, + const char *path); + +INLINE_DEVICE_TREE int +device_tree_find_boolean(device_node *root, + const char *path) +{ + device_node *int_node = device_tree_find_node(root, path); + if (int_node == NULL) { + error("device_tree_find_boolean() - node %s does not exist\n", path); + return 0; + } + else if (int_node->type != boolean_type_device) { + error("device_tree_find_boolean() - node %s is not a boolean\n", path); + return 0; + } + else { + return (signed_word)(int_node->data); + } +} + + +INLINE_DEVICE_TREE void *device_tree_find_bytes +(device_node *root, + const char *path); + +/* dump out a device node and addresses */ + +INLINE_DEVICE_TREE void +device_tree_dump(device_node *device, + void *ignore_data_argument) +{ + printf_filtered("(device_node@0x%x\n", device); + printf_filtered(" (parent 0x%x)\n", device->parent); + printf_filtered(" (children 0x%x)\n", device->children); + printf_filtered(" (sibling 0x%x)\n", device->sibling); + printf_filtered(" (name %s)\n", device->name ? device->name : "(null)"); + printf_filtered(" (type %d)\n", device->type); + printf_filtered(" (handlers 0x%x)\n", device->callbacks); + printf_filtered(" (addresses %d)\n", device->addresses); + printf_filtered(" (data %d)\n", device->data); + printf_filtered(")\n"); +} + +#endif /* _DEVICE_TREE_C_ */ diff --git a/sim/ppc/device_tree.h b/sim/ppc/device_tree.h new file mode 100644 index 0000000..59d764e --- /dev/null +++ b/sim/ppc/device_tree.h @@ -0,0 +1,234 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1995, Andrew Cagney <cagney@highland.com.au> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + */ + + +#ifndef _DEVICE_TREE_H_ +#define _DEVICE_TREE_H_ + +#ifndef INLINE_DEVICE_TREE +#define INLINE_DEVICE_TREE +#endif + + +/* forward declaration of types */ + +typedef struct _device_node device_node; +typedef struct _device_address device_address; +typedef struct _device_callbacks device_callbacks; + + +/* Device callbacks: */ + + +/* Memory operations: transfer data to/from a processor. + + These callbacks pass/return data in *host* byte order. + + Should a memory read/write operation cause an interrupt (external + exception) then a device would typically pass an interrupt message + to the devices parent. Hopefully that is an interrupt controler + and will know what to do with it. + + Devices normally never either restart a processor or issue an + interrupt directly. The only exception I've thought of could be + machine check type event. */ + +typedef unsigned64 (device_reader_callback) + (device_node *device, + unsigned_word base, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia); + +typedef void (device_writer_callback) + (device_node *device, + unsigned_word base, + unsigned nr_bytes, + unsigned64 val, + cpu *processor, + unsigned_word cia); + +/* Interrupts: + + A child device uses the below to pass on to its parent changes in + the state of a child devices interrupt lines. + + Typically, the parent being an interrupt control device, would, in + responce, schedule an event at the start of the next clock cycle. + On this event, the state of any cpu could be changed. Other + devices could either ignore or pass on the interrupt message */ + +typedef void (device_interrupt_callback) + (device_node *me, + int interrupt_status, + device_node *device, + cpu *processor, + unsigned_word cia); + +/* Create: + + DEVICE_CREATOR is called once, as part of building the device tree. + This function gives the device the chance to attach any additional + data to this particular device instance. + + DEVICE_INIT_CALLBACK is (re)called when ever the system is + (re)initialised. */ + +typedef device_node *(device_creator) + (device_node *parent, + char *name); + +typedef void (device_init_callback) + (device_node *device); + + + +/* constructs to describe the hardware's tree of devices */ + +typedef enum _device_type { + /* default */ + unknown_device, + /* typical devices */ + memory_device, + sequential_device, + block_device, + bus_device, + other_device, + /* atypical devices, these are for data being loaded into ram/rom */ + data_device, + options_device, + /* types of primative nodes containing just data */ + boolean_type_device, + integer_type_device, + string_type_device, + byte_type_device, +} device_type; + +typedef enum _device_access { + device_is_readable = 1, + device_is_writeable = 2, + device_is_read_write = 3, + device_is_executable = 4, + device_is_read_exec = 5, + device_is_write_exec = 6, + device_is_read_write_exec = 7, +} device_access; + +struct _device_address { + unsigned_word lower_bound; + unsigned_word upper_bound; + unsigned size; /* host limited */ + void *init; /* initial data */ + device_access access; + device_address *next_address; +}; + +struct _device_callbacks { + device_reader_callback *read_callback; + device_writer_callback *write_callback; + device_interrupt_callback *interrupt_callback; + /* device_init_callback *init_callback; */ + /* device_init_hander *post_init_handler; */ +}; + +struct _device_node { + /* where i am */ + device_node *parent; + device_node *children; + device_node *sibling; + /* what I am */ + char *name; /* eg rom@0x1234,0x40 */ + device_type type; + device_callbacks *callbacks; + device_address *addresses; + void *data; +}; + + +/* given the image to run, return its device tree */ + +INLINE_DEVICE_TREE device_node *device_tree_create +(const char *hardware_description); + + +/* traverse the tree eiter pre or post fix */ + +typedef void (device_tree_traverse_function) + (device_node *device, + void *data); + +INLINE_DEVICE_TREE void device_tree_traverse +(device_node *root, + device_tree_traverse_function *prefix, + device_tree_traverse_function *postfix, + void *data); + + +/* query the device tree */ + +INLINE_DEVICE_TREE device_node *device_tree_find_node +(device_node *root, + const char *path); + +INLINE_DEVICE_TREE device_node *device_tree_find_next_node +(device_node *root, + const char *path, + device_node *last); + +INLINE_DEVICE_TREE signed_word device_tree_find_int +(device_node *root, + const char *path); + +INLINE_DEVICE_TREE const char *device_tree_find_string +(device_node *root, + const char *path); + +INLINE_DEVICE_TREE int device_tree_find_boolean +(device_node *root, + const char *path); + +INLINE_DEVICE_TREE void *device_tree_find_bytes +(device_node *root, + const char *path); + +/* add to the device tree */ + +INLINE_DEVICE_TREE device_node *device_node_create +(device_node *parent, + char *name, + device_type type, + device_callbacks *callbacks, + void *data); + +INLINE_DEVICE_TREE void device_node_add_address +(device_node *node, + unsigned_word lower_bound, + unsigned size, + device_access access, + void *init); + +/* dump a node, pass this to the device_tree_traverse() function to + dump the tree */ + +INLINE_DEVICE_TREE void device_tree_dump +(device_node *device, + void *ignore_data_argument); + +#endif /* _DEVICE_TREE_H_ */ diff --git a/sim/ppc/devices.c b/sim/ppc/devices.c new file mode 100644 index 0000000..96b8109 --- /dev/null +++ b/sim/ppc/devices.c @@ -0,0 +1,442 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1995, Andrew Cagney <cagney@highland.com.au> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + */ + + +#ifndef _DEVICES_C_ +#define _DEVICES_C_ + +#ifndef STATIC_INLINE_DEVICES +#define STATIC_INLINE_DEVICES STATIC_INLINE +#endif + +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <fcntl.h> + +#include "basics.h" +#include "device_tree.h" +#include "devices.h" +#include "events.h" + +#include "cpu.h" /* drats */ + +/* Helper functions */ + +STATIC_INLINE_DEVICES void +parse_device_address(char *name, + unsigned *base, + unsigned *flags) +{ + /* extract the two arguments */ + name = strchr(name, '@'); + if (name == NULL) + error("missing address for device %s\n", name); + name++; + *base = strtol(name, &name, 0); + *flags = (*name == ',' + ? strtol(name+1, &name, 0) + : 0); +} + + +/* Simple console device: + + Implements a simple text output device that is attached to stdout + of the process running the simulation. The devices has four + word registers: + + 0: read + 4: read-status + 8: write + c: write-status + + Where a nonzero status register indicates that the device is ready + (input fifo contains a character or output fifo has space). + + Illustrates: Mapping read/write to device operations onto actual + registers. + + */ + +typedef struct _console_buffer { + char buffer; + int status; + event_entry_tag event_tag; +} console_buffer; + +typedef struct _console_device { + unsigned_word my_base_address; + int interrupt_delay; + console_buffer input; + console_buffer output; +} console_device; + +typedef enum { + console_read_buffer = 0, + console_read_status = 4, + console_write_buffer = 8, + console_write_status = 12, + console_offset_mask = 0xc, + console_size = 16, +} console_offsets; + + +STATIC_INLINE_DEVICES unsigned64 +console_read_callback(device_node *device, + unsigned_word base, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + console_device *con = (console_device*)device->data; + TRACE(trace_console_device, + ("device=0x%x, base=0x%x, nr_bytes=%d\n", + device, base, nr_bytes)); + + /* handle the request */ + + switch (base & console_offset_mask) { + + case console_read_buffer: + return con->input.buffer; + + case console_read_status: + { /* check for input */ + int flags; + int status; + /* get the old status */ + flags = fcntl(0, F_GETFL, 0); + if (flags == -1) { + perror("console"); + return 0; + } + /* temp, disable blocking IO */ + status = fcntl(0, F_SETFL, flags | O_NDELAY); + if (status == -1) { + perror("console"); + return 0; + } + /* try for input */ + status = read(0, &con->input.buffer, 1); + if (status == 1) { + con->input.status = 1; + } + else { + con->input.status = 0; + } + /* return to regular vewing */ + fcntl(0, F_SETFL, flags); + } + return con->input.status; + + case console_write_buffer: + return con->output.buffer; + + case console_write_status: + return con->output.status; + + default: + error("console_read_callback() internal error\n"); + return 0; + + } + +} + +STATIC_INLINE_DEVICES void +console_write_callback(device_node *device, + unsigned_word base, + unsigned nr_bytes, + unsigned64 val, + cpu *processor, + unsigned_word cia) +{ + console_device *con = (console_device*)device->data; + + TRACE(trace_console_device, + ("device=0x%x, base=0x%x, nr_bytes=%d, val=0x%x\n", + device, base, nr_bytes, val)); + + /* check for bus error */ + if (base & 0x3) { + error("%s - misaligned base address, base=0x%x, nr_bytes=%d\n", + "console_write_callback", base, nr_bytes); + } + + switch (base & console_offset_mask) { + case console_read_buffer: con->input.buffer = val; break; + case console_read_status: con->input.status = val; break; + case console_write_buffer: + TRACE(trace_console_device, + ("<%c:%d>", val, val)); + printf_filtered("%c", val); + con->output.buffer = val; + con->output.status = 1; + break; + case console_write_status: + con->output.status = val; + break; + } + +} + +static device_callbacks console_callbacks = { + console_read_callback, + console_write_callback, +}; + +STATIC_INLINE_DEVICES device_node * +console_create(device_node *parent, + char *name) +{ + device_node *device; + unsigned address_base; + unsigned address_flags; + + /* create the descriptor */ + console_device *console = ZALLOC(console_device); + + /* extract the two arguments */ + parse_device_address(name, &address_base, &address_flags); + + /* fill in the details */ + console->my_base_address = address_base; + console->interrupt_delay = address_flags; + console->output.status = 1; + console->output.buffer = '\0'; + console->input.status = 0; + console->input.buffer = '\0'; + + /* insert into the device tree along with its address info */ + device = device_node_create(parent, name, sequential_device, + &console_callbacks, console); + device_node_add_address(device, + address_base, + console_size, + device_is_read_write_exec, + NULL); + + return device; +} + +static device_descriptor console_descriptor = { + "console", + console_create, +}; + + +/* ICU device: + + Single 4 byte register. Read returns processor number. Write + interrupts specified processor. + + Illustrates passing of events to parent device. Passing of + interrupts to parent bus. + + NB: For the sake of illustrating the passing of interrupts. This + device doesn't pass interrupt events to its parent. Instead it + passes them back to its self. */ + +STATIC_INLINE_DEVICES unsigned64 +icu_read_callback(device_node *device, + unsigned_word base, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + TRACE(trace_icu_device, + ("device=0x%x, base=0x%x, nr_bytes=%d\n", + device, base, nr_bytes)); + return cpu_nr(processor); +} + +STATIC_INLINE_DEVICES void +icu_write_callback(device_node *device, + unsigned_word base, + unsigned nr_bytes, + unsigned64 val, + cpu *processor, + unsigned_word cia) +{ + psim *system = cpu_system(processor); + device_node *parent = device; /* NB: normally would be device->parent */ + TRACE(trace_icu_device, + ("device=0x%x, base=0x%x, nr_bytes=%d, val=0x%x\n", + device, base, nr_bytes, val)); + /* tell the parent device that the interrupt lines have changed. + For this fake ICU. The interrupt lines just indicate the cpu to + interrupt next */ + parent->callbacks->interrupt_callback(parent, val, device, processor, cia); +} + +STATIC_INLINE_DEVICES void +icu_do_interrupt(event_queue *queue, + void *data) +{ + cpu *target = (cpu*)data; + /* try to interrupt the processor. If the attempt fails, try again + on the next tick */ + if (!external_interrupt(target)) + event_queue_schedule(queue, 1, icu_do_interrupt, target); +} + +STATIC_INLINE_DEVICES void +icu_interrupt_callback(device_node *me, + int interrupt_status, + device_node *device, + cpu *processor, + unsigned_word cia) +{ + /* the interrupt controler can't interrupt a cpu at any time. + Rather it must synchronize with the system clock before + performing an interrupt on the given processor */ + psim *system = cpu_system(processor); + cpu *target = psim_cpu(system, interrupt_status); + if (target != NULL) { + event_queue *events = cpu_event_queue(target); + event_queue_schedule(events, 1, icu_do_interrupt, target); + } +} + +static device_callbacks icu_callbacks = { + icu_read_callback, + icu_write_callback, + icu_interrupt_callback, +}; + +STATIC_INLINE_DEVICES device_node * +icu_create(device_node *parent, + char *name) +{ + device_node *device; + unsigned address_base; + unsigned address_flags; + + /* extract the two arguments */ + parse_device_address(name, &address_base, &address_flags); + + /* insert into the device tree along with its address info */ + device = device_node_create(parent, name, sequential_device, + &icu_callbacks, 0); + device_node_add_address(device, + address_base, + 4, + device_is_read_write_exec, + NULL); + + return device; +} + +static device_descriptor icu_descriptor = { + "icu", + icu_create, +}; + + + + + +/* HALT device: + + With real hardware, the processor operation is normally terminated + through a reset. This device illustrates how a reset device could + be attached to an address */ + +STATIC_INLINE_DEVICES unsigned64 +halt_read_callback(device_node *device, + unsigned_word base, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + cpu_halt(processor, cia, was_exited, 0); + return 0; +} + +STATIC_INLINE_DEVICES void +halt_write_callback(device_node *device, + unsigned_word base, + unsigned nr_bytes, + unsigned64 val, + cpu *processor, + unsigned_word cia) +{ + cpu_halt(processor, cia, was_exited, 0); +} + + +static device_callbacks halt_callbacks = { + halt_read_callback, + halt_write_callback, +}; + +STATIC_INLINE_DEVICES device_node * +halt_create(device_node *parent, + char *name) +{ + device_node *device; + unsigned address_base; + unsigned address_flags; + + parse_device_address(name, &address_base, &address_flags); + device = device_node_create(parent, name, other_device, + &halt_callbacks, NULL); + device_node_add_address(device, + address_base, + 4, + device_is_read_write_exec, + NULL); + return device; +} + +static device_descriptor halt_descriptor = { + "halt", + halt_create, +}; + + +static device_descriptor *devices[] = { + &console_descriptor, + &halt_descriptor, + &icu_descriptor, + NULL, +}; + + +INLINE_DEVICES device_descriptor * +find_device_descriptor(char *name) +{ + device_descriptor **device; + int name_len; + char *chp; + chp = strchr(name, '@'); + name_len = (chp == NULL ? strlen(name) : chp - name); + for (device = devices; *device != NULL; device++) { + if (strncmp(name, (*device)->name, name_len) == 0 + && ((*device)->name[name_len] == '\0' + || (*device)->name[name_len] == '@')) + return *device; + } + return NULL; +} + +#endif /* _DEVICES_C_ */ diff --git a/sim/ppc/devices.h b/sim/ppc/devices.h new file mode 100644 index 0000000..e115779 --- /dev/null +++ b/sim/ppc/devices.h @@ -0,0 +1,42 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1995, Andrew Cagney <cagney@highland.com.au> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + */ + + +#ifndef _DEVICES_H_ +#define _DEVICES_H_ + +#ifndef INLINE_DEVICES +#define INLINE_DEVICES +#endif + +#include "device_tree.h" + +/* table of all the configured devices */ + +typedef struct _device_descriptor device_descriptor; +struct _device_descriptor { + char *name; + device_creator *creator; +}; + + +INLINE_DEVICES device_descriptor *find_device_descriptor(char *name); + +#endif /* _DEVICES_H_ */ diff --git a/sim/ppc/double.c b/sim/ppc/double.c new file mode 100644 index 0000000..8594301 --- /dev/null +++ b/sim/ppc/double.c @@ -0,0 +1,42 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1995, Andrew Cagney <cagney@highland.com.au> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + */ + + +#ifndef _DOUBLE_C_ +#define _DOUBLE_C_ + +#include "basics.h" + +#define SFtype unsigned32 +#define DFtype unsigned64 + +#define HItype signed16 +#define SItype signed32 +#define DItype signed64 + +#define UHItype unsigned16 +#define USItype unsigned32 +#define UDItype unsigned64 + + +#define US_SOFTWARE_GOFAST +#include "dp-bit.c" + +#endif diff --git a/sim/ppc/dp-bit.c b/sim/ppc/dp-bit.c new file mode 100644 index 0000000..3313132 --- /dev/null +++ b/sim/ppc/dp-bit.c @@ -0,0 +1,1307 @@ +/* This is a software floating point library which can be used instead of + the floating point routines in libgcc1.c for targets without hardware + floating point. */ + +/* Copyright (C) 1994 Free Software Foundation, Inc. + +This file is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +In addition to the permissions in the GNU General Public License, the +Free Software Foundation gives you unlimited permission to link the +compiled version of this file with other programs, and to distribute +those programs without any restriction coming from the use of this +file. (The General Public License restrictions do apply in other +respects; for example, they cover modification of the file, and +distribution when not linked into another program.) + +This file 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; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* As a special exception, if you link this library with other files, + some of which are compiled with GCC, to produce an executable, + this library does not by itself cause the resulting executable + to be covered by the GNU General Public License. + This exception does not however invalidate any other reasons why + the executable file might be covered by the GNU General Public License. */ + +/* This implements IEEE 754 format arithmetic, but does not provide a + mechanism for setting the rounding mode, or for generating or handling + exceptions. + + The original code by Steve Chamberlain, hacked by Mark Eichin and Jim + Wilson, all of Cygnus Support. */ + +/* The intended way to use this file is to make two copies, add `#define FLOAT' + to one copy, then compile both copies and add them to libgcc.a. */ + +/* The following macros can be defined to change the behaviour of this file: + FLOAT: Implement a `float', aka SFmode, fp library. If this is not + defined, then this file implements a `double', aka DFmode, fp library. + FLOAT_ONLY: Used with FLOAT, to implement a `float' only library, i.e. + don't include float->double conversion which requires the double library. + This is useful only for machines which can't support doubles, e.g. some + 8-bit processors. + CMPtype: Specify the type that floating point compares should return. + This defaults to SItype, aka int. + US_SOFTWARE_GOFAST: This makes all entry points use the same names as the + US Software goFast library. If this is not defined, the entry points use + the same names as libgcc1.c. + _DEBUG_BITFLOAT: This makes debugging the code a little easier, by adding + two integers to the FLO_union_type. + NO_NANS: Disable nan and infinity handling + SMALL_MACHINE: Useful when operations on QIs and HIs are faster + than on an SI */ + +#ifndef SFtype +typedef SFtype __attribute__ ((mode (SF))); +#endif +#ifndef DFtype +typedef DFtype __attribute__ ((mode (DF))); +#endif + +#ifndef HItype +typedef int HItype __attribute__ ((mode (HI))); +#endif +#ifndef SItype +typedef int SItype __attribute__ ((mode (SI))); +#endif +#ifndef DItype +typedef int DItype __attribute__ ((mode (DI))); +#endif + +/* The type of the result of a fp compare */ +#ifndef CMPtype +#define CMPtype SItype +#endif + +#ifndef UHItype +typedef unsigned int UHItype __attribute__ ((mode (HI))); +#endif +#ifndef USItype +typedef unsigned int USItype __attribute__ ((mode (SI))); +#endif +#ifndef UDItype +typedef unsigned int UDItype __attribute__ ((mode (DI))); +#endif + +#define MAX_SI_INT ((SItype) ((unsigned) (~0)>>1)) +#define MAX_USI_INT ((USItype) ~0) + + +#ifdef FLOAT_ONLY +#define NO_DI_MODE +#endif + +#ifdef FLOAT +# define NGARDS 7L +# define GARDROUND 0x3f +# define GARDMASK 0x7f +# define GARDMSB 0x40 +# define EXPBITS 8 +# define EXPBIAS 127 +# define FRACBITS 23 +# define EXPMAX (0xff) +# define QUIET_NAN 0x100000L +# define FRAC_NBITS 32 +# define FRACHIGH 0x80000000L +# define FRACHIGH2 0xc0000000L + typedef USItype fractype; + typedef UHItype halffractype; + typedef SFtype FLO_type; + typedef SItype intfrac; + +#else +# define PREFIXFPDP dp +# define PREFIXSFDF df +# define NGARDS 8L +# define GARDROUND 0x7f +# define GARDMASK 0xff +# define GARDMSB 0x80 +# define EXPBITS 11 +# define EXPBIAS 1023 +# define FRACBITS 52 +# define EXPMAX (0x7ff) +# define QUIET_NAN 0x8000000000000LL +# define FRAC_NBITS 64 +# define FRACHIGH 0x8000000000000000LL +# define FRACHIGH2 0xc000000000000000LL + typedef UDItype fractype; + typedef USItype halffractype; + typedef DFtype FLO_type; + typedef DItype intfrac; +#endif + +#ifdef US_SOFTWARE_GOFAST +# ifdef FLOAT +# define add fpadd +# define sub fpsub +# define multiply fpmul +# define divide fpdiv +# define compare fpcmp +# define si_to_float sitofp +# define float_to_si fptosi +# define float_to_usi fptoui +# define negate __negsf2 +# define sf_to_df fptodp +# define dptofp dptofp +#else +# define add dpadd +# define sub dpsub +# define multiply dpmul +# define divide dpdiv +# define compare dpcmp +# define si_to_float litodp +# define float_to_si dptoli +# define float_to_usi dptoul +# define negate __negdf2 +# define df_to_sf dptofp +#endif +#else +# ifdef FLOAT +# define add __addsf3 +# define sub __subsf3 +# define multiply __mulsf3 +# define divide __divsf3 +# define compare __cmpsf2 +# define _eq_f2 __eqsf2 +# define _ne_f2 __nesf2 +# define _gt_f2 __gtsf2 +# define _ge_f2 __gesf2 +# define _lt_f2 __ltsf2 +# define _le_f2 __lesf2 +# define si_to_float __floatsisf +# define float_to_si __fixsfsi +# define float_to_usi __fixunssfsi +# define negate __negsf2 +# define sf_to_df __extendsfdf2 +#else +# define add __adddf3 +# define sub __subdf3 +# define multiply __muldf3 +# define divide __divdf3 +# define compare __cmpdf2 +# define _eq_f2 __eqdf2 +# define _ne_f2 __nedf2 +# define _gt_f2 __gtdf2 +# define _ge_f2 __gedf2 +# define _lt_f2 __ltdf2 +# define _le_f2 __ledf2 +# define si_to_float __floatsidf +# define float_to_si __fixdfsi +# define float_to_usi __fixunsdfsi +# define negate __negdf2 +# define df_to_sf __truncdfsf2 +# endif +#endif + + +#ifndef INLINE +#define INLINE __inline__ +#endif + +/* Preserve the sticky-bit when shifting fractions to the right. */ +#define LSHIFT(a) { a = (a & 1) | (a >> 1); } + +/* numeric parameters */ +/* F_D_BITOFF is the number of bits offset between the MSB of the mantissa + of a float and of a double. Assumes there are only two float types. + (double::FRAC_BITS+double::NGARGS-(float::FRAC_BITS-float::NGARDS)) + */ +#define F_D_BITOFF (52+8-(23+7)) + + +#define NORMAL_EXPMIN (-(EXPBIAS)+1) +#define IMPLICIT_1 (1LL<<(FRACBITS+NGARDS)) +#define IMPLICIT_2 (1LL<<(FRACBITS+1+NGARDS)) + +/* common types */ + +typedef enum +{ + CLASS_SNAN, + CLASS_QNAN, + CLASS_ZERO, + CLASS_NUMBER, + CLASS_INFINITY +} fp_class_type; + +typedef struct +{ +#ifdef SMALL_MACHINE + char class; + unsigned char sign; + short normal_exp; +#else + fp_class_type class; + unsigned int sign; + int normal_exp; +#endif + + union + { + fractype ll; + halffractype l[2]; + } fraction; +} fp_number_type; + +typedef union +{ + FLO_type value; +#ifdef _DEBUG_BITFLOAT + int l[2]; +#endif + struct + { +#ifndef FLOAT_BIT_ORDER_MISMATCH + unsigned int sign:1 __attribute__ ((packed)); + unsigned int exp:EXPBITS __attribute__ ((packed)); + fractype fraction:FRACBITS __attribute__ ((packed)); +#else + fractype fraction:FRACBITS __attribute__ ((packed)); + unsigned int exp:EXPBITS __attribute__ ((packed)); + unsigned int sign:1 __attribute__ ((packed)); +#endif + } + bits; +} +FLO_union_type; + + +/* end of header */ + +/* IEEE "special" number predicates */ + +#ifdef NO_NANS + +#define nan() 0 +#define isnan(x) 0 +#define isinf(x) 0 +#else + +INLINE +static fp_number_type * +nan () +{ + static fp_number_type thenan; + + return &thenan; +} + +INLINE +static int +isnan ( fp_number_type * x) +{ + return x->class == CLASS_SNAN || x->class == CLASS_QNAN; +} + +INLINE +static int +isinf ( fp_number_type * x) +{ + return x->class == CLASS_INFINITY; +} + +#endif + +INLINE +static int +iszero ( fp_number_type * x) +{ + return x->class == CLASS_ZERO; +} + +INLINE +static void +flip_sign ( fp_number_type * x) +{ + x->sign = !x->sign; +} + +static FLO_type +pack_d ( fp_number_type * src) +{ + FLO_union_type dst; + fractype fraction = src->fraction.ll; /* wasn't unsigned before? */ + + dst.bits.sign = src->sign; + + if (isnan (src)) + { + dst.bits.exp = EXPMAX; + dst.bits.fraction = src->fraction.ll; + if (src->class == CLASS_QNAN || 1) + { + dst.bits.fraction |= QUIET_NAN; + } + } + else if (isinf (src)) + { + dst.bits.exp = EXPMAX; + dst.bits.fraction = 0; + } + else if (iszero (src)) + { + dst.bits.exp = 0; + dst.bits.fraction = 0; + } + else if (fraction == 0) + { + dst.value = 0; + } + else + { + if (src->normal_exp < NORMAL_EXPMIN) + { + /* This number's exponent is too low to fit into the bits + available in the number, so we'll store 0 in the exponent and + shift the fraction to the right to make up for it. */ + + int shift = NORMAL_EXPMIN - src->normal_exp; + + dst.bits.exp = 0; + + if (shift > FRAC_NBITS - NGARDS) + { + /* No point shifting, since it's more that 64 out. */ + fraction = 0; + } + else + { + /* Shift by the value */ + fraction >>= shift; + } + fraction >>= NGARDS; + dst.bits.fraction = fraction; + } + else if (src->normal_exp > EXPBIAS) + { + dst.bits.exp = EXPMAX; + dst.bits.fraction = 0; + } + else + { + dst.bits.exp = src->normal_exp + EXPBIAS; + /* IF the gard bits are the all zero, but the first, then we're + half way between two numbers, choose the one which makes the + lsb of the answer 0. */ + if ((fraction & GARDMASK) == GARDMSB) + { + if (fraction & (1 << NGARDS)) + fraction += GARDROUND + 1; + } + else + { + /* Add a one to the guards to round up */ + fraction += GARDROUND; + } + if (fraction >= IMPLICIT_2) + { + fraction >>= 1; + dst.bits.exp += 1; + } + fraction >>= NGARDS; + dst.bits.fraction = fraction; + } + } + return dst.value; +} + +static void +unpack_d (FLO_union_type * src, fp_number_type * dst) +{ + fractype fraction = src->bits.fraction; + + dst->sign = src->bits.sign; + if (src->bits.exp == 0) + { + /* Hmm. Looks like 0 */ + if (fraction == 0) + { + /* tastes like zero */ + dst->class = CLASS_ZERO; + } + else + { + /* Zero exponent with non zero fraction - it's denormalized, + so there isn't a leading implicit one - we'll shift it so + it gets one. */ + dst->normal_exp = src->bits.exp - EXPBIAS + 1; + fraction <<= NGARDS; + + dst->class = CLASS_NUMBER; +#if 1 + while (fraction < IMPLICIT_1) + { + fraction <<= 1; + dst->normal_exp--; + } +#endif + dst->fraction.ll = fraction; + } + } + else if (src->bits.exp == EXPMAX) + { + /* Huge exponent*/ + if (fraction == 0) + { + /* Attached to a zero fraction - means infinity */ + dst->class = CLASS_INFINITY; + } + else + { + /* Non zero fraction, means nan */ + if (dst->sign) + { + dst->class = CLASS_SNAN; + } + else + { + dst->class = CLASS_QNAN; + } + /* Keep the fraction part as the nan number */ + dst->fraction.ll = fraction; + } + } + else + { + /* Nothing strange about this number */ + dst->normal_exp = src->bits.exp - EXPBIAS; + dst->class = CLASS_NUMBER; + dst->fraction.ll = (fraction << NGARDS) | IMPLICIT_1; + } +} + +static fp_number_type * +_fpadd_parts (fp_number_type * a, + fp_number_type * b, + fp_number_type * tmp) +{ + intfrac tfraction; + + /* Put commonly used fields in local variables. */ + int a_normal_exp; + int b_normal_exp; + fractype a_fraction; + fractype b_fraction; + + if (isnan (a)) + { + return a; + } + if (isnan (b)) + { + return b; + } + if (isinf (a)) + { + /* Adding infinities with opposite signs yields a NaN. */ + if (isinf (b) && a->sign != b->sign) + return nan (); + return a; + } + if (isinf (b)) + { + return b; + } + if (iszero (b)) + { + return a; + } + if (iszero (a)) + { + return b; + } + + /* Got two numbers. shift the smaller and increment the exponent till + they're the same */ + { + int diff; + + a_normal_exp = a->normal_exp; + b_normal_exp = b->normal_exp; + a_fraction = a->fraction.ll; + b_fraction = b->fraction.ll; + + diff = a_normal_exp - b_normal_exp; + + if (diff < 0) + diff = -diff; + if (diff < FRAC_NBITS) + { + /* ??? This does shifts one bit at a time. Optimize. */ + while (a_normal_exp > b_normal_exp) + { + b_normal_exp++; + LSHIFT (b_fraction); + } + while (b_normal_exp > a_normal_exp) + { + a_normal_exp++; + LSHIFT (a_fraction); + } + } + else + { + /* Somethings's up.. choose the biggest */ + if (a_normal_exp > b_normal_exp) + { + b_normal_exp = a_normal_exp; + b_fraction = 0; + } + else + { + a_normal_exp = b_normal_exp; + a_fraction = 0; + } + } + } + + if (a->sign != b->sign) + { + if (a->sign) + { + tfraction = -a_fraction + b_fraction; + } + else + { + tfraction = a_fraction - b_fraction; + } + if (tfraction > 0) + { + tmp->sign = 0; + tmp->normal_exp = a_normal_exp; + tmp->fraction.ll = tfraction; + } + else + { + tmp->sign = 1; + tmp->normal_exp = a_normal_exp; + tmp->fraction.ll = -tfraction; + } + /* and renormalize it */ + + while (tmp->fraction.ll < IMPLICIT_1 && tmp->fraction.ll) + { + tmp->fraction.ll <<= 1; + tmp->normal_exp--; + } + } + else + { + tmp->sign = a->sign; + tmp->normal_exp = a_normal_exp; + tmp->fraction.ll = a_fraction + b_fraction; + } + tmp->class = CLASS_NUMBER; + /* Now the fraction is added, we have to shift down to renormalize the + number */ + + if (tmp->fraction.ll >= IMPLICIT_2) + { + LSHIFT (tmp->fraction.ll); + tmp->normal_exp++; + } + return tmp; + +} + +FLO_type +add (FLO_type arg_a, FLO_type arg_b) +{ + fp_number_type a; + fp_number_type b; + fp_number_type tmp; + fp_number_type *res; + + unpack_d ((FLO_union_type *) & arg_a, &a); + unpack_d ((FLO_union_type *) & arg_b, &b); + + res = _fpadd_parts (&a, &b, &tmp); + + return pack_d (res); +} + +FLO_type +sub (FLO_type arg_a, FLO_type arg_b) +{ + fp_number_type a; + fp_number_type b; + fp_number_type tmp; + fp_number_type *res; + + unpack_d ((FLO_union_type *) & arg_a, &a); + unpack_d ((FLO_union_type *) & arg_b, &b); + + b.sign ^= 1; + + res = _fpadd_parts (&a, &b, &tmp); + + return pack_d (res); +} + +static fp_number_type * +_fpmul_parts ( fp_number_type * a, + fp_number_type * b, + fp_number_type * tmp) +{ + fractype low = 0; + fractype high = 0; + + if (isnan (a)) + { + a->sign = a->sign != b->sign; + return a; + } + if (isnan (b)) + { + b->sign = a->sign != b->sign; + return b; + } + if (isinf (a)) + { + if (iszero (b)) + return nan (); + a->sign = a->sign != b->sign; + return a; + } + if (isinf (b)) + { + if (iszero (a)) + { + return nan (); + } + b->sign = a->sign != b->sign; + return b; + } + if (iszero (a)) + { + a->sign = a->sign != b->sign; + return a; + } + if (iszero (b)) + { + b->sign = a->sign != b->sign; + return b; + } + + /* Calculate the mantissa by multiplying both 64bit numbers to get a + 128 bit number */ + { + fractype x = a->fraction.ll; + fractype ylow = b->fraction.ll; + fractype yhigh = 0; + int bit; + +#if defined(NO_DI_MODE) + { + /* ??? This does multiplies one bit at a time. Optimize. */ + for (bit = 0; bit < FRAC_NBITS; bit++) + { + int carry; + + if (x & 1) + { + carry = (low += ylow) < ylow; + high += yhigh + carry; + } + yhigh <<= 1; + if (ylow & FRACHIGH) + { + yhigh |= 1; + } + ylow <<= 1; + x >>= 1; + } + } +#elif defined(FLOAT) + { + /* Multiplying two 32 bit numbers to get a 64 bit number on + a machine with DI, so we're safe */ + + DItype answer = (DItype)(a->fraction.ll) * (DItype)(b->fraction.ll); + + high = answer >> 32; + low = answer; + } +#else + /* Doing a 64*64 to 128 */ + { + UDItype nl = a->fraction.ll & 0xffffffff; + UDItype nh = a->fraction.ll >> 32; + UDItype ml = b->fraction.ll & 0xffffffff; + UDItype mh = b->fraction.ll >>32; + UDItype pp_ll = ml * nl; + UDItype pp_hl = mh * nl; + UDItype pp_lh = ml * nh; + UDItype pp_hh = mh * nh; + UDItype res2 = 0; + UDItype res0 = 0; + UDItype ps_hh__ = pp_hl + pp_lh; + if (ps_hh__ < pp_hl) + res2 += 0x100000000LL; + pp_hl = (ps_hh__ << 32) & 0xffffffff00000000LL; + res0 = pp_ll + pp_hl; + if (res0 < pp_ll) + res2++; + res2 += ((ps_hh__ >> 32) & 0xffffffffL) + pp_hh; + high = res2; + low = res0; + } +#endif + } + + tmp->normal_exp = a->normal_exp + b->normal_exp; + tmp->sign = a->sign != b->sign; +#ifdef FLOAT + tmp->normal_exp += 2; /* ??????????????? */ +#else + tmp->normal_exp += 4; /* ??????????????? */ +#endif + while (high >= IMPLICIT_2) + { + tmp->normal_exp++; + if (high & 1) + { + low >>= 1; + low |= FRACHIGH; + } + high >>= 1; + } + while (high < IMPLICIT_1) + { + tmp->normal_exp--; + + high <<= 1; + if (low & FRACHIGH) + high |= 1; + low <<= 1; + } + /* rounding is tricky. if we only round if it won't make us round later. */ +#if 0 + if (low & FRACHIGH2) + { + if (((high & GARDMASK) != GARDMSB) + && (((high + 1) & GARDMASK) == GARDMSB)) + { + /* don't round, it gets done again later. */ + } + else + { + high++; + } + } +#endif + if ((high & GARDMASK) == GARDMSB) + { + if (high & (1 << NGARDS)) + { + /* half way, so round to even */ + high += GARDROUND + 1; + } + else if (low) + { + /* but we really weren't half way */ + high += GARDROUND + 1; + } + } + tmp->fraction.ll = high; + tmp->class = CLASS_NUMBER; + return tmp; +} + +FLO_type +multiply (FLO_type arg_a, FLO_type arg_b) +{ + fp_number_type a; + fp_number_type b; + fp_number_type tmp; + fp_number_type *res; + + unpack_d ((FLO_union_type *) & arg_a, &a); + unpack_d ((FLO_union_type *) & arg_b, &b); + + res = _fpmul_parts (&a, &b, &tmp); + + return pack_d (res); +} + +static fp_number_type * +_fpdiv_parts (fp_number_type * a, + fp_number_type * b, + fp_number_type * tmp) +{ + fractype low = 0; + fractype high = 0; + fractype r0, r1, y0, y1, bit; + fractype q; + fractype numerator; + fractype denominator; + fractype quotient; + fractype remainder; + + if (isnan (a)) + { + return a; + } + if (isnan (b)) + { + return b; + } + if (isinf (a) || iszero (a)) + { + if (a->class == b->class) + return nan (); + return a; + } + a->sign = a->sign ^ b->sign; + + if (isinf (b)) + { + a->fraction.ll = 0; + a->normal_exp = 0; + return a; + } + if (iszero (b)) + { + a->class = CLASS_INFINITY; + return b; + } + + /* Calculate the mantissa by multiplying both 64bit numbers to get a + 128 bit number */ + { + int carry; + intfrac d0, d1; /* weren't unsigned before ??? */ + + /* quotient = + ( numerator / denominator) * 2^(numerator exponent - denominator exponent) + */ + + a->normal_exp = a->normal_exp - b->normal_exp; + numerator = a->fraction.ll; + denominator = b->fraction.ll; + + if (numerator < denominator) + { + /* Fraction will be less than 1.0 */ + numerator *= 2; + a->normal_exp--; + } + bit = IMPLICIT_1; + quotient = 0; + /* ??? Does divide one bit at a time. Optimize. */ + while (bit) + { + if (numerator >= denominator) + { + quotient |= bit; + numerator -= denominator; + } + bit >>= 1; + numerator *= 2; + } + + if ((quotient & GARDMASK) == GARDMSB) + { + if (quotient & (1 << NGARDS)) + { + /* half way, so round to even */ + quotient += GARDROUND + 1; + } + else if (numerator) + { + /* but we really weren't half way, more bits exist */ + quotient += GARDROUND + 1; + } + } + + a->fraction.ll = quotient; + return (a); + } +} + +FLO_type +divide (FLO_type arg_a, FLO_type arg_b) +{ + fp_number_type a; + fp_number_type b; + fp_number_type tmp; + fp_number_type *res; + + unpack_d ((FLO_union_type *) & arg_a, &a); + unpack_d ((FLO_union_type *) & arg_b, &b); + + res = _fpdiv_parts (&a, &b, &tmp); + + return pack_d (res); +} + +/* according to the demo, fpcmp returns a comparison with 0... thus + a<b -> -1 + a==b -> 0 + a>b -> +1 + */ + +static int +_fpcmp_parts (fp_number_type * a, fp_number_type * b) +{ +#if 0 + /* either nan -> unordered. Must be checked outside of this routine. */ + if (isnan (a) && isnan (b)) + { + return 1; /* still unordered! */ + } +#endif + + if (isnan (a) || isnan (b)) + { + return 1; /* how to indicate unordered compare? */ + } + if (isinf (a) && isinf (b)) + { + /* +inf > -inf, but +inf != +inf */ + /* b \a| +inf(0)| -inf(1) + ______\+--------+-------- + +inf(0)| a==b(0)| a<b(-1) + -------+--------+-------- + -inf(1)| a>b(1) | a==b(0) + -------+--------+-------- + So since unordered must be non zero, just line up the columns... + */ + return b->sign - a->sign; + } + /* but not both... */ + if (isinf (a)) + { + return a->sign ? -1 : 1; + } + if (isinf (b)) + { + return b->sign ? 1 : -1; + } + if (iszero (a) && iszero (b)) + { + return 0; + } + if (iszero (a)) + { + return b->sign ? 1 : -1; + } + if (iszero (b)) + { + return a->sign ? -1 : 1; + } + /* now both are "normal". */ + if (a->sign != b->sign) + { + /* opposite signs */ + return a->sign ? -1 : 1; + } + /* same sign; exponents? */ + if (a->normal_exp > b->normal_exp) + { + return a->sign ? -1 : 1; + } + if (a->normal_exp < b->normal_exp) + { + return a->sign ? 1 : -1; + } + /* same exponents; check size. */ + if (a->fraction.ll > b->fraction.ll) + { + return a->sign ? -1 : 1; + } + if (a->fraction.ll < b->fraction.ll) + { + return a->sign ? 1 : -1; + } + /* after all that, they're equal. */ + return 0; +} + +CMPtype +compare (FLO_type arg_a, FLO_type arg_b) +{ + fp_number_type a; + fp_number_type b; + + unpack_d ((FLO_union_type *) & arg_a, &a); + unpack_d ((FLO_union_type *) & arg_b, &b); + + return _fpcmp_parts (&a, &b); +} + +#ifndef US_SOFTWARE_GOFAST + +/* These should be optimized for their specific tasks someday. */ + +CMPtype +_eq_f2 (FLO_type arg_a, FLO_type arg_b) +{ + fp_number_type a; + fp_number_type b; + + unpack_d ((FLO_union_type *) & arg_a, &a); + unpack_d ((FLO_union_type *) & arg_b, &b); + + if (isnan (&a) || isnan (&b)) + return 1; /* false, truth == 0 */ + + return _fpcmp_parts (&a, &b) ; +} + +CMPtype +_ne_f2 (FLO_type arg_a, FLO_type arg_b) +{ + fp_number_type a; + fp_number_type b; + + unpack_d ((FLO_union_type *) & arg_a, &a); + unpack_d ((FLO_union_type *) & arg_b, &b); + + if (isnan (&a) || isnan (&b)) + return 1; /* true, truth != 0 */ + + return _fpcmp_parts (&a, &b) ; +} + +CMPtype +_gt_f2 (FLO_type arg_a, FLO_type arg_b) +{ + fp_number_type a; + fp_number_type b; + + unpack_d ((FLO_union_type *) & arg_a, &a); + unpack_d ((FLO_union_type *) & arg_b, &b); + + if (isnan (&a) || isnan (&b)) + return -1; /* false, truth > 0 */ + + return _fpcmp_parts (&a, &b); +} + +CMPtype +_ge_f2 (FLO_type arg_a, FLO_type arg_b) +{ + fp_number_type a; + fp_number_type b; + + unpack_d ((FLO_union_type *) & arg_a, &a); + unpack_d ((FLO_union_type *) & arg_b, &b); + + if (isnan (&a) || isnan (&b)) + return -1; /* false, truth >= 0 */ + return _fpcmp_parts (&a, &b) ; +} + +CMPtype +_lt_f2 (FLO_type arg_a, FLO_type arg_b) +{ + fp_number_type a; + fp_number_type b; + + unpack_d ((FLO_union_type *) & arg_a, &a); + unpack_d ((FLO_union_type *) & arg_b, &b); + + if (isnan (&a) || isnan (&b)) + return 1; /* false, truth < 0 */ + + return _fpcmp_parts (&a, &b); +} + +CMPtype +_le_f2 (FLO_type arg_a, FLO_type arg_b) +{ + fp_number_type a; + fp_number_type b; + + unpack_d ((FLO_union_type *) & arg_a, &a); + unpack_d ((FLO_union_type *) & arg_b, &b); + + if (isnan (&a) || isnan (&b)) + return 1; /* false, truth <= 0 */ + + return _fpcmp_parts (&a, &b) ; +} + +#endif /* ! US_SOFTWARE_GOFAST */ + +FLO_type +si_to_float (SItype arg_a) +{ + fp_number_type in; + + in.class = CLASS_NUMBER; + in.sign = arg_a < 0; + if (!arg_a) + { + in.class = CLASS_ZERO; + } + else + { + in.normal_exp = FRACBITS + NGARDS; + if (in.sign) + { + /* Special case for minint, since there is no +ve integer + representation for it */ + if (arg_a == 0x80000000) + { + return -2147483648.0; + } + in.fraction.ll = (-arg_a); + } + else + in.fraction.ll = arg_a; + + while (in.fraction.ll < (1LL << (FRACBITS + NGARDS))) + { + in.fraction.ll <<= 1; + in.normal_exp -= 1; + } + } + return pack_d (&in); +} + +SItype +float_to_si (FLO_type arg_a) +{ + fp_number_type a; + SItype tmp; + + unpack_d ((FLO_union_type *) & arg_a, &a); + if (iszero (&a)) + return 0; + if (isnan (&a)) + return 0; + /* get reasonable MAX_SI_INT... */ + if (isinf (&a)) + return a.sign ? MAX_SI_INT : (-MAX_SI_INT)-1; + /* it is a number, but a small one */ + if (a.normal_exp < 0) + return 0; + if (a.normal_exp > 30) + return a.sign ? (-MAX_SI_INT)-1 : MAX_SI_INT; + tmp = a.fraction.ll >> ((FRACBITS + NGARDS) - a.normal_exp); + return a.sign ? (-tmp) : (tmp); +} + +#ifdef US_SOFTWARE_GOFAST +/* While libgcc2.c defines its own __fixunssfsi and __fixunsdfsi routines, + we also define them for GOFAST because the ones in libgcc2.c have the + wrong names and I'd rather define these here and keep GOFAST CYG-LOC's + out of libgcc2.c. We can't define these here if not GOFAST because then + there'd be duplicate copies. */ + +USItype +float_to_usi (FLO_type arg_a) +{ + fp_number_type a; + + unpack_d ((FLO_union_type *) & arg_a, &a); + if (iszero (&a)) + return 0; + if (isnan (&a)) + return 0; + /* get reasonable MAX_USI_INT... */ + if (isinf (&a)) + return a.sign ? MAX_USI_INT : 0; + /* it is a negative number */ + if (a.sign) + return 0; + /* it is a number, but a small one */ + if (a.normal_exp < 0) + return 0; + if (a.normal_exp > 31) + return MAX_USI_INT; + else if (a.normal_exp > (FRACBITS + NGARDS)) + return a.fraction.ll << ((FRACBITS + NGARDS) - a.normal_exp); + else + return a.fraction.ll >> ((FRACBITS + NGARDS) - a.normal_exp); +} +#endif + +FLO_type +negate (FLO_type arg_a) +{ + fp_number_type a; + + unpack_d ((FLO_union_type *) & arg_a, &a); + flip_sign (&a); + return pack_d (&a); +} + +#ifdef FLOAT + +SFtype +__make_fp(fp_class_type class, + unsigned int sign, + int exp, + USItype frac) +{ + fp_number_type in; + + in.class = class; + in.sign = sign; + in.normal_exp = exp; + in.fraction.ll = frac; + return pack_d (&in); +} + +#ifndef FLOAT_ONLY + +/* This enables one to build an fp library that supports float but not double. + Otherwise, we would get an undefined reference to __make_dp. + This is needed for some 8-bit ports that can't handle well values that + are 8-bytes in size, so we just don't support double for them at all. */ + +extern DFtype __make_dp (fp_class_type, unsigned int, int, UDItype frac); + +DFtype +sf_to_df (SFtype arg_a) +{ + fp_number_type in; + + unpack_d ((FLO_union_type *) & arg_a, &in); + return __make_dp (in.class, in.sign, in.normal_exp, + ((UDItype) in.fraction.ll) << F_D_BITOFF); +} + +#endif +#endif + +#ifndef FLOAT + +extern SFtype __make_fp (fp_class_type, unsigned int, int, USItype); + +DFtype +__make_dp (fp_class_type class, unsigned int sign, int exp, UDItype frac) +{ + fp_number_type in; + + in.class = class; + in.sign = sign; + in.normal_exp = exp; + in.fraction.ll = frac; + return pack_d (&in); +} + +SFtype +df_to_sf (DFtype arg_a) +{ + fp_number_type in; + + unpack_d ((FLO_union_type *) & arg_a, &in); + return __make_fp (in.class, in.sign, in.normal_exp, + in.fraction.ll >> F_D_BITOFF); +} + +#endif diff --git a/sim/ppc/events.c b/sim/ppc/events.c new file mode 100644 index 0000000..4b26a6a --- /dev/null +++ b/sim/ppc/events.c @@ -0,0 +1,238 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1995, Andrew Cagney <cagney@highland.com.au> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + */ + + +#ifndef _EVENTS_C_ +#define _EVENTS_C_ + +#ifndef STATIC_INLINE_EVENTS +#define STATIC_INLINE_EVENTS STATIC_INLINE +#endif + + +#include "basics.h" +#include "events.h" + + +/* The event queue maintains a single absolute time using two + variables. + + TIME_OF_EVENT: this holds the time at which the next event is ment + to occure. If no next event it will hold the time of the last + event. The first event occures at time 0 - system start. + + TIME_FROM_EVENT: The current distance from TIME_OF_EVENT. If an + event is pending, this will be positive. If no future event is + pending this will be negative. This variable is decremented once + for each iteration of a clock cycle. + + Clearly there is a bug in that this code assumes that the absolute + time counter will never become greater than 2^62. */ + +typedef struct _event_entry event_entry; +struct _event_entry { + void *data; + event_handler *handler; + signed64 time_of_event; + event_entry *next; +}; + +struct _event_queue { + event_entry *queue; + event_entry *volatile held; + event_entry *volatile *volatile held_end; + signed64 time_of_event; + signed64 time_from_event; +}; + + +INLINE_EVENTS event_queue * +event_queue_create(void) +{ + event_queue *new_event_queue = ZALLOC(event_queue); + + new_event_queue->queue = NULL; + new_event_queue->held = NULL; + new_event_queue->held_end = &new_event_queue->held; + /* both times are already zero */ + return new_event_queue; +} + + + +STATIC_INLINE_EVENTS void +insert_event_entry(event_queue *events, + event_entry *new_event, + signed64 delta) +{ + event_entry *curr; + event_entry **last; + signed64 time_of_event; + + if (delta <= 0) + error("can not schedule event for current time\n"); + + /* compute when the event should occure */ + time_of_event = (events->time_of_event + - events->time_from_event + + delta); + + /* find the queue insertion point - things are time ordered */ + last = &events->queue; + curr = events->queue; + while (curr != NULL && time_of_event >= curr->time_of_event) { + last = &curr->next; + curr = curr->next; + } + + /* insert it */ + new_event->next = curr; + *last = new_event; + new_event->time_of_event = time_of_event; + + /* adjust the time until the first event */ + events->time_from_event = (events->queue->time_of_event + - (events->time_of_event + - events->time_from_event)); + events->time_of_event = events->queue->time_of_event; +} + +INLINE_EVENTS event_entry_tag +event_queue_schedule(event_queue *events, + signed64 delta_time, + event_handler *handler, + void *data) +{ + event_entry *new_event = ZALLOC(event_entry); + new_event->data = data; + new_event->handler = handler; + insert_event_entry(events, new_event, delta_time); + return new_event; +} + + +INLINE_EVENTS event_entry_tag +event_queue_schedule_after_signal(event_queue *events, + signed64 delta_time, + event_handler *handler, + void *data) +{ + event_entry *new_event = ZALLOC(event_entry); + + new_event->data = data; + new_event->handler = handler; + new_event->time_of_event = delta_time; /* work it out later */ + new_event->next = NULL; + + /*-LOCK-*/ + if (events->held == NULL) { + events->held = new_event; + } + else { + *events->held_end = new_event; + } + events->held_end = &new_event->next; + /*-UNLOCK-*/ + + return new_event; +} + + +INLINE_EVENTS void +event_queue_deschedule(event_queue *events, + event_entry_tag event_to_remove) +{ + if (event_to_remove != NULL) { + event_entry *current; + event_entry **ptr_to_current; + for (ptr_to_current = &events->queue, current = *ptr_to_current; + current != NULL && current != event_to_remove; + ptr_to_current = ¤t->next, current = *ptr_to_current); + if (current == event_to_remove) { + *ptr_to_current = current->next; + zfree(current); + /* Just forget to recompute the delay to the next event */ + } + } +} + + + + +INLINE_EVENTS int +event_queue_tick(event_queue *events) +{ + /* remove things from the asynchronous event queue onto the real one */ + if (events->held != NULL) { + event_entry *held_events; + event_entry *curr_event; + + /*-LOCK-*/ + held_events = events->held; + events->held = NULL; + events->held_end = &events->held; + /*-UNLOCK-*/ + + do { + curr_event = held_events; + held_events = curr_event->next; + insert_event_entry(events, curr_event, curr_event->time_of_event); + } while (held_events != NULL); + } + + /* advance time, checking to see if we've reached time zero which + would indicate the time for the next event has arrived */ + events->time_from_event -= 1; + return events->time_from_event == 0; +} + +INLINE_EVENTS void +event_queue_process(event_queue *events) +{ + if (events->time_from_event == 0) { + /* consume all events for this or earlier times */ + do { + event_entry *to_do = events->queue; + events->queue = to_do->next; + to_do->handler(events, + to_do->data); + zfree(to_do); + } while (events->queue != NULL + && events->queue->time_of_event <= events->time_of_event); + /* re-caculate time for new events */ + if (events->queue != NULL) { + events->time_from_event = (events->queue->time_of_event + - events->time_of_event); + events->time_of_event = events->queue->time_of_event; + } + else { + /* nothing to do, time_from_event will go negative */ + } + } +} + +INLINE_EVENTS signed64 +event_queue_time(event_queue *queue) +{ + return queue->time_of_event - queue->time_from_event; +} + + +#endif /* _EVENTS_C_ */ diff --git a/sim/ppc/events.h b/sim/ppc/events.h new file mode 100644 index 0000000..fc81263 --- /dev/null +++ b/sim/ppc/events.h @@ -0,0 +1,75 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1995, Andrew Cagney <cagney@highland.com.au> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + */ + + +#ifndef _EVENTS_H_ +#define _EVENTS_H_ + +#ifndef INLINE_EVENTS +#define INLINE_EVENTS +#endif + + +typedef struct _event_queue event_queue; +typedef void *event_entry_tag; + +typedef void event_handler +(event_queue *queue, + void *data); + +INLINE_EVENTS event_queue *event_queue_create +(void); + + +/* (de)Schedule things to happen in the future. */ + +INLINE_EVENTS event_entry_tag event_queue_schedule +(event_queue *queue, + signed64 delta_time, + event_handler *handler, + void *data); + +INLINE_EVENTS event_entry_tag event_queue_schedule_after_signal +(event_queue *queue, + signed64 delta_time, + event_handler *handler, + void *data); + +INLINE_EVENTS void event_queue_deschedule +(event_queue *queue, + event_entry_tag event_to_remove); + + +/* progress time. In to parts so that if something is pending, the + caller has a chance to save any cached state */ + +INLINE_EVENTS int event_queue_tick +(event_queue *queue); + +INLINE_EVENTS void event_queue_process +(event_queue *events); + + +/* local concept of time */ + +INLINE_EVENTS signed64 event_queue_time +(event_queue *queue); + +#endif /* _EVENTS_H_ */ diff --git a/sim/ppc/gen.c b/sim/ppc/gen.c new file mode 100644 index 0000000..e6e5c16 --- /dev/null +++ b/sim/ppc/gen.c @@ -0,0 +1,3111 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1995, Andrew Cagney <cagney@highland.com.au> + + 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. + + */ + + +/* BUGS: + + Instead of using/abusing macro's the semantic code should be parsed + and each `cachable' expression replaced with the corresponding + value. + + If not expanding fields, invalid_insn has call to its self this is + a little fatal when semantic_invalid() is being called because an + instruction is invalid */ + + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdlib.h> +#include <unistd.h> +#include <getopt.h> +#include <stdio.h> +#include <ctype.h> +#include <stdarg.h> +#include <string.h> + +#include "config.h" + +#undef WITH_ASSERT +#define WITH_ASSERT 1 + +#include "debug.h" + + +/****************************************************************/ + +void +error (char *msg, ...) +{ + va_list ap; + va_start(ap, msg); + vprintf(msg, ap); + va_end(ap); + exit (1); +} + +void * +zmalloc(long size) +{ + void *memory = malloc(size); + if (memory == NULL) + error("zmalloc failed\n"); + bzero(memory, size); + return memory; +} + +void +dumpf (int indent, char *msg, ...) +{ + va_list ap; + for (; indent > 0; indent--) + printf(" "); + va_start(ap, msg); + vprintf(msg, ap); + va_end(ap); +} + + +/****************************************************************/ + +int idecode_expand_semantics = WITH_IDECODE_EXPAND_SEMANTICS; +int idecode_cache = WITH_IDECODE_CACHE; +int spreg_lookup_table = WITH_SPREG_LOOKUP_TABLE; + +/****************************************************************/ + +typedef struct { + int valid; + char *old_name; + char *new_name; + char *type; + char *expression; +} extraction_rules; + +extraction_rules cachable_values[] = WITH_IDECODE_CACHE_RULES; + +/****************************************************************/ + +typedef struct _opcode_rules { + int valid; + int first; + int last; + int force_first; + int force_last; + int force_slash; + char *force_expansion; + int use_switch; + unsigned special_mask; + unsigned special_value; + unsigned special_rule; +} opcode_rules; + +/* FIXME - this should be loaded from a file */ +opcode_rules opcode_table[] = WITH_IDECODE_OPCODE_RULES; + +dump_opcode_rule(opcode_rules *rule, + int indent) +{ + printf("(opcode_rules*)0x%x\n", rule); + dumpf(indent, "(valid %d)\n", rule->valid); + ASSERT(rule != NULL); + if (rule->valid) { + dumpf(indent, "(first %d)\n", rule->first); + dumpf(indent, "(last %d)\n", rule->last); + dumpf(indent, "(force_first %d)\n", rule->force_first); + dumpf(indent, "(force_last %d)\n", rule->force_last); + dumpf(indent, "(force_slash %d)\n", rule->force_slash); + dumpf(indent, "(force_expansion %s)\n", rule->force_expansion); + } +} + + +/****************************************************************/ + +enum gen_constants { + insn_size = 32, + nr_of_sprs = 1024 +}; + +char cache_idecode_formal[] = "cpu *processor,\n instruction_word instruction,\n unsigned_word cia,\n idecode_cache *cache_entry"; +char cache_idecode_actual[] = "processor, instruction, cia, cache_entry"; +char cache_insn_formal[] = "cpu *processor,\n idecode_cache *cache_entry,\n unsigned_word cia"; +char cache_insn_actual[] = "processor, entry, cia"; + +char insn_formal[] = "cpu *processor,\n instruction_word instruction,\n unsigned_word cia"; +char insn_actual[] = "processor, instruction, cia"; + +char insn_local[] = "unsigned_word nia = cia + 4;"; + + +/****************************************************************/ + +int +bin2i(char *bin, int width) +{ + int i = 0; + while (*bin != '\0' && width != 0) { + i = (i << 1) + (*bin - '0'); + width--; + bin++; + } + return i; +} + + +int +it_is(char *flag, + char *flags) +{ + int flag_len = strlen(flag); + while (*flags != '\0') { + if (!strncmp(flags, flag, flag_len) + && (flags[flag_len] == ',' || flags[flag_len] == '\0')) + return 1; + while (*flags != ',') { + if (*flags == '\0') + return 0; + flags++; + } + flags++; + } + return 0; +} + + +/****************************************************************/ + +typedef struct _lf lf; +struct _lf { + FILE *stream; + int line_nr; /* nr complete lines written, curr line is line_nr+1 */ + int indent; + int line_blank; + char *file_name; +}; + + +lf * +lf_open(char *name) +{ + /* create a file object */ + lf *new_lf = zmalloc(sizeof(lf)); + ASSERT(new_lf != NULL); + new_lf->file_name = name; + + /* attach to stdout if pipe */ + if (!strcmp(name, "-")) { + new_lf->stream = stdout; + } + else { + /* create a new file */ + new_lf->stream = fopen(name, "w"); + ASSERT(new_lf->stream != NULL); + } + return new_lf; +} + + +void +lf_close(lf *file) +{ + if (file->stream != stdout) { + if (fclose(file->stream)) { + perror("lf_close.fclose"); + exit(1); + } + free(file); + } +} + + +void +lf_putchr(lf *file, + const char chr) +{ + if (chr == '\n') { + file->line_nr += 1; + file->line_blank = 1; + } + else if (file->line_blank) { + int pad; + for (pad = file->indent; pad > 0; pad--) + putc(' ', file->stream); + file->line_blank = 0; + } + putc(chr, file->stream); +} + +void +lf_indent_suppress(lf *file) +{ + file->line_blank = 0; +} + + +void +lf_putstr(lf *file, + const char *string) +{ + const char *chp; + if (string != NULL) { + for (chp = string; *chp != '\0'; chp++) { + lf_putchr(file, *chp); + } + } +} + +void +do_lf_putunsigned(lf *file, + unsigned u) +{ + if (u > 0) { + do_lf_putunsigned(file, u / 10); + lf_putchr(file, (u % 10) + '0'); + } +} + + +void +lf_putint(lf *file, + int decimal) +{ + if (decimal == 0) + lf_putchr(file, '0'); + else if (decimal < 0) { + lf_putchr(file, '-'); + do_lf_putunsigned(file, -decimal); + } + else if (decimal > 0) { + do_lf_putunsigned(file, decimal); + } + else + ASSERT(0); +} + +void +lf_printf(lf *file, + const char *fmt, + ...) +{ + char buf[1024]; + va_list ap; + int nr_chars; + + va_start(ap, fmt); + vsprintf(buf, fmt, ap); + /* FIXME - this is really stuffed but so is vsprintf() on a sun! */ + ASSERT(strlen(buf) > 0 && strlen(buf) < sizeof(buf)); + lf_putstr(file, buf); + va_end(ap); +} + +void +lf_print_file_line_nr(lf *file) +{ +#if WITH_LINE_NUMBERS + lf_indent_suppress(file); + lf_putstr(file, "#line "); + lf_putint(file, file->line_nr+2); /*line_nr == last_line, want next */ + lf_putstr(file, " \""); + lf_putstr(file, file->file_name); + lf_putstr(file, "\"\n"); +#endif +} + +lf_indent(lf *file, int delta) +{ + file->indent += delta; +} + + +/****************************************************************/ + +/* read entries from ppc.instructions */ + +enum { + file_table_max_fields = 6, +}; + +typedef struct _file_table file_table; +struct _file_table { + size_t size; + char *buffer; + char *pos; + int line_nr; + char *file_name; +}; + +typedef struct _file_table_entry file_table_entry; +struct _file_table_entry { + char *fields[file_table_max_fields]; + int line_nr; + char *file_name; + char *annex; +}; + + +file_table * +file_table_open(char *file_name) +{ + int fd; + struct stat stat_buf; + file_table *file; + + /* create a file descriptor */ + file = (file_table*)zmalloc(sizeof(file_table)); + ASSERT(file != NULL); + + /* save the file name */ + file->file_name = (char*)zmalloc(strlen(file_name) + 1); + ASSERT(file->file_name != NULL); + strcpy(file->file_name, file_name); + + /* open the file */ + fd = open(file->file_name, O_RDONLY, 0); + ASSERT(fd >= 0); + + /* determine the size */ + if (fstat(fd, &stat_buf) < 0) { + perror("file_table_open.fstat"); + exit(1); + } + file->size = stat_buf.st_size; + + /* allocate this much memory */ + file->buffer = (char*)zmalloc(file->size+1); + if(file->buffer == NULL) { + perror("file_table_open.calloc.file->size+1"); + exit(1); + } + file->pos = file->buffer; + + /* read it in */ + if (read(fd, file->buffer, file->size) < file->size) { + perror("file_table_open.read"); + exit(1); + } + file->buffer[file->size] = '\0'; + + /* done */ + close(fd); + return file; +} + + +file_table_entry * +file_table_read(file_table *file) +{ + int field; + file_table_entry *entry; + + /* skip comments/blanks */ + while(1) { + /* leading white space */ + while (*file->pos != '\0' + && *file->pos != '\n' + && isspace(*file->pos)) + file->pos++; + /* comment */ + if (*file->pos == '#') { + do { + file->pos++; + } while (*file->pos != '\0' && *file->pos != '\n'); + } + /* end of line? */ + if (*file->pos == '\n') { + file->pos++; + file->line_nr++; + } + else + break; + } + if (*file->pos == '\0') + return NULL; + + /* create this new entry */ + entry = (file_table_entry*)zmalloc(sizeof(file_table_entry)); + ASSERT(entry != NULL); + entry->file_name = file->file_name; + + /* break the line into its colon delimitered fields */ + for (field = 0; field < file_table_max_fields-1; field++) { + entry->fields[field] = file->pos; + while(*file->pos && *file->pos != ':' && *file->pos != '\n') + file->pos++; + if (*file->pos == ':') { + *file->pos = '\0'; + file->pos++; + } + } + + /* any trailing stuff not the last field */ + ASSERT(field == file_table_max_fields-1); + entry->fields[field] = file->pos; + while (*file->pos && *file->pos != '\n') { + file->pos++; + } + if (*file->pos == '\n') { + *file->pos = '\0'; + file->pos++; + } + file->line_nr++; + entry->line_nr = file->line_nr; + + /* if following lines tab indented, put in the annex */ + if (*file->pos == '\t') { + entry->annex = file->pos; + do { + do { + file->pos++; + } while (*file->pos != '\0' && *file->pos != '\n'); + if (*file->pos == '\n') { + file->pos++; + file->line_nr++; + } + } while (*file->pos != '\0' && *file->pos == '\t'); + if (file->pos[-1] == '\n') + file->pos[-1] = '\0'; + } + else + entry->annex = NULL; + + /* return it */ + return entry; + +} + + +void +dump_file_table_entry(file_table_entry *entry, + int indent) +{ + printf("(file_table_entry*)0x%x\n", entry); + + if (entry != NULL) { + int field; + char sep; + + sep = ' '; + dumpf(indent, "(fields"); + for (field = 0; field < file_table_max_fields; field++) { + printf("%c%s", sep, entry->fields[field]); + sep = ':'; + } + printf(")\n"); + + dumpf(indent, "(line_nr %d)\n", entry->line_nr); + + dumpf(indent, "(file_name %s)\n", entry->file_name); + + dumpf(indent, "(annex\n%s\n", entry->annex); + dumpf(indent, " )\n"); + + } +} + + +/****************************************************************/ + +typedef struct _insn_field insn_field; +struct _insn_field { + int first; + int last; + int width; + int is_int; + int is_slash; + int is_string; + int val_int; + char *pos_string; + char *val_string; + insn_field *next; + insn_field *prev; +}; + +typedef struct _insn_fields insn_fields; +struct _insn_fields { + insn_field *bits[insn_size]; + insn_field *first; + insn_field *last; + unsigned value; +}; + +insn_field * +insn_field_new() +{ + insn_field *new_field = (insn_field*)zmalloc(sizeof(insn_field)); + ASSERT(new_field != NULL); + return new_field; +} + +insn_fields * +insn_fields_new() +{ + insn_fields *new_fields = (insn_fields*)zmalloc(sizeof(insn_fields)); + ASSERT(new_fields != NULL); + return new_fields; +} + + +insn_fields * +parse_insn_format(file_table_entry *entry, + char *format) +{ + char *chp; + insn_fields *fields = insn_fields_new(); + + /* create a leading sentinal */ + fields->first = insn_field_new(); + fields->first->first = -1; + fields->first->last = -1; + fields->first->width = 0; + + /* and a trailing sentinal */ + fields->last = insn_field_new(); + fields->last->first = insn_size; + fields->last->last = insn_size; + fields->last->width = 0; + + /* link them together */ + fields->first->next = fields->last; + fields->last->prev = fields->first; + + /* now work through the formats */ + chp = format; + + while (*chp != '\0') { + char *start_pos; + char *start_val; + int strlen_val; + int strlen_pos; + insn_field *new_field; + + /* sanity check */ + if (!isdigit(*chp)) { + fprintf(stderr, "%s:%d: missing position field at %s\n", + entry->file_name, entry->line_nr, chp); + break; + } + + /* break out the bit position */ + start_pos = chp; + while (isdigit(*chp)) + chp++; + strlen_pos = chp - start_pos; + if (*chp == '.' && strlen_pos > 0) + chp++; + else { + fprintf(stderr, "%s:%d: missing field value at %s\n", + entry->file_name, entry->line_nr, chp); + break; + } + + /* break out the value */ + start_val = chp; + while (*start_val == '/' && *chp == '/' + || isdigit(*start_val) && isdigit(*chp) + || isalpha(*start_val) && (isalnum(*chp) || *chp == '_')) + chp++; + strlen_val = chp - start_val; + if (*chp == ',') + chp++; + else if (*chp != '\0' || strlen_val == 0) { + fprintf(stderr, "%s:%d: missing field terminator at %s\n", + entry->file_name, entry->line_nr, chp); + break; + } + + /* create a new field and insert it */ + new_field = insn_field_new(); + new_field->next = fields->last; + new_field->prev = fields->last->prev; + new_field->next->prev = new_field; + new_field->prev->next = new_field; + + /* the value */ + new_field->val_string = (char*)zmalloc(strlen_val+1); + strncpy(new_field->val_string, start_val, strlen_val); + if (isdigit(*new_field->val_string)) { + new_field->val_int = atoi(new_field->val_string); + new_field->is_int = 1; + } + else if (new_field->val_string[0] == '/') { + new_field->is_slash = 1; + } + else { + new_field->is_string = 1; + } + + /* the pos */ + new_field->pos_string = (char*)zmalloc(strlen_pos+1); + strncpy(new_field->pos_string, start_pos, strlen_pos); + new_field->first = atoi(new_field->pos_string); + new_field->last = new_field->next->first - 1; /* guess */ + new_field->width = new_field->last - new_field->first + 1; /* guess */ + new_field->prev->last = new_field->first-1; /*fix*/ + new_field->prev->width = new_field->first - new_field->prev->first; /*fix*/ + } + + /* fiddle first/last so that the sentinals `disapear' */ + ASSERT(fields->first->last < 0); + ASSERT(fields->last->first >= insn_size); + fields->first = fields->first->next; + fields->last = fields->last->prev; + + /* now go over this again, pointing each bit position at a field + record */ + { + int i; + insn_field *field; + field = fields->first; + for (i = 0; i < insn_size; i++) { + while (field->last < i) + field = field->next; + fields->bits[i] = field; + } + } + + /* go over each of the fields, and compute a `value' for the insn */ + { + insn_field *field; + fields->value = 0; + for (field = fields->first; + field->last < insn_size; + field = field->next) { + fields->value <<= field->width; + if (field->is_int) + fields->value |= field->val_int; + } + } + return fields; +} + + +typedef enum { + field_constant_int = 1, + field_constant_slash = 2, + field_constant_string = 3 +} constant_field_types; + + +int +insn_field_is_constant(insn_field *field, + opcode_rules *rule) +{ + /* field is an integer */ + if (field->is_int) + return field_constant_int; + /* field is `/' and treating that as a constant */ + if (field->is_slash && rule->force_slash) + return field_constant_slash; + /* field, though variable is on the list */ + if (field->is_string && rule->force_expansion != NULL) { + char *forced_fields = rule->force_expansion; + while (*forced_fields != '\0') { + int field_len; + char *end = strchr(forced_fields, ','); + if (end == NULL) + field_len = strlen(forced_fields); + else + field_len = end-forced_fields; + if (strncmp(forced_fields, field->val_string, field_len) == 0 + && field->val_string[field_len] == '\0') + return field_constant_string; + forced_fields += field_len; + if (*forced_fields == ',') + forced_fields++; + } + } + return 0; +} + + +void +dump_insn_field(insn_field *field, + int indent) +{ + + printf("(insn_field*)0x%x\n", field); + + dumpf(indent, "(first %d)\n", field->first); + + dumpf(indent, "(last %d)\n", field->last); + + dumpf(indent, "(width %d)\n", field->width); + + if (field->is_int) + dumpf(indent, "(is_int %d)\n", field->val_int); + + if (field->is_slash) + dumpf(indent, "(is_slash)\n"); + + if (field->is_string) + dumpf(indent, "(is_string `%s')\n", field->val_string); + + dumpf(indent, "(next 0x%x)\n", field->next); + + dumpf(indent, "(prev 0x%x)\n", field->prev); + + +} + +void +dump_insn_fields(insn_fields *fields, + int indent) +{ + insn_field *field; + int i; + + printf("(insn_fields*)0x%x\n", fields); + + dumpf(indent, "(first 0x%x)\n", fields->first); + dumpf(indent, "(last 0x%x)\n", fields->last); + + dumpf(indent, "(value 0x%x)\n", fields->value); + + for (i = 0; i < insn_size; i++) { + dumpf(indent, "(bits[%d] ", i, fields->bits[i]); + dump_insn_field(fields->bits[i], indent+1); + dumpf(indent, " )\n"); + } + +} + + +/****************************************************************/ + +typedef struct _opcode_field opcode_field; +struct _opcode_field { + int first; + int last; + int is_boolean; + opcode_field *parent; +}; + +opcode_field * +opcode_field_new() +{ + opcode_field *new_field = (opcode_field*)zmalloc(sizeof(opcode_field)); + ASSERT(new_field != NULL); + new_field->first = insn_size; + new_field->last = -1; + return new_field; +} + +void +dump_opcode_field(opcode_field *field, int indent, int levels) +{ + printf("(opcode_field*)0x%x\n", field); + if (levels && field != NULL) { + dumpf(indent, "(first %d)\n", field->first); + dumpf(indent, "(last %d)\n", field->last); + dumpf(indent, "(is_boolean %d)\n", field->is_boolean); + dumpf(indent, "(parent "); + dump_opcode_field(field->parent, indent, levels-1); + } +} + + +/****************************************************************/ + +typedef struct _insn_bits insn_bits; +struct _insn_bits { + int is_expanded; + int value; + insn_field *field; + opcode_field *opcode; + insn_bits *last; +}; + +insn_bits * +insn_bits_new() +{ + insn_bits *new_bits = (insn_bits*)zmalloc(sizeof(insn_bits)); + ASSERT(new_bits); + return new_bits; +} + + +void +dump_insn_bits(insn_bits *bits, int indent, int levels) +{ + printf("(insn_bits*)0x%x\n", bits); + + if (levels && bits != NULL) { + dumpf(indent, "(value %d)\n", bits->value); + dumpf(indent, "(opcode "); + dump_opcode_field(bits->opcode, indent+1, 0); + dumpf(indent, " )\n"); + dumpf(indent, "(field "); + dump_insn_field(bits->field, indent+1); + dumpf(indent, " )\n"); + dumpf(indent, "(last "); + dump_insn_bits(bits->last, indent+1, levels-1); + } +} + + +/****************************************************************/ + + +typedef enum { + insn_format, + insn_form, + insn_flags, + insn_nmemonic, + insn_name, + insn_comment, + nr_insn_table_fields = file_table_max_fields +} insn_table_fields; +char *insn_field_name[] = { + "format", "form", "flags", "nmemonic", "name", "comments" +}; + +typedef struct _insn insn; +struct _insn { + file_table_entry *file_entry; + insn_fields *fields; + insn *next; +}; + +typedef struct _insn_table insn_table; +struct _insn_table { + int opcode_nr; + insn_bits *expanded_bits; + int nr_insn; + insn *insns; + opcode_rules *opcode_rule; + opcode_field *opcode; + int nr_entries; + insn_table *entries; + insn_table *sibling; + insn_table *parent; +}; + + + +insn * +insn_new() +{ + insn *new_entry = ((insn*) zmalloc(sizeof(insn))); + ASSERT(new_entry != NULL); + return new_entry; +} + +insn_table * +insn_table_new() +{ + insn_table *new_table = (insn_table*)zmalloc(sizeof(insn_table)); + ASSERT(new_table != NULL); + return new_table; +} + + +void +insn_table_insert_insn(insn_table *table, + file_table_entry *file_entry, + insn_fields *fields) +{ + insn **ptr_to_cur_insn = &table->insns; + insn *cur_insn = *ptr_to_cur_insn; + + /* create a new instruction */ + insn *new_insn = insn_new(); + new_insn->file_entry = file_entry; + new_insn->fields = fields; + + /* insert it according to the order of the fields */ + while (cur_insn != NULL + && new_insn->fields->value >= cur_insn->fields->value) { + ptr_to_cur_insn = &cur_insn->next; + cur_insn = *ptr_to_cur_insn; + } + + new_insn->next = cur_insn; + *ptr_to_cur_insn = new_insn; + + table->nr_insn++; +} + + +opcode_field * +insn_table_find_opcode_field(insn *insns, + opcode_rules *rule, + int string_only) +{ + opcode_field *curr_opcode = opcode_field_new(); + insn *entry; + + ASSERT(rule->valid); + + for (entry = insns; entry != NULL; entry = entry->next) { + insn_fields *fields = entry->fields; + opcode_field new_opcode; + + /* find a start point for the opcode field */ + new_opcode.first = rule->first; + while (new_opcode.first <= rule->last + && (!string_only + || insn_field_is_constant(fields->bits[new_opcode.first], + rule) != field_constant_string) + && (string_only + || !insn_field_is_constant(fields->bits[new_opcode.first], + rule))) + new_opcode.first = fields->bits[new_opcode.first]->last + 1; + ASSERT(new_opcode.first > rule->last + || (string_only + && insn_field_is_constant(fields->bits[new_opcode.first], + rule) == field_constant_string) + || (!string_only + && insn_field_is_constant(fields->bits[new_opcode.first], + rule))); + + /* find the end point for the opcode field */ + new_opcode.last = rule->last; + while (new_opcode.last >= rule->first + && (!string_only + || insn_field_is_constant(fields->bits[new_opcode.last], + rule) != field_constant_string) + && (string_only + || !insn_field_is_constant(fields->bits[new_opcode.last], + rule))) + new_opcode.last = fields->bits[new_opcode.last]->first - 1; + ASSERT(new_opcode.last < rule->first + || (string_only + && insn_field_is_constant(fields->bits[new_opcode.last], + rule) == field_constant_string) + || (!string_only + && insn_field_is_constant(fields->bits[new_opcode.last], + rule))); + + /* now see if our current opcode needs expanding */ + if (new_opcode.first <= rule->last + && curr_opcode->first > new_opcode.first) + curr_opcode->first = new_opcode.first; + if (new_opcode.last >= rule->first + && curr_opcode->last < new_opcode.last) + curr_opcode->last = new_opcode.last; + + } + + /* was any thing interesting found? */ + if (curr_opcode->first > rule->last) { + ASSERT(curr_opcode->last < rule->first); + free(curr_opcode); + return NULL; + } + ASSERT(curr_opcode->last >= rule->first); + ASSERT(curr_opcode->first <= rule->last); + + /* if something was found, check it includes the forced field range */ + if (!string_only + && curr_opcode->first > rule->force_first) { + curr_opcode->first = rule->force_first; + } + if (!string_only + && curr_opcode->last < rule->force_last) { + curr_opcode->last = rule->force_last; + } + /* handle special case elminating any need to do shift after mask */ + if (string_only + && rule->force_last == insn_size-1) { + curr_opcode->last = insn_size-1; + } + + /* handle any special cases */ + switch (rule->special_rule) { + case 0: /* let the above apply */ + break; + case 1: /* expand a limited nr of bits, ignoring the rest */ + curr_opcode->first = rule->force_first; + curr_opcode->last = rule->force_last; + break; + case 2: /* boolean field */ + curr_opcode->is_boolean = 1; + break; + } + + return curr_opcode; +} + + +void +insn_table_insert_expanded(insn_table *table, + insn *old_insn, + int new_opcode_nr, + insn_bits *new_bits) +{ + insn_table **ptr_to_cur_entry = &table->entries; + insn_table *cur_entry = *ptr_to_cur_entry; + + /* find the new table for this entry */ + while (cur_entry != NULL + && cur_entry->opcode_nr < new_opcode_nr) { + ptr_to_cur_entry = &cur_entry->sibling; + cur_entry = *ptr_to_cur_entry; + } + + if (cur_entry == NULL || cur_entry->opcode_nr != new_opcode_nr) { + insn_table *new_entry = insn_table_new(); + new_entry->opcode_nr = new_opcode_nr; + new_entry->expanded_bits = new_bits; + new_entry->opcode_rule = table->opcode_rule+1; + new_entry->sibling = cur_entry; + new_entry->parent = table; + *ptr_to_cur_entry = new_entry; + cur_entry = new_entry; + table->nr_entries++; + } + /* ASSERT new_bits == cur_entry bits */ + ASSERT(cur_entry != NULL && cur_entry->opcode_nr == new_opcode_nr); + insn_table_insert_insn(cur_entry, + old_insn->file_entry, + old_insn->fields); +} + +void +insn_table_expand_opcode(insn_table *table, + insn *instruction, + int field_nr, + int opcode_nr, + insn_bits *bits) +{ + + if (field_nr > table->opcode->last) { + insn_table_insert_expanded(table, instruction, opcode_nr, bits); + } + else { + insn_field *field = instruction->fields->bits[field_nr]; + if (field->is_int || field->is_slash) { + ASSERT(field->first >= table->opcode->first + && field->last <= table->opcode->last); + insn_table_expand_opcode(table, instruction, field->last+1, + ((opcode_nr << field->width) + field->val_int), + bits); + } + else { + int val; + int last_pos = ((field->last < table->opcode->last) + ? field->last : table->opcode->last); + int first_pos = ((field->first > table->opcode->first) + ? field->first : table->opcode->first); + int width = last_pos - first_pos + 1; + int last_val = (table->opcode->is_boolean + ? 2 : (1 << width)); + for (val = 0; val < last_val; val++) { + insn_bits *new_bits = insn_bits_new(); + new_bits->field = field; + new_bits->value = val; + new_bits->last = bits; + new_bits->opcode = table->opcode; + insn_table_expand_opcode(table, instruction, last_pos+1, + ((opcode_nr << width) | val), + new_bits); + } + } + } +} + +insn_table_insert_expanding(insn_table *table, + insn *entry) +{ + insn_table_expand_opcode(table, + entry, + table->opcode->first, + 0, + table->expanded_bits); +} + + +void +insn_table_expand_insns(insn_table *table) +{ + + ASSERT(table->nr_insn >= 1); + + /* determine a valid opcode */ + while (table->opcode_rule->valid) { + /* specials only for single instructions */ + if ((table->nr_insn > 1 + && table->opcode_rule->special_mask == 0 + && table->opcode_rule->special_rule == 0) + || (table->nr_insn == 1 + && table->opcode_rule->special_mask != 0 + && ((table->insns->fields->value + & table->opcode_rule->special_mask) + == table->opcode_rule->special_value)) + || (idecode_expand_semantics + && table->opcode_rule->special_mask == 0 + && table->opcode_rule->special_rule == 0)) + table->opcode = + insn_table_find_opcode_field(table->insns, + table->opcode_rule, + table->nr_insn == 1/*string*/ + ); + if (table->opcode != NULL) + break; + table->opcode_rule++; + } + + /* did we find anything */ + if (table->opcode == NULL) { + return; + } + ASSERT(table->opcode != NULL); + + /* back link what we found to its parent */ + if (table->parent != NULL) { + ASSERT(table->parent->opcode != NULL); + table->opcode->parent = table->parent->opcode; + } + + /* expand the raw instructions according to the opcode */ + { + insn *entry; + for (entry = table->insns; entry != NULL; entry = entry->next) { + insn_table_insert_expanding(table, entry); + } + } + + /* and do the same for the sub entries */ + { + insn_table *entry; + for (entry = table->entries; entry != NULL; entry = entry->sibling) { + insn_table_expand_insns(entry); + } + } +} + + + +insn_table * +insn_table_load_insns(char *file_name) +{ + file_table *file = file_table_open(file_name); + insn_table *table = insn_table_new(); + file_table_entry *file_entry; + table->opcode_rule = opcode_table; + + while (file_entry = file_table_read(file)) { + insn_fields *fields; + /* skip instructions that aren't relevant to the mode */ + if (it_is("64", file_entry->fields[insn_flags]) && !WITH_64BIT_TARGET + || it_is("32", file_entry->fields[insn_flags]) && WITH_64BIT_TARGET) + continue; + /* create/insert the new instruction */ + fields = parse_insn_format(file_entry, file_entry->fields[insn_format]); + insn_table_insert_insn(table, file_entry, fields); + } + + return table; +} + + +void +dump_insn(insn *entry, int indent, int levels) +{ + printf("(insn*)0x%x\n", entry); + + if (levels && entry != NULL) { + + dumpf(indent, "(file_entry "); + dump_file_table_entry(entry->file_entry, indent+1); + dumpf(indent, " )\n"); + + dumpf(indent, "(fields "); + dump_insn_fields(entry->fields, indent+1); + dumpf(indent, " )\n"); + + dumpf(indent, "(next "); + dump_insn(entry->next, indent+1, levels-1); + dumpf(indent, " )\n"); + + } + +} + + +void +dump_insn_table(insn_table *table, + int indent, int levels) +{ + + printf("(insn_table*)0x%x\n", table); + + if (levels && table != NULL) { + insn *entry; + + dumpf(indent, "(opcode_nr %d)\n", table->opcode_nr); + + dumpf(indent, "(expanded_bits "); + dump_insn_bits(table->expanded_bits, indent+1, -1); + dumpf(indent, " )\n"); + + dumpf(indent, "(int nr_insn %d)\n", table->nr_insn); + + dumpf(indent, "(insns "); + dump_insn(table->insns, indent+1, table->nr_insn); + dumpf(indent, " )\n"); + + dumpf(indent, "(opcode_rule "); + dump_opcode_rule(table->opcode_rule, indent+1); + dumpf(indent, " )\n"); + + dumpf(indent, "(opcode "); + dump_opcode_field(table->opcode, indent+1, 1); + dumpf(indent, " )\n"); + + dumpf(indent, "(nr_entries %d)\n", table->entries); + dumpf(indent, "(entries "); + dump_insn_table(table->entries, indent+1, table->nr_entries); + dumpf(indent, " )\n"); + + dumpf(indent, "(sibling ", table->sibling); + dump_insn_table(table->sibling, indent+1, levels-1); + dumpf(indent, " )\n"); + + dumpf(indent, "(parent ", table->parent); + dump_insn_table(table->parent, indent+1, 0); + dumpf(indent, " )\n"); + + } +} + + +/****************************************************************/ + + +void +lf_print_copyleft(lf *file) +{ + lf_putstr(file, "\ +/* This file is part of psim (model of the PowerPC(tm) architecture) + + Copyright (C) 1994-1995, Andrew Cagney <cagney@highland.com.au> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + -- + + PowerPC is a trademark of International Business Machines Corporation. + + -- + + This file was generated by the program gen */ +"); +} + + +void +lf_print_c_line_nr(lf *file, file_table_entry *entry) +{ +#if WITH_LINE_NUMBERS + lf_indent_suppress(file); + lf_putstr(file, "#line "); + lf_putint(file, entry->line_nr); + lf_putstr(file, " \""); + lf_putstr(file, entry->file_name); + lf_putstr(file, "\"\n"); +#endif +} + + +void +lf_print_c_code(lf *file, char *code) +{ + char *chp = code; + int in_bit_field = 0; + while (*chp != '\0') { + if (*chp == '\t') + chp++; + if (*chp == '#') + lf_indent_suppress(file); + while (*chp != '\0' && *chp != '\n') { + if (chp[0] == '{' && !isspace(chp[1])) { + in_bit_field = 1; + lf_putchr(file, '_'); + } + else if (in_bit_field && chp[0] == ':') { + lf_putchr(file, '_'); + } + else if (in_bit_field && *chp == '}') { + lf_putchr(file, '_'); + in_bit_field = 0; + } + else { + lf_putchr(file, *chp); + } + chp++; + } + if (in_bit_field) + error("bit field paren miss match some where\n"); + if (*chp == '\n') { + lf_putchr(file, '\n'); + chp++; + } + } + lf_putchr(file, '\n'); +} + + +void +lf_print_binary(lf *file, int decimal, int width) +{ + int bit; + ASSERT(width > 0); + + for (bit = 1 << (width-1); bit != 0; bit >>= 1) { + if (decimal & bit) + lf_putchr(file, '1'); + else + lf_putchr(file, '0'); + } + +} + + +void +lf_print_insn_bits(lf *file, insn_bits *bits) +{ + if (bits == NULL) + return; + lf_print_insn_bits(file, bits->last); + lf_putchr(file, '_'); + lf_putstr(file, bits->field->val_string); + if (!bits->opcode->is_boolean || bits->value == 0) { + if (bits->opcode->last < bits->field->last) + lf_putint(file, bits->value << (bits->field->last - bits->opcode->last)); + else + lf_putint(file, bits->value); + } +} + +void +lf_print_opcodes(lf *file, + insn_table *table) +{ + if (table != NULL) { + while (1) { + lf_printf(file, "_%d_%d", + table->opcode->first, + table->opcode->last); + if (table->parent == NULL) break; + lf_printf(file, "__%d", table->opcode_nr); + table = table->parent; + } + } +} + +void +lf_print_table_name(lf *file, + insn_table *table) +{ + lf_printf(file, "idecode_table"); + lf_print_opcodes(file, table); +} + + + +typedef enum { + function_name_prefix_semantics, + function_name_prefix_idecode, + function_name_prefix_none +} lf_function_name_prefixes; + +void +lf_print_function_name(lf *file, + insn *instruction, + insn_bits *expanded_bits, + lf_function_name_prefixes prefix) +{ + /* the prefix */ + switch (prefix) { + case function_name_prefix_semantics: + lf_putstr(file, "semantic_"); + break; + case function_name_prefix_idecode: + lf_printf(file, "idecode_"); + break; + default: + break; + } + + /* the function name */ + { + char *pos; + for (pos = instruction->file_entry->fields[insn_name]; + *pos != '\0'; + pos++) { + switch (*pos) { + case '/': + case '-': + break; + case ' ': + lf_putchr(file, '_'); + break; + default: + lf_putchr(file, *pos); + break; + } + } + } + + /* the suffix */ + if (idecode_expand_semantics) + lf_print_insn_bits(file, expanded_bits); +} + + +void +lf_print_idecode_table(lf *file, + insn_table *entry) +{ + int can_assume_leaf; + int rule; + + /* have a look at the rule table, if all table rules follow all + switch rules, I can assume that all end points are leaves */ + rule = 0; + while (opcode_table[rule].valid + && opcode_table[rule].use_switch) + rule++; + while (opcode_table[rule].valid + && !opcode_table[rule].use_switch + && !opcode_table[rule].special_rule) + rule++; + can_assume_leaf = !opcode_table[rule].valid; + + lf_printf(file, "{\n"); + lf_indent(file, +2); + { + lf_printf(file, "idecode_table_entry *table = "); + lf_print_table_name(file, entry); + lf_printf(file, ";\n"); + lf_printf(file, "int opcode = EXTRACTED32(instruction, %d, %d);\n", + entry->opcode->first, entry->opcode->last); + lf_printf(file, "idecode_table_entry *table_entry = table + opcode;\n"); + lf_printf(file, "while (1) {\n"); + lf_indent(file, +2); + { + lf_printf(file, "while (table_entry->mask != 0) {\n"); + lf_indent(file, +2); + { + lf_printf(file, "table = ((idecode_table_entry*)\n"); + lf_printf(file, " table_entry->function_or_table);\n"); + lf_printf(file, "opcode = ((instruction & table_entry->mask)\n"); + lf_printf(file, " >> table_entry->shift);\n"); + lf_printf(file, "table_entry = table + opcode;\n"); + } + lf_indent(file, -2); + lf_printf(file, "}\n"); + if (!idecode_cache && can_assume_leaf) { + lf_printf(file, "return (((idecode_semantic*)\n"); + lf_printf(file, " table_entry->function_or_table)\n"); + lf_printf(file, " (%s));\n", insn_actual); + } + else if (!idecode_cache && !can_assume_leaf) { + lf_printf(file, "if (table_entry->shift == 0)"); + lf_printf(file, " return (((idecode_semantic*)\n"); + lf_printf(file, " table_entry->function_or_table)\n"); + lf_printf(file, " (%s));\n", insn_actual); + } + else if (idecode_cache == 1 && can_assume_leaf) { + lf_printf(file, "ASSERT(!entry->shift);\n"); + lf_printf(file, "return ((idecode_semantic*)\n"); + lf_printf(file, " table_entry->function_or_table);\n"); + } + else if (idecode_cache == 1 && !can_assume_leaf) { + lf_printf(file, "if (table_entry->shift == 0)\n"); + lf_printf(file, " return ((idecode_semantic*)\n"); + lf_printf(file, " table_entry->function_or_table);\n"); + lf_printf(file, "else if (table_entry->shift == -1)\n"); + lf_printf(file, " return (((idecode_crack*)\n"); + lf_printf(file, " table_entry->function_or_table)\n"); + lf_printf(file, " (%s));\n", insn_actual); + } + else { + lf_printf(file, "if (table_entry->shift == 0)\n"); + lf_printf(file, " return (((idecode_crack*)\n"); + lf_printf(file, " table_entry->function_or_table)\n"); + lf_printf(file, " (%s));\n", cache_idecode_actual); + } + if (!can_assume_leaf) { + lf_printf(file, "opcode = (instruction & table_entry->shift) != 0;\n"); + lf_printf(file, "table = ((idecode_table_entry*)\n"); + lf_printf(file, " table_entry->function_or_table);\n"); + lf_printf(file, "table_entry = table + opcode;\n"); + } + } + lf_indent(file, -2); + lf_printf(file, "}\n"); + } + lf_indent(file, -2); + lf_printf(file, "}\n"); +} + + +void +lf_print_my_prefix(lf *file, + file_table_entry *file_entry) +{ + lf_printf(file, "const char *const my_prefix = \n"); + lf_printf(file, " \"%s:%s:%d:cache\";\n", + file_entry->file_name, + file_entry->fields[insn_name], + file_entry->line_nr); +} + + +void +lf_print_ptrace(lf *file) +{ + lf_printf(file, "\n"); + lf_putstr(file, "ITRACE(trace_semantics, (\"cia=0x%x\\n\", cia));\n"); +} + + +/****************************************************************/ + +typedef void leaf_handler +(insn_table *entry, + void *data, + int depth); +typedef void padding_handler +(insn_table *table, + void *data, + int depth, + int opcode_nr); + + +void +insn_table_traverse_tree(insn_table *table, + void *data, + int depth, + leaf_handler *start, + leaf_handler *leaf, + leaf_handler *end, + padding_handler *padding) +{ + insn_table *entry; + int entry_nr; + + ASSERT(table != NULL + && table->opcode != NULL + && table->nr_entries > 0 + && table->entries != 0); + + if (start != NULL && depth >= 0) + start(table, data, depth); + + for (entry_nr = 0, entry = table->entries; + entry_nr < (table->opcode->is_boolean + ? 2 + : (1 << (table->opcode->last - table->opcode->first + 1))); + entry_nr ++) { + if (entry == NULL + || (!table->opcode->is_boolean + && entry_nr < entry->opcode_nr)) { + if (padding != NULL && depth >= 0) + padding(table, data, depth, entry_nr); + } + else { + ASSERT(entry != NULL && (entry->opcode_nr == entry_nr + || table->opcode->is_boolean)); + if (entry->opcode != NULL && depth != 0) { + insn_table_traverse_tree(entry, data, depth+1, + start, leaf, end, padding); + } + else if (depth >= 0) { + if (leaf != NULL) + leaf(entry, data, depth); + } + entry = entry->sibling; + } + } + if (end != NULL && depth >= 0) + end(table, data, depth); +} + + +typedef void insn_handler +(insn_table *table, + void *data, + insn *instruction); + +void +insn_table_traverse_insn(insn_table *table, + void *data, + insn_handler *leaf) +{ + insn *instruction; + for (instruction = table->insns; + instruction != NULL; + instruction = instruction->next) { + leaf(table, data, instruction); + } +} + + +void +update_depth(insn_table *entry, + void *data, + int depth) +{ + int *max_depth = (int*)data; + if (*max_depth < depth) + *max_depth = depth; +} + + +int +insn_table_depth(insn_table *table) +{ + int depth = 0; + insn_table_traverse_tree(table, + &depth, + 1, + NULL, /*start*/ + update_depth, + NULL, /*end*/ + NULL); /*padding*/ + return depth; +} + + +/****************************************************************/ + +void +dump_traverse_start(insn_table *table, + void *data, + int depth) +{ + dumpf(depth*2, "(%d\n", table->opcode_nr); +} + +void +dump_traverse_leaf(insn_table *entry, + void *data, + int depth) +{ + ASSERT(entry->nr_entries == 0 + && entry->nr_insn == 1 + && entry->opcode == NULL); + dumpf(depth*2, ".%d %s\n", entry->opcode_nr, + entry->insns->file_entry->fields[insn_format]); +} + +void +dump_traverse_end(insn_table *table, + void *data, + int depth) +{ + dumpf(depth*2, ")\n"); +} + +void +dump_traverse_padding(insn_table *table, + void *data, + int depth, + int opcode_nr) +{ + dumpf(depth*2, ".<%d>\n", opcode_nr); +} + + +void +dump_traverse(insn_table *table) +{ + insn_table_traverse_tree(table, NULL, 1, + dump_traverse_start, + dump_traverse_leaf, + dump_traverse_end, + dump_traverse_padding); +} + + +/****************************************************************/ + + +void +semantics_h_print_function(lf *file, + insn *instruction, + insn_bits *expanded_bits) +{ + lf_printf(file, "\n"); + lf_printf(file, "INLINE_SEMANTICS unsigned_word "); + lf_print_function_name(file, + instruction, + expanded_bits, + function_name_prefix_semantics); + lf_printf(file, "\n(%s);\n", + idecode_cache > 1 ? cache_insn_formal : insn_formal); +} + + +void +semantics_h_leaf(insn_table *entry, + void *data, + int depth) +{ + lf *file = (lf*)data; + ASSERT(entry->nr_insn == 1); + semantics_h_print_function(file, entry->insns, entry->expanded_bits); +} + +void +semantics_h_insn(insn_table *entry, + void *data, + insn *instruction) +{ + lf *file = (lf*)data; + semantics_h_print_function(file, instruction, NULL); +} + + +void +gen_semantics_h(insn_table *table, lf *file) +{ + + lf_print_copyleft(file); + lf_printf(file, "\n"); + lf_printf(file, "#ifndef _SEMANTICS_H_\n"); + lf_printf(file, "#define _SEMANTICS_H_\n"); + lf_printf(file, "\n"); + lf_printf(file, "#ifndef INLINE_SEMANTICS\n"); + lf_printf(file, "#define INLINE_SEMANTICS\n"); + lf_printf(file, "#endif\n"); + lf_printf(file, "\n"); + lf_printf(file, "\n"); + + if (idecode_expand_semantics) + insn_table_traverse_tree(table, + file, + 1, + NULL, /* start */ + semantics_h_leaf, /* leaf */ + NULL, /* end */ + NULL); /* padding */ + else + insn_table_traverse_insn(table, + file, + semantics_h_insn); + + lf_printf(file, "\n"); + lf_printf(file, "#endif /* _SEMANTICS_H_ */\n"); + +} + +/****************************************************************/ + +typedef struct _icache_tree icache_tree; +struct _icache_tree { + char *name; + icache_tree *next; + icache_tree *children; +}; + +icache_tree * +icache_tree_new() +{ + icache_tree *new_tree = (icache_tree*)zmalloc(sizeof(icache_tree)); + ASSERT(new_tree != NULL); + return new_tree; +} + +icache_tree * +icache_tree_insert(icache_tree *tree, + char *name) +{ + icache_tree *new_tree; + /* find it */ + icache_tree **ptr_to_cur_tree = &tree->children; + icache_tree *cur_tree = *ptr_to_cur_tree; + while (cur_tree != NULL + && strcmp(cur_tree->name, name) < 0) { + ptr_to_cur_tree = &cur_tree->next; + cur_tree = *ptr_to_cur_tree; + } + ASSERT(cur_tree == NULL + || strcmp(cur_tree->name, name) >= 0); + /* already in the tree */ + if (cur_tree != NULL + && strcmp(cur_tree->name, name) == 0) + return cur_tree; + /* missing, insert it */ + ASSERT(cur_tree == NULL + || strcmp(cur_tree->name, name) > 0); + new_tree = icache_tree_new(); + new_tree->name = name; + new_tree->next = cur_tree; + *ptr_to_cur_tree = new_tree; + return new_tree; +} + + +icache_tree * +insn_table_cache_fields(insn_table *table) +{ + icache_tree *tree = icache_tree_new(); + insn *instruction; + for (instruction = table->insns; + instruction != NULL; + instruction = instruction->next) { + insn_field *field; + icache_tree *form = + icache_tree_insert(tree, + instruction->file_entry->fields[insn_form]); + for (field = instruction->fields->first; + field != NULL; + field = field->next) { + if (field->is_string) + icache_tree_insert(form, field->val_string); + } + } + return tree; +} + + + +void +gen_icache_h(icache_tree *tree, + lf *file) +{ + lf_print_copyleft(file); + lf_printf(file, "\n"); + lf_printf(file, "#ifndef _ICACHE_H_\n"); + lf_printf(file, "#define _ICACHE_H_\n"); + lf_printf(file, "\n"); + lf_printf(file, "#ifndef INLINE_ICACHE\n"); + lf_printf(file, "#define INLINE_ICACHE\n"); + lf_printf(file, "#endif\n"); + lf_printf(file, "\n"); + lf_printf(file, "\n"); + + /* create an instruction cache if being used */ + if (idecode_cache) { + lf_printf(file, "typedef struct _idecode_cache {\n"); + lf_printf(file, " unsigned_word address;\n"); + lf_printf(file, " void *semantic;\n"); + if (idecode_cache == 1) { + lf_printf(file, " instruction_word instruction;\n"); + } + else { + icache_tree *form; + lf_printf(file, " union {\n"); + for (form = tree->children; + form != NULL; + form = form->next) { + icache_tree *field; + lf_printf(file, " struct {\n"); + for (field = form->children; + field != NULL; + field = field->next) { + extraction_rules *rule; + int found_rule = 0; + for (rule = cachable_values; + rule->valid; + rule++) { + if (strcmp(field->name, rule->old_name) == 0) { + found_rule = 1; + if (rule->new_name != NULL) + lf_printf(file, " %s %s; /* %s */\n", + rule->type == NULL ? "unsigned" : rule->type, + rule->new_name, rule->old_name); + } + } + if (!found_rule) + lf_printf(file, " unsigned %s;\n", field->name); + } + lf_printf(file, " } %s;\n", form->name); + } + lf_printf(file, " } crack;\n"); + } + lf_printf(file, "} idecode_cache;\n"); + } + + /* define various fields according to the cache */ + if (idecode_cache <= 1) { + extraction_rules *rule; + lf_printf(file, "\n"); + for (rule = cachable_values; + rule->valid; + rule++) { + if (rule->expression != NULL) + lf_printf(file, "#define %s %s\n", + rule->new_name, rule->expression); + } + } + + lf_printf(file, "\n"); + lf_printf(file, "#endif /* _ICACHE_H_ */\n"); +} + + + + +/****************************************************************/ + + +void +lf_print_c_extraction(lf *file, + insn *instruction, + char *field_name, + char *field_type, + char *field_expression, + insn_field *cur_field, + insn_bits *bits, + int get_value_from_cache, + int put_value_in_cache) +{ + ASSERT(field_name != NULL); + if (bits != NULL + && (!bits->opcode->is_boolean || bits->value == 0) + && strcmp(field_name, cur_field->val_string) == 0) { + ASSERT(bits->field == cur_field); + ASSERT(field_type == NULL); + lf_print_c_line_nr(file, instruction->file_entry); + lf_printf(file, "const unsigned %s = ", + field_name); + if (bits->opcode->last < bits->field->last) + lf_printf(file, "%d;\n", + bits->value << (bits->field->last - bits->opcode->last)); + else + lf_printf(file, "%d;\n", bits->value); + } + else { + /* put the field in the local variable */ + lf_print_c_line_nr(file, instruction->file_entry); + lf_printf(file, "%s const %s = ", + field_type == NULL ? "unsigned" : field_type, + field_name); + /* getting it from the cache */ + if (get_value_from_cache || put_value_in_cache) { + lf_printf(file, "cache_entry->crack.%s.%s", + instruction->file_entry->fields[insn_form], + field_name); + if (put_value_in_cache) /* also put it in the cache? */ + lf_printf(file, " = "); + } + if (!get_value_from_cache) { + if (strcmp(field_name, cur_field->val_string) == 0) + lf_printf(file, "EXTRACTED32(instruction, %d, %d)", + cur_field->first, cur_field->last); + else if (field_expression != NULL) + lf_printf(file, "%s", field_expression); + else + lf_printf(file, "eval_%s", field_name); + } + lf_printf(file, ";\n"); + } +} + + +void +lf_print_c_extractions(lf *file, + insn *instruction, + insn_bits *expanded_bits, + int get_value_from_cache, + int put_value_in_cache) +{ + insn_field *cur_field; + + /* extract instruction fields */ + lf_printf(file, "/* extraction: %s */\n", + instruction->file_entry->fields[insn_format]); + + for (cur_field = instruction->fields->first; + cur_field->first < insn_size; + cur_field = cur_field->next) { + if (cur_field->is_string) { + insn_bits *bits; + int found_rule = 0; + /* find any corresponding value */ + for (bits = expanded_bits; + bits != NULL; + bits = bits->last) { + if (bits->field == cur_field) + break; + } + /* try the cache rule table for what to do */ + if (get_value_from_cache || put_value_in_cache) { + extraction_rules *field_rule; + for (field_rule = cachable_values; + field_rule->valid; + field_rule++) { + if (strcmp(cur_field->val_string, field_rule->old_name) == 0) { + found_rule = 1; + if (field_rule->valid > 1 && put_value_in_cache) + lf_print_c_extraction(file, + instruction, + field_rule->new_name, + field_rule->type, + field_rule->expression, + cur_field, + bits, + 0, + 0); + else if (field_rule->valid == 1) + lf_print_c_extraction(file, + instruction, + field_rule->new_name, + field_rule->type, + field_rule->expression, + cur_field, + bits, + get_value_from_cache, + put_value_in_cache); + } + } + } + if (found_rule == 0) + lf_print_c_extraction(file, + instruction, + cur_field->val_string, + 0, + 0, + cur_field, + bits, + get_value_from_cache, + put_value_in_cache); + /* if any (XXX == 0), output a corresponding test */ + if (instruction->file_entry->annex != NULL) { + char *field_name = cur_field->val_string; + char *is_0_ptr = instruction->file_entry->annex; + int field_len = strlen(field_name); + if (strlen(is_0_ptr) >= (strlen("_is_0") + field_len)) { + is_0_ptr += field_len; + while ((is_0_ptr = strstr(is_0_ptr, "_is_0")) != NULL) { + if (strncmp(is_0_ptr - field_len, field_name, field_len) == 0 + && !isalpha(is_0_ptr[ - field_len - 1])) { + lf_print_c_line_nr(file, instruction->file_entry); + lf_printf(file, "const unsigned %s_is_0 = (", field_name); + if (bits != NULL) + lf_printf(file, "%d", bits->value); + else + lf_printf(file, "%s", field_name); + lf_printf(file, " == 0);\n"); + break; + } + is_0_ptr += strlen("_is_0"); + } + } + } + /* any thing else ... */ + } + } + lf_print_file_line_nr(file); +} + +void +lf_print_c_validate(lf *file, + insn *instruction, + opcode_field *opcodes) +{ + unsigned check_mask = 0; + unsigned check_val = 0; + insn_field *field; + opcode_field *opcode; + + for (field = instruction->fields->first; + field->first < insn_size; + field = field->next) { + + check_mask <<= field->width; + check_val <<= field->width; + + /* is it a constant that could need validating? */ + if (!field->is_int && !field->is_slash) + continue; + + /* has it been checked by a table? */ + for (opcode = opcodes; opcode != NULL; opcode = opcode->parent) { + if (field->first >= opcode->first + && field->last <= opcode->last) + break; + } + if (opcode != NULL) + continue; + + check_mask |= (1 << field->width)-1; + check_val |= field->val_int; + } + + /* if any bits not checked by opcode tables, output code to check them */ + if (!it_is("illegal", instruction->file_entry->fields[insn_flags]) + && check_mask) { + lf_printf(file, "\n"); + lf_printf(file, "/* validate: %s */\n", + instruction->file_entry->fields[insn_format]); + lf_printf(file, "if ((instruction & 0x%x) != 0x%x) {\n", + check_mask, check_val); + switch (idecode_cache) { + case 0: + lf_printf(file, " return semantic_illegal(%s);\n", insn_actual); + break; + case 1: + lf_printf(file, " return semantic_illegal;\n"); + break; + default: + lf_printf(file, " return idecode_illegal(%s);\n", cache_idecode_actual); + } + lf_printf(file, "}\n"); + } + +} + + +void +lf_print_c_cracker(lf *file, + insn *instruction, + insn_bits *expanded_bits, + opcode_field *opcodes) +{ + + /* function header */ + lf_printf(file, "{\n"); + lf_indent(file, +2); + + lf_print_my_prefix(file, + instruction->file_entry); + + lf_print_ptrace(file); + + lf_print_c_validate(file, instruction, opcodes); + + lf_printf(file, "\n"); + lf_printf(file, "{\n"); + lf_indent(file, +2); + lf_print_c_extractions(file, + instruction, + expanded_bits, + 0/*get_value_from_cache*/, + 1/*put_value_in_cache*/); + lf_indent(file, -2); + lf_printf(file, "}\n"); + + /* return the function propper (main sorts this one out) */ + lf_printf(file, "\n"); + lf_printf(file, "/* semantic routine */\n"); + lf_print_c_line_nr(file, instruction->file_entry); + lf_printf(file, "return "); + lf_print_function_name(file, + instruction, + expanded_bits, + function_name_prefix_semantics); + lf_printf(file, ";\n"); + + lf_print_file_line_nr(file); + lf_indent(file, -2); + lf_printf(file, "}\n"); +} + + +void +lf_print_c_semantic(lf *file, + insn *instruction, + insn_bits *expanded_bits, + opcode_field *opcodes) +{ + + lf_printf(file, "{\n"); + lf_indent(file, +2); + + lf_print_my_prefix(file, + instruction->file_entry); + lf_putstr(file, insn_local); + lf_printf(file, "\n"); + + lf_printf(file, "\n"); + lf_print_c_extractions(file, + instruction, + expanded_bits, + idecode_cache > 1/*get_value_from_cache*/, + 0/*put_value_in_cache*/); + + lf_print_ptrace(file); + + /* generate code to check previously unchecked fields */ + if (idecode_cache < 2) + lf_print_c_validate(file, instruction, opcodes); + + /* if OEA and a floating point generate a check that fp is enabled */ + if (it_is("f", instruction->file_entry->fields[insn_flags])) { + lf_printf(file, "\n"); + lf_printf(file, "/* verify FP is enabled */\n"); + lf_printf(file, "if (!IS_FP_AVAILABLE(processor))\n"); + lf_printf(file, " floating_point_unavailable_interrupt(processor, cia);\n"); + } + + /* generate the code (or at least something */ + if (instruction->file_entry->annex != NULL) { + /* true code */ + lf_printf(file, "\n"); + lf_print_c_line_nr(file, instruction->file_entry); + lf_printf(file, "{\n"); + lf_indent(file, +2); + lf_print_c_code(file, instruction->file_entry->annex); + lf_indent(file, -2); + lf_printf(file, "}\n"); + lf_print_file_line_nr(file); + } + else if (it_is("nop", instruction->file_entry->fields[insn_flags])) { + lf_print_file_line_nr(file); + } + else if (it_is("f", instruction->file_entry->fields[insn_flags])) { + /* unimplemented floating point - call for assistance */ + lf_printf(file, "\n"); + lf_print_c_line_nr(file, instruction->file_entry); + lf_putstr(file, "floating_point_assist_interrupt(processor, cia);\n"); + lf_print_file_line_nr(file); + } + else { + /* abort so it is implemented now */ + lf_print_c_line_nr(file, instruction->file_entry); + lf_putstr(file, "error(\"%s: unimplemented, cia=0x%x\\n\", my_prefix, cia);\n"); + lf_print_file_line_nr(file); + lf_printf(file, "\n"); + } + + /* the function footer */ + lf_printf(file, "return nia;\n"); + lf_indent(file, -2); + lf_printf(file, "}\n"); +} + +void +lf_print_c_semantic_function(lf *file, + insn *instruction, + insn_bits *expanded_bits, + opcode_field *opcodes) +{ + + /* build the semantic routine to execute the instruction */ + + /* function header */ + lf_printf(file, "\n"); + lf_printf(file, "INLINE_SEMANTICS unsigned_word\n"); + lf_print_function_name(file, + instruction, + expanded_bits, + function_name_prefix_semantics); + lf_printf(file, "\n(%s)\n", + idecode_cache > 1 ? cache_insn_formal : insn_formal); + + lf_print_c_semantic(file, + instruction, + expanded_bits, + opcodes); + +} + + +void +semantics_c_leaf(insn_table *entry, + void *data, + int depth) +{ + lf *file = (lf*)data; + ASSERT(entry->nr_insn == 1 + && entry->opcode == NULL + && entry->parent != NULL + && entry->parent->opcode != NULL); + lf_print_c_semantic_function(file, + entry->insns, + entry->expanded_bits, + entry->parent->opcode); +} + +void +semantics_c_insn(insn_table *table, + void *data, + insn *instruction) +{ + lf *file = (lf*)data; + lf_print_c_semantic_function(file, instruction, + NULL, NULL); +} + + + +void +gen_semantics_c(insn_table *table, lf *file) +{ + lf_print_copyleft(file); + lf_printf(file, "\n"); + lf_printf(file, "#ifndef _SEMANTICS_C_\n"); + lf_printf(file, "#define _SEMANTICS_C_\n"); + lf_printf(file, "\n"); + lf_printf(file, "#ifndef STATIC_INLINE_SEMANTICS\n"); + lf_printf(file, "#define STATIC_INLINE_SEMANTICS STATIC_INLINE\n"); + lf_printf(file, "#endif\n"); + lf_printf(file, "\n"); + lf_printf(file, "#include \"cpu.h\"\n"); + lf_printf(file, "#include \"idecode.h\"\n"); + lf_printf(file, "#include \"semantics.h\"\n"); + lf_printf(file, "\n"); + + if (idecode_expand_semantics) + insn_table_traverse_tree(table, + file, + 1, + NULL, /* start */ + semantics_c_leaf, + NULL, /* end */ + NULL); /* padding */ + else + insn_table_traverse_insn(table, + file, + semantics_c_insn); + + lf_printf(file, "\n"); + lf_printf(file, "#endif /* _SEMANTICS_C_ */\n"); +} + + +/****************************************************************/ + +void +gen_idecode_h(insn_table *table, lf *file) +{ + lf_print_copyleft(file); + lf_printf(file, "\n"); + lf_printf(file, "#ifndef _IDECODE_H_\n"); + lf_printf(file, "#define _IDECODE_H_\n"); + lf_printf(file, "\n"); + lf_printf(file, "#ifndef INLINE_IDECODE\n"); + lf_printf(file, "#define INLINE_IDECODE\n"); + lf_printf(file, "#endif\n"); + lf_printf(file, "\n"); + lf_printf(file, "#include \"idecode_insn.h\"\n"); + lf_printf(file, "#include \"idecode_expression.h\"\n"); + lf_printf(file, "#include \"idecode_fields.h\"\n"); + lf_printf(file, "#include \"idecode_branch.h\"\n"); + lf_printf(file, "\n"); + lf_printf(file, "#include \"icache.h\"\n"); + lf_printf(file, "\n"); + lf_printf(file, "typedef unsigned_word idecode_semantic\n(%s);\n", + idecode_cache < 2 ? insn_formal : cache_insn_formal); + lf_printf(file, "\n"); + if (!idecode_cache) + lf_printf(file, "INLINE_IDECODE unsigned_word idecode_issue\n(%s);\n", + insn_formal); + else if (idecode_cache) + lf_printf(file, "INLINE_IDECODE idecode_semantic *idecode\n(%s);\n", + idecode_cache == 1 ? insn_formal : cache_idecode_formal); + lf_printf(file, "\n"); + lf_printf(file, "#endif /* _IDECODE_H_ */\n"); +} + + +/****************************************************************/ + + +void +idecode_table_start(insn_table *table, + void *data, + int depth) +{ + lf *file = (lf*)data; + ASSERT(depth == 0); + /* start of the table */ + if (!table->opcode_rule->use_switch) { + lf_printf(file, "\n"); + lf_printf(file, "static idecode_table_entry "); + lf_print_table_name(file, table); + lf_printf(file, "[] = {\n"); + } +} + +void +idecode_table_leaf(insn_table *entry, + void *data, + int depth) +{ + lf *file = (lf*)data; + ASSERT(entry->parent != NULL); + ASSERT(depth == 0); + + /* add an entry to the table */ + if (!entry->parent->opcode_rule->use_switch) { + if (entry->opcode == NULL) { + /* table leaf entry */ + lf_printf(file, " /*%d*/ { 0, 0, ", entry->opcode_nr); + lf_print_function_name(file, + entry->insns, + entry->expanded_bits, + (idecode_cache < 2 + ? function_name_prefix_semantics + : function_name_prefix_idecode)); + lf_printf(file, " },\n"); + } + else if (entry->opcode_rule->use_switch) { + /* table calling switch statement */ + lf_printf(file, " /*%d*/ { -1, 0, ", + entry->opcode_nr); + lf_print_table_name(file, entry); + lf_printf(file, " },\n"); + } + else { + /* table `calling' another table */ + lf_printf(file, " /*%d*/ { ", entry->opcode_nr); + if (entry->opcode->is_boolean) + lf_printf(file, "MASK32(%d,%d), 0, ", + entry->opcode->first, entry->opcode->last); + else + lf_printf(file, "%d, MASK32(%d,%d), ", + insn_size - entry->opcode->last - 1, + entry->opcode->first, entry->opcode->last); + lf_print_table_name(file, entry); + lf_printf(file, " },\n"); + } + } +} + +void +idecode_table_end(insn_table *table, + void *data, + int depth) +{ + lf *file = (lf*)data; + ASSERT(depth == 0); + + if (!table->opcode_rule->use_switch) { + lf_printf(file, "};\n"); + } +} + +void +idecode_table_padding(insn_table *table, + void *data, + int depth, + int opcode_nr) +{ + lf *file = (lf*)data; + ASSERT(depth == 0); + + if (!table->opcode_rule->use_switch) { + lf_printf(file, " /*%d*/ { 0, 0, %s_illegal },\n", + opcode_nr, idecode_cache > 1 ? "idecode" : "semantic"); + } +} + + +/****************************************************************/ + + +void lf_print_idecode_switch +(lf *file, + insn_table *table); + + +void +idecode_switch_start(insn_table *table, + void *data, + int depth) +{ + lf *file = (lf*)data; + ASSERT(depth == 0); + ASSERT(table->opcode_rule->use_switch); + + lf_printf(file, "switch (EXTRACTED32(instruction, %d, %d)) {\n", + table->opcode->first, table->opcode->last); +} + + +void +idecode_switch_leaf(insn_table *entry, + void *data, + int depth) +{ + lf *file = (lf*)data; + ASSERT(entry->parent != NULL); + ASSERT(depth == 0); + ASSERT(entry->parent->opcode_rule->use_switch); + + lf_printf(file, "case %d:\n", entry->opcode_nr); + lf_indent(file, +2); + { + if (entry->opcode == NULL) { + /* switch calling leaf */ + lf_printf(file, "return "); + lf_print_function_name(file, + entry->insns, + entry->expanded_bits, + (idecode_cache < 2 + ? function_name_prefix_semantics + : function_name_prefix_idecode)); + if (!idecode_cache) + lf_printf(file, "(%s);\n", insn_actual); + else if (idecode_cache == 1) + lf_printf(file, ";\n"); + else + lf_printf(file, "(%s);\n", cache_idecode_actual); + } + else if (entry->opcode_rule->use_switch) { + /* switch calling switch */ + lf_print_idecode_switch(file, entry); + } + else { + /* switch calling table */ + lf_printf(file, "return "); + lf_print_idecode_table(file, entry); + } + lf_printf(file, "break;\n"); + } + lf_indent(file, -2); +} + +lf_print_idecode_switch_illegal(lf *file) +{ + switch (idecode_cache) { + case 0: + lf_printf(file, " return semantic_illegal(%s);\n", insn_actual); + break; + case 1: + lf_printf(file, " return semantic_illegal;\n"); + break; + default: + lf_printf(file, " return idecode_illegal(%s);\n", cache_idecode_actual); + } + lf_printf(file, " break;\n"); +} + +void +idecode_switch_end(insn_table *table, + void *data, + int depth) +{ + lf *file = (lf*)data; + ASSERT(depth == 0); + ASSERT(table->opcode_rule->use_switch); + + if (table->opcode_rule->use_switch == 1) { + lf_printf(file, "default:\n"); + lf_print_idecode_switch_illegal(file); + } + lf_printf(file, "}\n"); +} + +void +idecode_switch_padding(insn_table *table, + void *data, + int depth, + int opcode_nr) +{ + lf *file = (lf*)data; + + ASSERT(depth == 0); + ASSERT(table->opcode_rule->use_switch); + + if (table->opcode_rule->use_switch > 1) { + lf_printf(file, "case %d:\n", opcode_nr); + lf_print_idecode_switch_illegal(file); + } +} + + +void +lf_print_idecode_switch(lf *file, + insn_table *table) +{ + insn_table_traverse_tree(table, + file, + 0, + idecode_switch_start, + idecode_switch_leaf, + idecode_switch_end, + idecode_switch_padding); +} + + +void +idecode_expand_if_switch(insn_table *table, + void *data, + int depth) +{ + lf *file = (lf*)data; + + if (table->opcode_rule->use_switch + && table->parent != NULL /* don't expand the top one yet */ + && !table->parent->opcode_rule->use_switch) { + lf_printf(file, "\n"); + lf_printf(file, "STATIC_INLINE_IDECODE void\n"); + lf_print_table_name(file, table); + lf_printf(file, "\n(%s)\n", + idecode_cache ? cache_idecode_formal : insn_formal); + lf_printf(file, "{\n"); + { + lf_indent(file, +2); + lf_print_idecode_switch(file, table); + lf_indent(file, -2); + } + lf_printf(file, "}\n"); + } +} + + +lf_print_c_cracker_function(lf *file, + insn *instruction, + insn_bits *expanded_bits, + opcode_field *opcodes) +{ + /* if needed, generate code to enter this routine into a cache */ + lf_printf(file, "\n"); + lf_printf(file, "STATIC_INLINE_IDECODE idecode_semantic *\n"); + lf_print_function_name(file, + instruction, + expanded_bits, + function_name_prefix_idecode); + lf_printf(file, "\n(%s)\n", cache_idecode_formal); + + lf_print_c_cracker(file, + instruction, + expanded_bits, + opcodes); +} + +void +idecode_crack_leaf(insn_table *entry, + void *data, + int depth) +{ + lf *file = (lf*)data; + ASSERT(entry->nr_insn == 1 + && entry->opcode == NULL + && entry->parent != NULL + && entry->parent->opcode != NULL); + lf_print_c_cracker_function(file, + entry->insns, + entry->expanded_bits, + entry->opcode); +} + +void +idecode_crack_insn(insn_table *entry, + void *data, + insn *instruction) +{ + lf *file = (lf*)data; + lf_print_c_cracker_function(file, + instruction, + NULL, + NULL); +} + +/****************************************************************/ + +gen_idecode_c(insn_table *table, lf *file) +{ + int depth; + + /* the intro */ + lf_print_copyleft(file); + lf_printf(file, "\n"); + lf_printf(file, "\n"); + lf_printf(file, "#ifndef _IDECODE_C_\n"); + lf_printf(file, "#define _IDECODE_C_\n"); + lf_printf(file, "\n"); + lf_printf(file, "#ifndef STATIC_INLINE_IDECODE\n"); + lf_printf(file, "#define STATIC_INLINE_IDECODE STATIC_INLINE\n"); + lf_printf(file, "#endif\n"); + lf_printf(file, "\n"); + lf_printf(file, "#include \"cpu.h\"\n"); + lf_printf(file, "#include \"idecode.h\"\n"); + lf_printf(file, "#include \"semantics.h\"\n"); + lf_printf(file, "\n"); + lf_printf(file, "\n"); + lf_printf(file, "typedef idecode_semantic *idecode_crack\n(%s);\n", + idecode_cache > 1 ? cache_idecode_formal : insn_formal); + lf_printf(file, "\n"); + lf_printf(file, "typedef struct _idecode_table_entry {\n"); + lf_printf(file, " unsigned shift;\n"); + lf_printf(file, " unsigned mask;\n"); + lf_printf(file, " void *function_or_table;\n"); + lf_printf(file, "} idecode_table_entry;\n"); + lf_printf(file, "\n"); + lf_printf(file, "\n"); + + /* output cracking functions where needed */ + if (idecode_cache > 1) { + if (idecode_expand_semantics) + insn_table_traverse_tree(table, + file, + 1, + NULL, + idecode_crack_leaf, + NULL, + NULL); + else + insn_table_traverse_insn(table, + file, + idecode_crack_insn); + } + + + /* output tables where needed */ + for (depth = insn_table_depth(table); + depth > 0; + depth--) { + insn_table_traverse_tree(table, + file, + 1-depth, + idecode_table_start, + idecode_table_leaf, + idecode_table_end, + idecode_table_padding); + } + + /* output switch functions where needed */ + insn_table_traverse_tree(table, + file, + 1, + idecode_expand_if_switch, /* START */ + NULL, NULL, NULL); + + /* output the main idecode routine */ + lf_printf(file, "\n"); + if (!idecode_cache) + lf_printf(file, "INLINE_IDECODE unsigned_word\nidecode_issue\n(%s)\n", + insn_formal); + else if (idecode_cache) + lf_printf(file, "INLINE_IDECODE idecode_semantic *\nidecode\n(%s)\n", + idecode_cache == 1 ? insn_formal : cache_idecode_formal); + lf_printf(file, "{\n"); + lf_indent(file, +2); + if (table->opcode_rule->use_switch) + lf_print_idecode_switch(file, table); + else + lf_print_idecode_table(file, table); + lf_indent(file, -2); + lf_printf(file, "}\n"); + lf_printf(file, "\n"); + lf_printf(file, "#endif\n"); +} + + +/****************************************************************/ + + +typedef enum { + spreg_name, + spreg_reg_nr, + spreg_readonly, + spreg_length, + nr_spreg_registers = file_table_max_fields +} spreg_fields; + +typedef struct _spreg_table_entry spreg_table_entry; +struct _spreg_table_entry { + char *name; + int spreg_nr; + int is_readonly; + int length; + file_table_entry *entry; + spreg_table_entry *next; +}; + +typedef struct _spreg_table spreg_table; +struct _spreg_table { + spreg_table_entry *sprs; +}; + +spreg_table_entry * +spreg_table_entry_new() +{ + spreg_table_entry *new_entry = + (spreg_table_entry*)zmalloc(sizeof(spreg_table_entry)); + ASSERT(new_entry != NULL); + return new_entry; +} + +spreg_table * +spreg_table_new() +{ + spreg_table *new_table = (spreg_table*)zmalloc(sizeof(spreg_table)); + ASSERT(new_table != NULL); + return new_table; +} + +void +spreg_table_insert(spreg_table *table, file_table_entry *entry) +{ + /* create a new spr entry */ + spreg_table_entry *new_spr = spreg_table_entry_new(); + new_spr->next = NULL; + new_spr->entry = entry; + new_spr->spreg_nr = atoi(entry->fields[spreg_reg_nr]); + new_spr->is_readonly = (entry->fields[spreg_readonly] + ? atoi(entry->fields[spreg_readonly]) + : 0); + new_spr->length = atoi(entry->fields[spreg_length]); + new_spr->name = (char*)zmalloc(strlen(entry->fields[spreg_name]) + 1); + ASSERT(new_spr->name != NULL); + { + int i; + for (i = 0; entry->fields[spreg_name][i] != '\0'; i++) { + if (isupper(entry->fields[spreg_name][i])) + new_spr->name[i] = tolower(entry->fields[spreg_name][i]); + else + new_spr->name[i] = entry->fields[spreg_name][i]; + } + } + + /* insert, by spreg_nr order */ + { + spreg_table_entry **ptr_to_spreg_entry = &table->sprs; + spreg_table_entry *spreg_entry = *ptr_to_spreg_entry; + while (spreg_entry != NULL && spreg_entry->spreg_nr < new_spr->spreg_nr) { + ptr_to_spreg_entry = &spreg_entry->next; + spreg_entry = *ptr_to_spreg_entry; + } + ASSERT(spreg_entry == NULL || spreg_entry->spreg_nr != new_spr->spreg_nr); + *ptr_to_spreg_entry = new_spr; + new_spr->next = spreg_entry; + } + +} + + +spreg_table * +spreg_table_load(char *file_name) +{ + file_table *file = file_table_open(file_name); + spreg_table *table = spreg_table_new(); + + { + file_table_entry *entry; + while (entry = file_table_read(file)) { + spreg_table_insert(table, entry); + } + } + + return table; +} + + +/****************************************************************/ + +char *spreg_attributes[] = { + "is_valid", + "is_readonly", + "name", + "index", + "length", + 0 +}; + +void +gen_spreg_h(spreg_table *table, lf *file) +{ + spreg_table_entry *entry; + char **attribute; + + lf_print_copyleft(file); + lf_printf(file, "\n"); + lf_printf(file, "#ifndef _SPREG_H_\n"); + lf_printf(file, "#define _SPREG_H_\n"); + lf_printf(file, "\n"); + lf_printf(file, "#ifndef INLINE_SPREG\n"); + lf_printf(file, "#define INLINE_SPREG\n"); + lf_printf(file, "#endif\n"); + lf_printf(file, "\n"); + lf_printf(file, "typedef unsigned_word spreg;\n"); + lf_printf(file, "\n"); + lf_printf(file, "typedef enum {\n"); + + for (entry = table->sprs; + entry != NULL ; + entry = entry->next) { + lf_printf(file, " spr_%s = %d,\n", entry->name, entry->spreg_nr); + } + + lf_printf(file, " nr_of_sprs = %d\n", nr_of_sprs); + lf_printf(file, "} sprs;\n"); + lf_printf(file, "\n"); + for (attribute = spreg_attributes; + *attribute != NULL; + attribute++) { + if (strcmp(*attribute, "name") == 0) + lf_printf(file, "INLINE_SPREG char *spr_%s(sprs spr);\n", + *attribute); + else + lf_printf(file, "INLINE_SPREG int spr_%s(sprs spr);\n", + *attribute); + } + lf_printf(file, "\n"); + lf_printf(file, "#endif /* _SPREG_H_ */\n"); +} + + +void +gen_spreg_c(spreg_table *table, lf *file) +{ + spreg_table_entry *entry; + char **attribute; + int spreg_nr; + + lf_print_copyleft(file); + lf_printf(file, "\n"); + lf_printf(file, "#ifndef _SPREG_C_\n"); + lf_printf(file, "#define _SPREG_C_\n"); + lf_printf(file, "\n"); + lf_printf(file, "#include \"words.h\"\n"); + lf_printf(file, "#include \"spreg.h\"\n"); + + lf_printf(file, "\n"); + lf_printf(file, "typedef struct _spreg_info {\n"); + lf_printf(file, " char *name;\n"); + lf_printf(file, " int is_valid;\n"); + lf_printf(file, " int length;\n"); + lf_printf(file, " int is_readonly;\n"); + lf_printf(file, " int index;\n"); + lf_printf(file, "} spreg_info;\n"); + lf_printf(file, "\n"); + lf_printf(file, "static spreg_info spr_info[nr_of_sprs+1] = {\n"); + entry = table->sprs; + for (spreg_nr = 0; spreg_nr < nr_of_sprs+1; spreg_nr++) { + if (entry == NULL || spreg_nr < entry->spreg_nr) + lf_printf(file, " { 0, 0, 0, 0, %d},\n", spreg_nr); + else { + lf_printf(file, " { \"%s\", %d, %d, %d, spr_%s /*%d*/ },\n", + entry->name, 1, entry->length, entry->is_readonly, + entry->name, entry->spreg_nr); + entry = entry->next; + } + } + lf_printf(file, "};\n"); + + for (attribute = spreg_attributes; + *attribute != NULL; + attribute++) { + lf_printf(file, "\n"); + if (strcmp(*attribute, "name") == 0) + lf_printf(file, "INLINE_SPREG char *\n"); + else + lf_printf(file, "INLINE_SPREG int\n"); + lf_printf(file, "spr_%s(sprs spr)\n", *attribute); + lf_printf(file, "{\n"); + if (spreg_lookup_table + || strcmp(*attribute, "name") == 0 + || strcmp(*attribute, "index") == 0) + lf_printf(file, " return spr_info[spr].%s;\n", + *attribute); + else { + spreg_table_entry *entry; + lf_printf(file, " switch (spr) {\n"); + for (entry = table->sprs; entry != NULL; entry = entry->next) { + lf_printf(file, " case %d:\n", entry->spreg_nr); + if (strcmp(*attribute, "is_valid") == 0) + lf_printf(file, " return 1;\n"); + else if (strcmp(*attribute, "is_readonly") == 0) + lf_printf(file, " return %d;\n", entry->is_readonly); + else if (strcmp(*attribute, "length") == 0) + lf_printf(file, " return %d;\n", entry->length); + else + ASSERT(0); + } + lf_printf(file, " default:\n"); + lf_printf(file, " return 0;\n"); + lf_printf(file, " }\n"); + } + lf_printf(file, "}\n"); + } + + lf_printf(file, "\n"); + lf_printf(file, "#endif /* _SPREG_C_ */\n"); +} + + + +/****************************************************************/ + + +int +main(int argc, + char **argv, + char **envp) +{ + insn_table *instructions = NULL; + spreg_table *sprs = NULL; + icache_tree *cache_fields = NULL; + int ch; + + while ((ch = getopt(argc, argv, "i:I:r:S:s:D:d:P:p:C:")) != -1) { + fprintf(stderr, "\t-%c %s\n", ch, optarg); + switch(ch) { + case 'I': + case 'i': + instructions = insn_table_load_insns(optarg); + fprintf(stderr, "\texpanding ...\n"); + insn_table_expand_insns(instructions); + fprintf(stderr, "\tcache fields ...\n"); + cache_fields = insn_table_cache_fields(instructions); + if (ch == 'I') { + dump_traverse(instructions); + dump_insn_table(instructions, 0, 1); + } + break; + case 'r': + sprs = spreg_table_load(optarg); + break; + default: + { + lf *file = lf_open(optarg); + switch (ch) { + case 'S': + gen_semantics_h(instructions, file); + break; + case 's': + gen_semantics_c(instructions, file); + break; + case 'P': + gen_spreg_h(sprs, file); + break; + case 'p': + gen_spreg_c(sprs, file); + break; + case 'D': + gen_idecode_h(instructions, file); + break; + case 'd': + gen_idecode_c(instructions, file); + break; + case 'C': + gen_icache_h(cache_fields, file); + break; + } + lf_close(file); + } + } + } + return 0; +} diff --git a/sim/ppc/idecode_branch.h b/sim/ppc/idecode_branch.h new file mode 100644 index 0000000..ecae98e --- /dev/null +++ b/sim/ppc/idecode_branch.h @@ -0,0 +1,62 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1995, Andrew Cagney <cagney@highland.com.au> + + 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. + + */ + + +/* branch macro's: + + The macro's below implement the semantics of the PowerPC jump + instructions. */ + + +/* If so required, update the Link Register with the next sequential + instruction address */ + +#define UPDATE_LK \ +do { \ + if (update_LK) { \ + ppc_ia target = cia + 4; \ + ppc_spr new_address = (ppc_spr)IEA_MASKED(ppc_is_64bit(processor), \ + target); \ + LR = new_address; \ + } \ + ITRACE(trace_branch, \ + ("UPDATE_LK - update_LK=%d lr=0x%x cia=0x%x\n", \ + update_LK, LR, cia); \ +} while (0) + + +/* take the branch - absolute or relative - possibly updating the link + register */ + +#define BRANCH(ADDRESS) \ +do { \ + UPDATE_LK; \ + if (update_AA) { \ + ppc_ia target = (ppc_ia)(ADDRESS); \ + nia = (ppc_ia)IEA_MASKED(ppc_is_64bit(processor), target); \ + } \ + else { \ + ppc_ia target = cia + ADDRESS; \ + nia = (ppc_ia)IEA_MASKED(ppc_is_64bit(processor), target); \ + } \ + PTRACE(trace_branch, \ + ("BRANCH - update_AA=%d update_LK=%d nia=0x%x cia=0x%x\n", \ + update_AA, update_LK, nia, cia); \ +} while (0) diff --git a/sim/ppc/idecode_fields.h b/sim/ppc/idecode_fields.h new file mode 100644 index 0000000..8d65b17 --- /dev/null +++ b/sim/ppc/idecode_fields.h @@ -0,0 +1,103 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1995, Andrew Cagney <cagney@highland.com.au> + + 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. + + */ + + +/* Instruction field macros: + + The macro's below greatly simplify the process of translating the + pseudo code found in the PowerPC manual into C. + + In addition to the below, more will be found in the gen program's + cache table */ + + +/* map some statements and variables directly across */ + +#define then /*then*/ +#define is_64bit_implementation WITH_64BIT_TARGET +#define is_64bit_mode IS_64BIT_MODE(processor) + +#define NIA nia +#define CIA cia + + +/* reservation */ + +#define RESERVE cpu_reservation(processor)->valid +#define RESERVE_ADDR cpu_reservation(processor)->addr +#define RESERVE_DATA cpu_reservation(processor)->data + +#define real_addr(EA, IS_READ) vm_real_data_addr(cpu_data_map(processor), \ + EA, \ + IS_READ, \ + processor, \ + cia) + + +/* depending on mode return a 32 or 64bit number */ + +#define IEA(X) (is_64bit_mode \ + ? (X) \ + : MASKED((X), 32, 63)) + +/* Expand argument to current architecture size */ + +#define EXTS(X) EXTS_##X + + +/* Gen translates text of the form A{XX:YY} into A_XX_YY_ the macro's + below define such translated text into real expressions */ + +/* the spr field as it normally is used */ + +#define spr_5_9_ (spr & 0x1f) +#define spr_0_4_ (spr >> 5) +#define spr_0_ ((spr & BIT10(0)) != 0) + +#define tbr_5_9_ (tbr & 0x1f) +#define tbr_0_4_ (tbr >> 5) + + +#define TB cpu_get_time_base(processor) + + +/* various registers with important masks */ + +#define LR_0b00 (LR & ~3) +#define CTR_0b00 (CTR & ~3) + +#define CR_BI_ ((CR & BIT32_BI) != 0) +#define CR_BA_ ((CR & BIT32_BA) != 0) +#define CR_BB_ ((CR & BIT32_BB) != 0) + + +/* extended extracted fields */ + +#define TO_0_ ((TO & BIT5(0)) != 0) +#define TO_1_ ((TO & BIT5(1)) != 0) +#define TO_2_ ((TO & BIT5(2)) != 0) +#define TO_3_ ((TO & BIT5(3)) != 0) +#define TO_4_ ((TO & BIT5(4)) != 0) + +#define BO_0_ ((BO & BIT5(0)) != 0) +#define BO_1_ ((BO & BIT5(1)) != 0) +#define BO_2_ ((BO & BIT5(2)) != 0) +#define BO_3_ ((BO & BIT5(3)) != 0) +#define BO_4_ ((BO & BIT5(4)) != 0) diff --git a/sim/ppc/idecode_insn.h b/sim/ppc/idecode_insn.h new file mode 100644 index 0000000..4e56b78 --- /dev/null +++ b/sim/ppc/idecode_insn.h @@ -0,0 +1,67 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1995, Andrew Cagney <cagney@highland.com.au> + + 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. + + */ + + +/* + * Interface for the Instruction execution routines + */ + +/* + * Macro's that define the parts of an instruction + */ + +#define FLOATING_POINT_ENABLED_PROGRAM_INTERRUPT \ + program_interrupt(processor, \ + cia, \ + floating_point_enabled_program_interrupt) + +#define ILLEGAL_INSN_PROGRAM_INTERRUPT \ + program_interrupt(processor, \ + cia, \ + illegal_instruction_program_interrupt) +#define PRIVILEGED_INSN_PROGRAM_INTERRUPT \ + program_interrupt(processor, \ + cia, \ + privileged_instruction_program_interrupt) + +#define TRAP_PROGRAM_INTERRUPT \ + program_interrupt(processor, \ + cia, \ + trap_program_interrupt) + +#define FLOATING_POINT_UNAVAILABLE_INTERRUPT \ + floating_point_unavailable_interrupt(processor, \ + cia) + +#define FLOATING_POINT_ASSIST_INTERRUPT \ + floating_point_assist_interrupt(processor, \ + cia) + +#define BREAKPOINT \ + do { \ + ITRACE(trace_breakpoint, \ + ("breakpoint - cia0x%x\n", \ + cia)); \ + cpu_halt(processor, cia, was_trap, 0); \ + } while (0) + +#define SYSTEM_CALL_INTERRUPT \ + system_call_interrupt(processor, \ + cia) diff --git a/sim/ppc/memory_map.c b/sim/ppc/memory_map.c new file mode 100644 index 0000000..af97006 --- /dev/null +++ b/sim/ppc/memory_map.c @@ -0,0 +1,355 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1995, Andrew Cagney <cagney@highland.com.au> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + */ + + +#ifndef _MEMORY_MAP_C_ +#define _MEMORY_MAP_C_ + +#ifndef STATIC_INLINE_MEMORY_MAP +#define STATIC_INLINE_MEMORY_MAP STATIC_INLINE +#endif + + +#include "basics.h" +#include "device_tree.h" +#include "memory_map.h" +#include "interrupts.h" + + +typedef struct _memory_mapping memory_mapping; +struct _memory_mapping { + /* ram map */ + void *buffer; + /* device map */ + device_node *device; + device_reader_callback *reader; + device_writer_callback *writer; + /* common */ + unsigned_word base; + unsigned_word bound; + unsigned_word size; + struct _memory_mapping *next; +}; + +struct _memory_map { + memory_mapping *first; +}; + +INLINE_MEMORY_MAP memory_map * +new_memory_map(void) +{ + memory_map *new_map; + new_map = ZALLOC(memory_map); + return new_map; +} + +STATIC_INLINE_MEMORY_MAP void +memory_map_add_memory(memory_map *map, + device_node *device, + device_reader_callback *reader, + device_writer_callback *writer, + void *buffer, + unsigned_word base, + unsigned size) +{ + memory_mapping *new_mapping; + memory_mapping *next_mapping; + memory_mapping **last_mapping; + + /* actually do occasionally get a zero size map */ + if (size == 0) + return; + + new_mapping = ZALLOC(memory_mapping); + + /* ram */ + new_mapping->buffer = buffer; + /* devices */ + new_mapping->device = device; + new_mapping->reader = reader; + new_mapping->writer = writer; + /* common */ + new_mapping->base = base; + new_mapping->size = size; + new_mapping->bound = base + size; + + /* find the insertion point (between last/next) */ + next_mapping = map->first; + last_mapping = &map->first; + while(next_mapping != NULL && next_mapping->bound <= new_mapping->base) { + /* assert: new_mapping->base > all bases before next_mapping */ + /* assert: new_mapping->bound >= all bounds before next_mapping */ + last_mapping = &next_mapping->next; + next_mapping = next_mapping->next; + } + + /* check insertion point correct */ + if (next_mapping != NULL && next_mapping->base < new_mapping->bound) { + error("memory_map_add_callback_memory() internal error - map overlap\n"); + } + + /* insert the new mapping */ + *last_mapping = new_mapping; + new_mapping->next = next_mapping; + +} + + +INLINE_MEMORY_MAP void +memory_map_add_callback_memory(memory_map *map, + device_node *device, + device_reader_callback *reader, + device_writer_callback *writer, + unsigned_word base, + unsigned size) +{ + memory_map_add_memory(map, device, reader, writer, NULL, base, size); +} + +INLINE_MEMORY_MAP void +memory_map_add_raw_memory(memory_map *map, + void *buffer, + unsigned_word base, + unsigned size) +{ + memory_map_add_memory(map, NULL, NULL, NULL, buffer, base, size); +} + + + + +STATIC_INLINE_MEMORY_MAP memory_mapping * +memory_map_find_mapping(memory_map *map, + unsigned_word addr, + unsigned nr_bytes, + int abort, + cpu *processor, + unsigned_word cia) +{ + memory_mapping *mapping = map->first; + ASSERT((addr & (nr_bytes-1)) == 0); + while (1) { + if (addr >= mapping->base + && (addr + nr_bytes) <= mapping->bound) + break; + mapping = mapping->next; + if (mapping == NULL) { + if (abort) { + switch (CURRENT_ENVIRONMENT) { + case VIRTUAL_ENVIRONMENT: + data_storage_interrupt(processor, + cia, + addr, + vea_storage_interrupt, + 0/* doesnt matter */); + break; + default: + error("memory_map_find_mapping() - %s%x%s%s%s%s%s", + "access to undefined address 0x", addr, "\n", + "this code should be passing back up the device tree an\n", + "abort event (with processor attached). Somewhere in\n", + "the device tree this would be caught and either halt,\n", + "interrupt, or reset the processor\n"); + } + return NULL; + } + else + return NULL; + } + } + return mapping; +} + + +STATIC_INLINE_MEMORY_MAP void * +memory_map_translate(memory_mapping *mapping, + unsigned_word addr) +{ + return mapping->buffer + addr - mapping->base; +} + + +INLINE_MEMORY_MAP unsigned +memory_map_read_buffer(memory_map *map, + void *buffer, + unsigned_word addr, + unsigned len, + transfer_mode mode) +{ + unsigned count; + unsigned_1 byte; + for (count = 0; count < len; count++) { + unsigned pos = 0; + unsigned_word raddr = addr + count; + memory_mapping *mapping = + memory_map_find_mapping(map, raddr, 1, + 0/*abort*/, + 0, 0/*processor, cia*/); + if (mapping == NULL) + break; + if (mode == raw_transfer || + CURRENT_TARGET_BYTE_ORDER == CURRENT_HOST_BYTE_ORDER) + pos = count; + else if (mode == cooked_transfer) + pos = len-count-1; + else + error("memory_map_read_buffer() - transfer mode unknown\n"); + if (mapping->reader != NULL) + /* hope it doesn't barf */ + byte = mapping->reader(mapping->device, + raddr - mapping->base, + 1, + 0, 0/*processor, cia*/); + else + byte = *(unsigned_1*)memory_map_translate(mapping, + raddr); + ((unsigned_1*)buffer)[pos] = T2H_1(byte); + } + return count; +} + + +INLINE_MEMORY_MAP unsigned +memory_map_write_buffer(memory_map *map, + const void *buffer, + unsigned_word addr, + unsigned len, + transfer_mode mode) +{ + unsigned count; + unsigned_1 byte; + for (count = 0; count < len; count++) { + unsigned pos = 0; + unsigned_word raddr = addr + count; + memory_mapping *mapping = + memory_map_find_mapping(map, raddr, 1, + 0/*abort*/, + 0, 0/*processor, cia*/); + if (mapping == NULL) + break; + if (mode == raw_transfer || + CURRENT_TARGET_BYTE_ORDER == CURRENT_HOST_BYTE_ORDER) + pos = count; + else if (mode == cooked_transfer) + pos = len-count-1; + else + error("memory_map_write_buffer() - transfer mode unknown\n"); + byte = H2T_1(((unsigned_1*)buffer)[pos]); + if (mapping->writer != NULL) + /* hope it doesn't barf */ + mapping->writer(mapping->device, + raddr - mapping->base, + 1, + byte, + 0, 0/*processor, cia*/); + else + *(unsigned_1*)memory_map_translate(mapping, raddr) = byte; + } + return count; +} + + +INLINE_MEMORY_MAP unsigned +memory_map_zero(memory_map *map, + unsigned_word addr, + unsigned len) +{ + unsigned pos; + for (pos = 0; pos < len; pos++) { + unsigned_word raddr = addr + pos; + memory_mapping *mapping = + memory_map_find_mapping(map, raddr, 1, + 0/*abort*/, + 0, 0/*processor, cia*/); + if (mapping == NULL) + break; + if (mapping->writer != NULL) + mapping->writer(mapping->device, + raddr - mapping->base, + 1, + 0, + 0, 0/*processor, cia*/); + else + *(unsigned_1*)memory_map_translate(mapping, raddr) = 0; + } + return pos; +} + + +#define DEFINE_MEMORY_MAP_READ_N(N) \ +INLINE_MEMORY_MAP unsigned_##N \ +memory_map_read_##N(memory_map *map, \ + unsigned_word addr, \ + cpu *processor, \ + unsigned_word cia) \ +{ \ + memory_mapping *mapping = memory_map_find_mapping(map, addr, \ + sizeof(unsigned_##N), \ + 1, \ + processor, \ + cia); \ + if (WITH_CALLBACK_MEMORY && mapping->reader != NULL) \ + return ((unsigned_##N) \ + mapping->reader(mapping->device, \ + addr - mapping->base, \ + sizeof(unsigned_##N), \ + processor, \ + cia)); \ + else \ + return T2H_##N(*(unsigned_##N*)memory_map_translate(mapping, addr)); \ +} + +DEFINE_MEMORY_MAP_READ_N(1) +DEFINE_MEMORY_MAP_READ_N(2) +DEFINE_MEMORY_MAP_READ_N(4) +DEFINE_MEMORY_MAP_READ_N(8) +DEFINE_MEMORY_MAP_READ_N(word) + +#define DEFINE_MEMORY_MAP_WRITE_N(N) \ +INLINE_MEMORY_MAP void \ +memory_map_write_##N(memory_map *map, \ + unsigned_word addr, \ + unsigned_##N val, \ + cpu *processor, \ + unsigned_word cia) \ +{ \ + memory_mapping *mapping = memory_map_find_mapping(map, addr, \ + sizeof(unsigned_##N), \ + 1, \ + processor, \ + cia); \ + if (WITH_CALLBACK_MEMORY && mapping->writer != NULL) \ + mapping->writer(mapping->device, \ + addr - mapping->base, \ + sizeof(unsigned_##N), \ + val, \ + processor, \ + cia); \ + else \ + *(unsigned_##N*)memory_map_translate(mapping, addr) = H2T_##N(val); \ +} + +DEFINE_MEMORY_MAP_WRITE_N(1) +DEFINE_MEMORY_MAP_WRITE_N(2) +DEFINE_MEMORY_MAP_WRITE_N(4) +DEFINE_MEMORY_MAP_WRITE_N(8) +DEFINE_MEMORY_MAP_WRITE_N(word) + +#endif /* _MEMORY_MAP_C_ */ diff --git a/sim/ppc/memory_map.h b/sim/ppc/memory_map.h new file mode 100644 index 0000000..c197f43 --- /dev/null +++ b/sim/ppc/memory_map.h @@ -0,0 +1,126 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1995, Andrew Cagney <cagney@highland.com.au> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + */ + + +#ifndef _MEMORY_MAP_H_ +#define _MEMORY_MAP_H_ + +#ifndef INLINE_MEMORY_MAP +#define INLINE_MEMORY_MAP +#endif + +/* basic types */ + +typedef struct _memory_map memory_map; + + +/* constructor */ + +INLINE_MEMORY_MAP memory_map *new_memory_map +(void); + + +/* operators to add memory to a memory map + + callback-memory: + + includes a callback routine that is called upon for the data. + Useful when modeling memory mapped devices. + + raw-memory: + + normal base and bound memory map used to model ram or mapped memory + pages */ + +INLINE_MEMORY_MAP void memory_map_add_callback_memory +(memory_map *map, + device_node *device, + device_reader_callback *reader, + device_writer_callback *writer, + unsigned_word base, + unsigned size); /* host limited */ + +INLINE_MEMORY_MAP void memory_map_add_raw_memory +(memory_map *map, + void *buffer, + unsigned_word base, + unsigned size/*host limited*/); + + +/* Variable sized read/write/zero: + + Transfer (zero) a variable size block of data between the host and + target (possibly byte swapping it). Should any problems occure, + the number of bytes actually transfered is returned. */ + +INLINE_MEMORY_MAP unsigned memory_map_read_buffer +(memory_map *map, + void *buffer, + unsigned_word addr, + unsigned len, + transfer_mode mode); + +INLINE_MEMORY_MAP unsigned memory_map_write_buffer +(memory_map *map, + const void *buffer, + unsigned_word addr, + unsigned len, + transfer_mode mode); + +INLINE_MEMORY_MAP unsigned memory_map_zero +(memory_map *map, + unsigned_word addr, + unsigned len); + + +/* Fixed sized read/write: + + Transfer a fixed amout of memory between the host and target. The + memory always being translated and the operation always aborting + should a problem occure */ + +#define DECLARE_MEMORY_MAP_WRITE_N(N) \ +INLINE_MEMORY_MAP void memory_map_write_##N \ +(memory_map *map, \ + unsigned_word addr, \ + unsigned_##N val, \ + cpu *processor, \ + unsigned_word cia); + +DECLARE_MEMORY_MAP_WRITE_N(1) +DECLARE_MEMORY_MAP_WRITE_N(2) +DECLARE_MEMORY_MAP_WRITE_N(4) +DECLARE_MEMORY_MAP_WRITE_N(8) +DECLARE_MEMORY_MAP_WRITE_N(word) + +#define DECLARE_MEMORY_MAP_READ_N(N) \ +INLINE_MEMORY_MAP unsigned_##N memory_map_read_##N \ +(memory_map *map, \ + unsigned_word addr, \ + cpu *processor, \ + unsigned_word cia); + +DECLARE_MEMORY_MAP_READ_N(1) +DECLARE_MEMORY_MAP_READ_N(2) +DECLARE_MEMORY_MAP_READ_N(4) +DECLARE_MEMORY_MAP_READ_N(8) +DECLARE_MEMORY_MAP_READ_N(word) + +#endif diff --git a/sim/ppc/ppc-endian.c b/sim/ppc/ppc-endian.c new file mode 100644 index 0000000..0158116 --- /dev/null +++ b/sim/ppc/ppc-endian.c @@ -0,0 +1,70 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1995, Andrew Cagney <cagney@highland.com.au> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + */ + + +#ifndef _ENDIAN_C_ +#define _ENDIAN_C_ + +#ifndef STATIC_INLINE_ENDIAN +#define STATIC_INLINE_ENDIAN STATIC_INLINE +#endif + + +#include "config.h" +#include "words.h" +#include "ppc-endian.h" +#include "sim_callbacks.h" + + +typedef union { + unsigned_1 val_1[8]; + unsigned_2 val_2[4]; + unsigned_4 val_4[2]; + unsigned_8 val_8[1]; +} endian_map; + +#define ENDIAN_N(NAME,BYTE_SIZE) \ +unsigned_##BYTE_SIZE \ +endian_##NAME##_##BYTE_SIZE(unsigned_##BYTE_SIZE raw_in) \ +{ \ + if (CURRENT_TARGET_BYTE_ORDER == CURRENT_HOST_BYTE_ORDER) { \ + return raw_in; \ + } \ + else { \ + endian_map in; \ + endian_map out; \ + int byte_nr; \ + in.val_##BYTE_SIZE[0] = raw_in; \ + for (byte_nr = 0; byte_nr < BYTE_SIZE; byte_nr++) { \ + out.val_1[BYTE_SIZE-1-byte_nr] = in.val_1[byte_nr]; \ + } \ + return out.val_##BYTE_SIZE[0]; \ + } \ +} + + +ENDIAN_N(h2t, 2) +ENDIAN_N(h2t, 4) +ENDIAN_N(h2t, 8) +ENDIAN_N(t2h, 2) +ENDIAN_N(t2h, 4) +ENDIAN_N(t2h, 8) + +#endif /* _ENDIAN_C_ */ diff --git a/sim/ppc/ppc-endian.h b/sim/ppc/ppc-endian.h new file mode 100644 index 0000000..6ac0601 --- /dev/null +++ b/sim/ppc/ppc-endian.h @@ -0,0 +1,256 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1995, Andrew Cagney <cagney@highland.com.au> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + */ + + +#ifndef _ENDIAN_H_ +#define _ENDIAN_H_ + +/* C byte conversion functions */ + +extern unsigned_1 endian_h2t_1(unsigned_1 x); +extern unsigned_2 endian_h2t_2(unsigned_2 x); +extern unsigned_4 endian_h2t_4(unsigned_4 x); +extern unsigned_8 endian_h2t_8(unsigned_8 x); + +extern unsigned_1 endian_t2h_1(unsigned_1 x); +extern unsigned_2 endian_t2h_2(unsigned_2 x); +extern unsigned_4 endian_t2h_4(unsigned_4 x); +extern unsigned_8 endian_t2h_8(unsigned_8 x); + +/* Host dependant: + + The CPP below defines information about the compilation host. In + particular it defines the macro's: + + WITH_HOST_BYTE_ORDER The byte order of the host. Could + be any of LITTLE_ENDIAN, BIG_ENDIAN + or 0 (unknown). Those macro's also + need to be defined. + + WITH_NTOH Network byte order macros defined. + Possible value is 32 or (maybe one + day 64 because some 64bit network + byte order macro is defined. + */ + + +/* NetBSD: + + NetBSD is easy, everything you could ever want is in a header file + (well almost :-) */ + +#if defined(__NetBSD__) +# include <machine/endian.h> +# define WITH_NTOH 32 /* what about alpha? */ +# if (WITH_HOST_BYTE_ORDER == 0) +# undef WITH_HOST_BYTE_ORDER +# define WITH_HOST_BYTE_ORDER BYTE_ORDER +# endif +# if (BYTE_ORDER != WITH_HOST_BYTE_ORDER) +# error "host endian incorrectly configured, check config.h" +# endif +#endif + +/* Linux is similarly easy. */ + +#if defined(__linux__) +# include <endian.h> +# include <asm/byteorder.h> +# if defined(__LITTLE_ENDIAN) && !defined(LITTLE_ENDIAN) +# define LITTLE_ENDIAN __LITTLE_ENDIAN +# endif +# if defined(__BIG_ENDIAN) && !defined(BIG_ENDIAN) +# define BIG_ENDIAN __BIG_ENDIAN +# endif +# if defined(__BYTE_ORDER) && !defined(BYTE_ORDER) +# define BYTE_ORDER __BYTE_ORDER +# endif +# if !defined(__alpha__) +# define WITH_NTOH 32 /* what about alpha? */ +# endif +# if (WITH_HOST_BYTE_ORDER == 0) +# undef WITH_HOST_BYTE_ORDER +# define WITH_HOST_BYTE_ORDER BYTE_ORDER +# endif +# if (BYTE_ORDER != WITH_HOST_BYTE_ORDER) +# error "host endian incorrectly configured, check config.h" +# endif +#endif + +/* INSERT HERE - hosts that have available LITTLE_ENDIAN and + BIG_ENDIAN macro's */ + + +/* Some hosts don't define LITTLE_ENDIAN or BIG_ENDIAN, help them out */ + +#ifndef LITTLE_ENDIAN +#define LITTLE_ENDIAN 1234 +#endif +#ifndef BIG_ENDIAN +#define BIG_ENDIAN 4321 +#endif + + +/* SunOS on SPARC: + + Big endian last time I looked */ + +#if defined(sparc) || defined(__sparc__) +# if (WITH_HOST_BYTE_ORDER == 0) +# undef WITH_HOST_BYTE_ORDER +# define WITH_HOST_BYTE_ORDER BIG_ENDIAN +# endif +# if (WITH_HOST_BYTE_ORDER != BIG_ENDIAN) +# error "sun was big endian last time I looked ..." +# endif +#endif + + +/* Random x86 + + Little endian last time I looked */ + +#if defined(i386) || defined(i486) || defined(i586) || defined(__i386__) || defined(__i486__) || defined(__i586__) +# if (WITH_HOST_BYTE_ORDER == 0) +# undef WITH_HOST_BYTE_ORDER +# define WITH_HOST_BYTE_ORDER LITTLE_ENDIAN +# endif +# if (WITH_HOST_BYTE_ORDER != LITTLE_ENDIAN) +# error "x86 was little endian last time I looked ..." +# endif +#endif + + +/* INSERT HERE - additional hosts that do not have LITTLE_ENDIAN and + BIG_ENDIAN definitions available. */ + + +/* SWAPPING: + + According to the following table: + + TARG BE TARG LE TARG ?? + HOST BE ok s/w s/w + HOST LE htohl ok ok|ntohl + HOST ?? ntohl s/w s/w + + define host <-> target byte order conversion macro's */ + + +/* IN PLACE: + + These macro's given a variable argument swap its value in place if + so required */ + +#define H2T(VARIABLE) \ +do { \ + switch (sizeof(VARIABLE)) { \ + case 1: VARIABLE = H2T_1(VARIABLE); break; \ + case 2: VARIABLE = H2T_2(VARIABLE); break; \ + case 4: VARIABLE = H2T_4(VARIABLE); break; \ + case 8: VARIABLE = H2T_8(VARIABLE); break; \ + } \ +} while (0) + +#define T2H(VARIABLE) \ +do { \ + switch (sizeof(VARIABLE)) { \ + case 1: VARIABLE = T2H_1(VARIABLE); break; \ + case 2: VARIABLE = T2H_2(VARIABLE); break; \ + case 4: VARIABLE = T2H_4(VARIABLE); break; \ + case 8: VARIABLE = T2H_8(VARIABLE); break; \ + } \ +} while (0) + + +/* TARGET WORD: + + Byte swap a quantity the size of the targets word */ + +#if WITH_64BIT_TARGET +#define H2T_word(X) H2T_8(X) +#define T2H_word(X) T2H_8(X) +#else +#define H2T_word(X) H2T_4(X) +#define T2H_word(X) T2H_4(X) +#endif + + +/* FUNCTIONS: + + Returns the value swapped according to the host/target byte order */ + +/* no need to swap */ +#if 0 +#if (WITH_HOST_BYTE_ORDER \ + && WITH_TARGET_BYTE_ORDER \ + && WITH_HOST_BYTE_ORDER == WITH_TARGET_BYTE_ORDER ) +#define H2T_1(X) (X) +#define H2T_2(X) (X) +#define H2T_4(X) (X) +#define H2T_8(X) (X) +#define T2H_1(X) (X) +#define T2H_2(X) (X) +#define T2H_4(X) (X) +#define T2H_8(X) (X) +#endif + +/* have ntoh and big endian target */ +#if (WITH_TARGET_BYTE_ORDER == BIG_ENDIAN \ + && WITH_HOST_BYTE_ORDER != BIG_ENDIAN \ + && WITH_NTOH) +#define H2T_8(X) endian_h2t_8(X) +#define H2T_4(X) htonl(X) +#define H2T_2(X) htons(X) +#define H2T_1(X) (X) +#define T2H_8(X) endian_t2h_8(X) +#define T2H_4(X) htonl(X) +#define T2H_2(X) htons(X) +#define T2H_1(X) (X) +#endif + +/* have ntoh, little host and unknown target */ +#if (WITH_HOST_BYTE_ORDER == LITTLE_ENDIAN \ + && WITH_TARGET_BYTE_ORDER == 0 \ + && WITH_NTOH) +#define H2T_8(X) (CURRENT_TARGET_BYTE_ORDER == CURRENT_HOST_BYTE_ORDER ? (X) : endian_h2t_8(X)) +#define H2T_4(X) (CURRENT_TARGET_BYTE_ORDER == CURRENT_HOST_BYTE_ORDER ? (X) : htonl(X)) +#define H2T_2(X) (CURRENT_TARGET_BYTE_ORDER == CURRENT_HOST_BYTE_ORDER ? (X) : htons(X)) +#define H2T_1(X) (CURRENT_TARGET_BYTE_ORDER == CURRENT_HOST_BYTE_ORDER ? (X) : (X)) +#define T2H_8(X) (CURRENT_TARGET_BYTE_ORDER == CURRENT_HOST_BYTE_ORDER ? (X) : endian_t2h_8(X)) +#define T2H_4(X) (CURRENT_TARGET_BYTE_ORDER == CURRENT_HOST_BYTE_ORDER ? (X) : htonl(X)) +#define T2H_2(X) (CURRENT_TARGET_BYTE_ORDER == CURRENT_HOST_BYTE_ORDER ? (X) : htons(X)) +#define T2H_1(X) (CURRENT_TARGET_BYTE_ORDER == CURRENT_HOST_BYTE_ORDER ? (X) : (X)) +#endif +#endif + +/* if all else fails use software */ +#ifndef H2T_1 +#define H2T_1(X) (X) +#define H2T_2(X) endian_h2t_2(X) +#define H2T_4(X) endian_h2t_4(X) +#define H2T_8(X) endian_h2t_8(X) +#define T2H_1(X) (X) +#define T2H_2(X) endian_t2h_2(X) +#define T2H_4(X) endian_t2h_4(X) +#define T2H_8(X) endian_t2h_8(X) +#endif + +#endif diff --git a/sim/ppc/ppc.mt b/sim/ppc/ppc.mt new file mode 100644 index 0000000..ef3ea68 --- /dev/null +++ b/sim/ppc/ppc.mt @@ -0,0 +1,3 @@ +ALL=all-ppc +CLEAN=clean-ppc +DO_INSTALL=install-ppc diff --git a/sim/ppc/sim_callbacks.h b/sim/ppc/sim_callbacks.h new file mode 100644 index 0000000..a8ee960 --- /dev/null +++ b/sim/ppc/sim_callbacks.h @@ -0,0 +1,36 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1995, Andrew Cagney <cagney@highland.com.au> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + */ + + +#ifndef _SIM_CALLBACKS_H_ +#define _SIM_CALLBACKS_H_ + +#ifndef NORETURN +#define NORETURN +#endif + + +void printf_filtered (char *msg, ...); +void NORETURN error (char *msg, ...); +void *zalloc (long size); +#define ZALLOC(TYPE) (TYPE*)zalloc(sizeof (TYPE)) +void zfree(void*); + +#endif diff --git a/sim/ppc/system.c b/sim/ppc/system.c new file mode 100644 index 0000000..ca62b27 --- /dev/null +++ b/sim/ppc/system.c @@ -0,0 +1,387 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1995, Andrew Cagney <cagney@highland.com.au> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + */ + + +#ifndef _SYSTEM_C_ +#define _SYSTEM_C_ + +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <signal.h> +#include <sys/errno.h> +#include <sys/param.h> + +#if (NetBSD >= 199306) /* here NetBSD as that is what we're emulating */ +#include <sys/syscall.h> /* FIXME - should not be including this one */ +#include <sys/sysctl.h> +#endif + +#if (BSD < 199306) /* here BSD as just a bug */ +extern int errno; +#endif + +#include "cpu.h" +#include "idecode.h" +#include "system.h" + + +void +system_call(cpu *processor, + unsigned_word cia) +{ + switch (cpu_registers(processor)->gpr[0]) { + + + case 1/*SYS_exit*/: +#if (NetBSD >= 199306) && (SYS_exit != 1) +# error "SYS_exit" +#endif + { + int status = (int)cpu_registers(processor)->gpr[3]; + cpu_halt(processor, cia, was_exited, status); + break; + } + + + case 3/*SYS_read*/: +#if (NetBSD >= 199306) && (SYS_read != 3) +# error "SYS_read" +#endif + { + void *scratch_buffer; + int d = (int)cpu_registers(processor)->gpr[3]; + unsigned_word buf = cpu_registers(processor)->gpr[4]; + int nbytes = cpu_registers(processor)->gpr[5]; + int status; + int nr_moved; + + /* get a tempoary bufer */ + scratch_buffer = zalloc(cpu_registers(processor)->gpr[5]); + + /* check if buffer exists by reading it */ + nr_moved = vm_data_map_read_buffer(cpu_data_map(processor), + scratch_buffer, + buf, + nbytes, + raw_transfer); + if (nr_moved != nbytes) + error("system_call()read - check on buffer failed\n"); + + /* read */ + if (d == 0) { + status = fread (scratch_buffer, 1, nbytes, stdin); + if (status == 0 && ferror (stdin)) + status = -1; + } else { + status = read (d, scratch_buffer, nbytes); + } + + if (status == -1) { + cpu_registers(processor)->gpr[0] = errno; + break; + } else { + cpu_registers(processor)->gpr[0] = 0; + cpu_registers(processor)->gpr[3] = status; + + if (status > 0) { + nr_moved = vm_data_map_write_buffer(cpu_data_map(processor), + scratch_buffer, + buf, + status, + raw_transfer, + 0/*violate_ro*/); + + if (nr_moved != nbytes) + error("system_call()read - write to buffer failed\n"); + } + } + + zfree(scratch_buffer); + + break; + } + + + case 4/*SYS_write*/: +#if (NetBSD >= 199306) && (SYS_write != 4) +# error "SYS_write" +#endif + { + void *scratch_buffer; + int nr_moved; + int d = (int)cpu_registers(processor)->gpr[3]; + unsigned_word buf = cpu_registers(processor)->gpr[4]; + int nbytes = cpu_registers(processor)->gpr[5]; + int status; + + /* get a tempoary bufer */ + scratch_buffer = zalloc(cpu_registers(processor)->gpr[5]); + + /* copy in */ + nr_moved = vm_data_map_read_buffer(cpu_data_map(processor), + scratch_buffer, + buf, + nbytes, + raw_transfer); + if (nr_moved != nbytes) { + /* FIXME - should handle better */ + error("system_call()write copy failed (nr_moved=%d != nbytes=%d)\n", + nr_moved, nbytes); + } + + /* write */ + status = write(d, scratch_buffer, nbytes); + if (status == -1) { + cpu_registers(processor)->gpr[0] = errno; + break; + } + cpu_registers(processor)->gpr[0] = 0; + cpu_registers(processor)->gpr[3] = status; + + zfree(scratch_buffer); + + break; + } + + + case 17/*SYS_break*/: +#if (NetBSD >= 199306) && (SYS_break != 17) +# error "SYS_break" +#endif + { + unsigned_word new_sbrk = ALIGN_PAGE(cpu_registers(processor)->gpr[3]); + unsigned_word old_sbrk = core_data_upper_bound(cpu_core(processor)); + signed_word delta = new_sbrk - old_sbrk; + if (delta > 0) + core_add_data(cpu_core(processor), delta); + cpu_registers(processor)->gpr[0] = 0; + cpu_registers(processor)->gpr[3] = new_sbrk; + break; + } + + + case 20/*SYS_getpid*/: +#if (NetBSD >= 199306) && (SYS_getpid != 20) +# error "SYS_getpid" +#endif + { + cpu_registers(processor)->gpr[3] = (int)getpid(); + break; + } + + + case 37/*SYS_kill*/: +#if (NetBSD >= 199306) && (SYS_kill != 37) +# error "SYS_kill" +#endif + { + pid_t pid = cpu_registers(processor)->gpr[3]; + int sig = cpu_registers(processor)->gpr[4]; + TRACE(trace_tbd, ("SYS_kill - more to this than just a kill\n")); + cpu_halt(processor, cia, was_signalled, sig); + break; + } + + + case 48/*SYS_sigprocmask*/: +#if (NetBSD >= 199306) && (SYS_sigprocmask != 48) +# error "SYS_sigprocmask" +#endif + { + natural_word how = cpu_registers(processor)->gpr[3]; + unsigned_word set = cpu_registers(processor)->gpr[4]; + unsigned_word oset = cpu_registers(processor)->gpr[5]; + TRACE(trace_system, ("SYS_sigprocmask: how=%d, set=0x%x, oset=0x%x\n", + how, set, oset)); + cpu_registers(processor)->gpr[0] = 0; + cpu_registers(processor)->gpr[3] = 0; + cpu_registers(processor)->gpr[4] = set; + break; + } + + + case 54/*SYS_ioctl*/: +#if (NetBSD >= 199306) && (SYS_ioctl != 54) +# error "SYS_ioctl" +#endif + { + TRACE(trace_system, ("SYS_ioctl: d=%d, request=0x%x, argp=0x%x\n", + cpu_registers(processor)->gpr[3], cpu_registers(processor)->gpr[4], cpu_registers(processor)->gpr[5])); + cpu_registers(processor)->gpr[0] = 0; + cpu_registers(processor)->gpr[3] = 0; + break; + } + + + case 189/*SYS_fstat*/: +#if (NetBSD >= 199306) && (SYS_fstat != 189) +# error "SYS_fstat" +#endif + { + int fd = cpu_registers(processor)->gpr[3]; + unsigned_word stat_buf_addr = cpu_registers(processor)->gpr[4]; + struct stat buf; + int nr_moved; + int status; + + /* check buffer all there, by reading it */ + nr_moved = vm_data_map_read_buffer(cpu_data_map(processor), + &buf, + stat_buf_addr, + sizeof(buf), + raw_transfer); + if (nr_moved != sizeof(buf)) + error("system_call()fstat - check on buffer failed\n"); + + /* do the fstat call */ + status = fstat(fd, &buf); + if (status == -1) { + cpu_registers(processor)->gpr[0] = errno; + break; + } + cpu_registers(processor)->gpr[0] = 0; + cpu_registers(processor)->gpr[3] = 0; + + H2T(buf.st_dev); + H2T(buf.st_ino); + H2T(buf.st_mode); + H2T(buf.st_nlink); + H2T(buf.st_uid); + H2T(buf.st_gid); + H2T(buf.st_rdev); + H2T(buf.st_size); + H2T(buf.st_atime); + /* H2T(buf.st_spare1); */ + H2T(buf.st_mtime); + /* H2T(buf.st_spare2); */ + H2T(buf.st_ctime); + /* H2T(buf.st_spare3); */ + H2T(buf.st_blksize); + H2T(buf.st_blocks); +#if (NetBSD >= 199306) + H2T(buf.st_flags); + H2T(buf.st_gen); +#endif + + nr_moved = vm_data_map_write_buffer(cpu_data_map(processor), + &buf, + stat_buf_addr, + sizeof(buf), + raw_transfer, + 0/*violate_ro*/); + break; + } + + + case 202/*SYS___sysctl*/: +#if (NetBSD >= 199306) && (SYS___sysctl != 202) +# error "SYS__sysctl" +#endif + { + /* call the arguments by their real name */ + unsigned_word name = cpu_registers(processor)->gpr[3]; + natural_word namelen = cpu_registers(processor)->gpr[4]; + unsigned_word oldp = cpu_registers(processor)->gpr[5]; + unsigned_word oldlenp = cpu_registers(processor)->gpr[6]; + natural_word oldlen; + natural_word mib; + natural_word int_val; + + /* pluck out the management information base id */ + if (namelen < 1 + || sizeof(mib) != vm_data_map_read_buffer(cpu_data_map(processor), + &mib, + name, + sizeof(mib), + cooked_transfer)) + error("system_call()SYS___sysctl bad name[0]\n"); + name += sizeof(mib); + + /* see what to do with it ... */ + switch (mib) { + case 6/*CTL_HW*/: +#if (NetBSD >= 199306) && (CTL_HW != 6) +# error "CTL_HW" +#endif + if (namelen < 2 + || sizeof(mib) != vm_data_map_read_buffer(cpu_data_map(processor), + &mib, + name, + sizeof(mib), + cooked_transfer)) + error("system_call()SYS___sysctl - CTL_HW - bad name[1]\n"); + name += sizeof(mib); + switch (mib) { + case 7/*HW_PAGESIZE*/: +#if (NetBSD >= 199306) && (HW_PAGESIZE != 7) +# error "HW_PAGESIZE" +#endif + if (sizeof(oldlen) != vm_data_map_read_buffer(cpu_data_map(processor), + &oldlen, + oldlenp, + sizeof(oldlen), + cooked_transfer)) + error("system_call()sysctl - CTL_HW.HW_PAGESIZE - oldlen read\n"); + if (sizeof(natural_word) > oldlen) + error("system_call()sysctl - CTL_HW.HW_PAGESIZE - to small\n"); + int_val = 8192; + oldlen = sizeof(int_val); + if (sizeof(int_val) != vm_data_map_write_buffer(cpu_data_map(processor), + &int_val, + oldp, + sizeof(int_val), + cooked_transfer, + 0/*violate_ro*/)) + error("system_call()sysctl - CTL_HW.HW_PAGESIZE - int_val\n"); + if (sizeof(oldlen) != vm_data_map_write_buffer(cpu_data_map(processor), + &oldlen, + oldlenp, + sizeof(oldlen), + cooked_transfer, + 0/*violate_ro*/)) + error("system_call()sysctl - CTL_HW.HW_PAGESIZE - oldlen write\n"); + break; + default: + error("sysctl() CTL_HW.%d unknown\n", mib); + break; + } + break; + default: + error("sysctl() name[0]=%s unknown\n", (int)mib); + break; + } + cpu_registers(processor)->gpr[0] = 0; + cpu_registers(processor)->gpr[3] = 0; + break; + } + + + default: + error("system_call() unimplemented system call %d, cia=0x%x, arg[0]=0x%x, lr=0x%x\n", + cpu_registers(processor)->gpr[0], cia, cpu_registers(processor)->gpr[3], LR); + break; + + } +} + +#endif /* _SYSTEM_C_ */ diff --git a/sim/ppc/system.h b/sim/ppc/system.h new file mode 100644 index 0000000..4a0df87 --- /dev/null +++ b/sim/ppc/system.h @@ -0,0 +1,29 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1995, Andrew Cagney <cagney@highland.com.au> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + */ + + +#ifndef _SYSTEM_H_ +#define _SYSTEM_H_ + +void system_call +(cpu *processor, + unsigned_word cia); + +#endif diff --git a/sim/ppc/vm.h b/sim/ppc/vm.h new file mode 100644 index 0000000..335b0bc --- /dev/null +++ b/sim/ppc/vm.h @@ -0,0 +1,127 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1995, Andrew Cagney <cagney@highland.com.au> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + */ + + +#ifndef _VM_H_ +#define _VM_H_ + +#ifndef INLINE_VM +#define INLINE_VM +#endif + +typedef struct _vm vm; +typedef struct _vm_data_map vm_data_map; +typedef struct _vm_instruction_map vm_instruction_map; + + +/* each PowerPC requires two virtual memory maps */ + +INLINE_VM vm *vm_create +(core *memory); + +INLINE_VM vm_data_map *vm_create_data_map +(vm *memory); + +INLINE_VM vm_instruction_map *vm_create_instruction_map +(vm *memory); + + +/* address translation, if the translation is invalid + these will not return */ + +INLINE_VM unsigned_word vm_real_data_addr +(vm_data_map *data_map, + unsigned_word ea, + int is_read, + cpu *processor, + unsigned_word cia); + +INLINE_VM unsigned_word vm_real_instruction_addr +(vm_instruction_map *instruction_map, + cpu *processor, + unsigned_word cia); + + +/* block transfers */ + +INLINE_VM int vm_data_map_read_buffer +(vm_data_map *data_map, + void *target, + unsigned_word addr, + unsigned len, + transfer_mode mode); + +INLINE_VM int vm_data_map_write_buffer +(vm_data_map *data_map, + const void *source, + unsigned_word addr, + unsigned len, + transfer_mode mode, + int violate_read_only_section); + + +/* fetch the next instruction from memory */ + +INLINE_VM instruction_word vm_instruction_map_read +(vm_instruction_map *instruction_map, + cpu *processor, + unsigned_word cia); + + +/* read data from memory */ + +#define DECLARE_VM_DATA_MAP_READ_N(N) \ +INLINE_VM unsigned_##N vm_data_map_read_##N \ +(vm_data_map *map, \ + unsigned_word ea, \ + cpu *processor, \ + unsigned_word cia); + +DECLARE_VM_DATA_MAP_READ_N(1) +DECLARE_VM_DATA_MAP_READ_N(2) +DECLARE_VM_DATA_MAP_READ_N(4) +DECLARE_VM_DATA_MAP_READ_N(8) + + +/* write data to memory */ + +#define DECLARE_VM_DATA_MAP_WRITE_N(N) \ +INLINE_VM void vm_data_map_write_##N \ +(vm_data_map *map, \ + unsigned_word addr, \ + unsigned_##N val, \ + cpu *processor, \ + unsigned_word cia); + +DECLARE_VM_DATA_MAP_WRITE_N(1) +DECLARE_VM_DATA_MAP_WRITE_N(2) +DECLARE_VM_DATA_MAP_WRITE_N(4) +DECLARE_VM_DATA_MAP_WRITE_N(8) + + +/* update vm data structures due to a synchronization point */ + +INLINE_VM void vm_synchronize_context +(vm *memory, + spreg *sprs, + sreg *srs, + msreg msr); + +#endif diff --git a/sim/ppc/words.h b/sim/ppc/words.h new file mode 100644 index 0000000..35bdadd --- /dev/null +++ b/sim/ppc/words.h @@ -0,0 +1,100 @@ +/* This file is part of psim (model of the PowerPC(tm) architecture) + + Copyright (C) 1994-1995, Andrew Cagney <cagney@highland.com.au> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + -- + + PowerPC is a trademark of International Business Machines Corporation. */ + + +/* Basic type sizes for the PowerPC */ + +#ifndef _WORDS_H_ +#define _WORDS_H_ + +/* TYPES: + + natural* sign determined by host + signed* signed type of the given size + unsigned* The corresponding insigned type + + SIZES + + *NN Size based on the number of bits + *_NN Size according to the number of bytes + *_word Size based on the target architecture's word + word size (32/64 bits) + +*/ + + +/* bit based */ +typedef char natural8; +typedef short natural16; +typedef long natural32; +typedef long long natural64; + +typedef signed char signed8; +typedef signed short signed16; +typedef signed long signed32; +typedef signed long long signed64; + +typedef unsigned char unsigned8; +typedef unsigned short unsigned16; +typedef unsigned long unsigned32; +typedef unsigned long long unsigned64; + + +/* byte based */ +typedef natural8 natural_1; +typedef natural16 natural_2; +typedef natural32 natural_4; +typedef natural64 natural_8; + +typedef signed8 signed_1; +typedef signed16 signed_2; +typedef signed32 signed_4; +typedef signed64 signed_8; + +typedef unsigned8 unsigned_1; +typedef unsigned16 unsigned_2; +typedef unsigned32 unsigned_4; +typedef unsigned64 unsigned_8; + + +/* for general work, the following are defined */ +/* unsigned: >= 32 bits */ +/* signed: >= 32 bits */ +/* long: >= 32 bits, sign undefined */ +/* int: small indicator */ + +/* target architecture based */ +#if (WITH_64BIT_TARGET) +typedef natural64 natural_word; +typedef unsigned64 unsigned_word; +typedef signed64 signed_word; +#else +typedef natural32 natural_word; +typedef unsigned32 unsigned_word; +typedef signed32 signed_word; +#endif + + +/* Other instructions */ +typedef unsigned32 instruction_word; + +#endif /* _WORDS_H_ */ |