diff options
author | Stan Shebs <shebs@apple.com> | 1999-04-16 01:33:56 +0000 |
---|---|---|
committer | Stan Shebs <shebs@apple.com> | 1999-04-16 01:33:56 +0000 |
commit | 838ae13dc4ab603f1efdf1da653e2ca1b7b009e1 (patch) | |
tree | 8d3c114b0ba9d2a1f0dcadd192ba2aaeeafe7175 /sim/ppc | |
download | gdb-838ae13dc4ab603f1efdf1da653e2ca1b7b009e1.tar.gz |
Initial revision
Diffstat (limited to 'sim/ppc')
137 files changed, 64494 insertions, 0 deletions
diff --git a/sim/ppc/.gdbinit b/sim/ppc/.gdbinit new file mode 100644 index 00000000000..e1e62d62a8c --- /dev/null +++ b/sim/ppc/.gdbinit @@ -0,0 +1,2 @@ +set output-radix 16 +break error diff --git a/sim/ppc/BUGS b/sim/ppc/BUGS new file mode 100644 index 00000000000..362b079e90c --- /dev/null +++ b/sim/ppc/BUGS @@ -0,0 +1,123 @@ +ChangeLog + +See the ChangeLog file looking for lines taged with the word FIXME. + + +COREFILE.C: + +The implementation of corefile.c (defined by corefile.h) isn't the +best. It is intended to be functionaly correct rather than fast. One +option being considered is to add a data cache to reduce the overhead +of the most common case of data read/writes. + + +VEA: + +Missing VEA system calls. + +ppc-instructions: + +Missing or commented out instructions. + + +64bit: + +64bit target untested. 64bit host broken. For instance use of scanf +"%x", &long long. + + + +hw_*.c: + +Better and more devices. + +PORTABILITY: + +(Notes taken from Michael Meissner): Heavy use of the ## operator - +fix using the clasic X/**/Y hack; Use of the signed keyword. In +particular, signed char has no analogue in classic C (though most +implementations of classic C use signed chars); Use of long long which +restricts the target compiler to be GCC. + + +TRACING: + +debug.c: Macro's should be extended to include: + + IS_*TRACE: True if tracing enabled + *TRACE_PREFIX: Outputs just the prefix line + +hw_trace.c: Flush, replace with a psim_set_tracing or some + such program. + + +CIA/NIA: + +Replace with functions to return/increment the CIA? + + + +SMP & GDB: + +GDB doesn't understand SMP! + + + + +OVERALL STRUCTURE: + +A new file pstruct.h is to be created that contains a single flat data +structure containing: + + pstruct { + events; + core; + processor[nr_cpus]; + monitor; + devices; + trace; + } + +The CPU's structure, in turn would contain the VM sub structures. + +When SMP==0, everything would have PSTRUCT passed. In SMP mode, +however, there are two choices: PSTRUCT + CPU_NR or PROCESSOR. I +suspect the latter is better. + +It is believed that this would significantly improve performance (at +the price of reduced control over object scope). + + + + +IGEN: + +Igen at present can't do the following: + + o duplication is an all or nothing afair. + + It should be configurable according to + the instruction or the sub-table. + + + o Due to the naming, only a single generated + simulator can be included in a program. + + IGEN should be able to generate multiple + engines that can all be included in a program + + o handle alternate architectures. + + + o Igen should support the generation of a + disasembler and posibly an assembler. + + I suggest that the table be extended to + include, for each instruction, additional + lines describing the extual format of the + instruction. + + One possible format is: + + "mtlr %RS":SPR.something + "mtspr %SPR, %RS" diff --git a/sim/ppc/COPYING b/sim/ppc/COPYING new file mode 100644 index 00000000000..60549be514a --- /dev/null +++ b/sim/ppc/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 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 00000000000..eb685a5ec98 --- /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 00000000000..49cf8ce11f0 --- /dev/null +++ b/sim/ppc/ChangeLog @@ -0,0 +1,3183 @@ +1999-02-01 Jim Blandy <jimb@zwingli.cygnus.com> + + Make the simulator compatible with the MPC750. It would be nicer + to make this a real multi-sim, but that's more work than we have + time for. + * emul_generic.c (emul_add_tree_options): Only require strict + alignment if it was explicitly requested at configuration time. + Don't make it the default for little-endian machines. + * ppc-spr-table (UMMCR0, UMMCR1, UPMC1, UPMC2, USIA, UPMC3, UPMC4, + MMCR0, PMC1, PMC2, SIA, MMCR1, PMC3, PMC4, L2CR, ICTC, THRM1, + THRM2, THRM3): Plop in the MPC750 SPR registers. + (DABR): This is weird. This was HID5, but the PPC spec says this + should be DABR; why did some random processor use it for something + else? The HID5 entry dates back to the original checkin of the + simulator code in 1995, so remove it. + * sim_calls.c (register_names): Delete this; since the user can + now change GDB's list of register names dynamically, we can't + pretend there's a static mapping here. + (sim_fetch_register, sim_store_register): Call GDB's REGISTER_NAME + function to get the register name. That ought to be accurate. + However, we're changing a compile-time dependency (using the + REGISTER_NAMES macro) into a link- and run-time dependency + (calling REGISTER_NAME, which happens to be a function call on the + PPC). + +1999-01-22 Jim Lemke <jlemke@cygnus.com> + + * igen.c(gen_semantics_[ch]): setup/use of new option + (-o mpc860c0[=n]). + interrupts.[ch](mpc860c0_instruction_program_interrupt): added. + ppc-instructions(the four branch insn groups): detect problematic br's. + psim.c(is_num - added, psim_options): Parse and init new option. + These changes are currently under #ifdef WITH_OPTION_MPC860C0. + +1998-12-01 Ken Raeburn <raeburn@cygnus.com> + + * hw_nvram.c (hw_nvram_bcd): Force value to fit in 0..99. + +Fri Nov 20 12:17:28 1998 Andrew Cagney <cagney@b1.cygnus.com> + + * main.c (sim_io_poll_quit): Stub function. + + * events.c (SIM_EVENTS_POLL_RATE): Define. + (sim_events_poll): Copy function from common/sim-events.c. + (event_queue_init): Copy scheduling of sim_events_poll from same. + + * sim_callbacks.h, sim_calls.c (sim_io_poll_quit): New function, + poll the external environment. + +1998-11-19 Michael Meissner <meissner@cygnus.com> + + * ppc-instructions (is_{NaN,inf}): Use unsigned64 to get the + fractional type, so that quiet NaN's aren't treated like + Infinities. + +Mon Sep 28 09:42:45 1998 Drew Moseley <dmoseley@cygnus.com> + + * table.c (table_open): For cygwin hosts, we need to use the + return value from the read routine as the number of bytes to + process. This apparently is due to text-mode vs binary-mode. If + the mounts are done text-mode, then the size returnedby fstat() + may be different than the number of bytes "read" in text mode. + +Sun Oct 4 00:50:47 1998 Felix Lee <flee@cygnus.com> + + * emul_netbsd.c (do_open): fix order-of-evaluation problem. + (do_close): ditto. + (do_fstat): ditto. + (do_lstat): ditto. + +1998-09-03 Michael Meissner <meissner@cygnus.com> + + * emul_{netbsd,unix}.c: Update copyright year. + +Mon Jun 29 10:57:36 1998 Michael Snyder <msnyder@cleaver.cygnus.com> + + * sim_calls.c (sim_fetch_register, sim_store_register): + return zero when nothing to do. + +1998-06-26 Michael Meissner <meissner@cygnus.com> + + * configure.in (AC_CHECK_HEADERS): Don't check for sys/mount.h. + * configure: Regenerate. + * emul_{netbsd,unix}.c (toplevel): No longer try to include + sys/mount.h. It conflicts on Linux when gnu libc2 is used. + +Tue May 12 12:10:33 PDT 1998 James Ingham <jingham@leda.cygnus.com> + + * Makefile.in: The run target depended on a target psim$(EXEEXT), + but there was no such target, only plain psim. So I changed the + run target to depend on psim. + +Sat May 2 01:10:12 1998 Stu Grossman <grossman@babylon-5.cygnus.com> + + * aclocal.m4: Remove defs of AM_EXEEXT and AM_CYGWIN32. These are + now defined in ../common/aclocal.m4, and the double definition causes + problems with AC_SUBST of EXEEXT. + * configure: Regenerate. + +Wed Apr 29 15:44:52 1998 Geoffrey Noer <noer@cygnus.com> + + * aclocal.m4: new file for AM_EXEEXT macro + * configure.in: call AM_EXEEXT + * configure: regenerate with autoconf 2.12.1. + * Makefile.in: add EXEEXT support + +Sun Apr 26 15:31:55 1998 Tom Tromey <tromey@creche> + + * configure: Regenerated to track ../common/aclocal.m4 changes. + * config.in: Ditto. + +Sun Apr 26 15:19:51 1998 Tom Tromey <tromey@cygnus.com> + + * acconfig.h: New file. + * configure.in: Reverted change of Apr 24; use sinclude again. + Don't call AC_C_CROSS. + +Fri Apr 24 14:16:40 1998 Tom Tromey <tromey@creche> + + * configure: Regenerated to track ../common/aclocal.m4 changes. + * config.in: Ditto. + +Fri Apr 24 11:18:46 1998 Tom Tromey <tromey@cygnus.com> + + * Makefile.in (top_builddir): New macro. + (INTLLIBS): New macro. + (INTLDEPS): Likewise. + (psim): Depend on INTLDEPS; link against INTLLIBS. + * configure.in: Call CY_GNU_GETTEXT. + +Wed Apr 22 14:28:48 1998 Michael Meissner <meissner@cygnus.com> + + * configure: Regenerate with autoconf 2.12.1. + +Fri Mar 13 09:25:58 1998 Andrew Cagney <cagney@b1.cygnus.com> + + * psim.c (psim_read_register, psim_write_register): Handle updates + for FPSCR. + + * registers.c (register_description): Reconize "FPSCR". + + * emul_netbsd.c (emul_netbsd_create): When FP available, enable + MSR FP exception mode. Do not enable FPSCR bits. + * emul_unix.c (emul_unix_create): Ditto. + +Tue Feb 17 12:48:58 1998 Andrew Cagney <cagney@b1.cygnus.com> + + * sim_calls.c (sim_store_register, sim_fetch_register): Pass in + length parameter. Return -1. + +Mon Feb 9 14:13:14 1998 Andrew Cagney <cagney@b1.cygnus.com> + + * ppc-instructions (fdiv, fdivs): Check for divide by zero. + (is_invalid_zero_divide, invalid_zero_divide_operation): New + functions. + +Wed Dec 10 17:38:28 1997 Andrew Cagney <cagney@b1.cygnus.com> + + * sim_calls.c (sim_load): Do not parse PROG using buildargv, use + raw value instead. + +1997-11-05 Felix Lee <flee@cygnus.com> + + * emul_chirp.c: #ifdef HAVE_UNISTD_H + +Wed Oct 15 08:50:54 1997 Andrew Cagney <cagney@b1.cygnus.com> + + * corefile.c (core_attach): Pad out allocated memory regions so + that they are always correctly aligned. + (struct _core_mapping, core_map_attach, core_init, + new_core_mapping): Change free_buffer to type void*. + +Mon Oct 6 18:09:26 1997 Michael Meissner <meissner@cygnus.com> + + * sim_calls.c (zfree): Call free correctly. + +Mon Sep 29 10:05:01 1997 Andrew Cagney <cagney@b1.cygnus.com> + + * sim_calls.c (zfree): Use free, not mfree. + (sim_io_flush_stdoutput): Replace gdb_flush with callback -> + flush_stdout. + +Fri Sep 26 09:50:29 1997 Andrew Cagney <cagney@b1.cygnus.com> + + * ppc-instructions (sraw, slw, srw): From Charles Lefurgy, Fix + mask extracting shift amount. Correctly condition for setting XER + in sraw. + (ldhau): From Johannes Reisinger, update rA after load. + +Tue Sep 9 22:13:23 1997 Felix Lee <flee@cygnus.com> + + * basics.h (CONCAT*): token-pasting macros, if ALMOST_STDC, + for MSVC. + * words.h: __int64 instead of long long for MSVC. + +Wed Aug 27 10:24:15 1997 Andrew Cagney <cagney@b1.cygnus.com> + + * sim_calls.c (sim_create_inferior): Check the simulator was + initialized before creating inferior. + + * idecode_expression.h (ALU_END): From Charles Lefurgy - Extract + sign bit using 64 bit and not a 32 bit mask. + +Wed Aug 27 10:15:48 1997 Andrew Cagney <cagney@b1.cygnus.com> + + * sim_calls.c (sim_load): From Ian Lance Taylor - free argv after + it has been used, not before. + +Tue Aug 26 10:41:35 1997 Andrew Cagney <cagney@b1.cygnus.com> + + * sim_calls.c (sim_kill): Delete. + (sim_create_inferior): Add ABFD argument. + (entry_point): Delete variable. + (sim_load): Move setting of PC from here. + (sim_create_inferior): To here. + +Mon Aug 25 16:17:06 1997 Andrew Cagney <cagney@b1.cygnus.com> + + * sim_calls.c (sim_open): Add ABFD argument. + +Thu Jul 3 10:18:06 1997 Andrew Cagney <cagney@b1.cygnus.com> + + * ppc-instructions (PPC_INSN_INT): From Michael Thies - Monitoring + CR register updates dependant on RC value had logic backwards. + + * ppc-instructions (Load String Word Immediate): From Brad Parker + - sense of wrap test in check for overwriting RA wrong. + (Load String Word Indexed): Ditto. + + * configure.in: From Erik Landry - set sim_default_model not + sim_model for sim-default-model option. + * configure: Regenerate. + + * interrupts.c (check_masked_interrupts): Schedule a hardware + interrupt delivery when FP interrupts get enabled. + (program_interrupt): Generate FP exceptions instead of aborting. + (deliver_hardware_interrupt): Deliver a FP exception if so + enabled. + + * registers.h: Add definition of fpscr_vx_bits. + + * idecode_expression.h (FPSCR_END): Always update FEX and VX bits + in FPSCR. + (FPSCR_END): Explicitly check for possible floating point + exception conditions. + (FPSCR_BEGIN): Simplify. + + * ppc-instructions (Move From FPSCR): Enable. + (Move To FPSCR Bit 1): Ditto. + (Move To FPSCR Bit 0): Ditto. + (Move To FPSCR Field Immediate): Ditto. + (Move to Condition Register from FPSCR): Simplify. + (invalid_arithemetic_operation): Generate a QNaN when invalid + operation exception disabled. + +Tue May 20 10:22:50 1997 Andrew Cagney <cagney@b1.cygnus.com> + + * sim_calls.c (sim_open): Add callback argument. + (sim_set_callbacks): Delete. + +Tue Apr 22 22:36:57 1997 Mike Meissner <meissner@cygnus.com> + + * sim_callbacks.h (error): Make declaration match gdb's. + * main.c (error): Ditto. + +Fri Apr 18 17:03:09 1997 Andrew Cagney <cagney@b1.cygnus.com> + + * sim_calls.c (sim_stop_reason): Simplify. Was running implies + stopped/SIGINT. Exit implies a status code. + + * psim.c (cntrl_c_simulation): From main.c. Event function that + halts the simulator. + (psim_stop): New. Asynchronously schedule a stop simulator event. + (psim_run_until_stop): Delete. Made redundant by psim_stop. + + * main.c (cntrl_c): Update. + (cntrl_c_simulation): Moved to psim.c. + + * sim_calls.c (sim_stop): New function. Use psim_stop which + schedules a stop event. + (sim_resume): Drop SIGINT handler, now in gdb/main.c. + (sim_resume): Use psim_run as stop variable no longer needed. + +Fri Apr 18 17:03:08 1997 Andrew Cagney <cagney@b1.cygnus.com> + + * psim.c (psim_options): Handle -E option correctly. + (psim_usage): Document. + +Thu Apr 17 03:28:03 1997 Doug Evans <dje@canuck.cygnus.com> + + * psim.c (psim_options): Ignore -E option (sets endianness). + * sim_calls.c: #include bfd.h. + (entry_point): New static local. + (sim_load): Return SIM_RC. New arg abfd. Set start address from bfd. + (sim_create_inferior): Return SIM_RC. Delete arg start_address. + +Tue Apr 15 14:57:18 1997 Ian Lance Taylor <ian@cygnus.com> + + * Makefile.in (INSTALL): Set to @INSTALL@. + (INSTALL_XFORM, INSTALL_XFORM1): Remove. + (install): Depend upon installdirs. Use $(program_transform_name) + directly, rather than using $(INSTALL_XFORM). + (installdirs): New target. + +Fri Apr 4 17:54:36 1997 Jim Wilson <wilson@cygnus.com> + + * Makefile.in (tmp-hw, tmp-pk): Use for loop to eliminate duplicates + rather than the non-portable cat -n. + +Mon Apr 14 16:29:51 1997 Ian Lance Taylor <ian@cygnus.com> + + * Makefile.in (INSTALL): Change install.sh to install-sh. + +Tue Apr 1 18:15:14 1997 Jim Wilson <wilson@cygnus.com> + + * ppc-instructions: Change milhwu to mulhwu. + +Wed Apr 2 15:38:08 1997 Doug Evans <dje@canuck.cygnus.com> + + * sim_calls.c (sim_open): New arg `kind'. + +Wed Apr 2 14:51:17 1997 Ian Lance Taylor <ian@cygnus.com> + + * COPYING: Update FSF address. + +Tue Mar 25 16:17:59 1997 Andrew Cagney <cagney@kremvax.cygnus.com> + + * main.c (sim_io_read_stdin): Only compile unbuffered IO code if + all the required features are supported by the host OS. + +Tue Mar 25 12:13:02 1997 Andrew Cagney <cagney@kremvax.cygnus.com> + + * emul_bugapi.c (emul_bugapi_create): Guard against NULL images. + + * configure.in (enable-sim-endain): Correct typo in usage (from + Erik Landry <landry@ENGR.ORST.EDU>). + * configure: Re-generate. + +Fri Mar 14 18:23:02 1997 Andrew Cagney <cagney@kremvax.cygnus.com> + + * Makefile.in (targ-vals.def): Re-do rule so that it works with + FreeBSD's make. Didn't like $<. + +Thu Mar 13 12:55:48 1997 Doug Evans <dje@canuck.cygnus.com> + + * sim_calls.c (sim_open): New SIM_DESC result. Argument is now + in argv form. + (other sim_*): New SIM_DESC argument. + +Thu Feb 13 10:35:14 1997 Andrew Cagney <cagney@phydeaux.cygnus.com> + + * ppc-opcode-simple-array, ppc-opcode-simple-goto, + ppc-opcode-simple-switch, ppc-opcode-complex-array, + ppc-opcode-complex-goto, ppc-opcode-complex-switch, + ppc-opcode-jump, ppc-opcode-goto, ppc-opcode-flat: Delete, + superseeded by --sim-decode-mechanism option. + + * ppc-opcode-simple, dc-simple: Rename to be 8.3 + * ppc-opcode-complex, dc-complex: Ditto. + * ppc-opcode-stupid, dc-stupid: Ditto. + * ppc-opcode-test-1, dc-test.01: Ditto. + * ppc-opcode-test-2, dc-test.02: Ditto. + + * configure.in (--enable-sim-opcode): Change prefix to dc- instead + of ppc-opcode-. + +Wed Feb 12 19:33:45 1997 Andrew Cagney <cagney@phydeaux.cygnus.com> + + * Many of the ppc-opcode-* files are identical baring the type of + lookup table. Instead of having multiple tables, igen can do this + via an additional option. + + * ld-decode.h, ld-decode.c (force_decode_gen_type): New function, + allow the type of generated table specified in the decode file to + be overridden. + + * ld-decode.c (load_decode_table): Allow the table type to be + overridden. + + * igen.c (main): Add -T <mechanism> option so that an overriding + instruction decode mechanism can be specified. + + * configure.in: New option --sim-decode-mechanism to control + igen's new -T <mechanism> flag. + * Makefile.in (IGEN_FLAGS): Add IGEN_IDECODE_MECHANISM set by the + configure script. + * configure: Regenerate. + +Tue Feb 11 13:49:10 1997 Michael Meissner <meissner@tiktok.cygnus.com> + + * events.c (event_queue_create): Don't use NULL to initialize an + integer field. + (even_queue_{init,schedule_after_signal,tick}): Conditionalize use + of sigprocmask to appropriate autoconf test. + + * main.c ({cntrl_c,main}): Use RETSIGTYPE for signal return type, + don't assume void. + * sim_calls.c (sim_{ctrl_c,resume}): Ditto. + + * Makefile.in (callback.o): Define HAVE_CONFIG_H, so callback.c + includes our config.h. + +Tue Feb 4 13:42:59 1997 Doug Evans <dje@canuck.cygnus.com> + + * configure.in: Fix typo in test for callback.c. + * configure: Regenerated. + +Fri Feb 7 10:04:25 1997 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * emul_chirp.c (emul_chirp_create): Handle a virtbase of -1 being + found in the device tree. + +Wed Feb 5 10:56:27 1997 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * Property create/initialization still wasn't correctly ordered. + Should be delaying everything related to ihandle creation until + after the rest of the tree has been established. + + * device.c (device_find_ihandle_runtime_property): Update. + (device_add_ihandle_runtime_property): Update. + + * tree.c (parse_ihandle_property): Delay lookup of the device to + be opened until the ihandle initialization phase. + * tree.c (print_properties): Update. + +Wed Feb 5 10:56:27 1997 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * gen-icache.c (print_icache_extraction): Add a reason parameter. + Augment each extracted field with a comment citing the codes + origin. Should simplify tracking down incorrect cache + extractions. + +Tue Feb 4 17:44:51 1997 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * gen-icache.c: Generalize code handling XXX_is_NNN so that it + works for normal and boolean table entries. + + * psim.c (psim_write_memory): last_cpu == -1 or nr_cpus is now + valid. Handle this just like *_{read,write}_register now handles + it. + +Mon Feb 3 17:18:16 1997 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * events.c (insert_event_entry): Correct loop termination + assertions. + +Fri Jan 31 16:20:26 1997 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * psim.c (psim_options): Add new option -c for max-iterations or + count. + (psim_usage): Document. + (psim_max_iterations_exceeded): New function, abort simulation if + max iterations exceeded. + + * gen-idecode.c: Re-work the table lookup code so that it assumes + that the entry is a leaf by default. Simplify the boolean table + entry code so that it involves a mask + test instead of shift + + shift + mask + test. + + * gen-idecode.c: Correct generated igen body so that it no drops + or doubles clock interrupts. + + +Thu Jan 30 11:23:20 1997 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * Makefile.in (BUILT_SRC_WO_CONFIG): Change targ-vals.* to + @sim_targ_vals@ + + * configure.in (sim_callback, sim_targ_vals): Set sim_targ_valls + if common callback is present. + +Wed Jan 29 12:32:41 1997 Michael Meissner <meissner@tiktok.cygnus.com> + + * configure.in (sim_callback): If the gdb is post 4.16, configure + callback support from the common directory. + * configure: Regenerate. + + * Makefile.in (BUILT_SRC_WO_CONFIG): Add targ-vals.{h,def} and + targ-map.c. + (GDB_OBJ): Add callback support configured in. + (gentmap,targ-vals.def): Build from common directory. + (targ-vals.h,targ-map.c): Build by running gentmap. + (callback.o): Build from source in common directory. + (targ-map.o): Add dependency. + (clean): Remove gentmap. + +Wed Jan 29 12:14:19 1997 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * igen wasn't aborting if the opcode table contained no valid + fields. + + * misc.c (name2i): Possibly abort if an invalid name is + encountered. + * ld-decode.c: Abort if the table type isn't found. + +Wed Jan 29 12:14:19 1997 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * When performance monitoring is disabled, it is still possible to + determine the simulation speed by looking at the number of elapsed + ticks recorded by the event queue. + + * psim.c (psim_write_register, psim_read_register): Force the cpu + to zero when it is either of `-1' or `nr_cpus'. In both cases the + next cpu would be zero any way. + + * mon.c (mon_print_info): If possible, print the system cycle + performance. This is an indication of the number of instructions + per second. + +Wed Jan 29 12:14:19 1997 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * The code to allow an event queue to be updated during a signal + was missing. For main.c, a cntrl-c simulation termination wasn't + handled cleanly. + + * The simulation would not correctly restart if an event requested + that the simulation be halted. + + * psim.c (psim_options): Add hack to -i option to optionally + include a level vis -i2. + (psim_usage): Document. + + * main.c (cntrl_c, cntrl_c_simulation): New functions. When a + cntrl-c occures schedule an event to halt the simulation. + (main): Catch CNTRL-C signals with the function cntrl_c. + + * events.c (event_queue_process): Mask interrupts while + manipulating the async event queue. + (event_queue_init): Ditto. + (event_queue_schedule_after_signal): Ditto. + + * events.c (event_queue_process): Mark the event queue as being in + the processing state when processing has started. Adjust code + so that it is tolerant of halts. + (event_queue_init): Start the event queue out with processing + false. + (event_queue_tick): Check that processing isn't still being + performed. + + * gen-idecode.c (print_run_until_stop_body): Call + event_queue_process_events to clear possibly pending events before + starting a simulation run. Re-arange main loop so that simulator + is correctly restarted when an event halts the simulation. + + * psim.c (psim_halt): Handle an event halting the simulation. + * psim.c (psim_init): Adjust initial cpu - == -1 - to match + reworked idecode. + +Wed Jan 29 12:14:19 1997 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * ppc-opcode-complex: Correct typo - was expanding ORA instead of + RA. Based on instruction frequency stats, expand additional + instructions. + * ppc-instructions: Change all `RA == 0' to RA_is_0. + + * ppc-opcode-stupid: Move all but the basic table in -complex into + here. Update to new format. + + * Makefile.in (tmp-defines): New target. Force defines.h to always + be built. Hence get ppc-opcode-goto to build. + +Tue Jan 28 13:00:19 1997 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * hw_com.c (hw_com_instance_read, hw_com_instance_write): + Implement. + +Thu Jan 23 09:07:26 1997 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * hw_trace.c (hw_trace_init_data): Delete. The trace options need + to be initialized independant of the rest of the simulation + initalization. Otherwize a trace option explictly set from gdb + could be overridden by hw_trace. + + * psim.c (psim_options): Clarify reason why the trace ioctl occures. + + * FIXME: The trace code is too scattered - hw_trace.c, psim.c, + debug.c. It could be much simpler. + +Thu Jan 23 09:07:26 1997 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * Some devices support removable media. Add hooks to the disk + device so that it supports this. + + * device.c (device_add_string_array_property, + device_find_string_array_property): New functions, manipulate + properties containing an array of strings. + (device_find_string_property): Allow a string array. + (device_init_static_properties): Update. + (device_init_runtime_properties): Update. + + * hw_disk.c (hw_disk_ioctl): Add ioctl for changing the disk + media. If no file image is specified, use the next one in the + image property list. + (hw_disk_init_address): Change the file property so that it is a + string array - use the first entry for the initial file image. + + * tree.c (print_string_aray_property): New function - print a + string array. + (print_properties): Adjust. + (print_string): Write a string, handling double quotes. + + * device.h: Define an ioctl to `change-media' with an optional new + media image. + + * hw_disk.c: Allow floppy disk devices to be specified. + + * psim.c (psim_command): New function, parse more complex psim + commands such as "change-media" and "trace". + * sim_calls.c (sim_do_command): Use. + +Wed Jan 22 09:38:33 1997 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * For expressions like (RA == 0) that are entered in to the cache + as RA_is_0. If possible generate the result of the expression so + that the compiler gets a better chance of eliminating dead + branches. + + * gen-icache.c (print_icache_extraction): For a cache entry of + the form <name>_is_<const> where it is a boolean field, generate + the result of the expression instead of the expression its self. + (print_icache_body): Remove code that was looking for *_is_0 and + then generating corresponding definitions. + + * gen-icache.c (print_icache_struct): If there is no cache, do not + output expressions in idecode.h file. + + * gen-icache.c (print_icache_body): Output them here. + + * ppc-opcode-complex: Clarify constant values for SPR==LR register + expansion. + + * ppc-cache-rules (RA_is_0, SPR_is_256): Two new cache entries. + +Wed Jan 22 12:24:52 1997 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * The code that put values in and extracted values from the cache + was too compilicated. The cache table did not allow values to be + computed from cache entries. #defines could only be used when a + cache was present, remove the restriction. + + * ld-cache.h, ld-cache.c: Add a new cache entry type - SCRATCH. A + scratch variable is defined when a cache entry is beinf + filled. Change the definition of a COMPUTE variable to be defined + when the cache entry is being used. + * gen-icache.c: Update. + + * ld-cache.h, ld-cache.c: Change field names so that their meaning + is more obvious. old_name->field_name, new_name->derived_name. + * gen-icache.c: Update + + * gen-icache.h, gen-icache.c (print_icache_body): Make the three + different types of cache code - put into cache, extract from + cache, no cache - an explicit argument to print_icache_body. + * gen-icache.c (print_icache_extraction): Ditto. + + * gen-semantics.c (print_c_semantic): Update use. + * gen-idecode.c (print_jump_insn): Update use. + * gen-icache.c (print_icache_function): Update use. + + * igen.c (main): Change 'R' option so that it does not force the + cache. + + * configure.in (enable-sim-icache): Clarify description. Make + #define one of the defaults regardless of the cache. Probably + should revamp and add a separate option. + +Tue Jan 21 13:26:10 1997 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * pk_disklabel.c (block_is_fdisk): Tidy up traces - use dos + partition numbering. + (pk_disklabel_create_instance): Partition 1..4 are valid - not + 1..3. + (is_iso9660): New function, verify a CD9660 File system. + (pk_disklabel_create_instance): Start expanding so that active + partition selection is supported. + +Mon Jan 20 11:20:15 1997 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * The cap object was retaining a reference to the instance of a + device after it was deleted. Instead add and remove cap's from the + cap db as they are created and deleted. This ensuring that a + capibility is only used during the lifetime of the corresponding + object. + + * cap.h, cap.c: Correct cap type - was signed32 should be + signed_cell. + + * cap.c (cap_add, cap_remove): New methods for cap object that + allow the explicit addition and removal of internal objects that + the cap knows about. + + * cap.c (cap_init): Rewrite. Verify that the only objects + remaining in the cap data base are those that were entered first. + Thse objects will be the permenant ones. + * device.c (device_init_address): Remember to initialize the cap + database. + + * device.c (device_create_instance_from): Explicitly add device + instances to the cap database. Simplify create code. + (device_instance_delete): Explicitly remove device instances from + the cap database. + + * device.c (device_create_from): Explicitly add a device to the + cap data base. + + * device.c (device_create_from): Always set the cap members. + + * hw_disk.c: Output the instance when tracing. + +Sun Jan 19 16:44:29 1997 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * tree.c (split_device_specifier): Add support for aliases when + looking up a device. Now needs a device as an argument. + (split_property_specifier): Ditto. + +Sun Jan 19 15:28:23 1997 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * The memory "claim" and "release" methods take an address and + size as arguments. These may be multi cell values. Initially fix + the memory code so that they check/detect this. Leave the + adjustment of any clients to later. + + * hw_memory.c (hw_memory_instance_claim, + hw_memory_instance_release): Handle multi-cell memory devices. + + * hw_memory.c (hw_memory_instance_claim): Be tolerant towards the + release of memory regions that were not claimed. + +Fri Jan 17 12:01:07 1997 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * device.h, device.c (device_instance_call_method): Correct return + type - can return either 0 or -1, hence should be a signed type. + * device_table.h: Ditto. + + * hw_memory.c (hw_memory_instance_claim, + hw_memory_instance_release): Update. + * hw_disk.c (hw_disk_max_transfer, hw_disk_block_size, + hw_disk_nr_blocks): Ditto. + +Fri Jan 17 11:50:13 1997 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * emul_chirp.c (chirp_emul_claim): Implement using the "claim" + method belonging to "/chosen/memory". + (chirm_emul_release): Ditto. + + * Makefile.in (LIB_INLINE_SRC): Remove emul_* from list of files + that are inlined. These modules are called via a table and are + not made inline. + + * hw_init.c (update_for_binary_section): Fix failure to allocate + memory used by the binary in real-mode executions. If "claim" + property is present, allocate memory from the "/chosen/memory" + device. + + * emul_chirp.c (emul_chirp_create): Specify that memory should be + claimed when loading a real image. + + * hw_memory.c (hw_memory_instance_claim): Don't page align memory + allocations. + + * hw_memory.c (hw_memory_instance_release): Avoid infinite loop + when merging adjacent memory chunks. + +Thu Jan 16 08:51:25 1997 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * vm.h (vm_data_map_read_buffer, vm_data_map_write_buffer): Add + optional PROCESSOR & CIA args so that this routine also abort an + access. + + * vm_n.h (vm_data_map_read_N, vm_data_map_write_N): For a + miss-aligned access when a transfer fails abort. + + * emul_bugapi.c (emul_bugapi_do_write): Use emul_read_buffer + instead of the vm_read_buffer. + * emul_netbsd.c (do_write): Ditto. + * emul_unix.c (do_unix_write): Ditto. + +Wed Jan 15 14:38:25 1997 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * configure.in (--enable-sim-jump): Default is NULL and not -E. + * configure: Regenerate. + + * basics.h (__attribute__): Enable attributes if GCC >= 2.6. + (UNUSED): Only enable UNUSED if GCC >= 2.7. + + * gen-icache.c (print_icache_extraction): Print UNUSED macro + instead of explicit __unused__ attribute. + (print_icache_body): Ditto. + * idecode_expression.h (FPSCR_BEGIN): Use UNUSED. + +Wed Jan 15 13:54:50 1997 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * cpu.h, cpu.c (cpu_synchronize_context): Add CIA argument as + reference point. + + * vm.c (vm_synchronize_context): Add PROCESSOR and CIA as + arguments so that there is a reference point for recovery. + (vm_synchronize_context): Pass processor+cia for errors. + (om_unpack_sr): Ditto. + (om_unpack_srs): Ditto. + * vm.c (vm_create): Review error messages. + + * vm.c: Include "cpu.h" so that cpu_error is visible. + + * ppc-instructions (Return From Interrupt): Pass CIA. + (Instruction Synchronize): Ditto. + * psim.c (psim_init): Ditto. + +Wed Jan 15 12:25:11 1997 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * cpu.h, cpu.c (cpu_error): Aborts simulation with error message, + but also saves current processor state. + + * basics.h: Move #include <stdarg.h> to here from device_table.h. + + * interrupts.c (perform_oea_interrupt): Use. No longer loose CIA + when simulation aborted. + (program_interrupt): Ditto. + (floating_point_unavailable_interrupt): Ditto. + (alignment_interrupt): Ditto. + (floating_point_assist_interrupt): Ditto. + (perform_oea_interrupt): Ditto. + (machine_check_interrupt): Ditto. + +Tue Jan 14 12:19:10 1997 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * ppc-instructions (Move from Special Purpose Register): Support + move from DEC. + +Mon Jan 13 16:58:12 1997 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * debug.h, debug.c: Add "interrupts" trace option. + + * interrupts.c (data_storage_interrupt): Add tracing. + (machine_check_interrupt): Ditto. + (instruction_storage_interrupt): Ditto. + (alignment_interrupt): Ditto. + (program_interrupt): Ditto. + (floating_point_unavailable_interrupt): Ditto. + (system_call_interrupt): Ditto. + (floating_point_assist_interrupt): Ditto. + (deliver_hardware_interrupt): Ditto. + + * interrupts.c (program_interrupt): For UEA mode, halt the + processor - so that the current state is saved - instead of + aborting. + (floating_point_unavailable_interrupt): Ditto. + (floating_point_assist_interrupt): Ditto. + +Thu Jan 2 09:10:41 1997 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * interrupts.c (perform_oea_interrupt): Halt rather than abort on + a double interrupt. + +Wed Jan 1 22:54:52 1997 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * ppc-instructions (Store Multiple Word, Load Multiple Word): + Enable. + + * tree.c (print_properties): For an array consider printing it out + as an integer array. + + * hw_memory.c (hw_memory_init_address): If an "available" property + is present, use that to initialize the available memory instead of + using the reg property. + + * emul_generic.c (emul_add_tree_hardware): Add "available" + property to memory device. + +Fri Dec 20 13:19:07 1996 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * ppc-instructions (Rotate Left Word then AND with Mask): Enable. + + * device.c (device_instance_call_method): Was only looking at the + first method. + + * hw_disk.c (hw_disk_nr_blocks): Implement #blocks method. + (hw_disk_block_size): Implement block-size method. + (hw_disk_max_transfer): Implement max-transfer method. + + * hw_phb.c (hw_phb_init_address): Reinit the rest of the PHB. + + * emul_chirp.c (chirp_emul_instance_to_path): Recover from an + invalid ihandle. + (chirp_emul_instance_to_package): Ditto. + (chirp_emul_method): Ditto. + (chirp_emul_read): Ditto. + (chirp_emul_write): Ditto. + (chirp_emul_close): Ditto. + (chirp_emul_seek): Ditto. + (chirp_emul_package_to_path): Ditto (for phandle). + (chirp_emul_package_to_path): Return the length. + + * psim.c (psim_merge_device_file): Allow continuation lines. + +Thu Dec 19 11:09:43 1996 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * emul_chirp.c (chirp_emul_boot): Implement. Well report the new + string and exit. + + * emul_chirp.c (chirp_emul_exit): Correct type of args struct + members - *_cell not host dependant int. + +Wed Dec 18 17:49:59 1996 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * interrupts.c (perform_oea_interrupt): Print additional + information if a double interrupt is encountered. + +Wed Dec 18 17:49:59 1996 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * psim.c (psim_merge_device_file): Tolerate an incorrect file-name + being specified with the -f option. + (psim_merge_device_file): Correct check for end of string. + +Wed Dec 18 17:49:59 1996 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * emul_chirp.c (chirp_emul_peer): Was falling off the end of the + list of devices. Return zero to the client instead. + * emul_chirp.c (chirp_emul_child): Ditto + * emul_chirp.c (chirp_emul_parent): Ditto + + * device.c (device_root): Assert assumption about the device being + valid. + +Tue Dec 17 15:12:38 1996 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * emul_chirp.c (emul_chirp_create): Add description property to + each significant node in the device tree. + * emul_bugapi.c (emul_bugapi_create): Ditto. + +Fri Dec 13 14:30:31 1996 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * main.c (sim_io_read_stdin): For a single byte STDIO read, use a + tempoary two byte buffer. Single byte read with fgets will not + work. + * main.c: Include errno.h. + (sim_io_read_stdin): For non-STDIO, make it work. + + * emul_chirp.c (chirp_emul_read): Return the correct error status. + +Fri Dec 13 14:30:31 1996 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * std-config.h (WITH_STDIO): Don't hard-wire the STDIO mechanism. + Instead have each emulation default it to DO_USE_STDIO. + + * emul_generic.c (emul_add_tree_options): Select the STDIO I/O + mechanism as the default if enabled or if nothing selected. + + * sim_calls.c (sim_io_read_stdin): Passify GCC's desire for a + return value. + (sim_io_write_stdout): Ditto. + (sim_io_write_stderr): Ditto. + * main.c (sim_io_write_stdout): Ditto. + (sim_io_write_stderr): Ditto. + (sim_io_read_stdin): Ditto. + +Tue Dec 10 10:31:48 1996 Michael Meissner <meissner@tiktok.cygnus.com> + + * emul_chirp.c (emul_chirp_instruction_call): Make *printf calls + type correct. + * vm.c (om_effective_to_virtual): Ditto. + * events.c (event_queue_schedule{,_after_signal}): Ditto. + (event_queue_{deschedule,process}): Ditto. + * hw_htab.c (htab_decode_hash_table): Ditto. + (htab_map_{page,binary}): Ditto. + * hw_opic.c (hw_opic_init_data): Ditto. + (handle_interrupt): Ditto. + (do_processor_init_register_{read,write}): Ditto. + (write_vector_priority_register): Ditto. + ({read,write}_destination_register): Ditto. + (do_suprious_vector_register_{read,write}): Ditto. + (do_current_task_priority_register_N_{read,write}): Ditto. + (do_timer_frequency_reporting_register_{read,write}): Ditto. + (do_timer_N_{current,base}_count_register_{read,write}): Ditto. + (do_ipi_N_dispatch_register_write): Ditto. + (do_vendor_identification_register_read): Ditto. + (do_feature_reporting_register_N_read): Ditto. + (do_global_configuration_register_N_{read,write}): Ditto. + * hw_phb.c (hw_phb_attach_address): Ditto. + (hw_phb_unit_decode): Ditto. + (hw_phb_address_to_attach_address): Ditto. + (hw_phb_io_{read,write}_buffer): Ditto. + * hw_ide.c (setup_fifo): Ditto. + + * sim_calls.c ({defs,callback,remote-sim}.h): Find gdb include + files via -I<dir> instead of using "../../gdb/" prefixes. + +Tue Dec 10 10:12:44 1996 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * debug.h: Add tracing for the pal device. + * hw_pal.c: Update. + + * emul_chirp.c (chirp_emul_getprop): More tracing. + +Tue Dec 10 10:12:44 1996 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * device.h, device.c (device_find_ihandle_runtime_property): New + function. Reverse of add_ihandle_runtime property. + (device_init_runtime_properties): Use it. + + * device.c (find_property_entry): New function returns the + internal property spec. + (device_set_property): Use. + (device_find_property): Use. + +Tue Dec 10 10:12:44 1996 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * psim.c (psim_merge_device_file): Strip newline from device + specs. + +Tue Dec 10 10:12:44 1996 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * hw_htab.c (htab_map_binary): For overlapping text / data maps + merge the two. Also check that the merge is safe. + + * emul_chirp.c (emul_chirp_create): Add a description property to + the pte's so that they are easier to identify. + + (emul_chirp_create): Don't specify a load address for the CHRP + image. Always use the values specified by the executable. + + * hw_htab.c (htab_map_page): Abort if a duplicate map is + encountered. + +Mon Dec 9 12:08:46 1996 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * hw_htab.c (htab_map_page): Formatting. + + * emul_chirp.c (emul_chirp_instruction_call): Check for a NULL + method name when handling the client call. Also check for other + bad call arguments. + + * emul_chirp.c (emul_chirp_create): Allow real-mode?, real-base, + etc to be overriden. + +Mon Dec 9 12:08:46 1996 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * os_emul.c (os_emul_create): Use tree find property instead of + device find property - sigh. + +Thu Dec 5 10:46:42 1996 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * 961205: Release snapshot 961205. + +Thu Dec 5 10:46:42 1996 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * configure.in (hostbitsize, bitsize): Fix typo in error message - + cannot contain a comma. + (sim-warnings): Check for more potential errors. + + * psim.c (psim_usage): Add -f <file> option. Specifies a file + containing device tree specifications that should be merged into + the device tree. + + * configure.in: Sort options. + * configure: Rebuild + +Wed Dec 4 13:57:31 1996 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * psim.c (psim_usage): Add -n option - specify number of + processors. + + * emul_chirp.c: Add description. + * emul_bugapi.c: Ditto. + * emul_unix.c: Ditto. + * emul_netbsd.c: Ditto. + +Fri Nov 29 11:12:22 1996 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * hw_pal.c (hw_pal_attach_address): New function, if an attach is + encountered, assume that it is the single disk. + * hw_pal.c: Add generic device/size decode methods. + + * hw_nvram.c (hw_nvram_init_address): Use the first nonzero reg + property entry when determining the nvram size. + + * hw_core.c: Add generic address/size decode methods. + + * emul_chirp.c (emul_chirp_instruction_call): Return and trace + nonzero status from client functions. + + * main.c (error): Always include a cariage return when writing out + errors. + +Wed Nov 20 00:36:55 1996 Doug Evans <dje@canuck.cygnus.com> + + * sim_calls.c (sim_resume): Reset sim_should_run if single + stepping. + +Thu Nov 28 13:19:46 1996 Andrew Cagney <cagney@kremvax.tpgi.com.au> + + * emul_bugapi.c (emul_bugapi_do_diskio): Add support for multiple + optional disks. + + * emul_generic.c (emul_add_tree_hardware): Drop the dummy eeprom. + Attach the pal - for I/O - as a pseudo device haning from the + firmware sub tree. + + * emul_bugapi.c (emul_bugapi_create): Add a small memory device to + the device tree at the address of the hi-mem interrupt vector + addreses. Used by bugapi to establish its trap instructions. + + * debug.h: Add a new macro DITRACE for tracing device instances. + + * debug.h: Extend the DTRACE macro so that it can also tests for + device specific tracint. + + * device.h, device.c (device_trace): Add method to determine + device specific tracing. + (device_init_address): Set the devices tracing level. + +Thu Nov 21 12:05:32 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * tree.h, tree.c (tree_device): New files - separate out the + device parser and other tree operations from the device. + + * inline.h, inline.c (INLINE_TREE): Add. + * device.h, device.c (device_tree_add_parsed): Delete. + * Makefile.in (tree.c): Add rules for new file. + * Makefile.in: Better order the emul_* files. + + * emul_generic.c (emul_add_tree_hardware): Update. + * emul_netbsd.c (emul_netbsd_create): Update. + * emul_unix.c (emul_unix_create): Ditto. + * emul_chirp.c (emul_chirp_create): Ditto. + * emul_bugapi.c (emul_bugapi_create): Ditto. + * psim.c (psim_tree): Ditto. + * hw_init.c: Ditto. + + * emul_generic.h: Include tree.h + * Makefile.in: Add to EMUL_GENERIC_H dependencies. + + * device.h, device.c (device_root): New function - returns the + root of the tree. + * corefile.c: Use. + + * device.h, device.c (device_clean): New function, clean up device + ready for next simulation run. This includes things like deleting + interrupt edges and properties created during the simulation and + also scrubbing any pre-defined properties. + * tree.c (tree_init): Use. + + * device.h, device.c (device_init_static_properties): New + function. Initialize any static predefined properties. By static + we mean those that have values that can be determined before the + device tree initialization has started. + * tree.c (tree_init): Use. + + * device.h, device.c (device_init_address): Add code to + check/verify the devices #address-cells and #size-cells. + (device_add_integer_property): Delete corresponding code. + (device_nr_address_cells, device_nr_data_cells): Check for + property when returning value. + + * device.h, device.c (device_init_runtime_properties): New + function. Initialize those properties that are not `static'. At + present the only such property is the ihandle. + * tree.c (tree_init): Use. + + * device.h, device.c (reg, ranges): Rework these so that they use + an array of the fundamental type - single reg or single range + entry. + + * device.h, device.c (device_add_ihandle_runtime_property): + Re-implement the adding of an ihandle during tree construction so + that it better fits in with device initialization. + +Thu Nov 21 12:05:32 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * device.h, device.c (device_ioctl): Add additional argument - + request - so that the caller must always specify the type of + the ioctl request. + + * device_table.h: Update. + * hw_trace.c (hw_trace_ioctl): Ditto. + * hw_vm.c (hw_vm_ioctl_callback): Ditto. + * hw_init.c (hw_stack_ioctl_callback): Ditto. + * psim.c (psim_options): Ditto. + +Thu Nov 21 12:05:32 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * BUGS: Updated a bit. + +Wed Nov 20 14:06:37 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * hw_opic.c: Finish - third round. + +Wed Nov 20 12:02:08 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * hw_glue.c (hw_glue_io_read_buffer_callback): Fix miscalc of glue + reg index. + (hw_glue_io_write_buffer_callback): Ditto. + +Tue Nov 19 21:17:08 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * events.c (event_queue_process): Was incorrectly consuming future + events on the queue when they should be left alone. + + * debug.h, debug.c (events): Add support for event queue tracing. + * events.c: Add event tracing. + + * debug.h, debug.c: Order device trace options. + +Fri Nov 15 15:23:39 1996 Michael Meissner <meissner@tiktok.cygnus.com> + + * pk_disklabel.c (pk_disklabel_create_instance): Fix up some + warnings generated by GCC. + +Sun Nov 17 17:59:14 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * sim-endian.h: Add LE versions of byte swap macros. Needed for + PCI devices which are little-endian. + + * sim-endian-n.h (endian_le2h_N, endian_h2le_N): Ditto + +Sun Nov 17 17:59:14 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * hw_iobus.c (hw_iobus_attach_address_callback): Change the iobus + so that it is implementing a 1-1 address map. + + * emul_generic.c (emul_add_tree_hardware): Adjust. + + * emul_generic.c (emul_add_tree_hardware): Don't add the nvram as + a default. + +Sun Nov 17 17:59:14 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * device.c (split_find_device): Be tolerant of missing unit + addresses. + +Fri Nov 15 16:49:49 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * basics.h (port_direction): New type, specify the direction of + any `port'. + + * device.h, device.c (device_interrupt_decode): Include + specification of port direction in operations. + (device_interrupt_encode): Ditto. + + * device_table.h: Add a direction field to the interrupt port + table. + + * device.c (device_tree_add_parsed): Specify port direction. + +Thu Nov 14 21:38:13 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * hw_opic.c: Finish - second round. + +Thu Nov 7 00:18:59 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * hw_htab.c (htab_init_data_callback): Allow the virtual-address + to be specified as an array which allows 64bit addresses. + + * device.c (device_find_integer_array_property): New function. + Simplify the querying of elements of an integer array. + (device_add_integer_property, device_find_integer_property): + Update to correctly use the cell type. + + * vm.c (om_unpack_sr): Clarify shifting comment. + (om_pte_0_masked_vsid): Ditto. Add 64bit version. + + * emul_chirp.c (emul_chirp_create): Initialize the segment + registers. + + * vm.c (om_effective_to_virtual): Trace segment register use. + + * hw_htab.c (htab_map_page): Print out the pteg base address to + simplify cross checking between vm and the htab. + (htab_decode_hash_table): Use device_error instead of error. + (htab_map_page): Ditto. + (htab_dma_binary): Ditto. + (htab_map_binary): Ditto. + (htab_init_data_callback): Ditto. + +Wed Nov 6 20:20:58 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * interrupts.h: Clarify what the optional instruction program + interrupt is - a subset of illegal instruction program interrupt. + + * interrupts.c (program_interrupt): For UEA mode, clarify what an + optional instruction program interrupt is. + (program_interrupt): For OEA mode, as per spec, generate an + illegal instruction program interrupt when an optional instruction + is encountered. + + * gen-semantics.c (print_semantic_body): Delete code + differentiating between an unimplemented floating point and normal + instruction. Instead, such a case can be handled explicitly. + + * ppc-instructions (store floating-point as integer word indexed): + Mark as optional. + (Floating Convert to Integer Doubleword): Make the floating point + assist interrupt explicit. + (Floating Convert To Integer Doubleword with round towards Zero): + Ditto. + (Floating Convert To Integer Word): Ditto + (Move From FPSCR): Ditto. + (Move to Condition Register from FPSCR): Ditto. + (Move To FPSCR Fields): Ditto. + (Move To FPSCR Field Immediate): Ditto. + (Move To FPSCR Bit 0): Ditto. + (Move To FPSCR Bit 1): Ditto. + +Mon Nov 4 12:49:13 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * corefile.c (core_map_read_buffer, core_map_write_buffer): Avoid + breaking up transfers. + + * corefile.c: Adjust arguments so that the client server + relationship is clarified. + + * hw_glue.c (hw_glue_init_address): Update so it can be attached + to a PCI bus. + + * hw_disk.c (hw_disk_instance_write): Add more checks to disk IO - + looking for things like overflow/underflow. + +Sun Nov 3 18:45:20 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * emul_generic.c (emul_add_tree_hardware): Hang the disk off the + PAL device instead of the IOBUS. The disk must be attached to a + logical bus. + + * hw_disk.c (hw_disk_init_address): Just use the unit address + directly in the attach - the rest isn't relevant. + +Sat Nov 2 21:48:57 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * configure.in (sim-hardware, sim-packages): Allow additional + hardware and packages to be prefixed as well as appended. + + * Makefile.in (tmp-hw, tmp-pk): Retain the user specified order of + packages when building them. Consequently, a user can override a + standard device by prefixing their own version. + + * Makefile.in (hw_opic.o, hw_pci.o, hw_ide.o): Add dependencies. + +Fri Nov 1 14:42:57 1996 Michael Meissner <meissner@tiktok.cygnus.com> + + * mon.c (_cpu_mon): Add fields for counting 1, 2, 4, and 8 byte + reads and writes. + (mon_{read,write}): Count 1, 2, 4, and 8 byte reads/writes. + (mon_print_info): Correct typo regarding # of unaligned reads and + writes. Print out how many 1, 2, 4, and 8 byte reads/writes there + are. + +Tue Oct 29 17:55:43 1996 Michael Meissner <meissner@tiktok.cygnus.com> + + * configure.in (AC_CHECK_FUNCS): Add access. + * config{.in,ure}: Regenerate. + + * emul_unix.c (do_unix_nop): System call that always succeeds. + (do_unix_access): Support access system call. + (solaris_descriptors): Make sigaltstack and sigaction nops. + ({solaris,linux}_descriptors): Add support for access. + +Tue, 8 Oct 18:42:26 1996 Jason Molenda <crash@cygnus.co.jp> + + * Makefile.in (clean): Move config.log to distclean. + +Fri Nov 1 16:44:28 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * corefile-n.h (core_map_write_N): Improve abort messages. + + * device.h, device.c (device_attach_address): Remove unused name + parameter. + (device_detach_address): Ditto. + * device_table.h, device_table.c: Update. + * hw_iobus.c (hw_iobus_attach_address_callback): Ditto. + * hw_nvram.c (hw_nvram_init_address): Ditto. + * hw_memory.c (hw_memory_init_address): Ditto. + * hw_vm.c (hw_vm_init_address_callback): Ditto. + (hw_vm_attach_address): Ditto. + (hw_vm_add_space): Ditto. + * hw_init.c (update_for_binary_section): Ditto. + * hw_core.c (hw_core_attach_address_callback): Ditto. + + * hw_iobus.c (hw_iobus_attach_address_callback): Rewrite to handle + configurable parent busses. + +Wed Oct 30 18:46:32 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * device_table.c (generic_device_size_to_attach_size): Provide + limited support for multi-cell sizes. + (generic_device_address_to_attach_address): Ditto for addresses. + +Tue Oct 29 02:01:29 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * device.c (device_add_integer_property): Check for setting of + #address-cells and #size-cells properties. For these, update the + corresponding device values. + (device_nr_address_cells, device_nr_size_cells): Use the value + from the device instead of the property. + + * hw_core.c: Use generic address and size conversions for the top + bus. + + * hw_memory.c (hw_memory_init_address): Tolerate case where + #address and #size cells is greater than 1. + + * device.c (device_tree_print_device): Clean out printing of + properties. + + * device.c (split_device_specifier): Don't detect comments here - + "#" can be a valid prefix - eg #size-cells. + + * psim.c (psim_merge_device_file): Suppress comments and blank + lines here. + + * emul_generic.c (emul_add_tree_hardware): Fix typo of incorrect + pal unit address. Add the property /#address-cells to the root of + the tree. + + * device.c (device_template_create_device): Check that the unit + address was successfully parsed. + + * device_table.c (generic_device_unit_decode): Rewrite to better + handle multi-cell addresses. + (generic_device_unit_encode): Ditto. + + * emul_generic.c (emul_add_tree_hardware): "reg" properties no + longer need the explicit array type - the parser takes care of it. + + * pk_disklabel.c (pk_disklabel_create_instance): Add NULL return + to keep GCC happy. + +Mon Oct 28 22:55:48 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * hw_ide.c: New file. Model of a basic IDE interface attached to + a PCI bus. + + * configure.in (hardware): Add the ide device to the default + configuration. + * configure: Regenerate. + + * debug.h, debug.c: Add tracing option for the IDE device. + +Fri Oct 25 21:28:25 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * hw_phb.c, hw_phb.h: New files - implement a PHB. + + * configure.in (hardware): Add the phb to the list of devices to + build by default. + +Fri Oct 25 21:28:25 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * hw_com.c: Review description. + * hw_disk.c: Ditto. + * hw_htab.c: Ditto. + * hw_eeprom.c: Ditto. + * hw_init.c: Ditto. + * hw_cpu.c: Ditto. + + * hw_com.c: Update event handling. + + * hw_disk.c: Implement tracing. + +Fri Oct 25 21:28:25 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * device_table.c (generic_device_init_address): Use + assigned-addresses property in preference to any other reg + property. + +Fri Oct 25 21:28:25 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * device.h, device.c (device_find_ranges_property): New function. + Simplify the manipulation of "ranges" properties. + + * device.c (device_add_parsed): Extend to include support for the + ranges property. + + * device.c (device_add_parsed): Add assigned-addresses to the list + of reg type properties. + + * device.c (device_tree_print_device): Add code to format and + print a ranges property. + + * device.h, device.c (device_nr_address_cells, + device_nr_size_cells): New functions. Determine the values of the + standard properties #address-cells and #size-cells. Both of which + are optional and have default values of two and one respectfuly. + Previously, code that determined #address-cells was incorrectly + using a value of one. + +Fri Oct 25 21:28:25 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * debug.h, debug.c: Sort debug options, Add entries for the + comming PHB device. + +Fri Oct 18 12:12:21 1996 Michael Meissner <meissner@tiktok.cygnus.com> + + * ppc-instructions (Floating Select): Add support for fsel unless + model is 601. Allow user to filter out instruction unless -Fs is + passed to igen. + (Store Floating-Point as Integer Word Indexed): Raise optional + instruction program abort. Allow user to filter out instruction + unless -Fs is passed to igen. + (Floating Square Root{, Single}): Ditto. + (Floating Reciprocal Estimate Single): Ditto. + (Floating Reciprocal Square Root Estimate): Ditto. + + * configure.in (--enable-sim-filter): If not passed, pass 32,f,s + to igen. + * configure: Regenerate. + + * interrupts.h (program_interrupt_reasons): Add + optional_instruction_program_interrupt. + + * interrupts.c (program_interrupt): Call error with more detailed + information on program interrupts, particularly in user mode. Add + support for optional_instruction_program_interrupt. + +Wed Sep 25 10:20:29 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * hw_glue.c: New device. Hooks for manipulating interrupt ports. + + * debug.h, debug.c (trace_glue_device): Add tracing support for + the interrupt glue logic device. + + * configure.in (hardware): Add glue device. + * configure: Regenerate. + +Tue Sep 24 20:55:38 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * device.c (device_tree_parse_integer_property): New function, + parse a list of integers as an array property. + (device_tree_add_parsed): Call it. + + * device.c (device_tree_parse_string_property): New function, + parse a list of strings as a string property (with embeded + null's). For moment, don't try to implement a complext string + parser. + (device_tree_add_parsed): Call it. + +Tue Sep 24 16:30:48 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * hw_opic.c: New file. OpenPIC interrupt controller. + + * configure.in (hardware): Add opic device. + * configure: re-generate. + + * hw_pic.c: Delete, replaced with hw_opic.c. + + * debug.h, debug.c: Add debug option for OpenPIC device. - + opic-device. + +Tue Sep 24 16:30:48 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * Makefile.in (psim.o, idecode.o): Since idecode and not psim is + now the file that does all the inlining. + + * Makefile.in (LIB_SRC, LIB_INLINE_SRC, idecode.o): Break out the + library source code that could be involved in an inlining. Make + idecode.o only dependant on the inlined library source code. + + * Makefile.in (LIB_OBJ): Put options last on the list so that it + is compiled last. + + * std-config.h (DEVICE_INLINE): Only inline locals when the + default is to inline. + +Mon Sep 23 00:37:49 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * hw_htab.c (htab_sum_binary): Determine the real-base for the + binary. + + * hw_htab.c (htab_map_binary): Depending on the value of the + load-base, either map the program in as a contiguous section or as + separate sections controled by th binaries lma values. + (htab_init_data_callback): Ditto. + +Sun Sep 22 15:56:22 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * emul_generic.c (emul_add_tree_options): Remove load-base option. + + * emul_chirp.c (map_over_chirp_note): Add load_base field to note + struct. Don't require the load_base field to be present - just + issue warning - it is a recent addition. + (emul_chirp_create): Support both virtual and physical modes. + + * emul_chirp.c (emul_chirp_create): Add a stack initialization + property so that any arguments specified on the command line can + be passed on to user programs. + + * hw_init.c (create_ppc_chirp_bootargs): Add support for chirp + argument passing to the pseudo device stack. + +Sat Sep 21 19:39:56 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * device.c (device_error): Make it more tolerant to incomplete + devices. + + * hw_init.c (hw_data_init_data_callback): Extend the data device + so that it can perform initialization operations either dma or a + more complex instance open, seek, write operation. + * hw_init.c: Update the description of the data device to reflect + this. + +Sat Sep 21 00:13:02 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * device.c (device_event_queue_schedule, + device_event_queue_deschedule, device_event_queue_time): Have the + device object export the event operations. Making these available + from the device object should hopefully simplify writing device + models. + +Fri Sep 20 14:04:40 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * configure.in (sim-hardware): Add eeprom device to default build. + + * hw_eeprom.c: Rewrite so it works. + + * debug.h, debug.c: Add tracing support for the eeprom and com + devices. + +Thu Sep 19 14:40:40 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * debug.h: Add disklabel-package and disk-device trace options. + debug.h (PTRACE): Add macro to simplify tracing in packages. + +Thu Sep 19 14:40:40 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * device.c (device_create_instance_from): Tighten up loop + searching for device instances. + (device_instance_delete): Ditto. + (device_instance_delete): Only leaf instances need to be removed + from a devices list of active instances. + +Thu Sep 19 14:40:40 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * hw_disk.c: Add the cdrom as an alias. + + * pk_disklabel.c (disklabel_delete): Implement, remembering to + delete the raw disk instance while we're at it. + + * pk_disklabel.c (pk_disklabel_create_instance): Implement a + little bit more - still a long way to go. + + * pk_disklabel.c (disklabel_write, disklabel_read): Remember the + new head position after a read or write. + +Thu Sep 19 13:05:40 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * emul_chirp.c (chirp_emul_read): Allow reads to be longer then + the internal buffer. + +Thu Sep 19 13:05:40 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * emul_chirp.c (chirp_read_t2h_args): Call memset-0 with the args + in the correct order. + + * emul_chirp.c (chirp_emul_call_method): Correct computation for + the address of the first stack argument passed in from the client + program. + +Wed Sep 18 19:33:54 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * words.h: Add new types signed_cell and unsigned_cell which + correspond to the signed and unsigned IEEE 1275 memory locations. + * device.h, device.c, emul_chirp.c: Where refering to an IEEE 1275 + memory cell, replace uses of unsigned32 with unsigned_cell. + * device_table.h: Ditto. + + * sim-endian.h: Add new macros H2BE_cell and BE2H_cell which + convert cell sized values to from big endian. + * device.c, emul_chirp.c: Where refering to IEEE 1275 memory cells + use these new macros. + +Tue Sep 17 15:57:44 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * device.c (device_tree_add_parsed): Detect and report an + interrupt being attached to an invalid device. Was dumping core. + +Mon Sep 16 23:09:12 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * device.h, device.c (device_address_to_attach_address, + device_size_to_attach_size): New functions. Convert a devices + unit address or unit size structure into a form suitable for + passing on to the attach and detach functions. + * device_table.h: Add extra methods to device table. + + * device.h, device.c (device_find_reg_property): New function. + For a reg type property, return the selected address + size + tupple, along with a positive success status. Add a reg_property + to the list of property types. + * (device_tree_add_parsed): Make array properties with the name + reg or alternate-reg of type reg_property. + + * hw_memory.c (hw_memory_init_address): Rewrite to use new + find_reg_property method. + * hw_nvram.c (hw_nvram_init_address): Ditto. + + * device.c (device_tree_print_device): Add code to print out a reg + property. + + * device_table.c (generic_device_address_to_attach_address, + generic_device_size_to_attach_size ): New functions. Generic + functions for converting between unit and attach address or size. + + * device_table.c (generic_device_init_address): Rewrite to use the + new find_reg and address convert functions. Look for both reg and + alternate-reg properties. + +Mon Sep 16 23:09:12 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * hw_com.c: New file. A '550 serial device that can quickly be + attached to any bus. + * configure.in (enable-sim-hardware): Add the com device. + * configure: re-generate. + +Thu Sep 12 17:30:56 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * device.c (device_tree_verify_reg_unit_address): New + function. Check that the unit address as specified by the reg + property correctly corresponds to any unit address previously + specified by the devices name. + (device_tree_add_parsed): When adding a reg property, verify + that the unit-address - first value of property - correctly + matches any previous value specified when creating the device + node. + +Thu Sep 12 17:30:56 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * mon.c (mon_event): Remove assertion that an unsigned is >= 0. + +Fri Aug 16 12:05:24 1996 Michael Meissner <meissner@tiktok.cygnus.com> + + * device.c (device_full_name): Cast strdup to char *, since AIX + 3.2.5 mistakenly declares the function to be const char *. + (device_create_from): Ditto. + (device_create_instance_from): Ditto. + (device_add_property): Ditto. + +Tue Aug 13 11:40:14 1996 Michael Meissner <meissner@tiktok.cygnus.com> + + * debug.c (trace_option): For -t all, do not set the + trace_dump_device_tree flag, so that the simulator is run. + +Tue Aug 13 11:40:14 1996 Michael Meissner <meissner@tiktok.cygnus.com> + + * Makefile.in (options.o): Depend on defines.h. + (defines.h): New rule, go through config.h and make strings of all + of the #define HAVE_xxx macros. + (distclean): Remove defines.h. + + * options.c (print_options): Print whether many of the + configuration macros are defined. + + * main.c (main): If -t options and no filename, just print the + options, and don't print the usage message. + +Mon Aug 12 18:42:37 1996 Michael Meissner <meissner@tiktok.cygnus.com> + + * configure.in: Test whether /dev/zero works on the system, and if + it does, define HAVE_DEVZERO. + * configure: Regenerate. + + * emul_generic.c (emul_add_tree_hardware): Do not add /iobus/disk + if we don't have a working /dev/zero on the system. + + * emul_bugapi.c (emul_bugapi_init): If HAVE_DEVZERO is not + defined, don't add disk support. + (emul_bugapi_do_diskio): Ditto. + (emul_bugapi_instruction_call): Ditto. + +Wed Aug 7 14:34:20 1996 Michael Meissner <meissner@tiktok.cygnus.com> + + * emul_unix.c (HAVE_TCGETATTR): If HAVE_TERMIOS_STRUCTURE is not + defined, make sure HAVE_TCGETATTR is #undef'ed + +Wed Aug 7 14:34:20 1996 Michael Meissner <meissner@tiktok.cygnus.com> + + * std-config.h (REGPARM): Only define REGPARM attributes if using + GNU C. Test for __i686__ in case GCC ever defines it. If not on + a x86 platform, define REGPARM as nothing. + + * sim-endian.h (WITH_HOST_BYTE_ORDER): Test for i686 and __i686__ + also. + +Wed Aug 7 20:19:55 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * ld-decode.h, ld-decode.c: Rename goto_gen to the more correct + goto_switch_gen. + * gen-idecode.c: Ditto. + + * gen-idecode.c (print_idecode_table): Comment out check for + switch/table combination until a bug with it is fixed. + + * ppc-opcode-goto: New file. Like complex and flat but uses + goto-switch instead of padded-switch for the tables. + + * gen-idecode.c (print_goto_switch_name): New function. + (print_goto_switch_table_leaf): New function. + (print_goto_switch_break): New function. + (print_goto_switch_table): New function. Prints a jump table + that can be jumped into instead of a switch statement. + + * gen-idecode.c (*switch_*): As an option output a switch that is + implemented using a jump table but only if the switch is not + boolean. + +Tue Aug 6 09:28:22 1996 Michael Meissner <meissner@tiktok.cygnus.com> + + * configure.in (--enable-sim-{hardware,packages}): Fix typos. + * configure: Regenerate. + + * device.c (device_instance_call_method): Fixup format message in + error case. Return 0 in case of error to shut up compiler + warnings. + +Wed Aug 7 00:17:37 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * device_table.c (generic_device_unit_decode): Require a comma + between elements of a unit address. + + * device.c (device_tree_print_device): For reg, alternate-reg and + ranges properties use special print functions. + (device_print_ranges_property): Print formatted ranges property. + (device_print_reg_property): Print formatted reg property. + +Tue Aug 6 21:35:18 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * device.c (device_tree_add_parsed): For reg, ranges and + alternate-reg properties use a special parser. + (device_tree_parse_reg_property): New function to parse a reg + property. + (device_tree_parse_ranges_property): New function to parse a + ranges property. + (device_encode_unit): Wrapper for encode_unit callback. + (device_decoce_unit): Wrapper for decode_unit callback. + +Wed Jul 31 00:02:30 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * device_table.h (device_instance_callbacks): Relace the claim and + release methods with a more general table mapping from method-name + to method-function. + + * device.c (device_instance_call_method): New function. Implement + the OpenBoot call-method client interface. Attempts to locate the + instances method in the callback table. + (device_instance_claim, device_instance_release): Delete. + Replaced with call-method and a lookup table. + + * emul_chirp.c (chirp_emul_call_method): Use the new device + instance call method and let that handle a client claim call. + + * hw_htab.c (claim_memory): Wrapper function to call the memory + devices "claim" method using the new device-instance call-method + interface. Replaces the previous direct calls to claim. + (htab_map_region): Use claim_memory. + (htab_init_data_callback): Ditto. + + * hw_memory.c (hw_memory_instance_claim): Update function + interface so that it is compatible with call-method. + (hw_memory_instance_release): Ditto. + (hw_memory_instance_methods): New table of memory specific + methods claim and release. Add to the hw_memory_callback + table. + +Tue Jul 30 21:26:14 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * psim.c (psim_init): Back out of change to initial value of + system->last_cpu. + +Tue Jul 30 21:12:24 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * sim_callbacks.h (sim_io_printf_filtered): Replace + printf_filtered with a local simulator specific version. Add + #define printf_filtered to simplify updating of existing code. + + * sim_callbacks.h (sim_io_write_stdout, sim_io_read_stdin, + sim_io_write_stderr): New functions. Read / write to the + simulations stdin and stdout and stderr interfaces. Merge in code + from hw_pal that previously handled async I/O. + (sim_io_flush_stdoutput): Rename flush_stdoutput. Add #define + flush_stdoutput to simplify updating of existing code. + + * hw_pal.c (scan_hw_pal, write_hw_pal, + hw_pal_instance_write_callback): Use the new sim_io functions. + + * main.c: Implement standalone versions of the new sim_io + functions. Include support for async I/O. + * sim_calls.c: Ditto. This time using the gdb callback table. + + * std-config.h (CURRENT_STDIO, current_stdio): New macro. Set up + stdio configuration so that it works in the same way as the rest + of the simulation. + * psim.c (psim_create): Initialize current_stdio from the device + tree. + * emul_generic.c (emul_add_tree_options): Enter a default value + for use-stdio in the device tree. + +Fri Jul 26 19:43:03 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * gen-idecode.c (print_jump): Was always generating a jump back to + idecode. Only necessary at tail of semantic code. + (print_jump): Was always setting the processor's cia, even during + startup when the processor was still undefined. + (print_jump): For safety, restart smp loop when cpu_nr >= nr_cpus, + not just equal. + + * options.c (print_options): Add printing of WITH_REGPARM and + WITH_STDCALL. + + * std-config.h (WITH_REGPARM, WITH_STDCALL): Provide default + (disabled) values if not defined. + +Fri Jul 26 00:36:35 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * ppc-cache-rules (cache): Change RS and RB to cache instead of + compute. The block move instructions use them. + + * idecode_expression.h (FPSCR_SET): New macro, set specific FPSCR + register. + (CR_FIELD): New macro, extract specific CR register. + (FPSCR_FIELD): New macro, extract specific FPSCR register. + + * registers.h (GPR): New macro, simplify accesses to GPR[i]. + + * bits.c (INSERTED): Covert INSERTED macro into a function. + (EXTRACTED): Conditionally compile on correct bit size macro. + + * bits.h (BIT8): New macro, set a single bit in an 8 bit byte. + + * ppc-instructions: With hints from Paul Martin, type in missing + some instruction semantics. Leave disabled for the moment. + (Load Multiple Word): Ditto. + (Store Multiple Word): Ditto. + (Load String Word Immediate): Ditto. + (Load String Word Indexed): Ditto. + (Store String Word Immedate): Ditto. + (Store String Word Indexed): Ditto. + (Move to Condition Register from XER): Ditto. + (Move From Condition Register): Ditto. + (Move From FPSCR): Ditto. + (Move to Condition Register from FPSCR): Ditto. + (Move To FPSCR Field Immediate): Ditto. + (Move To FPSCR Fields): Ditto. + (Move To FPSCR Bit 0): Ditto. + (Move To FPSCR Bit 1): Ditto. + +Thu Jul 25 22:10:40 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * std-config.h (SEMANTICS_INLINE): By default, mask out the + inlining of semantic functions from DEFAULT_INLINE. Almost all + configurations call the semantic code via a pointer so there is + little benefit. + + * std-config.h (ICACHE_INLINE): Ditto. + +Thu Jul 25 20:07:30 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * configure.in (sim_regparm): Add configuration option for + enabling GCC's regparm attribute. + * (sim_stdcall): Add configuration option for enabling GCC's + stdcall attribute. + + * Makefile.in (REGPARM_CFLAGS): Pass regparam configuration onto + compilations. + * (STDCALL_CFLAGS): Pass stdcall configuration onto compilations. + + * std-config.h (REGPARM): Extend construction of REGPARM macro so + that it can include __stdcall__ function attribute. + +Wed Jul 24 19:04:20 1996 Andrew Cagney <cagney@sawnoff> + + * options.c (print_options): Include SUPPORT_INLINE in information + dump. + + * gen-idecode.c (print_run_until_stop_body): Only generate loop + termination test if creating idecode_run_until_stop. Push the + loop termination test back into each alternative branch. + +Wed Jul 24 15:47:09 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * gen-icache.c (print_icache_function): Have the cache function + always update the cache_entries semantic and address fields. + + * gen-idecode.c (print_idecode_switch_illegal): Include a break + when generating illegal instructions. This was commented out + which is a hangover from looking a at switch statements generated + using indirect jumps. + +Tue Jul 23 20:57:01 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * igen.c (print_my_defines): Replaces print_define_my_index. + Print both a definition for MY_INDEX and MY_PREFIX. + * gen-icache.c (print_icache_function): Adjust. + * gen-idecode.c (print_jump_insn): Adjust. + * gen-semantics.c (print_c_semantic): Adjust. + + * gen-support.c (gen_support_h): Add optional include to created + support.h so that, like cpu, it is optionally inlined for all + modules that include it. + * inline.h, inline.c: Adjust so that support.[hc] is handled the + same as cpu.[hc]. + + * idecode_fields.h (LABEL, GOTO): Macro's that create a unique + name for a lable and then branch to it. + + * ppc-instructions (convert_to_integer, Floating Round to + Single-Precision, Floating Convert from Integer Doubleword): Use + LABEL and GOTO instead of the recently added switch statements. + +Wed Jul 24 14:02:42 1996 Andrew Cagney <cagney@sawnoff.highland.com.au> + + * gen-idecode.c (print_run_until_stop_body): Too many rparen in + generated code. + +Tue Jul 23 20:57:01 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * configure.in (--enable-sim-line-nr): Typo - sim_line-nr. + * (--enable-sim-inline): Reorder patern matching of arguments so + that SUPPORT=ALL_INLINE is reconized as *=* and not *_INLINE. + + * configure: rebuild. + +Mon Jul 22 23:25:08 1996 Andrew Cagney <cagney@highland.com.au> + + * configure.in (--enable-sim-hardware, --enable-sim-packages): New + configuration options. Let the user specify the packages or + hardware devices that are to be included in the build. Makes it + possible for user packages to be specified. + + * Makefile.in (tmp-pk, tmp-hw): Just use the list of packages and + hardware instead of checking it using ls. configure.in should + have taken care of any problems. + (HW_SRC, HW_OBJ, PACKAGE_SRC, PACKAGE_OBJ): Set by configure. + +Mon Jul 22 22:38:59 1996 Andrew Cagney <cagney@highland.com.au> + + * psim.c (psim_options): Enter the argument to the memory size + option directly into the device tree. Was using atol() which is + dangerously non portable. + +Mon Jul 22 22:17:08 1996 Andrew Cagney <cagney@highland.com.au> + + * configure.in (icache): Extend icache flag to include an insn + option. If specifyed the insn - aka instruction - is included in + the instruction cache. Make this the default. + * configure: re-generate. + + * igen.c (main), igen.h: Add option -S - inSn - for specifying + that the instruction should be included in the icache. + + * gen-icache.c (print_icache_body): If enabled, output code to put + the instruction into the icache. + (print_icache_struct): If enabled, add insn to the icache struct. + +Mon Jul 22 20:46:12 1996 Andrew Cagney <cagney@highland.com.au> + + * Makefile.in (BUILD_CFLAGS): Include -g when building the + generators. + +Mon Jul 22 20:00:25 1996 Andrew Cagney <cagney@highland.com.au> + + * emul_generic.c (emul_add_tree_options): Was incorrectly setting + the strict-alignment option when hardwired for non-strict + alignment. + +Sun Jul 21 21:18:05 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * gen-semantics.c: Make the my_index variable a macro MY_INDEX. + + * ppc-instructions: Adjust so that references are to MY_INDEX and + not my_index. + +Sun Jul 21 21:18:05 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * gen-idecode.c: Output the complete run_until_stop function + instead of just the code to handle a single instruction issue. + * : Have the generated idecode.c include inline.c (instead of psim.c). + + * std-config.h: Change psim.c so that it isn't inlined (as this is + no longer needed). + + * psim.c (run_until_stop): Delete the old run_until_stop function + instead calling the idecode_run and idecode_run_until_stop + functions that gen-idecode.c is now creating. + +Sun Jul 21 21:18:05 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * dgen.c: Maintenance - update to use new features found in lf.c. + + * filter_filename.c (filter_filename): Maintenance - make the + string constant. + +Sun Jul 21 21:18:05 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * debug.c (TRACE, ITRACE, DTRACE): Have GCC instead of CPP + eliminate trace statements. + + * debug.c: Change trace format so that it is consistent + (file:line-nr) with CC's error output. + + * gen-itable.c (itable_c_insn): Add the source file name and + source line number to the instruction's informational entry. + + * debug.c (ITRACE): Use the itable (and my_index) to get the + current instructions name and source line number. + + * gen-semantics.c, gen-icache.c: Adjust generated ITRACE calls to + match new interface. + + * emul_bugapi.c (emul_bugapi_instruction_call): Adjust + corresponding call to ITRACE so that it still matches. + + * idecode_expression.h (ALU_END, CR0_COMPARE): Use TRACE instead + of ITRACE. The CPP line directives would have previously set the + line-nr and file name so ITRACE isn't needed. + +Sun Jul 21 21:18:05 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * gen-idecode.c (print_jump_until_stop_body): New function and + idecode generation option. Instead of generating and calling + separate functions containing the semantic and icache code + generate a single monolythic function and use goto's (and GCC's + indirect jump) to move between code blocks. + + * Makefile.in: Add sim_jump flag to those passed to igen. + + * configure.in: New option --enable-sim-jump (default disabled) + + * ppc-instructions: Eliminate any uses of labels and goto's. + These result in duplicate declarations when a single flat function + is being create. + + * ppc-opcode-jump: New file. Set of opcode rules useful when + testing jumping idecodes. + +Sun Jul 21 21:18:05 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * gen-idecode.c: Optionally include the semantic code for an + instruction in the function that is doing the decoding. + + * igen.c: Add option (-C) to generate semantics in the instruction + decode functions. + + * configure.in (--enable-sim-icache): Accept an option list such + as 1024,define. Add a new choice to the list - semantic - which + will cause igen to generate instruction decode functions that + include the corresponding semantic code. + +Sun Jul 21 21:18:05 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * configure.in: New option --enable-sim-line-nr (default enabled). + Enable/disable the inclusion of CPP line directives in the + generated files. Such directives refer back to the source files + used when generating the simulator code. + + * Makefile.in (sim_line_nr): Pass to igen. + +Sun Jul 21 21:18:05 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * igen.c (main): Revamp the options so that more letters are + available. + + * configure.in: Adjust to match igen's revamped options + +Sun Jul 21 21:18:05 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * Makefile.in (pk.h, hw.h): Rewrite depenencies for hw.h (etc) so + that they use the same technique as igen (ie a dummy targets + tmp-pk and tmp-hw are created). + +Mon Jun 24 22:28:00 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * Makefile.in (BUILD_CFLAGS): Include WARNING_CFLAGS. + +Wed Jun 19 21:45:28 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * ld-cache.[hc], ld-decode.[hc], ld-insn.[hc]: New files. Separate + out the loading of each of the tables from the rest of igen. + * Makefile.in: Adjust. + * igen.c: Adjust. + + * gen-icache.[hc], gen-idecode.[hc], gen-itable.[hc], + gen-model.[hc], gen-semantics.[hc]: New files. Separate out the + code creating each separate set of generated files. + * Makefile.in: Adjust. + * igen.c: Adjust. + + * gen-support.[ch]: New files. Output the support functions (found + in the ppc-instructions file) into a separate file. + * Makefile.in: Add. + * inline.h, inline.c: Add. + * std-config.h: Add. + + * ld-cache.c: Re-design the cache table format. + * ppc-cache-rules: Update to new format. + + * ld-decode.c: Re-design the decode table format. + * ppc-opcode-simple: Update to new format + * ppc-opcode-complex: Ditto + * ppc-opcode-flat: Ditto + + * filter.h, filter.c: New files. Separate the opcode filter table + reading code from the rest of igen.c. Re-design the filter so that + it works inclusivly not exclusivly. + * igen.c: Remove the opcode filter table loading code. + * Makefile.in (filter.o): Adjust + * configure.in: Adjust filter flag so that default includes 32bit + and floating point. + * ppc-instructions: Clean up filter fields so that only in use + entries are specifed (ie delete `be'). + + * misc.c (name2i, i2name): New function. Map between a string and + an integer value. + +Mon Jun 17 20:08:03 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * sim_calls.c (sim_close): If simulator not created, skip printing + of run information. + +Mon Jun 17 20:08:03 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * registers.c (register_description): Typo, insns not insn. + + * ppc-instructions (model_get_number_of_stalls): New model function, + returns number of stalls for the specified processor. + * psim.c (psim_read_register): Add call to new function + model_get_number_of_stalls(). + + * ppc-instructions (model_get_number_of_cycles): New model function, + returns number of stalls for the specified processor. + * psim.c (psim_read_register): Add call to new function + model_get_number_of_cycles(). + +Fri Jun 14 00:11:56 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * device_table.h: Don't pass the parent device into a devices + create function. This makes the create function consistent with + the documentation. + * device.c (device_template_create_device): Ditto + * hw_pal.c (hw_pal_create): Ditto + * hw_core.c (hw_core_create): Ditto + * hw_vm.c (hw_vm_create): Ditto + * hw_disk.c (hw_disk_create): Ditto + * hw_nvram.c (hw_nvram_create): Ditto + * hw_memory.c (hw_memory_create): Ditto + * hw_cpu.c (hw_cpu_create): Ditto. + + * device.c (split_find_device): Allow a null initial parent device. + (device_template_create_device): Ditto. + + * device.c (device_create_from): Make local (static) only used + within device.c. + * device_table.h: typedef device_callbacks moved here (from + device.h) where it belongs. + + * hw_core.c: New file. Implements just the core device using the + core object. + + * corefile.c: Moved all core device functions into the new + hw_core.c file. core_device_create() disapears. + + * psim.c (psim_tree): Use device_tree_add_parsed() to create the + core device. + +Thu Jun 13 00:09:29 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * hw_init.c: Correct typo in comment. + + * corefile.c (core_init): Remove any remaining references to a + default map. + (core_map_find_mapping): Ditto. + +Wed Jun 12 22:30:32 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * corefile.c (core_init): Make function global so that other + devices are able to use the full core object. + + * corefile.c (core_create, core_from_device): Break core_create + into two functions. The first creates a core object, the second + returns the core object associated with a core device. + + * corefile.c (core_device_create): Use core_create to make the + core object. + + * psim.c (psim_create): Use core_from_device() instead of + core_create(). + + * device.c (device_template_create_device): Make static as only + needed by functions internal to device.c. + +Fri Jun 7 23:47:18 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * ppc-opcode-test-2: Remove description of fields. + * ppc-opcode-complex: Ditto + * ppc-opcode-flat: Ditto + * ppc-opcode-simple: Ditto + * ppc-opcode-stupid: Ditto + * ppc-opcode-test-1: Ditto + * ppc-cache-rules: Ditto + + * igen.c: Add description of files as a comment at the front. + +Wed Jun 26 12:50:33 1996 Michael Meissner <meissner@tiktok.cygnus.com> + + * configure.in: Check for whether the termios and termio + structures are really defined, and whether or not, they define the + c_line field. + * configure: Regenerate. + + * Makefile.in ({,TERMIO_}CFLAGS): Add TERMIO_CFLAGS options set by + configure. + + * emul_unix.c: Various changes to allow for building on systems + with different termio and termios structures. If host has both + termio and termios, just use termios. No longer include + sys/ioctl.h. + +Wed Jun 26 12:26:55 1996 Jason Molenda (crash@godzilla.cygnus.co.jp) + + * Makefile.in (bindir, libdir, datadir, mandir, infodir, includedir, + INSTALL_PROGRAM, INSTALL_DATA): Use autoconf-set values. + (docdir): Removed. + * configure.in (AC_PREREQ): autoconf 2.5 or higher. + (AC_PROG_INSTALL): Added. + * configure: Rebuilt. + +Wed Jun 5 23:53:42 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * corefile.h: Rewrite documentation so that it can be extracted and + converted into texinfo (and hence ready for translation into html, + tex or nroff). + * device.h: Ditto + +Thu Jun 6 09:52:37 1996 Michael Meissner <meissner@tiktok.cygnus.com> + + * hw_disk.c (SEEK_SET): If SEEK_SET is not defined, define as 0. + +Wed Jun 5 11:46:52 1996 Andrew Cagney <cagney@puddin> + + * hw_disk.c: Include <unistd.h> if available. Under SunOS, that + is the source of SEEK_SET. + +Wed Jun 5 01:39:07 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * psim.c (psim_options): Correct type of dummy arguments being + passed to a device_ioctl call. + + * hw_init.c (hw_data_init_data_callback): Adjust printf arguments. + (write_stack_arguments): Ditto. + * hw_trace.c: Instance callback entry no longer a table. + +Wed Jun 5 01:39:07 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * emul_unix.c (do_unix_umask): Cast printf argument. + (convert_to_linux_termios): Use LINUX_VSWTC not LINUX_VSWCH + +Mon Jun 3 15:02:04 1996 Michael Meissner <meissner@tiktok.cygnus.com> + + * hw_init.c (update_for_binary_section): Abort if we find an + .interp section, which indicates the need for shared libraries to + be loaded. + +Mon Jun 3 15:02:04 1996 Michael Meissner <meissner@tiktok.cygnus.com> + + * emul_unix.c (do_unix_{time,gettimeofday,getrusage}): Add support + for time, gettimeofday, and getrusage system calls. + ({solaris,linux}_descriptors): Add new system calls. + (do_get{,e}{uid,gid}): Use gid_t/uid_t types. + (do_get{,p}pid): Use pic_t types. + + * configure.in (AC_TYPE_{GETGROUPS,SIGNAL}): Define. + (AC_TYPE_{MODE,OFF,PID,SIZE,UID}_T): Define. + * config{.in,ure}: Regenerate. + +Mon Jun 3 23:19:57 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * emul_netbsd.c (emul_netbsd_create): Use the more specific names + `ppc-elf' and `ppc-xcoff' for the stack-type. + * emul_unix.c (emul_unix_create): Ditto. + * emul_bugapi.c (emul_bugapi_create): Ditto. + * hw_init.c: Reconize the new names. + + * emul_unix.c (do_unix_break): Adjust so that the updated ioctl + call is used (no system parameter). + +Sun Jun 2 11:21:17 1996 Michael Meissner <meissner@tiktok.cygnus.com> + + * emul_unix.{h,c}: New files to provide Solaris and Linux system + call emulations. + + * Makefile.in (LIB_{SRC,OBJ}): Add emul_unix.{c,o}. + (os_emul.o): Depend on emul_unix.h. + (emul_unix.o): New dependency. + + * configure.in (--enable-sim-alignment): Add 0|default to mean set + alignment to 0, which means use appropriate alignment for mode. + (AC_CHECK_FUNCS): Add new functions needed by emul_unix.c. + (AC_CHECK_HEADERS): Add new include files needed by emul_unix.c. + * config.in: Regenerate. + * configure: Regenerate. + + * emul_generic.c (emul_write2_status): New function to return + results in r3 and r4 for Solaris system calls. + (emul_do_system_call): If the system call is not support, but + there is a string for the system call name, print out the string + instead of the system call number. + + * emul_generic.h (emul_write2_status): Declare it. + + * emul_netbsd.c: Use /* */ around comment on #endif. + + * os_emul.c: Include emul_unix.h. + (os_emulations): Add emulations for Solaris, and Linux. + + * psim.c (psim_usage): Add message about solaris, linux + emulations. + +Thu May 30 00:00:10 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * hw_iobus.c: Tidy up notes so that they can be auto-extracted. + + * README: Correct PSIM's title + +Wed May 29 23:50:26 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * basics.h: New global type object_disposition, used to indicate + the status of objects when things are restarted. + +Fri May 17 17:28:52 1996 Andrew Cagney <cagney@benjimen.highland.com.au> + + * device_table.h: Change the interrupt descriptor structure so + that it includes an additional member - an upper bound on the + interrupts by that name. + + * device.c (device_interrupt_decode): Allow a range of interrupt + ports (eg rst0 .. rst6) if the port descriptors bound is non zero. + + * device.c (device_tree_print_device): Include a list of valid + interrupt ports when listing supported devices. + + * device.h, device.c (device_child_interrupt_*): Delete. Not used. + + * emul_generic.c (emul_add_tree_hardware): Modify the creation of + the interrupt net so that it uses int0 .. intN. + +Tue May 14 23:03:53 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * device.h, device.c (device_ioctl): Drop the system argument. + Devices can not obtain this using the device_system() call. + * device_table.h: Adjust accordingly. + * hw_*.c: Adjust accordingly. + * emul_netbsd.c (do_break): Adjust call to vm device accordingly. + * psim.c (psim_options): Use a device_ioctl call to force the + hw_trace device to update the trace options. + * hw_trace.c: Replace the init function with an ioctl call. Adjust + doc accordingly. + + * psim.c (psim_init): Re-order initialization so that the + os-emulation is initialized after the device tree. Without this, + os-emul's are not able to create instances or access properties + that contain an instance handle. + + * device.h, device.c (device_add_*_property): Make these functions + internal to device.c. The user has access to the more generic + device_tree_add_parsed function. Differentiate between the initial + and current value for each property. + * (clean_device_properties): New function that deletes any + properties created after the start of a simulation and restores + the initial value of any others (ignoring ihandles). + * (init_device_properties): (Re)Initialize any properties that + contain ihandles. create + + * (device_tree_init): Include calls to clean the device tree's + properties and then initialize them. Document this in the device.h + file. + +Mon May 6 17:36:15 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * interrupts.c (decrementer_interrupt): Always pend a decrementer + interrupt even if it is not yet possible to deliver it. + +Wed May 1 12:26:51 1996 Andrew Cagney <cagney@benjimen> + + * mon.h, mon.c (mon_get_number_of_insns): Make this externally + visable adjusting the arguments so that the interface is correct. + (mon_print_info): Adjust calls. + + * registers.h, registers.c (register_description): Add phony + cycle, insn and stall registers. + + * psim.c (psim_read_register): Return nr of instructions for given + processor. + +Tue Apr 30 22:09:09 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * hw_htab.c: New file. Extract contents from disk_table.c. + Contains a device that, during initialization will create a + PowerPC htab in memory. + * hw_register.c: New file. Extract contents from disk_table.c. + Contains a device that, during initialization, will parse its + property list and use that to initialize various processor + registers (not target specific). + * hw_vm.c: New file. Extract contents from disk_table.c. Contains + a device that handles accesses to invalid virtual memory addresses + (in user mode). + * hw_init.c: New file. Extract contents from disk_table.c. Misc + devices that can initialize memory from a file. + * hw_trace.c: New file. Extract contents from disk_table.c. + Configure trace options from property values. + + * Makefile.in (hw_htab.o, hw_register.o, hw_vm.o, hw_init.o, + hw_trace.c): Add new device files. + + * device_table.c: Remove above code, now in separate independant + files. + +Fri Apr 26 00:00:07 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * hw_disk.c: New file. Disk and CDROM device. + + * Makefile.in (hw_disk.o): Add device hw_disk.c. + + * pk_disklabel.c: New file. Implement the miss-named disk-label + package. + + * Makefile.in (pk.h): Create the file pk.h that contains a list of all + the packages. + + * Makefile.in (hw.h, hw.c): Add dependancy on Makefile so that + they are re-created when the makefile is updated. + + * emul_generic.c (emul_add_tree_hardware): Add a disk device + (below the iobus) to the device tree. Include an ihandle of + the disk as /chosen/disk. + + * emul_bugapi.c (emul_bugapi_create): Don't initialize the input, + output and (new) disk handles yet. + * (emul_bugapi_init): Initialize the input, output (and just added) + disk ihandles here. + * (emul_bugapi_do_diskio): New. Performs disk i/o (well at least + what I think the behavour is). + * emul_bugapi.c (emul_bugapi_instruction_call): Add hook to disk + i/o bug call. For RETURN call, exit using gpr[3]'s status even + though this isn't part of the spec - makes it possible for machine + code to signal the aporting of a simulation run. + + * emul_chirp.c (chirp_emul_call_method): Add support for the + claim/release methods. + * (chirp_emul_exit): Add an optional exit status argument to + the exit method. Makes it possible for chirp emul simulations + to abort upon an error. + * device.h, device.c (device_instance_claim, + device_instance_release): New methods for claiming and releasing + memory. + * hw_memory.c: add claim and release memory methods. + * hw_*: Use the claim memory method when allocating physical + memory. + +Thu Apr 18 23:38:10 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * hw_nvram.c (hw_nvram_update_clock): Use the current not previous + time when updating the clock. + + * hw_nvram.c: Tidy up documentation + +Fri May 24 10:08:10 1996 Michael Meissner <meissner@tiktok.cygnus.com> + + * configure.in (AC_STRUCT_ST_{BLKSIZE,BLOCKS,RDEV}): Use these + macros to determine whether or not the appropriate st_<xxx> fields + exist in the stat structure. + (AC_CHECK_FUNCS): Check for all unix system calls used, except for + the real basic ones like open, read, write, etc. + * config{.in,ure}: Regenerate. + + * emul_netbsd.c: Add support for missing system calls, and/or + missing stat fields. + (MAXPATHLEN): Undefine if including unistd.h, since sys/param.h + might define it. + + * hw_pal.c (WITH_STDIO): Redefine if O_NDELAY, F_GETFL, or F_SETFL + are not defined. + (scan_hw_pal): Do not cause syntax error if O_NDELAY, F_GETFL, or + F_SETFL not defined. + +Tue May 21 17:24:45 1996 Michael Meissner <meissner@tiktok.cygnus.com> + + * emul_netbsd.c (write_stat): Don't convert st_blocks unless the + host is netbsd. + +Thu May 16 10:56:45 1996 Michael Meissner <meissner@tiktok.cygnus.com> + + * configure.in (AC_CHECK_HEADERS): Add sys/ioctl.h. + * config{.in,ure}: Regenerate. + * emul_netbsd.c: If HAVE_SYS_IOCTL_H is not defined, don't include + sys/ioctl.h. + +Tue May 7 17:28:12 1996 Michael Meissner <meissner@tiktok.cygnus.com> + + * hw_pal.c (hw_pal_instance_read_callback): Remove unused + variable. + + * misc.c ({,target_}a2i): Rewrite to not use strtoul. + + * Makefile.in ({spreg,misc}.o): Add dependency on .c file. + ({i,d}gen): Don't link in liberity. Use BUILD_LIBS instead of + LIBS. + +Mon May 6 11:31:43 1996 Michael Meissner <meissner@tiktok.cygnus.com> + + * hw_pal.c (hw_pal_instance_read_callback): If using stdio, use + fgets to read line. If not using stdio, do a simple blocking read + of len bytes. + +Fri May 3 15:07:42 1996 Michael Meissner <meissner@tiktok.cygnus.com> + + * Makefile.in: Correctly build simulator for build machine != host + machine. + +Tue Apr 30 18:46:05 1996 Michael Meissner <meissner@tiktok.cygnus.com> + + * configure.in (--enable-hostendian): Rework so the default uses + the AC_C_BIGENDIAN results. Only run AC_C_BIGENDIAN if not cross + compiling. + * configure: Regenerate. + + * sim-endian.h: Add more tests for host endian to support more + platforms in a cross compilation environment. + +Wed Apr 17 14:38:06 1996 Michael Meissner <meissner@tiktok.cygnus.com> + + * hw_pal.c ({scan,write}_hw_pal): If WITH_STDIO == DO_USE_STDIO, + use stdio, instead of unpended read/printf_filtered. + (hw_pal_instance_write_callback): If WITH_STDIO == DO_USE_STDIO, + flush stdout after writing the characters. + + * options.c (print_options): Print out WITH_STDIO. + + * Makefile.in (STDIO_CFLAGS): Pass on result of @sim_stdio@ + configuration variable. + (CONFIG_CFLAGS): Include STDIO_CFLAGS. + (hw.{c,h}): Allow for source dir != build dir, and for HW_SRC + files to contain directory pieces. + + * std-config.h (DO{,NT}_USE_STDIO): New flags for whether we + should use stdio for console input. + (WITH_STDIO): If not defined, define as DONT_USE_STDIO. + + * configure.in (--enable-sim-stdio): Add new switch to control + whether stdio is used for console I/O. + * configure: Regenerate. + + * interrupts.c (external_interrupt): Declare it to be + INLINE_INTERRUPTS, not INLINE_CPU. + +Mon Apr 15 23:30:56 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * events.c (insert_event_entry): Allow events to be scheduled + *NOW* (at delta time 0). Add assertions to clarify behavour of + event queue. + + * events.c (update_time_from_event): New function. Calculates the + number of ticks from the next event. Use this. + +Sun Apr 14 21:39:45 1996 Andrew Cagney <cagney@highland.com.au> + + * emul_netbsd.c (do_break): Return 0 if success (instead of + adjusted break). + + * device_table.c (vm_ioctl_callback): Don't return adjusted break + (isn't needed). + +Sun Apr 14 21:32:41 1996 Andrew Cagney <cagney@highland.com.au> + + * device_table.h: Change type of the device ioctl so that it + returns an int (status). + * device.h (device_ioctl): Ditto. + * device.c (device_ioctl): Ditto. + + * device_table.c (stack_ioctl_callback): Return 0 status. + (vm_ioctl_callback): Ditto + +Sat Apr 13 00:00:24 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * emul_netbsd.c (do_read): Correctly set the return value. + (do_getpid): Ditto. + (do_getuid): Ditto. + (do_geteuid): Ditto. + (do_dup): Ditto. + (do_getegid): Ditto. + (do_getgid): Ditto. + (do_sigprocmask): Ditto. + (do_umask): Ditto. + (do_dup2): Ditto. + (do_gettimeofday): Ditto. + (do_getrusage): Ditto. + (do_fstat): Ditto. + (do_stat): Ditto. + (do_lseek): Ditto. + (do___sysctl): Ditto. + +Fri Apr 12 20:56:47 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * device_table.c (vm_ioctl_callback): Don't access the processor + registers directly, instead leave it to the caller to handle this. + + * emul_netbsd.c (do_break): Which calls vm_ioctl_callback to + perform a break. Pass in the new break value and set the + registers according to the result. + + * emul_generic.c (emul_write_status): Change so that r3 contains + either status or errno and failure is indicated by SO. + +Thu Apr 4 23:03:38 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * emul_bugapi.c (emul_bugapi_create): More strict check of OEA + address. + +Thu Apr 4 20:58:05 1996 Andrew Cagney <cagney@highland.com.au> + + * interrupts.h (interrupts): New structure contains state of + pending interrupts. + + * cpu.c (cpu_interrupts): New function. Pending interrupt status + in the cpu and grant access to it. Add interrupts to cpu + structure. + +Fri Mar 29 22:09:25 1996 Andrew Cagney <cagney@highland.com.au> + + * device.c (device_tree_add_parsed): Check that the creation of a + device instance worked before using it. + + * psim.c (psim_halt): Remove cia argument from psim_halt. This + function does not save the CIA so do not pass it in. + +Fri Mar 29 21:30:56 1996 Andrew Cagney <cagney@highland.com.au> + + * hw_pal.c (hw_pal): Merge the halt and icu and console devices + found in device_table.c into a single hack pal. + + * device_table.c (halt, icu, console): Delete. + + * Makefile.in (hw_pal.o): New dependency. + + * emul_generic.c (emul_add_tree_hardware): Re-arange device tree + so that it uses the pal instead of the icu/halt/console devices. + Wire the pal's interrupt ports up to the cpu nodes. + +Fri Mar 29 20:17:17 1996 Andrew Cagney <cagney@highland.com.au> + + * hw_iobus.c (hw_iobus_attach_address_callback): Move from + device_table.c to here. + + * Makefile.in (hw_iobus.o): New dependency. + +Fri Mar 29 12:17:58 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * emul_bugapi.c (_os_emul_data): Add fields for output, input. + (emul_bugapi_create): Create input, output from /chosen/stdin and + /chosen/stdout. + (emul_bugapi_do_{read,write}): Switch to use device_instance + interface. + (emul_bugapi_instruction_call): Change calls to + emul_bugapi_do_{read,write} to pass device instance argument. + +Tue Mar 26 14:57:58 1996 Michael Meissner <meissner@tiktok.cygnus.com> + + * igen.c (idecode_switch_end): Fix 2/26 change so that an extra + default is not written out if a default was already written. + + * psim.c (psim_{read,write}_register): Use sizeof unsigned_8 to + size cooked_buf, not sizeof natural_word, since floating point + registers are 8 bytes. + +Mon Mar 25 22:07:13 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * configure: Regenerate with autoconf 2.9. + +Thu Mar 21 00:14:26 1996 Andrew Cagney <cagney@highland.com.au> + + * device_table.h: Always include string headers. + +Thu Mar 21 00:06:09 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * main.c (error): Be careful to not try to print out statistics + when the simulation was never created. + +Sun Mar 17 22:40:57 1996 Andrew Cagney <cagney@highland.com.au> + + * basics.h: Move the event queue's definition to here so that it + can be refered to globally with out importing all of events.h. + + * psim.h, psim.c (psim_event_queue): New function. Grant access + to the simulation event queue. Will make this the single point of + access (there is after all only one event queue in the + simulation). + + * cpu.c (cpu_create): Use psim_event_queue to obtain the event + queue instead of it being passed in. No longer allow access to + the cpu's copy of the event queue. + +Sun Mar 17 22:40:57 1996 Andrew Cagney <cagney@highland.com.au> + + * events.h, events.c (event_handler): Remove event_queue from + arguments passed to an event handler. That argument is redundant + - the `data' should refer to a data structure that contains the + event queue if queing is needed. + + * cpu.c (cpu_decrement_event): adjust + + * events.c (event_queue_process): adjust + +Sun Mar 17 22:40:57 1996 Andrew Cagney <cagney@highland.com.au> + + * device.h, device.c (device_system): New, returns a handle for + the system given the device. + + * device.c (device_address_init): Store a pointer back to the + system in each devices node. + + * device_table.h: Don't pass `system' into each device when it is + being initialized, this is now available using device_system(me). + + * device.c (device_address_init, device_data_init): Adjust. + + * hw_cpu.c, hw_nvram.c, hw_memory.c, hw_eeprom.c, device_table.c: + Adjust. + +Sun Mar 17 22:40:57 1996 Andrew Cagney <cagney@highland.com.au> + + * interrupts.c (decrementer_interrupt, external_interrupt): + Remember that an interrupt wasn't delivered so that it can be + tried again later. + + * interrupts.c (check_masked_interrupt): New function. (re) + checks for the posibility that a recent change to the MSR may have + made it possible to deliver an interrupt that was previously + masked be the EE bit. + + * ppc-instructions (mtmsr, mfmsr, rfi): Check for posibility of + a pending interrupt being delivered using check_masked_interrupt(). + + * cpu.c (cpu_decrement_event): Just call decrementer_interrupt() + leaving it to that module to handle both interrupt synchronization + and masking. + + * cpu.c (struct _cpu): remove variables that were going to record + pending decrementer and external interrupts. + +Sun Mar 17 22:40:57 1996 Andrew Cagney <cagney@highland.com.au> + + * hw_cpu.c, hw_cpu.h: New files. Implement a device that sits + between the interrupt controller and the simulators internal + processor model. Maps device interrupts onto the processor + interrupt function calls. + +Mon Mar 4 06:06:54 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * hw_nvram.c: NVRAM device that includes a real-time clock that is + updated each second. + +Mon Mar 4 04:18:50 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * device.h (attach_type): Remove attach_default type address + spaces. Will replace with levels of callback memory. + + * corefile.h, corefile.c (new_core_mapping), corefile.c + (core_map_attach): Replace default attach with a layerd callback + approach. + +Sun Mar 3 03:58:46 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * device.c (split_property_specifier): ensure that only a single + property is found. + (split_value): New function, parses the value part of a device + spec. + + * device.c (device_tree_add_parsed): Use the interrupt conversion + functions to determine the interrupt port numbers. + + * device_table.h: Add table that maps between an interrupts + symbolic name and its port number. + + * device.h, device.c (device_interrupt_decode, + device_interrupt_encode): new functions use the recently added + interrupt port name/number tables to perform conversion. + +Sun Mar 3 03:23:59 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * device.h, device.c (device_set_array_property, + device_set_boolean_property, device_set_ihandle_property, + device_set_integer_property, device_set_string_property): New + functions - allow the value of a given property to be changed. + + * device.h, device.c: Re-order declaration and definition of + property functions. + +Sun Mar 3 03:10:22 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * device.c (device_tree_print_device, device_tree_add_parsed): + Remove references to phandle properties. + +Wed Feb 28 00:43:07 1996 Andrew Cagney - aka Noid <cagney@highland.com.au> + + * Makefile.in (corefile.o): missing dependency on device_table.h + etc. + +Tue Feb 27 23:59:35 1996 Andrew Cagney - aka Noid <cagney@highland.com.au> + + * device_table.h: Revamp device init callbacks so that they are a + sub structure. + * device.c (device_init_data, device_init_address): If an init + callback is NULL assume it should do nothing. + * device_table.c (ignore_device_init, unimp_device_init): delete + as redundant. + * device_table.c, hw_memory.c: adjust. + + * (io): ditto. + * (dma): ditto. + * (device_instance): ditto. + * (ioctl): ditto. + * (address nee config_address): ditto. + * (interrupt): ditto. + +Mon Feb 26 21:11:20 1996 Andrew Cagney - aka Noid <cagney@highland.com.au> + + * igen.c (idecode_switch_end): Output a default entry when the + switch statement is perfect. Firstly stops GCC complaining about + an incomplete switch and secondly it will be eliminated by a good + compiler any way. + +Mon Feb 26 22:47:15 1996 Andrew Cagney - aka Noid <cagney@highland.com.au> + + * Makefile.in (hw.h, hw.c): New targets. Create from the list of + hw_*.c files. hw.h declares a device descriptor table for each hw + device while hw.c lists those tables in a form suitable for the + construction of a top leveltable in device_table.c. + + * Makefile.in (device_table.o): now depends on hw.c a generated + table of hw. + + * device_table.c (device_table): Re-arange the table of devices so + that two levels are possible. Make use of hw.c. + * device_table.h: ditto. + + * device.c (device_template_create_device): Handle new two level + device lookup table. + * device.c (device_usage): ditto. + +Mon Feb 26 22:24:00 1996 Andrew Cagney - aka Noid <cagney@highland.com.au> + + * device_table.c: Delete the memory device (moved to hw_memory.c). + + * hw_memory.c: New file. Just an OpenBoot memory device. + +Wed Jan 17 21:47:34 1996 Andrew Cagney <cagney@highland.com.au> + + * device.c (device_init_address): New. Split initialization into + two stages, address and address spaces + * device.c (device_init_data): New. ... and data or other work. + With out this, devices try to modify memory before it as been + attached. + + * device.c (device_tree_init): Update to perform staged + initialization. + + * device.c (device_init): Delete. + +Wed Jan 17 21:43:09 1996 Andrew Cagney <cagney@highland.com.au> + + * device_table.c (data_*): Rewrite to make heaver use of property + nodes. Allow initialization by different data types. + * device_table.c (htab_* pte_*): Rewrite to use properties. + + * emul_chirp.c (emul_chirp_create): Use + * emul_bugapi.c (emul_bugapi_create): Ditto + * emul_netbsd.c (emul_netbsd_create): Ditto + +Wed Jan 17 21:24:50 1996 Andrew Cagney <cagney@highland.com.au> + + * emul_generic.c (emul_add_tree_options): Annotate existing tree + with options that haven't yet been specified. + * emul_generic.c (emul_add_tree_hardware): Annotate existing tree + with demo devices and properties. + + * emul_chirp.c (emul_chirp_create): Update to use new + device_tree_add_parsed call and additional information now + included in the device tree. Use emul_add_tree* functions to add + any missing details. + * emul_bugapi.c (emul_bugapi_create): Ditto + * emul_netbsd.c (emul_netbsd_create): Ditto + +Wed Jan 17 21:18:27 1996 Andrew Cagney <cagney@highland.com.au> + + * device.c (device_instance_create): New. Create/delete and + operate on instances of a device. + * device.c (device_instance_delete): Ditto + * device.c (device_instance_read): Ditto + * device.c (device_instance_write): Ditto + * device.c (device_instance_seek): Ditto + * device.c (device_instance_data): Ditto + * device.c (device_instance_name): Ditto + * device.c (device_instance_path): Ditto + + * emul_chirp.c (chirp_emul_open): Implement using device_instance. + * emul_chirp.c (chirp_emul_close): Ditto + * emul_chirp.c (chirp_emul_read): Ditto + * emul_chirp.c (chirp_emul_write): Ditto + * emul_chirp.c (chirp_emul_seek): Ditto + + * emul_chirp.c (chirp_read_t2h_args): Read arguments from device. + Being careful to convert all from target to host byte order. + * emul_chirp.c (chirp_write_h2t_args): Converse. + +Wed Jan 17 20:07:15 1996 Andrew Cagney <cagney@highland.com.au> + + * device.c (device_tree_add_parsed): New. Rewrite code to add + devices to the device tree so that a single printf style function + is used. + + * device.c (device_tree_add_*): Delete. Replaced by above. + + * device.c (split_device_specifier): Functions to manipulate a + device specifier (path) breaking it into its components + * device.c (split_property_specifier): Ditto + * device.c (split_device_name): Ditto + * device.c (split_find_device): Ditto + + * device.c (scan_*): Delete + + * device.c (device_tree_find_device): Rewrite to use above. + * device.c (device_add_property): Ditto + +Wed Jan 17 19:51:56 1996 Andrew Cagney <cagney@highland.com.au> + + * psim.c(psim_options): Parse the psim options, installing their + value in the device tree. Options are now first entered into a + device tree and then extracted out again when needed. This allows + greater flexability in configuration. + + * psim.c (psim_tree): Returns a basic device tree ready for + parsing by psim_options. + * psim.c (psim_usage): New. Give usage to varing levels of detail + according to the verbosity. In turn output device and trace + usage. + + * main.c (main): Update to use new system + * sim_calls.c (sim_open, sim_do_command): Ditto + + * psim.c (psim_options): Add `r' option - ram size. + * psim.c (psim_options): Add `o' option - openboot tree entry. + * psim.c (psim_options): Add `h'/`H' options - more help. + + * debug.c (trace_usage): Add more detailed help. + * device.c (device_usage): New. Output help including a list of + the devices currently available in the device table. + * device_table.c: Add usage operator to each device. + + * corefile.c (core_create, core_device_create): Adjust so that the + core device is created earlier for psim_tree(). Core can later be + created from it. + + * psim.c (psim_create): Update to handle above way of creating + things. Extract all information from the device tree. + + * device_tree.c (trace_*): New device node, its properties are + used to set the value of the trace options. Init this device (in + psim_options) when ever the options are updated. + +Wed Jan 17 19:46:07 1996 Andrew Cagney <cagney@highland.com.au> + + * debug.h: Add trace_print_info, trace_print_device_tree and + trace_dump_device_tree. The first is a replacement for the + variable `print_info' found in main.c and sim_calls.c. The latter + two enable the dumping of the entire device tree. + + * debug.c: Add to trace_description table. + + * main.c (main): Use above trace instead of local variable + * sim_calls.c (sim_close): Ditto + + * device.c (device_tree_print_device): New. Prints the device + tree in a format that is consistent with what can be parsed by the + device tree load from file code. + + * psim.c (psim_create): Dump device tree if enabled. If nump + selected, exit psim immediatly. + +Wed Jan 17 19:36:52 1996 Andrew Cagney <cagney@highland.com.au> + + * corefile-n.h (core_map_read_N): When mapping from an address to + a device, do not subtract the devices base. The device its self + can do this. Brings the behavour into line with OpenBoot. + * corefile-n.h (core_map_write_N): Ditto + * corefile.c (core_map_read_buffer): Ditto + * corefile.c (core_map_write_buffer): Ditto + + * device_table.c (console_io_read_buffer_callback): Adjust to + handle biased address. + * device_table.c (console_io_write_buffer_callback): Ditto + +Wed Jan 17 18:36:09 1996 Andrew Cagney <cagney@highland.com.au> + + * device.c (attach_device_interrupt_edge): New. Interrupt model + did not allow interrupts to be wired up as a general net (edges). + Re-implement so that interrupt events can be passed to multiple + controllers and interrupt controllers can further propogate + interrupt events. + + * device.c (attach_device_interrupt_edge) : New, Ditto + * device.c (detach_device_interrupt_edge) : New, Ditto + * device.c (clean_device_interrupt_edges) : New, Ditto + * device.c (device_interrupt_event) : New, Ditto + * device.c (device_interrupt_attach) : New, Ditto + * device.c (device_interrupt_detach) : New, Ditto + * device.c (device_child_interrupt_attach) : New, Ditto + * device.c (device_child_interrupt_detach) : New, Ditto + + * device.c (device_attach_interrupt) : Delete old + * device.c (device_detach_interrupt) : Delete old + * device.c (device_interrupt) : Delete old + * device.c (device_interrupt_ack) : Delete old + + * device_table.c (unimp_*) : Update to match + + * device_table.c (icu_io_write_buffer_callback) : Update to use + interface. + * device_table.c (icu_interrupt_event_callback) : Ditto + +Wed Jan 17 18:18:40 1996 Andrew Cagney <cagney@highland.com.au> + + * device.c (external_to_device) : New function that provides a + standard mapping between a devices internal representation (a + pointer) and its external (or what is passed to a client) + representation (a phandle). Implement using the cap object + attached to the root node. + + * device.c (device_to_external) : Ditto + * device.c (external_to_device_instance) : Ditto but for ihandle + and device instance. + * device.c (device_instance_to_external) : Ditto + + * Makefile (device.o): Add dependency on cap. + + * emul_chirp.c (struct _emul_chirp_data) : Elimate use of cap. Code + needing to translate between internal and external representations + changed to use the external_to_device et.al. device operations. + * emul_chirp.c (chirp_emul_*) : Ditto + + * Makefile (emul_chirp.o): Remove dependency on cap + +Sat Jan 6 10:13:26 1996 Andrew Cagney - aka Noid <cagney@highland.com.au> + + * emul_chirp.c (map_over_chirp_note): Tighten up (and fix) checks + on OpenBoot note section. + +Fri Jan 5 20:28:53 1996 Andrew Cagney <cagney@hignland.com.au> + + * emul_generic.c (emul_write_buffer): Use vm faulting byte + read/write calls for buffer transfers. This will cause a fault to + occure if the transfer fails. CHRP catches the fault while the + others suffer the consequences. + (emul_read_buffer): Ditto. + (emul_write_word): Ditto. + (emul_read_word): Ditto. + (emul_read_string): Ditto. + +Fri Jan 5 18:55:34 1996 Andrew Cagney <cagney@highland.com.au> + + * emul_chirp.c (emul_chirp_create, emul_chirp_instruction_call), + emul_generic (emul_blr_instruction): Use a real blr instruction to + return from a client service call. + + * emul_chirp.c (services): Add all OpenBoot services to table. + + * emul_generic.h, emul_bugapi.c (emul_bugapi_create), emul_chirp.c + (emul_chirp_create) : Use names instead of numbers for + instructions being stored in memory. + +Fri Jan 5 18:52:28 1996 Andrew Cagney <cagney@highland.com.au> + + * Makefile.in (maintainer-clean): Remove .log, core and *.core + (From NetBSD) files. + +Wed May 29 22:57:40 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * ChangeLog.00, ChangeLog: ChangeLog from gdb-4.16 becomes + ChangeLog.00 + diff --git a/sim/ppc/ChangeLog.00 b/sim/ppc/ChangeLog.00 new file mode 100644 index 00000000000..8b8be820fc7 --- /dev/null +++ b/sim/ppc/ChangeLog.00 @@ -0,0 +1,2168 @@ +Wed May 29 22:57:40 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * ChangeLog.00, ChangeLog: ChangeLog from gdb-4.16 becomes + ChangeLog.00 + +Thu Apr 4 15:17:22 1996 Michael Meissner <meissner@tiktok.cygnus.com> + + * INSTALL: Fix some long lines and remove x86 specific options in + the examples. + +Sun Mar 31 15:47:33 1996 Andrew Cagney <cagney@kremvax.highland.com.au> + + * INSTALL: Update to reflect gdb-4.16. + + * RUN: Update to reflect gdb-4.16. Review notes on building a BSD + runtime environment. + + * README: Point out copyright status of simulator in introduction. + +Thu Mar 7 19:53:49 1996 Michael Meissner <meissner@cygnus.com> + + * emul_netbsd.c: Only include sys/mount.h if HAVE_SYS_MOUNT_H is + defined. + * configure.in: Test for sys/mount.h. + * configure,config.in: Regenerate. + +Thu Feb 22 22:48:57 1996 Andrew Cagney <cagney@highland.com.au> + + * README, RUN, INSTALL: Update to reflect announcement + * psim: PSIM 1.0.1 released + +Thu Feb 22 14:01:56 1996 Michael Meissner <meissner@tiktok.cygnus.com> + + * emul_bugapi.c (emul_bugapi_do_read): New function to handle + reads. + (emul_bugapi_instruction_call): Add support for .INCHR/.INLN + system calls. + + * emul_bugapi.c (emul_bugapi_do_write): Call flush_stdoutput. + * emul_netbsd.c (do_write): Call flush_stdoutput. + + * main.c (flush_stdoutput): Do fflush (stdout). + * sim_calls.c (flush_stdoutput): Do gdb_flush (gdb_stdout); + * sim_callbacks.h (flush_stdoutput): Declare. + +Wed Feb 21 10:39:35 1996 Michael Meissner <meissner@tiktok.cygnus.com> + + * emul_bugapi.c (bug_mapping): New structure to map bug system + call numbers to a string. + (toplevel): Add remaining bugapi system calls. + (emul_bugapi_instruction_name): Map bugapi system call number to a + string. + (emul_bugapi_do_write): Common code to process write system calls. + (emul_bugapi_instruction_call): If os-emul tracing is on, trace + the system call. Use the name to print unknown system call. + Correct implementation of _OUTLN. Add support for _OUTSTR and + _PCRLR system calls. + +Wed Feb 21 17:07:27 1996 Andrew Cagney <cagney@highland.com.au> + + * NOTES: New file. Ramblings on why things were done they way + they were. + + * psim.c (psim_options): Didn't enter the model value into the + device tree as a string. + + * cpu.c (cpu_synchronize_context): Wrong test for conditional + flush of cache. + + * emul_generic.c (emul_add_tree_hardware): reg value didn't match + bus address. + + * ppc-opcode-flat: new file. Generate an instruction decode + function like ppc-opcode-complex but use switch statements. + + * INSTALL: document new opcode file, add example configurations. + +Tue Feb 20 18:42:31 1996 Andrew Cagney <cagney@highland.com.au> + + * main.c (main): rename psim instance (system) to simulation and + make global. + * main.c (error): print out performance even when an error occures. + + * emul_bugapi.c (emul_bugapi_create): Fix argument passing. + + * emul_generic.c (emul_add_tree_hardware): Move hardware devices + to 0x80000000 from 0x400000. + +Mon Feb 19 22:54:40 1996 Andrew Cagney <cagney@highland.com.au> + + * ppc-instructions (TLB Invalidate Entry, TLB Invalidate ALL): + Implement by passing on request to all processors. + * ppc-instructions (TLB Synchronize): Implement as empty, processor + tlb's are always in sync. + + * cpu.c (cpu_page_tlb_invalidate_all): New function. Pass on TLB + invalidate request to processors VM sub-system. + * cpu.c (cpu_page_tlb_invalidate_entry): Ditto. + + * vm.c (vm_page_tlb_invalidate_all): New function. Mark all page + TLB entries as invalid. + * vm.c (vm_page_tlb_invalidate_entry): New function. Ditt but only + invalidate one TLB entry. + + * psim.c (psim_init): Invalidate TLB's before (re)starting. + +Mon Feb 19 21:25:56 1996 Andrew Cagney <cagney@highland.com.au> + + * emul_generic.c (emul_add_tree_options): Add argument + oea_interrupt_prefix (0 or 1) that specifies the prefix MSR[IP] + and hence the location of the interrupt vectors. Add this to the + device tree. + + * emul_chirp.c (emul_chirp_create): Allow configuration of + floating-point and interrupt prefix (default 0) using the above. + + * emul_netbsd.c (emul_netbsd_create): Pass dummy arg for + interrupt-prefix. + + * emul_bugapi.c (emul_bugapi_create): Allow configuration of + interrupt prefix (default 1) and configure interrupt table traps + accordingly. + + * emul_generic.c (emul_add_tree_hardware): Include a small eeprom + in the list of devices. + + * device_table.c: For moment fake eeprom device by a memory + device. In future will need a proper eeprom device. + +Tue Feb 20 17:01:26 1996 J.T. Conklin <jtc@rtl.cygnus.com> + + * config.in: Regenerated. + +Fri Feb 16 10:42:27 1996 Michael Meissner <meissner@tiktok.cygnus.com> + + * psim.c: Include options.h so print_options is declared. + +Thu Feb 15 18:10:13 1996 Michael Meissner <meissner@tiktok.cygnus.com> + + * emul_netbsd.c (toplevel): Do not include sys/resource.h if the + system doesn't have it, and turn off getrusage processing. + (write_rusage): #ifdef out if we don't have getrusage. + (do_getrusage): Define as 0 if we don't have getrusage. + +Wed Feb 14 17:38:12 1996 J. T. Conklin <jtc@cygnus.com> + + * configure.in (AC_HEADER_DIRENT): Add, so that we can figure out + where the directory functions are declared. + * configure: Regenerate + + * emul_netbsd.c: Use the macros defined by configure to find the + appropriate directory functions. + +Thu Feb 8 00:53:13 1996 Andrew Cagney <cagney@highland.com.au> + + * configure.in (xor_endian): Trace setting of xor-endian flag. + +Wed Feb 7 18:20:56 1996 Andrew Cagney <cagney@highland.com.au> + + * psim.c (psim_usage): Extend documentation. + + * ppc-instructions (model-print): fix typo. + +Sun Feb 4 23:58:02 1996 Andrew Cagney <cagney@highland.com.au> + + * configure.in (with-smp): Default configuration allow up to + five processors (but enable only one). + + * emul_bugapi.c (emul_bugapi_create): If floating-point is + allowed, enable the floating point instruction set in the + msr. + +Tue Jan 30 22:52:32 1996 Andrew Cagney <cagney@highland.com.au> + + * emul_chirp.c (chirp_emul_seek, chirp_emul_read, + chirp_emul_write): Tolerate invalid ihandles. + + * device.c (device_instance_create, device_instance_delete): + init/delete instance name + + * emul_chirp.c (emul_chirp_instruction_call): Read the nr args and + returns when determining the service. + + * emul_chirp.c (chirp_read_t2h_args): Allow variable number of + args for the method "call-method". + + * emul_chirp.c (chirp_emul_getprop): Tolerate a n_returns of zero + - should be one. Some OpenBoot code doesn't pass correct arg. + + * emul_chirp.c (chirp_emul_getprop): Trace more property types. + +Tue Jan 30 19:12:29 1996 Andrew Cagney <cagney@highland.com.au> + + * RUN: New file. Describe how to run PSIM + * INSTALL: New file. Describe how to install PSIM + * README: New file. Overview PSIM. + * BUGS: New file. Briefly discuss bugs and limitations + +Wed Jan 24 20:28:08 1996 Andrew Cagney <cagney@highland.com.au> + + * emul_bugapi.c (OEA_START_ADDRESS): Put it back to 0x100000, + wasn't correctly using GLD. + +Mon Jan 22 22:44:13 1996 Andrew Cagney <cagney@highland.com.au> + + * emul_generic.c (emul_add_tree_options): Make default number of + active processors 1 (even when SMP enabled). + +Mon Jan 22 22:37:34 1996 Andrew Cagney <cagney@highland.com.au> + + * device_table.c (icu_io_read_buffer_callback): Add extra register + (at addr + 4) that returns number of processors. + + * emul_generic.c (emul_add_tree_hardware): Update device node to + match. + +Mon Jan 22 22:00:42 1996 Andrew Cagney <cagney@highland.com.au> + + * emul_bugapi.c (OEA_START_ADDRESS): Change to 0x4000 so that it + matches gas-960116/ld. + +Fri Jan 19 00:32:27 1996 Andrew Cagney <cagney@highland.com.au> + + * psim-960119 released - psim-1.0b01. + +Fri Jan 19 00:32:27 1996 Andrew Cagney - aka Noid <cagney@highland.au.com> + + * psim.c (psim_create): Re-order so that all options are set + before the CPU's are created. Was breaking mon_create(); + + * psim.c (psim_create): Tidy up conflicting configuration errors. + + * debug.c: Add missing print-info entry to trace table. + + * os_emul.c (os_emul_create): Fix `-e' option. Was looking under + wrong name. + + * psim.c (psim_options): Fix `-r' option. Was entering under wrong + name. + +Thu Jan 18 20:33:48 1996 Andrew Cagney <cagney@highland.com.au> + + * vm.c (om_unpack_bats): Fix checking of bat bits. + + * emul_chirp.c (emul_chirp_create): Store address of OB in memory + in the os_emul_data structure. + + * emul_bugapi.c (emul_bugapi_create): Store the address of the + bugapi code (in main memory) in the os_emul_data structure. + +Thu Jan 18 01:14:55 1996 Andrew Cagney <cagney@highland.com.au> + + * vm.c (om_translate_effective_to_real): Fix trace output. + +Wed Jan 17 22:21:55 1996 Andrew Cagney <cagney@highland.com.au> + + * device_table.c (generic_device_init_address): Create memory from + information obtained from `reg' property. + * device_table.c (vm_init_address_callback): Use information + obtained from properties. + * emul_netbsd.c (emul_netbsd_create): Update to create device and + property entries to match + +Tue Jan 16 09:50:53 1996 Michael Meissner <meissner@tiktok.cygnus.com> + + * idecode_expression.h (ALU_END): Add ITRACE of the result. + + * ppc-instructions (Equivalent): Enable this instruction. + (Add to Minus One Extended): Ditto. + (Subtract from Minus One Extended): Ditto. + (Add/And/Or/Xor Immediate): Add alu trace of result. + (Add/And/Or/Xor Shifted Immediate): Ditto. + (And/Or/Equivalent/Nand/Nor): Ditto. + (And/Or with Complement): Ditto. + (Extend Sign Byte/Half Word): Ditto. + (Count Leading Zeros): Ditto. + (Shift Right Algerbraic Word): Ditto. + (Shift Right Algerbraic Word Immediate): Ditto. + +Tue Jan 9 15:10:27 1996 Andrew Cagney <cagney@highland.com.au> + + * emul_bugapi.c (emul_bugapi_instruction_call) : Make format type + correct. + * emul_chirp.c (map_over_chirp_note) : Ditto + * emul_chirp.c (chirp_emul_test) : Ditto + * device_table.c (register_init): Ditto + +Tue Jan 9 14:16:26 1996 Andrew Cagney <cagney@highland.com.au> + + * configure.in: Make disable-sim-switch default. Switch only + useful if using --enable-sim-opcode=ppc-opcode-stupid and then + only marginally so. + +Mon Jan 8 12:17:22 1996 Michael Meissner <meissner@tiktok.cygnus.com> + + * device_table.c (register_init): Make format type correct. + +Wed Jan 3 19:21:46 1996 Andrew Cagney <cagney@highland.com.au> + + * emul_bugapi.c (emul_bugapi_create): Add nodes to init the + system-call trap to the emul instruction call instruction (Along + with an rfi and infinate loop). + + * emul_bugapi.c (emul_bugapi_instruction_call): Expand to include + a few real PPC bug calls. Test with simple hello world. + +Tue Jan 2 20:51:19 1996 Andrew Cagney - aka Noid <cagney@highland.com.au> + + * device.h, device.c (device_child, device_sibling): New + functions. Return corresponding device value. + + * emul_chirp.c (chirp_emul_child, chirp_emul_peer, + chirp_emul_parent): New functions - emulate corresponding OpenBoot + interfaces. + + * device_table.c (register_init): Extend properties attached to + register init node to allow a specific processor's register to be + specified. + + * emul_chirp.c (emul_chirp_create): Init SMP correctly - the + initial PC for all processors is an infinate loop but then, for + processor zero, is quickly changed to be the correct code starting + address. + + * emul_chirp.c (emul_chirp_create): Add fake bootpath + et.al. properties to tree. + + * emul_chirp.c (chirp_emul_getproplen): New function. Emulate the + getproplen OpenBoot call. + + * emul_chirp.c (emul_chirp_instruction_call): Document other + possible chirp emulation internal states. + + * emul_chirp.c (emul_chirp_instruction_call): Trace failed lookups + as well as successful ones. + + * emul_chirp.c (emul_chirp_open): New function - handle open + client call. + + * Makefile.in (maintainer-clean): Proper rule that eliminates more + junk. + +Tue Dec 19 13:00:11 1995 Andrew Cagney <cagney@highland.com.au> + + * emul_chirp.c (chirp_emul_exit): Full out call. + + * device_table.c (htab_map_page): Wasn't handling byte swap when + creating entries in the hash table. + + * device.c (device_tree_find_node): Allow primative wild-card match + of device names with the path. + + FIXME: As mentioned earlier, the device stuff needs work to bring + it into line with OpenBoot. Part of this work is rewriting the + find_node function so that it behaves as specified in p1275. + +Mon Dec 18 19:58:56 1995 Andrew Cagney <cagney@highland.com.au> + + * emul_chrp.c (chirp_emul_write, chirp_emul_finddevice): add + better tracing. + + * emul_chrp.c: Change return type of emul functions to int. Emul + functions either return -1 or zero so unsigned was a bit + dangerous. + + * inline.h (*), igen.c, dgen.c, *: Update INLINE macros so that + they are paramaterised with the type of the function. Gets around + the problem of `static' needing to come first with `attribute' + comming last. Format declarations and definitions so that emacs + doesn't get confused. + +Fri Dec 15 17:06:44 1995 Andrew Cagney <cagney@highland.com.au> + + * std-config.h (PSIM_INLINE): Add missing inline configuration + control for the main loop. + + * mon.c (mon_print_info): If monitoring disabled still print out + the number seconds used. + + * psim.c (run_until_stop): Don't monitor the cache misses when + monitoring is disabled. + + * configure.in (sim_mon, sim_monitor): Correct typo - sim_mon -> + sim_monitor for shell variable (or should that have been the other + way around?). + + * vm.c (vm_synchronize_context): Fix wrong test for unsuported + change in endian-mode. + + * std-config.h (WITH_REGPARM), inline.h (IDECODE_INLINE, + SEMANTICS_INLINE): Add -DWITH_REGPARM=<n> option. Enables the + __attribute__((__regparm(WITH_REGPARM))) for some functions. + configure with --enable-sim-cflags="-DWITH_REGPARAM=3" (say). + Unfortunatly it tickles a bug (gcc?) and can't be used. + +Mon Dec 18 13:36:06 1995 Michael Meissner <meissner@tiktok.cygnus.com> + + * device.c (device_tree_add_device): Make trace fprintf arguments + type correct. + * device_table.c (htab_decode_hash_table): Ditto. + (htab_map_binary): Ditto. + (htab_init_callback): Ditto. + * vm.c (om_virtual_to_real): Ditto. + +Sat Dec 16 09:54:18 1995 Michael Meissner <meissner@wogglebug.tiac.net> + + * emul_netbsd.c (emul_netbsd_create): Deal with new BFD that + changed how big/little endian support is recorded, while remaining + compatible with the old BFD with #ifdefs. + * emul_chirp.c (emul_chirp_create): Ditto. + * emul_bugapi.c (emul_bugapi_create): Ditto. + +Fri Dec 15 15:55:56 1995 Michael Meissner <meissner@tiktok.cygnus.com> + + * std-config.h (MODEL_INLINE): Turn off INLINE_MODULE by default. + + * corefile.h: Delete declarations for unknown functions. + * device.h: Ditto. + * interrupts.h: Ditto. + * interrupts.c: Ditto. + +Thu Dec 14 18:49:34 1995 Andrew Cagney <cagney@highland.com.au> + + * lf.c (lf_print_function_type): New function. Munges a function + type so that the prefix (eg INLINE...) is inserted after the type + but before any `*'. + + * igen.c: Change to output functions using this. + +Wed Dec 13 23:47:00 1995 Andrew Cagney <cagney@highland.com.au> + + FIXME: Emul CHIRP does not correctly implement the find device + function. + + FIXME: Emul CHIRP and device do not implement device instance + operations. + +Wed Dec 13 23:47:00 1995 Andrew Cagney <cagney@highland.com.au> + + * options.c (options_inline): Function to output meaningful + description of the INLINE options. + + * configure.in (inline): Replace inline magic numbers with macro + names. Map 1->LOCALS_INLINE and 2->ALL_INLINE. + + * inline.h, inline.c: update to use inline method. + + * std-config.h (CPU_INLINE), cpu.h, inline.h, inline.c: make cpu.h + inline always. + + * std-config.h (EVENTS_INLINE): Inline events in psim. + +Wed Dec 13 22:01:21 1995 Andrew Cagney <cagney@highland.com.au> + + * device_table.c (htab_sum_binary): DMA binaries to correct byte + within a page. + +Tue Dec 12 22:51:18 1995 Andrew Cagney <cagney@highland.com.au> + + * psim.c (psim_merge_device_file): Change `=' to `==', was this an + error? + +Tue Dec 5 11:56:14 1995 Andrew Cagney <cagney@highland.com.au> + + * ppc-instructions (ppc_nr_mtcrf_crs, ppc_branch_conditional_name, + ppc_function_unit_name): Simplify by declaring these arrays as + pure and simple static (instead of STATIC_MODEL). + +Tue Dec 5 00:45:34 1995 Andrew Cagney <cagney@highland.com.au> + + * sim_calls.c (sim_create, sim_load), main.c (main), psim.c: Pass + an options device into psim_create() so that options can be merged + into the tree. + + * device.c (*add*): Change semantics so the add functions only add + when the new device (or property) doesn't already exist. This + allows merging of options and data. + +Mon Dec 4 17:12:13 1995 Andrew Cagney <cagney@highland.com.au> + + * Makefile.in (BASICS_H): Didn't include basics.h in the list of + header files to depend on. + +Mon Dec 4 17:12:13 1995 Andrew Cagney <cagney@highland.com.au> + + * std-config.h: (*_MODULE): Extend the <module>_inline macro's so + that they also allow control over static functions. Rewrite + document to reflect this. + + * std-config.h: (INLINE): Simplify definition, the above and + earlier changes to igen.h eliminate the need to be defensive about + enabling the inline of static functions. + + * std-config.h: (SIM_ENDIAN_INLINE, BITS_INLINE): Document limited + suport for inlineing of modules for all callers. Adjust relevant + macro's so that DEFAULT_INLINE will enable this. + + * basics.h: Re-order #includes and definitions so that c-code for + basic include files does not call functions delcared in later + #includes. + + * basics.h (__attribute__), sim_callbacks.h: Move attribute macro + to basics.h and add hack (include <stdio.h>) to try and bring that + and other possible conflicting macros into scope much earler. + + * sim-endian.h,c (SIM_ENDIAN_INLINE) bits.h,c (BITS_INLINE): + Change to use the updated inline definitions. If enabled + immediatly include the corresponding c-code so that it will inline + for all modules. + + * inline.h, inline.c (SIM_ENDIAN_INLINE, BITS_INLINE): Remove + these cases, moved to module specific header files. + +Sat Dec 2 18:37:51 1995 Andrew Cagney <cagney@highland.com.au> + + * vm.c, vm_n.c: Fix htab code. + + * vm.c (vm_data_map_read_buffer): Was using EA not RA when reading + the data from core. + + * device.c: Fix htab create code. + +Fri Nov 24 23:10:09 1995 Andrew Cagney <cagney@highland.com.au> + + * bits.h, bits.c (EXTRACTED): Convert to function, fix - had && + instead of &. + + * sim-endian.h (SWAP_N), sim-endian-n.h, sim-endian.c: How + embarasing - fix yet another bug in the swap code! Simplify + everything by using more functions. Add host to big-endian byte + swapping support. + +Fri Nov 24 23:10:09 1995 Andrew Cagney <cagney@highland.com.au> + + * devices.h, devices.c: delete, replaced by the files + device_table.[ch] and device.[ch]. + * device_tree.h, device_tree.c: ditto + + * device_table.h, device_table.c: New files. Contain a table of + devices. + + * device.h, device.c, Makefile.in, std-config.h (DEVICE_INLINE), + options.c (print_options): New files. Define the device object + along with any attached properties. + + * device_tree.h, device_tree.c: Update to use new device object. + For convenience, change the printd functions into device_tree_add + functions. + + * psim.c (create_*_tree): Use new device_tree create functions. + + * corefile.h, corefile.c corefile-n.h (core_n.h): Update to use + the new device.h / device_table.h interface. Rename core_n.h to + corefile-n.h to be consistent with other n files. + + * Makefile.in (run): add corefile-n.h to dependencies for + corefile. + + * basics.h (device_instance), device.h, device.c, device_table.h, + device_table.c: Add the concept of a device instance and operators + on these instances - corresponds to ihandle in OpenBoot speak. + Don't yet implement it. + +Tue Nov 14 12:27:08 1995 Andrew Cagney <cagney@highland.com.au> + + * emul_generic.h, emul_generic.c (emul_syscall_enter, + emul_syscall_exit): rename from emul_enter_call / + emul_exit_call. As only used by emul_do_system_call simplify + associated code. + + * os_emul.h, os_emul.c, emul_generic.h: Correct and fill an + os_emul interface. + + * os_emul.c, emul_bugapi.h, emul_bugapi.c, Makefile.in: Add + preliminary hooks for a kernel mode rom emulation. + + * cap.h (new), cap.c (new): Capability data base. Some emulations + pass object identifiers (capabilities?) to/from the simulated code + (for instance the phandle in OpenBoot). The cap object is able to + check/map between internal and external (target program) + representations of each identifier. + + * os_emul.c, emul_chirp.h, emul_chirp.c, Makefile.in: Add + preliminary hooks for a kernel mode IEEE-1275 emulation. + + * cpu.h, cpu.c (cpu_create, cpu_os_emulation, cpu): Add os_emul to + list of arguments passed in when creating a cpu. Grant access to + the element. + + * std-config.h (OS_EMUL_INLINE), options.c (print_options), + inline.h, inline.c: New to allow control over inline of + corresponding code files. + + * ppc-instructions (instruction_call): Add illegal instruction to + call the instruction-call emulation handler. + + * interrupts.c (system_call_interrupt): Call renamed + os_emul_system_call function(). + + * emul_netbsd.c: Update to interface to generic emulation. Since + all its functions are called via a table don't worry about any + inline. + + * emul_generic.h, emul_generic.c, spa-*(delete): Remove references + and code for spa, no longer to be used. + + * psim.c (create_chirp_device_tree): Fill out what was previously + the openboot create function so that it starts to create a full + OpenBoot device tree. + +Tue Nov 28 21:48:06 1995 Andrew Cagney <cagney@highland.com.au> + + * debug.h, debug.c: pte trace is made redundant by htab trace, + delete it. Add vm to list of options. Simplify tracing output so + lines are not as long. + +Tue Nov 14 12:27:08 1995 Andrew Cagney <cagney@highland.com.au> + + * events.h, events.c (event_queue_init), psim.c (psim_init): (re) + initialize the event queue. + + + + + + +Tue Nov 28 13:38:26 1995 Michael Meissner <meissner@tiktok.cygnus.com> + + * sim-endian.h: Look at WORDS_BIGENDIAN to determine if the host + is big endian or little endian. For SWAP_n, use htonl/htons if + host is little endian, not big endian and if WITH_NTOH is defined. + + * configure{,.in} (--enable-sim-model-issue): Instead of defining + 0/1, define it to be MODEL_ISSUE_{PROCESS,IGNORE}. Add + AC_C_BIGENDIAN to determine if the host is big endian or not. + * config.in: Regenerate. + + * std-config.h (WITH_MODEL_ISSUE): Default to 0. + (CURRENT_MODEL_ISSUE): Reference WITH_MODEL_ISSUE, and if that is + 0, use current_model_issue. + (MODEL_ISSUE_{PROCESS,IGNORE}): Define as -1/1. + + * psim.c (current_model_issue): New global variable. + + * cpu.c (cpu_create): Use CURRENT_MODEL_ISSUE > 0 instead of + WITH_MODEL_ISSUE. + (cpu_{init,halt}): Ditto. + * mon.c (mon_print_info): Ditto. + * ppc-instructions (PPC_INSN_* macros, branch handling): Ditto. + + * mon.c (mon_print_info): Print instructions/second if verbose > 0, + rather than > 1. + + * main.c (main): Set current_model_issue to MODEL_ISSUE_PROCESS if + the -I switch is used. + * sim_calls (sim_open): Ditto. + + * ppc-instructions (model support): Add support for determining + when we don't have enough writeback slots. Add tracing for the + beginning of each cycle. + +Mon Nov 27 17:46:33 1995 Michael Meissner <meissner@tiktok.cygnus.com> + + * mon.c: Check for whether to include sys/types.h and sys/time.h. + + * configure.in: Check for include files sys/types.h and + sys/time.h. + * configure: Regenerate. + * config.in: Regenerate. + + * cpu.h (CONST_ATTRIBUTE): Define as __attribute__((__const__)) if + not already defined. + (cpu_system): Use CONST_ATTRIBUTE, so that when we're not inlining + the world, the optimizer has a fair chance of CSE'ing function + calls. + (cpu_{monitor,nr,registers,model}): Ditto. + + * std-config.h (MODEL_INLINE): If not defined, define as 1 if + DEFAULT_INLINE is non-zero, 0 otherwise, rather than just the + value of DEFAULT_INLINE. + +Fri Nov 24 11:24:34 1995 Michael Meissner <meissner@tiktok.cygnus.com> + + * lf.h (__attribute__): If not GCC and at least 2.7.0, define as + nothing. + (lf_printf): Add printf __attribute__, so the compiler will + automatically check the format string. + + * configure{,.in} (--enable-sim-icache): If argument is define, + add -R to flags passed to igen. + + * igen.c (stdlib.h): Include if the system supplies one. + (semantics_use_cache_struct): New global for -R flag to say + semantics is to use the cache structure directly rather than + putting the values into local variables. + (first_undef, last_undef): New structures to remember names to + #undef if -R. + (lf_print_c_extraction): If -R and this is semantics, emit names + as #defines pointing to the cache structure, rather than loading + the values into local variables. + (lf_print_c_semantic_function): If -R, #undef all of the names + defined in lf_print_c_extraction. + (main): Recognize -R. + + * idecode_fields.h (SPR_*): Redefine spr_* macros as SPR_* to + avoid a name confict if -R passed to igen. + + * ppc-instructions (mfspr, mtspr): Rename spr field to SPR. + (model_data): Add field to count the various # of CRs that the + mtcrf instruction used. + (model_mon_info): Return structures counting the # of CRs that the + mtcrf instruction used. + (branches, sync instructions): Do not call model functions if + WITH_MODEL_ISSUE is 0. + + * mon.c (stdlib.h): Include if the system supplies one. + (mon_sort_instruction_names): New function to sort instruction + names alphabetically. + (mon_print_info): Call qsort with mon_sort_instruction_names to + sort instruction names. Don't abort if WITH_MODEL_ISSUE is 0. + + * debug.h (ITRACE): Make printf_filtered arguments type correct. + * idecode_expression.h (CR0_COMPARE): Ditto. + * psim.c (psim_read_register): Ditto. + + * igen.c (lf_print_my_prefix): Use __attribute__((__unused__)) to + silence compiler warnings about unused automatically generated + variables. + (lf_print_c_extraction): Ditto. + * idecode_expression.h (FPSCR_BEGIN): Ditto. + + * ppc-cache-rules: Define rules for making a bitmask for all + registers. + + * ppc-instructions: Rewrite model specific functions to use the + bitmask of the register number, instead of using the register + pointer to get the register number, and then making the bitmask. + +Wed Nov 22 15:24:27 1995 Michael Meissner <meissner@tiktok.cygnus.com> + + * ppc-instructions (model_branches): Add conditional argument to + count the number of times each type of conditional branch is used. + (conditional branches): Pass B0 or -1 to model_branches. + (model_mon_info): Print out conditional branch counts. + (model-data): Add support for printing out conditional branch + types. + +Tue Nov 21 16:31:25 1995 Michael Meissner <meissner@tiktok.cygnus.com> + + * igen.c (insn_table_load_insns): Add support for model-static for + internal functions that should not be inlined. + (lf_print_c_semantic): Remove model_cleanup. + (gen_model_{c,h}): Ditto. + + * ppc-instructions: Redo model specific support once again. Add + floating point support to the model specific information. Flesh + out all of the floating mutiply add/subtract instructions. Add + better tracing support to the model specific information. + +Sun Nov 19 23:00:52 1995 Michael Meissner <meissner@tiktok.cygnus.com> + + * ppc-instructions (model data, model_busy): Rather than using a + bit mask for the busy units, just use a char array. Also, only + support 2 function units an insn can use, rather than a loop. + +Fri Nov 17 14:08:08 1995 Michael Meissner <meissner@tiktok.cygnus.com> + + * table.c (table_entry_read): Move setting entry->line_nr to after + the model specific fields so the line numbers for the annex are + correct. + + * cpu.c (cpu_{create,init,halt}): Check for WITH_MODEL_ISSUE + before calling the model functions. + + * debug.c (trace_descriptor): Add trace_model support. + * debug.h (trace_options): Ditto. + + * igen.c (gen_icache_h): Create type idecode_cache as void if not + caching instructions. + (gen_model_{c,h}): Drop model_issue support. Add support for + model_cleanup. + (lf_print_my_prefix): Initialize a const itable_index with the + current index. + (lf_print_c_semantic): Call model_cleanup at the end of the + function to check for instructions that aren't supported yet by + the scheduling code. + + * mon.h (count_type): New type for counters. + * mon.c: Use count_type instead of unsigned. + + * ppc-instructions: Redo scheduling code once again. Make it all + inline friendly. Instead of having general code emitted by igen, + go the route of having each semantic routine call the appropriate + module. + +Thu Nov 16 09:52:26 1995 Michael Meissner <meissner@tiktok.cygnus.com> + + * table.c (table_entry_read): Allow the annex to have blank lines. + + * ppc-instructions: Change lines in model stuff that just have a + tab to just newline. Add 601 support. Document most instructions + in terms of model specific timing information. Drop 'FUNCTION' + from PPC_FUNCTION_UNIT_xxx enums. Change PPC_UNIT_UNKNOWN -> + PPC_UNIT_BAD. Add TRACE(trace_tbd) for all data cache + instruction.s. Signal illegal instruciton if data cache block + invalidate is issued from problem state. + + * igen.c (max_model_fields_len): New static to keep track of the + max size for the model specific fields. + (model_c_insn): Use max_model_fields_len to size fields. + (insn_table_insert_insn): Set max_model_fields_len. + (model_table_insert): Ditto. + (gen_model_{c,h}): Model_issue is now called with a processor + argument. + + * debug.c (trace_description): Add support for trace_tbd. + + * mon.c (mon_issue): Pass processor argument to model_issue. + + * Makefile.in: Delete all function unit support, since the newer + table driven model support replaces it. + * cpu.{c,h}: Ditto. + * mon.c: Ditto. + * inline.{c,h}: Ditto. + * std-config.h: Ditto. + * options.c: Ditto. + * configure{,.in}: Ditto. + * Makefile.in: Ditto. + * psim.c: Ditto. + * function_unit.{c,h}: Delete these now usused files. + + * std-config.h (WITH_MODEL_ISSUE): Add new macro on whether to + trace instructions in a model specific manor. + * options.c (print_options): Print it out. + * configure{,.in}: Add --enable-sim-model-issue option. + * Makefile.in: Add --enable-sim-model-issue flags. + * igen.c (lf_print_c_semantic): Add call to mon_issue here. Check + for WITH_MODEL_ISSUE. + * mon.c (mon_issue): Remove call to mon_issue_here. + + * ppc-instructions: Move branch tracing to the actual branch + instructions, rather than testing it in model_issue. Add code to + code successful/unsuccessful branch predictions, and the number of + conditional branches that fell through. + +Wed Nov 15 17:32:13 1995 Michael Meissner <meissner@tiktok.cygnus.com> + + * cpu.h (cpu_model): Add declaration. + + * cpu.c (struct _cpu): Add model_ptr to hold model specific + information. + (cpu_model): Return the model internal pointer. + (cpu_{create,init,halt}): Call the appropriate model function. + + * inline.c (mon.c): Move include of mon.c after model.c. + + * mon.c (_cpu_mon): Add fields to count unaligned memory + references. + (mon_issue): Call model_issue, not function_unit_issue. + (mon_{read,write}): Count # of unaligned memory accesses. + (mon_print_info): Switch to calling model_mon_info and + model_mon_info_free instead of function_unit version. Print out + number of unaligned reads/writes. + + * {ppc-instructions,igen.c}: More global changes to add model + specific features. + + * inline.{c,h}: Provide for inlining options.c. + * options.{c,h}: Ditto. + * std-config.h: Add OPTIONS_INLINE. + +Tue Nov 14 04:47:25 1995 Michael Meissner <meissner@tiktok.cygnus.com> + + * Makefile.in (devices.o, main.o): Update dependency. + + * igen.c (gen_model_h): Use correct variable in loop. + (gen_model_c): Use strcmp, strcasecmp. + (gen_model_c): Use EXTERN_MODEL for arrays. + (gen_model_h): Use STATIC_MODEL for arrays. + (lf_print_c_semantic_function_header): Delete unused function. + + * main.c (cpu.h): Include cpu.h to get model.h. + + * inline.h ({EXTERN,STATIC}_MODEL): Define. + +Mon Nov 13 09:14:13 1995 Michael Meissner <meissner@tiktok.cygnus.com> + + * igen.c ({insn,model}_table_fields): Spell mnemonic correctly. + (gen_itable_h,itable_c_insn): Ditto. + (model support): Move model support around, add support for + model-data, model-internal. Use annex field for model-macros + now. + + * configure.in (--enable-sim-inline): If --enable-sim-inline=no, + also define INLINE as nothing. + * configure: Regenerate. + + * std-config.h (INLINE): Rather than nuking INLINE, only define it + as __inline__ if any of the INLINE flags are non-zero. + + * options.c (print_options): Print out WITH_XOR_ENDAIN. + +Mon Nov 13 23:03:45 1995 Andrew Cagney <cagneyhighland.com.au> + + * ppc-instructions (rfi): Add missing code. + + * cpu.c (cpu_get_time_base): Fix calculation of current value of + time base register. + + * ppc-spr-table (TBL, TBU): Fix TBL/TBU entries - was confusing + m[tf]tb with m[tf]spr. + + * ppc-instructions (mtspr, mfspr): Fix mttbl - wasn't storing + lower word. + +Mon Nov 13 21:35:37 1995 Andrew Cagney <cagneyhighland.com.au> + + * std-config.h (INLINE, STATIC_INLINE): Was being set to static + inline.. Only problem being that with ppc-opcode-simple this gave + it the chance to inline all the idecode functions with potentially + disasterous results on a 16mb PC. For moment hobble INLINE. + + * configure.in, std-config.h (WITH_SMP): Make that 5 processors by + default ... + + * configure.in: Tweek flags passed to gcc for --with-sim-warnings. + Firstly make them errors and secondly remove the options gcc-245 + doesn't reconize. + +Mon Nov 13 17:57:24 1995 Andrew Cagney <cagney@highland.com.au> + + * misc.c (zalloc), cpu.c (cpu_init), devices + (console_io_read_buffer_callback, icu_io_read_buffer_callback, + vm_io_read_buffer_callback), main.c (zalloc), mon.c (memset), + sim_calls.c (zalloc) : replace bzero() with memset(). + + * emul_netbsd.c (write_direntries), psim.c (psim_read_register, + psim_write_register): replace bcopy() with memcpy(). + +Sun Nov 12 20:55:41 1995 Andrew Cagney <cagneyhighland.com.au> + + * configure.in: for --disable-sim-inline (--enable-sim-inline=no), + force DEFAULT_INLINE to 0 rather then trusting the std + configuration. + +Sun Nov 12 20:55:41 1995 Andrew Cagney <cagneyhighland.com.au> + + * igen.c (lf_print_idecode_table, idecode_table_leaf): Fix + generation of switch entries in tables - treat the same as + cracking/semantic functions. + + * igen.c (idecode_switch_end, idecode_switch_leaf): Fix generation + of a boolean switch statement (field zero or non-zero). + + * ppc-opcode-test-1, ppc-opcode-test-2: New files. These test the + switch/table generation ability of igen. + + * igen.c (idecode_switch_leaf): Fix code output when a switch + statement needs to look up a table. + + * igen.c (idecode_declare_if_switch): New function called from + gen_idecode_c - need to declare any idecode switch functions + before they are used in idecode tables. + + * igen.c (lf_print_c_cracker_function, idecode_crack_leaf, + idecode_crack_insn): Add is_inline_function argument to code + printing cracker functions which indicates if STATIC_IDECODE or + STATIC_INLINE_IDECODE should be used for definition. For + idecode_crack_insn (which implies not duplicating/expanding) don't + declare function as inline - we assume that the only time this is + code is generated is when things are being tested. For + idecode_crack_leaf, make static (instead of INLINE) if the + instructions parent is a table as function will always be called + via a table. + + * igen.c (idecode_expand_if_switch): Declare as STATIC_IDECODE not + STATIC_INLINE_IDECODE. Only the outermost idecode switch will be + called directly, all others are called via a table. + + * igen.c (lf_print_semantic_function_header, semantics_h_leaf, + semantics_h_insn, semantics_h_function, + lf_print_c_semantic_function, semantics_c_function): Add + is_inline_function argument to lf_print_semantic_function_header + to indicate if an inline or static function declaration/definition + should be output. Depending on situtation call accordingly: + functions (not instruction semantic routines) are always inline; + Semantic routines are made inline when there is no icache (cache + will contain the function address) and are duplicating (see above) + and the parent of the instruction is a switch statement. + + * igen.c (opcode_field_new): Delete. Code changed to use ZALLOC + and moved to insn_table_find_opcode_field. + + * table.c (table_open): Fix typo (nr_model_fields vs nr_fields). + + * igen.c (model_c_insn): Suggestion - document the name of the + instruction on each line of the instruction model table. + +Fri Nov 10 00:44:38 1995 Andrew Cagney <cagneyhighland.com.au> + + * emul_netbsd.c (do_ioctl): Cleanup compilation. + + * sim_callbacks.h (__attribute__): Only define if not defined (was + already defined on NetBSD host). + +Wed Nov 8 21:49:52 1995 Andrew Cagney <cagneyhighland.com.au> + + * std-config.h (WITH_XOR_ENDIAN), configure.in, Makefile.in: New + macro, indicates if the PowerPC's horrible XOR endian mode should + be suported. Add to configure and make. + + * vm_n.h (vm_data_map_read_N, vm_data_map_write_N), vm.c + (vm_instruction_map_read): If XOR endian, xor the address + with a value from an xor table (indexed by size of access). + + * vm.c (vm_synchronize_context), cpu.c (cpu_synchronize_context): + set up xor table to xor if there is a conflict between the + CURRENT_TARGET_ENDIAN and the endian indicated in the MSR. Move + check of suported change of endian mode from cpu.c to vm.c. + + * vm.c (vm_data_map_write_buffer, vm_data_map_read_buffer): + Hopefully added correct hack to handle XOR endian mode. + + FIXME: If NONSTRICT alignment and XOR ENDIAN and MSR indicates + little endian mode, the model accepts miss aligned transfers. + + FIXME: Need to create an `init' device that, during + initializatioin for XOR mode, it mushes (XOR address) all the dma + data before passing it on to the core for storage. Just like the + real thing really. + +Wed Nov 8 21:49:52 1995 Andrew Cagney <cagneyhighland.com.au> + + * devices.c (halt_io_write_buffer_callback): Use value written to + halt device to determine exit status. Thus allowing + success/failure of OEA tests. + +Wed Nov 8 00:10:38 1995 Andrew Cagney <cagneyhighland.com.au> + + * ppc-instructions (icbi): If icache present flush it. + +Tue Nov 7 23:36:31 1995 Andrew Cagney <cagneyhighland.com.au> + + * devices.c (htab_init_callback): Add code to create htab/pte. + + * devices.c (dma_file, file_init_callback, htab_init_callback): + New function - Dma the named file into memory at the specified + address. Use. + + * device_tree.h, device_tree.c (scand_*): New functions. + +Tue Nov 7 23:36:31 1995 Andrew Cagney <cagneyhighland.com.au> + + * filter_filename.c, Makefile.in: Change so that only dependant on + a very limited nr of files. Stops an unnecessary dependency. + +Tue Nov 7 15:44:33 1995 Andrew Cagney <cagney@highland.com.au> + + * core.c (core_map_find_mapping): Use cpu_halt rather than error + to abort an access to an undefined address. + +Sun Nov 12 07:58:09 1995 Michael Meissner <meissner@tiktok.cygnus.com> + + * igen.c (model_table_insert_{macro,function}): New functions. + (insn_table_load_insns): Call them. + (gen_model_h): Move section emiting model-macros to be first. + (model_{c,h}_function): New functions cloned from semantic + functions to print out the prototype and function for + model-functions. + (gen_model_{c,h}): Print out model-functions. + + * ppc-instructions (model_{start,halt,print_info}): Add dummy + model-functions. + + * options.c (print_options): Print out WITH_{,DEFAULT_}MODEL, not + WITH_PPC_{,DEFAULT_}_MODEL. + (options_ppc): Delete now unused function. + (cpu.h): Include cpu.h, not just basics.h. + + * std-config.h (WITH_{,DEFAULT_}MODEL): Define. + + * igen.c (model_macros, last_model_macro): New statics to keep + track of macros to go in model.h. + (insn_table_load_insns): Add model-macros to model_macros linked + list. + (model_table_fields): Add field for printable name. + (gen_model_h): If there are model macros defined, print them out. + Print out DEFAULT_MODEL as the first model if there any models + specified, otherwise MODEL_NONE. Print out external decl for + current_model. Print out decl for model_set. + (gen_model_c): Add function model_set. Switch to use printable + name for the model, not the internal identifier used. + + * psim.c (current_model): New global variable. + + * ppc-instructions: Add macros for flag defines. Switch first + model so 604 is first. + + * main.c (main): Call model_set, not function_unit_model. + * sim_calls.c (sim_open): Ditto. + * sim_calls.c, Makefile.in: sim_calls.c now includes cpu.h. + +Sat Nov 11 07:27:41 1995 Michael Meissner <meissner@tiktok.cygnus.com> + + * mon.h (mon_events): New enumeration for other events we want to + handle. + (mon_event): Add declaration for function. + + * mon.c (mon_event): New function. + (mon_print_info): Print icache misses. + + * psim.c (run_until_stop): Monitor icache misses. + + * configure.in (--enable-sim-inline): Fix typos in handling comma + separated inline options. + (--enable-sim-icache): Echo icache size. + * configure: Regenerate. + + * igen.c (semantics_h_print_function): Emit STATIC_SEMANTICS + instead of INLINE_SEMANTICS so that the compiler won't keep all of + the semantic functions as inline RTL, given that the address of + the function is taken which forces outline calls anyway. + (lf_print_c_semantic_function_header): Ditto. + (gen_semantics_h): Define STATIC_SEMANTICS as nothing if not + defined. + (lf_print_c_cracker_function): Emit STATIC_IDECODE instead of + STATIC_INLINE_IDECODE. + (gen_idecode_c): Define STATIC_IDECODE if not defined. + (gen_model_h): Use #ifdefs to define types to hold model units, + cycles, and flags. + (model_table_insert): Add a sentinel functional unit at the end to + simplify loop processing. + (model_c_insn): Use <function-unit>_SENTINAL instead of 0 for any + instruction not specifing a function unit for the current model. + (gen_model_{c,h}): Provide bounds for model_time_mapping. + + * inline.h (STATIC_SEMANTICS): Define to be static if + SEMANTICS_INLINE is defined. + (STATIC_IDECODE): Define to be static if IDECODE_INLINE is + defined. + + * options.c (print_options): Fix typo. + +Fri Nov 10 06:39:46 1995 Michael Meissner <meissner@tiktok.cygnus.com> + + * configure.in (--enable-sim-{opcode,config}): Use $srcdir when + check for the existence of files. + * configure: Regenerate. + + * table.c (table): New field nr_model_fields. + (table_open): New parameter nr_model_fields. + (table_entry_read): Parse model fields that begin with a '*' after + each instruction. + * igen.c, dgen.c: Change callers of table_open. + + * igen.c: Add support for dumping model specific information in + model.h and model.c. + (insn_field_name): Delete unused array. + (global variables): Make global variables static, so we can tell + when they are no longer used. + (cache_semantic_actual): Delete unused variable. + (insn_table_load_insns): If the insn is really a machine model, + call model_table_insert instead of other processing. + (model_table_insert): New function to handle defining the + functional units of a particular machine model. + (insn_table): Add last_function field so we can add functions at + the end. + (insn_table_insert_function): Use last_function field when + appending new function. + + * ppc-instructions: Add a few model specific information for 603, + 603e, and 604 for testing purposes. + + * table.h (table_model_entry): New linked list to hold model + specific information, one per line. + (table_entry): Add model_first, model_last fields. + + * configure.in (--enable-sim-inline): If gcc is found and + --enable-sim-inline is not specified, defaine DEFAULT_INLINE to 1, + not 2. + (--enable-sim-reserved-bits): New switch to check whether reserved + bits are set in the instruction. + (--enable-sim-opcode): Make complex the default. + (all switches): Add appropriate checks and error messages. + * configure: Regenerate. + + * Makefile.in (RESERVED_CFLAGS): New variable set by + --enable-sim-reserved-bits. + (CONFIG_CFLAGS): Include RESERVED_CFLAGS. + (BUILT_SRC): igen now generates model.c and model.h. + (LIB_OBJ): Include table.o. + (tmp-igen): Add -m/-M options to write model.c/model.h. + (model.o): New object. + (CPU_H): Include model.h. + + * cpu.h: Include model.h. + + * std-config.h (WITH_RESERVED_BITS): Define. + (MODEL_INLINE): Ditto. + + * options.c (print_options): Print out WITH_RESERVED_BITS. + +Thu Nov 9 12:22:15 1995 Michael Meissner <meissner@tiktok.cygnus.com> + + * configure.in: If --silent, don't output information messages. + * configure: Regenerate. + + * configure.in (--enable-sim-alignment): Fix typo in specifing non + strict alignment. + (--enable-sim-switch): Make default on. + (--enable-sim-duplicate): Make default on. + (--enable-sim-smp): Make default 0. + (--enable-sim-mon): Don't set sim_float if not set. + (--enable-sim-inline): If gcc is found and --enable-sim-inline is + not specified, define DEFAULT_INLINE to be 2. + (all --enable-sim-* rules): Echo rules set to non empty to file + descriptor 6. + * configure: Regenerate. + + * options.c (options_env): Fix typo if WITH_ENV is 0. + (print_options): Print GCC compiler version if available and + date/time options was compiled. If OPCODE_RULES, IGEN_FLAGS, + and/or DGEN_FLAGS are defined, print them. + + * Makefile.in (all link actions): Pass SIM_CFLAGS as well as + CFLAGS. + (options.o): Compile options.o with OPCODE_RULES, IGEN_FLAGS, and + DGEN_FLAGS defined, so they can be printed out. + + * igen.c (lf_print_c_validate): Check for WITH_ASSERT, so that + this test can be compiled away if the user really wants a fast + simulator by not doing assertion failures. + +Wed Nov 8 13:19:47 1995 Michael Meissner <meissner@tiktok.cygnus.com> + + * options.c: New file to print out all of the WITH_ options. + * options.h: New include file to declare print_options. + * debug.h (trace_options): Add trace_opts to call print_options. + * debug.c (trace_description): Add trace_opts support. + * main.c (main): If user requested options, print them. + * sim_calls.c (sim_open): Ditto. + + * igen.c (opcode_field_new): Add void to make it a proper prototype. + + * emul_generic.c (emul_enter_call): Make printf_filtered arguments + type correct. + * emul_netbsd.c (do_kill): Ditto. + * registers.c (registers_dump): Ditto. + * vm.c (om_translate_effective_to_real): Ditto. + * vm_n.h (vm_data_map_read_N): Ditto. + (vm_data_map_write_N): Ditto. + * devices.h (DTRACE_INIT): Ditto. + (DTRACE_{ATTACH,DETACH}_ADDRESS): Ditto. + (DTRACE_IO_{READ,WRITE}_BUFFER): Ditto. + (DTRACE_DMA_{READ,WRITE}_BUFFER): Ditto. + * devices.c (update_for_binary_section): Ditto. + (write_stack_arguments): Ditto. + (stack_ioctl_callback): Ditto. + * device_tree.c (device_tree_add_passthrough): Ditto. + (device_tree_{add,find}_device): Ditto. + (device_tree_{add,find}_integer): Ditto. + (device_tree_find_{string,boolean}): Ditto. + (device_tree_init{,_device}): Ditto. + (device_tree_dump): Ditto. + * sim_calls.c (sim_{read,write}): Ditto. + (sim_{fetch,store}_register): Ditto. + (sim_stop_reason): Ditto. + + * sim_callbacks.h (printf_filtered): Declare with attribute + printf, so we can enable format checks. + + * devices.c (console_io_{read,write}_buffer_callback): Cast swtich + argument to int, since ANSI doesn't allow long switch values. + * emul_netbsd.c (do___sysctl): Ditto. + + * emul_netbsd.c (do___sysctl): Fix up printf call. + + * corefile.c (core_translate): Don't do arithmetic with void * + pointers. Cast to char * first. + + * function_unit.c (FUNC_{LOAD,STORE}): Rename from {LOAD,STORE} + and change all uses. + + * Makefile.in ({FUNC,MODEL,WARNING}_CFLAGS): New flags set by + configure --enable switches. + (CONFIG_CFLAGS): Include FUNC_CFLAGS and MODE_CFLAGS. + (.c.o): Include WARNING_CFLAGS. + (CPU_H): Include function_unit.h. + (LIB_OBJ): Include function_unit.o. + (BUILT_SRC_WO_CONFIG): Split from BUILT_SRC and do not include + config.h or ppc-config.h. + (BUILT_SRC): Include BUILT_SRC_WO_CONFIG, config.h and + ppc-config.h. + (filter_filename.o): Include config.h/ppc-config.h dependencies. + (idecode.o, semantics.o, psim.o): Specify CC line without + WARNING_CFLAGS so that we don't get all of the unused variable + warnings that are generated. + (function_unit.o): Add rule to build. + (main.o, sim_calls.o): Add function_unit.h, itable.h dependencies. + (mon.o): Include mon.c dependency. + (TAGS): Depend on BUILT_SRC. + (clean): Don't delete config.h or ppc-config.h + + * basics.h (sim_callbacks.h): Move include after the include of + config.h and ppc-config.h. + + * bits.{h,c} (ROTL32,ROTL64): Move these functions to bits.c. Add + support for BITS_INLINE to inline these. Add declarations to + bits.h. + + * configure.in (--enable-sim-warnings): Add new option to specify + compiler warnings for all modules except idecode.o and semantics.o + which have lots of unused variables because they are machine + generated. + (--enable-sim-function-unit): New switch to configure whether + function unit support is compiled in or not. + (--enable-sim-{,default-}mode): New switches to control which cpu + model is used. + * configure: Regenerate. + + * corefile.c (core_attach_address_callback): Delete unused + variable device_address. + + * cpu.c (struct _cpu): Add function unit pointer field func_unit. + (cpu_create): If WITH_FUNCTION_UNIT, call function_unit_create. + (cpu_init): If WITH_FUNCTION_UNIT, call function_unit_init. + (cpu_halt): If WITH_FUNCTION_UNIT, call function_unit_halt. + (cpu_function_unit): New function to return func_unit field. + + * cpu.h (function_unit.h): Include new include file. + (cpu_function_unit): Declare. + + * debug.c (stdlib.h): Test HAVE_STDLIB_H, not HAVE_STDLIB. + (config.h): Include config.h. + + * devices.c (icu_io_write_buffer_callback): Delete unused variable + system. + + * emul_generic.c (emul_exit_call): Print out status value. + + * emul_netbsd.c (do_read): Delete unused variable nr_moved. + + * filter_filename.h (includes): Include config.h, ppc-config.h, + not basics.h. + + * inline.c: Include bits.c if BITS_INLINE. Include + function_unit.c if FUNCTION_UNIT_INLINE. + + * inline.h (INLINE_BITS): Define if BITS_INLINE. + (INLINE_FUNCTION_UNIT): Define if FUNCTION_UNIT_INLINE. + + * interrupts.c (instruction_storage_interrupt): Delete unused + variable nia. + + * lf.h (config.h): Include config.h. + + * main.c (includes): Include function_unit.c. If HAVE_UNISTD_H, + include unistd.h. + (usage): Update for -m model, -i, and -I options. + (main): Delete unused variables stack_pointer and i. Add support + for -i, -m model arguments. Call psim_print_info with verbose == + 1 if -i, and verbose == 2 if -I. + + * mon.c (stdio.h): Include stdio.h to pick up sprintf prototype. + (mon_issue): Call function_unit_issue if function units are + supported. + (mon_print_info): Take psim * argument. Print out information + from function_unit if available. Move read/write stats to always + print, instead of printing if verbose > 1. Fix up plural + vs. singular usage. + + * mon.h (mon_print_info): Update prototype. + + * psim.c (current_ppc_model): Add global variable. + (psim_print_info): Pass system argument to mon_print_info. + + * sim_calls.c (function_unit.h): Include. + (sim_open): Add support for -i and -m model options. If -i call + psim_print_info with verbose == 1, if -I, with verbose == 2. + (sim_resume): Delete unused variable program_counter. + + * std-config.h (WITH_FUNCTION_UNIT): Define. + (ppc_model): Add enumeration giving all PowerPC models currently + known about. + ({WITH,CURRENT}_PPC_MODEL): Define. + (FUNCTION_UNIT_INLINE): Define. + + * table.c (config.h): Include config.h. + + * vm.c (om_virtual_to_real): Print pte_word_{0,1} so the compiler + doesn't complain that they're unused. + + * vm_n.h (vm_data_map_read_N): Delete unused variable rval. + +Mon Nov 6 23:15:54 1995 Andrew Cagney <cagney@highland.com.au> + + * sim-endian.c (ppc-endian.c), sim-endian.h (ppc-endian.h): + renameed. These files are target independant. + * Makefile.in, basics.h: update for new name. + + * sim-endian.h (SWAP_N), sim-endian.c (_SWAP_1): Rename existing + SWAP_<N> to _SWAP_<N> so that sim-endian.h can contain SWAP_N + macro's as required. + + * sim-endian.c, sim-endian-n.h (new file): Move endian code into a + debugable header file. + + * ppc-instructions (Byte-Reverse): Enable byte reverse + instructions using SWAP_N macros. + +Mon Nov 6 10:39:28 1995 Michael Meissner <meissner@tiktok.cygnus.com> + + * Makefile.in (config.status): Remove references to config.make + and config.hdr. + + * config.{make,hdr}: Delete, no longer used. + * build-psim: Ditto. + +Mon Nov 6 20:49:56 1995 Andrew Cagney <cagney@highland.com.au> + + * sim_calls.c (sim_open): Fix parsing of `target sim' options. + + * device_tree.c (device_tree_add_string): Wasn't saving the value + of the string being entered into the tree. + + * psim.c (create_filed_device_tree): Not terminating string device + names with a null. + + * psim.c (psim_create): Use `env' instead of + `environment-architecture' to be consistent with configure. + Reconize user/uea, virtual/vea and operating/oea. + +Sat Nov 4 12:29:45 1995 Fred Fish <fnf@cygnus.com> + + * core.c: Rename to corefile.c + * core.h: Rename to corefile.h + * inline.c: Include corefile.h, renamed from core.h. + * cpu.h: Include corefile.h, renamed from core.h + * vm.c: Include corefile.h, renamed from core.h + * corefile.c: Include corefile.h rather than core.h + * README.psim (KNOWN PROBLEMS): Change core.* references to corefile.* + references. + * Makefile.in (CPU_H): Change core.h to corefile.h + (vm.o): Change dependency to corefile.h + (LIB_SRC): Change core.c to corefile.c. + (LIB_OBJ): Change core.o to corefile.o. + (corefile.o): Change dependencies to corefile.c, corefile.h. + +Fri Nov 3 11:37:24 1995 Michael Meissner <meissner@tiktok.cygnus.com> + + * ppc-instructions (data cache instructions): Make all data cache + instructions nops instead of invalid instructions. + + * Makefile.in (CONFIG_CFLAGS): Add ALIGNMENT_CFLAGS and + TIMEBASE_CFLAGS which weren't included. + +Thu Nov 2 08:54:04 1995 Michael Meissner <meissner@tiktok.cygnus.com> + + * Makefile.in: Uncomment built file dependencies. + + * configure.in: Rewrite --enable-sim switch handling to use the + autoconf builtins so it works correctly if the configure or + Makefile.in files are modified and make decides to rebuild + Makefile. Also document all of the --enable-sim switches + supported. Check whether getrusage and sys/resource.h are + supported. + * config.in: Regenerate. + * configure: Regenerate. + * Makefile.in: Add support for all of the variables set with + --enable-sim switches. + + * Makefile.in (clean): make clean now removes all built sources as + well. + + * cpu.c: Use HAVE_STRING_H, HAVE_STRINGS_H, HAVE_UNISTD_H, + HAVE_TIME_H, HAVE_SYS_TIMES_H, HAVE_SYS_RESOURCE_H defined in + the generated config.h. + * debug.c: Ditto. + * device_tree.c: Ditto. + * devices.c: Ditto. + * dgen.c: Ditto. + * emul_netbsd.c: Ditto. + * igen.c: Ditto. + * lf.c: Ditto. + * misc.c: Ditto. + * psim.c: Ditto. + * registers.c: Ditto. + * sim_calls.c: Ditt. + * table.c: Ditto. + + + * main.c (main): Call psim_print_info with verbose == 2. + + * mon.c (mon_print_info): Align the cpu number and number of + instructions fields. Do not print an instruction category if the + CPU did not execute any of those instructions. Print out number + of reads and writes. If getrusage is supported, print out number + of simulated instructins per second. + + * configure.in: Add support for --enable-sim-opcode=stupid. + * configure: Regenerate. + +Wed Nov 1 23:46:59 1995 Andrew Cagney <cagney@highland.com.au> + + * std-config (INLINE_DEVICE_TREE): Don't inline either of + device_tree.c or devices.c. There is no significant gain. + + * configure.in, Makefile.in: add --enable-sim-icache=[0-9]* and + IGEN_ICACHE macro. + +Wed Nov 1 23:46:59 1995 Andrew Cagney <cagney@highland.com.au> + + * igen.c (main), misc.h (target_a2i, i2target), misc.c: Add + functions to convert between target and igen internal bit numbers. + Make IO go through these functions. Add -b (bit size) and -h (high + bit nr) options to igen. Typical usage would be: ./igen -b 16 -h + 15 for a 16 bit instruction format with the msb given a number 15. + +Wed Nov 1 22:17:32 1995 Andrew Cagney <cagney@highland.com.au> + + * dgen.c (main): Was outputting optarg even when it was NULL. + +Tue Oct 31 23:48:33 1995 Andrew Cagney <cagney@highland.com.au> + + * vm_n.h (vm_data_map_load_N, vm_data_map_store_n), debug.h, + debug.c: Add tracing of load/store unit (virtual) with -t + load-store. + +Tue Oct 31 21:44:01 1995 Andrew Cagney <cagney@highland.com.au> + + * std-config.h (WITH_ENVIRONMENT): Add USER_ENVIRONMENT which does + not include things such as the time base and events. + + * interrupt.c, sim_calls.c, cpu.h, vm.c, configure.in: Add UEA to + all environment switches for above. + + * psim.c (psim_create): ditto - new device tree node name is + /options/environment-architecture with values user, virtual and + operating. + +Tue Oct 31 21:31:32 1995 Andrew Cagney <cagney@highland.com.au> + + * ppc-opcode-stupid: Third example of use of opcode table - this + one expands all mtspr/mfspr and branch instructions. Appears to + give about a 10% gain in performance if everything enabled. Also + takes about 150mb of swap to build. + +Wed Nov 1 10:49:48 1995 Michael Meissner <meissner@tiktok.cygnus.com> + + * emul_netbsd.c (do_exit): Print arguments and close parenthesis + if tracing, since exit doesn't go through emul_exit_call. + (do_read): Print arguments if tracing. + (do_write): Ditto. + (do_open): Ditto. + (do_break): Ditto. + (do_kill): Ditto. + (do_dup): Ditto. + (do_sigprocmask): Replace trace with printing arguments if + tracing. + (do_ioctl): Print arguments if tracing. + (do_umask): Ditto. + (do_dup2): Ditto. + (do_fcntl): Ditto. + (do_gettimeofday): Ditto. + (do_getrusage): Ditto. + (do_fstatfs): Ditto. + + * filter_filename.c: New file to provide filter_filename to strip + the directory prefix from a file. + * filter_filename.h: New include file to declare filter_filename. + + * debug.h: Include filter_filename.h. + (TRACE,DTRACE,ERROR): Use filter_filename on __FILE__. + + * misc.h: Include filter_filename.h. + (ASSERT): Use filter_filename on __FILE__. + + * igen.c (lf_print_my_prefix): Use filter_filename on the filename + argument. + + * Makefile.in: Add filter_filename support. + + * ppc-instructions (dcbi, icbi): Make these NOPs rather than + invalid instructions. + + * configure.in: Add support for more --enable-sim-* switches. + Use config.make and config.hdr to write to Makefile and config.h + respectively. Don't rewrite Makefile, just append to it. + * configure: Regenerate. + * config.{make,hdr}: New shell scripts. + + * Makefile.in: Remove all variables set by configure.in. + (psim.o): Depend on $(BUILT_SRC) also. + + * emul_netbsd.c (do_gettimeofday,do_getrusage): When comparing an + integer, use 0, not NULL. + +Tue Oct 31 15:20:04 1995 Michael Meissner <meissner@tiktok.cygnus.com> + + * configure.in: Add support for --enable-sim-inline, + --enable-sim-bswap, --enable-sim-cflags, --enable-sim-complex, + --enable-sim-switch, --enable-sim-duplicate, --enable-sim-filter, + and --enable-sim-endian switch to control various Makefile + variables. + * configure: Regenerate from configure.in. + * Makefile.in: Add various Make variables that the various + switches alter. + + * std-config.h (DEFAULT_INLINE): Don't set this to 2 if using GCC + and optimizing by default. + +Fri Oct 27 19:26:27 1995 Andrew Cagney <cagney@highland.com.au> + + * bits.h (ROTL32, ROTL64): Were functions, made them macros, now + make them functions again. Appears 2.6.3 is confused by just a + macro. + +Thu Oct 26 18:31:58 1995 Andrew Cagney <cagney@highland.com.au> + + * ppc-endian.c (SWAP_8): Fix 8 byte swap! + + * psim.c (psim_create): Not correctly checking that runtime + configuration of things like ENDIAN, ENVIRONMENT and ALIGNMENT + matched the compiled in ones. + + * debug.h (ITRACE), igen.c: Tidy up more tracing flags - + trace_semantics is now different to trace_idecode, the former + checks the cache. + +Tue Oct 24 21:54:13 1995 Andrew Cagney <cagney@highland.com.au> + + * ppc-instructions (mtsrin): Missing instruction + * ppc-instructions (mfsrin): Missing instruction + * ppc-instructions (eieio): Missing instruction + +Tue Oct 24 20:55:29 1995 Andrew Cagney <cagney@highland.com.au> + + * build-psim: New shell script - see internals for usage, + simplifies the process of building custom simulators. + +Mon Oct 23 23:48:59 1995 Andrew Cagney <cagney@highland.com.au> + + * std-config.h (SEMANTICS_INLINE): Tidy up notes on each of the + INLINE macros. Make SEMANTICS_INLINE == 1 if DEFAULT_INLINE == 2. + Don't use DEFAULT_INLINE to define REGISTERS_INLINE DEVICES_INLINE + DEVICE_TREE_INLINE or INTERRUPTS_INLINE as none of these are on + the instruction or data critical paths. + + * FIXME: devices.c/emul_netbsd.c would benefit (slightly) from + the inclusion of device_tree.c/emul_generic.c. + +Mon Oct 23 00:31:50 1995 Andrew Cagney <cagney@highland.com.au> + + * os_emul.[hc], emul_generic.[hc], emul_netbsd.[hc]: replace + system.[hc]. Start of suport for multiple emulations and + emulation state (os_emul object). + + * emul_generic.[hc]: Start of code to implement proper system call + tracing (from spy). + +Sun Oct 22 21:33:51 1995 Andrew Cagney <cagney@highland.com.au> + + * cpu.h, cpu.c (cpu_init): New function, zero the registers before + the processor is started. Fixes problem of registers being + undefined when restarting from within gdb. + + * cpu.h, cpu.c (cpu_flush_icache): New function, flushes the + instruction cache (if present). Fixes problem of cpu caching gdb + breakpoint instructions. + + FIXME: PSIM sometimes aborts calling error(), it should instead + call sim_error() say which takes care of housekeeping such as + saving the CIA before calling error. + + * NOTE: cpu_flush_cache() instead of cpu_synchronize_context() is + used when restarting a simulation because the latter has the + unwanted side effect (well I as a kernel hacker think it is) of + performing an isync when the instruction stream doesn't contain + one. + +Sun Oct 22 19:27:48 1995 Andrew Cagney <cagney@highland.com.au> + + * mon.h (new), mon.c (new), std-config.h (WITH_MON): Performance + monitoring module. Counts both instructions issued and + load/stores. + + * NOTE: mon does not contain to count instruction loads as this + information is already available from the mon_issue() hook. + + * igen.c (lf_print_c_semantic), vm_n.h: Add counting code. + + * psim.h, psim.c (psim_create), cpu.h, cpu.c (cpu_create): Attach + a common monitor to each of the cpus. Delete + cpu_increment_number_of_insns() and cpu_get_number_of_insns() + replaced by copied code in mon.[hc]. + +Sun Oct 22 18:42:45 1995 Andrew Cagney <cagney@highland.com.au> + + * sim_calls.c, main.c, psim.c (psim_create): always create + `WITH_SMP' cpus. The actual number of CPU's active in a + simulation run is taken from the device node: /init/smp (an + integer). WITH_SMP changed to 2 (remember to put it back to 0). + +Fri Oct 20 17:26:54 1995 Andrew Cagney <cagney@highland.com.au> + + * system.c: More system call emulation. If code appears NetBSD + specific, make conditional to being compiled on a NetBSD system + (sigh). + +Wed Oct 18 23:02:20 1995 Andrew Cagney <cagney@highland.com.au> + + * Makefile.in, gen.c(delete), igen.c(new), dgen.c(new), + lf.[ch](new), table.[ch](new): Split into two generators - igen + that outputs the instruction tables and dgen that outputs the spr + tables. Add -f (filter out) flag to igen to filter out certain + instructions (ex 64 bit ones) from the created tables. Include + $(LIBIBERTY_LIB) in link options in case host lacks some libc + functions. + + * NOTE: igen, since it was originally written for the + PowerPC/RS6000, things the MSB is 0 and the LSB is 63{31}. + + * Makefile.in, std-config.h, ppc-cache-rules(new), + ppc-opcode-complex(new), ppc-opcode-simple(new): (for igen) Create + cache-rule and opcode-rule tables from macros found std-config.h. + Delete corresponding macro's from std-config.h. + + * igen.c (gen_itable_c, gen_itable_h), Makefile.in: code to output + an table of all the instructions. Code to output a type + enumerating all the instructin names. + + * igen.c(lf_print_c_semantic): Move call to increment instruction + counter so that it occures _after_ the instruction has been fully + validated, was double counting illegal/invalid instructions. Add + conditional so only compiled in when WITH_PROFILE enabled (enabled + by default). + + * igen.c, cpu.h, cpu.c(cpu_increment_number_of_insns): Include + itable.h, count individual instruction types not just total, + adjust reporting functions to output this. + + * ppc-instructions (64 bit Load Doubleword with Update Indexed): + Had 32./ instead of 31./ + + * ppc-instructions (64 bit Store Double Word Conditional Indexed): + bitrot - updated to use newer CR register operators. + + * ppc-instructions (64bit Floating Convert from Integer + Doubleword): Correct call to Round_Float(). + +Mon Oct 16 00:31:20 1995 Andrew Cagney <cagney@highland.com.au> + + * basics.h: #include "sim_callbacks.h" earlier so that its + prototypes are declared in all other header files. + + * bits.h, bits.c, idecode_expression.h (ROTL32, ROTL64): Update + doc in bits.h, remove dead code in bits.c, move ROTL32/ROTL64 into + bits.h. + + * cpu.c(cpu_add_commas), device_tree.h, device_tree.c(scand_*): + Add size of buffer argument to functions writing a string into a + buffer. Check for buffer overflow. + +Sun Oct 15 22:16:11 1995 Andrew Cagney <cagney@highland.com.au> + + * devices.h, devices.c, debug.h, debug.c: add macro's for tracing + of each device. Make parameter names consistent so macros work. + Use macro's in device functions. + + * device_tree.c, devices.h, devices.c: include path to device in a + devices node when creating it. + + * device_tree.c, debug.h, debug.c: Add tracing of `device-tree'. + + * core.c: add tracing of core-device, adjust parameter names in + core functions to be consistent with those in devices*. + +Sun Oct 15 20:33:20 1995 Andrew Cagney <cagney@highland.com.au> + + * debug.h, debug.c (trace_option): New function. Parses the trace + option, updating the trace array. + + * debug.h, debug.c (trace_usage): New function. Outputs the list + of all possible trace options. + + * sim_calls.c (sim_open), main.c (main): Use new trace_option() to + parse trace options specified with the simpler -t flag. Adjust + usage. + + * FIXME: basic parsing of command line options is still duplicated + by main.c and sim_calls.c + +Thu Oct 26 10:42:28 1995 Michael Meissner <meissner@tiktok.cygnus.com> + + * Makefile.in (clean): Delete *.i and *.out files. + + * ppc-endian.c (SWAP_n): Add SET argument to allow use of SWAP + macros for either assignment or return. Fix SWAP_8 to use a + union, and two SWAP_4's. Delete SWAP_N, since nobody uses it now. + (ENDIAN_N): Add SET argument to SWAP_n calls. Delete macro defs + that hardwired swapping on/off, let optimizer delete dead code. + + * main.c (main): Add printf that we caught a signal and print out + the failing address. + +Thu Oct 19 21:43:39 1995 Fred Fish <fnf@fishfood.amigalib.com> + + * Makefile.in: Remove tabs from otherwise empty line. + Confuses many non-GNU versions of "make". + +Wed Oct 18 08:51:25 1995 Michael Meissner <meissner@tiktok.cygnus.com> + + * Makefile.in (clean): Delete files produced by gen. + +Mon Oct 16 17:34:24 1995 Michael Meissner <meissner@tiktok.cygnus.com> + + * gen.c (lf_print_c_semantic_function): Move counting # of + instructions here so it works with caching. + (gen_idecode_c): Move from here. + +Wed Oct 11 17:13:15 1995 Andrew Cagney <cagney@highland.com.au> + + * gen.c, ppc-instructions, psim.c: Fix code for generating + cracking instruction cache. Delete the code that cached just the + result from doing an instruction lookup - this ran slower than no + cache at all. + +Fri Oct 13 09:58:43 1995 Michael Meissner <meissner@tiktok.cygnus.com> + + * Makefile.in (gen.o): Include $(INLINE_CFLAGS). + + * debug.h (ppc_trace): Rename from trace, to avoid a conflict with + TCL when gdb is linked with the simulator. + * debug.c (ppc_trace): Ditto. + * sim_calls.c (sim_open): Change trace -> ppc_trace. + * main.c (main): Ditto. + + * cpu.c (cpu_add_commas): Remove extra static. + +Thu Oct 12 11:35:53 1995 Michael Meissner <meissner@tiktok.cygnus.com> + + * Makefile.in (psim.o): Now that inlines are turned on, make + psim.o depend on all sources. + + * cpu.c (cpu_add_commas): New function to format a long with + commas. + (cpu_print_info): Use it to print number_of_insns. + + * ppc-endian.c (SWAP_n): New macros to speed up byte swapping for + 2, 4, and 8 bytes. + (ENDIAN_N): If both target and host byte orders are known, don't + bother testing CURRENT_{TARGET,HOST}_BYTE_ORDER. + + * ppc-endian.h (target specific H2T_n/T2H_n macros): Remove #if 0 + to allow target specific H2T_n/T2H_n macros to be used. + (htonl, ntohl): If compiled on a 486 by GCC and WITH_BSWAP is + non-zero, redefine the htonl/ntohl macros to use the BSWAP instead + of the 3 instruction sequence that runs on 386s. + + * std-config.h (WITH_{HOST,TARGET}_BYTE_ORDER): Don't override if + specified on the compile line. + (WITH_BSWAP): If not defined, define as 0. + + * Makefile.in (INLINE_CFLAGS): Add -DDEFAULT_INLINE=2 to add + default inline support. Pass INLINE_CFLAGS when compiling. + + * devices.{h,c} (unimp_device_ioctl): Use STATIC_DEVICES, not + INLINE_DEVICES since GCC doesn't like inline functions that + accept variable arguments. + (stack_ioctl_callback): Make function just static because GCC + doesn't like inline functions that accept variable arguments. + + * devices.h (STATIC_DEVICES): Define as empty if not defined. + + * inline.c: Correct pathnames of included C files to match current + implementation. + + * inline.h (STATIC_DEVICES): If DEVICES_INLINE is defined to be + non-zero, define STATIC_DEVICES to be static. + + * std-config.h (INLINE): If GNU C and optimizing, define this as + __inline__. + (DEFAULT_INLINE): If not defined, define as 0. + (ENDIAN_INLINE): If not defined, define as DEFAULT_INLINE. + ({CORE,VM,CPU,EVENTS,REGISTERS,INTERRUPTS}_INLINE): Ditto. + ({SPREG,IDECODE}_INLINE): Ditto. + +Wed Oct 11 17:13:15 1995 Andrew Cagney <cagney@highland.com.au> + + * ppc-instructions: Initial cut of floating point suport added. + Of note include - use of host IEEE floating point instructions, + use of PowerPC manual pseudo code to handle the FPSCR. It is not + currently a pretty sight. + + * memory_map.h, memory_map.c, memory_map_n.h, core.h, core.c: + merge into core.h, core.c, core_n.h. The type memory_map replaced + with core_map. This removes a level of pointer indirection when + translating an address. + + * memory_map.h, memory_map.c, memory_map_n.h: delete. + + * Makefile.in et.al (sorry): tweek to use new core, core_map and + core.h. + +Wed Oct 11 12:10:26 1995 Andrew Cagney <cagney@highland.com.au> + + * sim_calls.c, main.c: Add -g (trace_gdb) option, add tracing to + most of the other functions in sim_calls.c. + + * basics.h (CONCAT3), memory_map.c, memory_map_n.h, Makefile.in: + Add macros to better cover up `generic' code. Makes it possible + to step through the generic code! + + * vm.c, vm_n.h, Makefile.in: ditto + +Tue Oct 10 15:42:59 1995 Andrew Cagney <cagney@highland.com.au> + + * devices.h, devices.c, memory_map.h, memory_map.c: Changed + callback interface so that there is a read/write buffer but no + read/write_word. VEA default memory read/write handler sometimes + couldn't resolve an access and of those some were for a memory + fault and some were because gdb was making a bogus request. + + * devices.h, devices.c, memory_map.h, memory_map.c, vm.h, vm.c: + eliminate transfer_mode (raw or cooked) parameter from read/write + buffer. + +Fri Oct 6 20:23:56 1995 Andrew Cagney <cagney@highland.com.au> + + * ppc-instructions (fmul, fmuls): correct instruction format - had + FRB instead of FRC. + +Wed Oct 4 17:31:12 1995 Andrew Cagney <cagney@highland.com.au> + + * psim.c, device_tree.h, device_tree.c, devices.c (printd_*, + scand_*): new functions to parse/print fields in device names + while hiding any machine dependency. + + * devices.c, psim.c: Change the stack init code so that it is + handled by a device. Arguments passed across using a device ioctl + (hack). + + * devices.h, devices.c: device ioctl callback changed to allow a + variable number of arguments. This gives greater flexability and + greater chance of bugs. + +Tue Oct 3 22:01:56 1995 Andrew Cagney <cagney@highland.com.au> + + * main.c (printf_filtered, error): Missing va_end() to close off + variable argument use. + + * Makefile.in (tmp-gencode): comment out hack to get around some + versions of make not handling files being created as side-effects. + + * gen.c (lf_open): Add -n (real_file_name) option. Specifies an + alternative file name to use in output files for things like #line + macros. + + Makefile.in (tmp-gencode): Use gen -n so that debug info is + correct. + + * Makefile.in (TARGETLIB): Use this instead of libsim.a in the + Makefile. + +Sat Oct 7 22:40:59 1995 Michael Meissner <meissner@tiktok.cygnus.com> + + * sim_calls.c (sim_set_callbacks): Define new function. + +Fri Oct 6 17:23:10 1995 Michael Meissner <meissner@tiktok.cygnus.com> + + * psim.c (psim_print_info): Print exit status or signal number. + +Mon Oct 2 11:46:37 1995 Michael Meissner <meissner@tiktok.cygnus.com> + + * cpu.c (struct _cpu): Add number_of_insns field to trace how many + instructions are executed. + (cpu_increment_number_of_insns): New function to increment the + number of instructions issued. + (cpu_get_number_of_insns): New function to return the number of + instructions issued. + (cpu_print_info): New function to print cpu related information. + At present, print the number of instructions executed. + + * gen_idecode_c: Emit call to cpu_increment_number_of_insns within + idecode_issue. + + * psim.c (psim_print_info): New function to iterate over each of + the CPU's calling cpu_print_info. + + * psim.h,cpu.h: Add new declarations. + + * sim_calls.c (sim_open): Add argument processing to add the same + switches main.c accepts for the standalone processor. + (sim_close): Call psim_print_info if -I. + + * main.c (main): Add comment saying to update sim_calls.c when + adding switches. Add -I to call psim_print_info when done. + (usage): Update usage message. + +Sun Oct 1 13:52:59 1995 Michael Meissner <meissner@tiktok.cygnus.com> + + * main.c (printf_filtered): Correct to match new prototype. + +Sat Sep 30 20:47:05 1995 Michael Meissner <meissner@tiktok.cygnus.com> + + * sim_callbacks.h (printf_filtered): Correct prototype. + +Thu Sep 21 16:26:49 1995 Michael Meissner <meissner@tiktok.cygnus.com> + + * device_tree.c (OEA_MEMORY_SIZE): Define if not defined to + 0x100000. + (clayton_memory_size): Define as OEA_MEMORY_SIZE. + + * std-config.h (WITH_TRACE): Default to 1 now. + + * psim.c (write_stack_arguments): Don't write any stack arguments + if OEA. + + * main.c (main): Switch to using getopt. Make -p also set + trace_semantics. Make -a turn on all trace flags. Make -C turn + on console tracing. + + * device_tree.c (create_option_device_node): Assume a program is + OEA if the start address is < 4096, not just == 0. + +Wed Sep 20 13:36:06 1995 Ian Lance Taylor <ian@cygnus.com> + + * Makefile.in (maintainer-clean): New synonym for realclean. + +Sun Sep 10 10:23:56 1995 Michael Tiemann <tiemann@axon.cygnus.com> + + * registers.c (register_description): Add gdb synonyms for cr + (cnd) and msr (ps). + +Fri Sep 8 13:16:10 1995 Ian Lance Taylor <ian@cygnus.com> + + * Makefile.in (install): Don't install in $(tooldir). + + * configure.in: Call AC_CONFIG_HEADER. Don't try to use + bfd/hosts/*.h file or bfd/config/*.mh file. Call AC_PROG_CC and + AC_PROG_RANLIB. Substitute in values for CFLAGS, HDEFINES, AR, + and CC_FOR_BUILD. Call AC_CHECK_HEADERS for various header files. + Touch stamp.h if creating config.h. + * configure: Rebuild. + * config.in: New file, created by autoheader. + * Makefile.in (AR): Define as @AR@. + (CC): New variable, defined as @CC@. + (CFLAGS): Define as @CFLAGS@. + (CC_FOR_BUILD): New variable, defined as @CC_FOR_BUILD@. + (RANLIB): Define as @RANLIB@. + (HDEFINES, TDEFINES): New variables. + (@host_makefile_frag@): Remove. + (mostlyclean): Make the same as clean, not distclean. + (clean): Remove config.log. + (distclean): Remove config.h and stamp-h. + (Makefile): Don't depend upon @frags@. Just rebuild Makefile when + invoking config.status. + (config.h, stamp-h): New targets. + (gen, gen.o): Build with CC_FOR_BUILD, not CC. + (ppc-config.h): Rename from old config.h build. + * (basics.h,gen.c,ppc-endian.c,psim.c): Include ppc-config.h. + +Fri Sep 8 09:51:03 1995 Michael Meissner <meissner@tiktok.cygnus.com> + + * configure{,.in}: Don't include sysdep.h from bfd, since bfd no + longer provides it. + * basics.h (sysdep.h): Don't include it. + * Makefile.in (BASICS_H): Remove sysdep.h. + +Wed Sep 6 13:25:42 1995 Andrew Cagney <cagney@highland.com.au> + + * core.c (core_add_data): First growth of bss was being put at + wrong address (0) instead of &end. + + * core.c (core_add_stack, core_add_data): Was not handling case + where bss/stack is grown across the current end-of-{bss,stack}. + +Wed Sep 6 00:46:10 1995 Andrew Cagney <cagney@highland.com.au> + + * system.c (system_call): Fix SYS_break - was aligning bss to a + page boundary instead of just an 8 byte one; On first call sbrk(0) + != sbrk(0). + +Thu Aug 24 14:48:54 1995 Michael Meissner <meissner@tiktok.cygnus.com> + + * Makefile.in (install): Fix install rule. + +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 <cagney@highland.com.au> + + * 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 <cagney@highland.com.au> + + * 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@highland.com.au> + + * 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 <cagney@highland.com.au> + + * 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/INSTALL b/sim/ppc/INSTALL new file mode 100644 index 00000000000..da288f1629d --- /dev/null +++ b/sim/ppc/INSTALL @@ -0,0 +1,812 @@ + + PSIM - model the PowerPC environment + + Copyright (C) 1994-1996, Andrew Cagney <cagney@highland.com.au>. + + ---------------------------------------------------------------------- + + + Building PSIM + + This file describes how to build the program PSIM + + o Walk through a basic build + + o Discussion of PSIM's components and + how they relate to the build process + + o Detailed description of each of PSIM's + compile time configuration options + + + ---------------------------------------------------------------------- + + +BUILDING PSIM: + +PSIM 1.0.2 is included in GDB-4.16. To build PSIM you will need the +following: + + gdb-4.16.tar.gz Available from your favorite GNU + ftp site + + gcc GCC version two includes suport + for long long (64bit integer) + arrithemetic which PSIM uses. Hence + it is recommended that you build PSIM + using GCC. + +Method: + + 1. Unpack gdb + + $ cd .../scratch + $ gunzip < gdb-4.16.tar.gz | tar xf - + + + 2. Configure gdb + + First consult the gdb documentation + + $ cd .../scratch + $ cd gdb-4.16 + $ more README + $ more gdb/README + + then something like (I assume SH): + + $ CC=gcc ./configure \ + --enable-sim-powerpc \ + --target=powerpc-unknown-eabi \ + --prefix=/applications/psim + + + 4. Build (again specifying GCC) + + $ make CC=gcc + + alternatively, if you are short on disk space or only + want to build the simulator: + + $ ( cd libiberty && make CC=gcc ) + $ ( cd bfd && make CC=gcc ) + $ ( cd sim/ppc && make CC=gcc ) + + + 5. Install + + $ make CC=gcc install + + or just + + $ cp gdb/gdb ~/bin/powerpc-unknown-eabisim-gdb + $ cp sim/ppc/run ~/bin/powerpc-unknown-eabisim-run + + + ---------------------------------------------------------------------- + + +UPDATING PSIM: + + +A PSIM is an ongoing development. Occasional snapshots which both contain new +features and fix old bugs are made available. See the ftp directory: + + ftp://ftp.ci.com.au/pub/psim/beta +or ftp://cambridge.cygnus.com/pub/psim/beta + +for the latest version. To build/install one of these snapshots, you +replace the sim/ppc found in the gdb archive with with one from the +snapshot. Then just re-configure and rebuild/install. + + Procedure: + + 0. A starting point + + $ cd gdb-4.16 + + + 1. Remove the old psim directory + + $ mv sim/ppc sim/old.ppc + + + 2. Unpack the new one + + $ gunzip < ../psim-NNNNNN.tar.gz | tar tf - + $ gunzip < ../psim-NNNNNN.tar.gz | tar tf - + + + 3. Reconfigure/rebuild (as seen above): + + $ CC=gcc ./configure \ + --enable-sim-powerpc \ + --target=powerpc-unknown-eabi \ + --prefix=/applications/psim + $ make CC=gcc + + + ---------------------------------------------------------------------- + + +UPDATES TO GDB: + +From time to time, problems involving the integration of PSIM into gdb +are found. While eventually each of these problems is resolved there +can be periouds during which a local hack may be needed. + +At the time of writing the following were outstanding: + + ATTACH command: + + ftp://ftp.ci.com.au/pub/psim/gdb-4.15+attach.diff.gz + or ftp://cambridge.cygnus.com/pub/psim/gdb-4.15+attach.diff.gz + + PSIM, unlike the other simulators found in GDB, is able to load + the description of a target machine (including the initial + state of all processor registers) from a file. + + Unfortunatly GDB does not yet have a standard command that + facilitates the use of this feature. Until such a command is + added, the patch (hack?) gdb-4.15+attach.diff.gz can be used to + extend GDB's attach command so that it can be used to initialize + the simulators configuration from a file. + + + + ---------------------------------------------------------------------- + + +RUNNING PROGRAMS: + + +See the file: + + ftp://ftp.ci.com.au/pub/psim/RUN +or ftp://cambridge.cygnus.com/pub/psim/RUN + + + ---------------------------------------------------------------------- + + +COMPILE TIME CONFIGURATION OPTIONS: + + +PSIM's compile time configuration is controlled by autoconf. PSIM's +configure script recognises options of the form: + + --enable-sim-<option>[=<val>] + +And can be specified on the configure command line (at the top level +of the gdb directory tree) vis: + + $ cd gdb-4.15 + $ CC=gcc ./configure \ + --target=powerpc-unknown-eabisim \ + --prefix=/applications/psim \ + --enable-sim-inline + $ make CC=gcc + +For a brief list of PSIM's configuration options, configure --help +will list them vis: + + $ cd sim/ppc + $ ./configure --help + +Each PSIM specific option is discussed in detail below. + + + +--enable-sim-cflags=<opts> + + +Specify additional C compiler flags that are to be used when compiling +just PSIM. + +PSIM places heavy demands on both the host machine and its C compiler. So that +the builder has better control over the compiler the above option can be used +to pass additional options to the compiler while PSIM is being built. + +Ex: No debug information + +PSIM can be built with everything inline. Unfortunately, because of +all the debugging information generated the C compiler can grow very +very large as a result. For GCC, the debug information can be +restricted with the `-g0' option. To specify that this option should +be include in the CFLAGS when compiling the psim source code use: + + --enable-sim-cflags=-g0 + +Ex: Additional optimization flags + +A significant gain in performance can be achieved by tuning the +optimization flags passed to the C compiler. For instance on an x86 +you may consider: + + --enable-sim-cflags='-g0 -O2 -fno-strength-reduce -f...' + + + +--enable-sim-warnings=<flags> + + +Turn on additional GCC specific checks. + +Some hosts (NetBSD, Linux, Solaris-2.5) have complete header files +that include correct prototypes for all library functions. On such +hosts, PSIM can be built with many more than the standard C checks +enabled. The option --enable-sim-warnings controls this. + +Ex: Default warnings + +With just --enable-sim-warnings, the following -W options are enabled: +-Werror -Wall -Wpointer-arith -Wmissing-prototypes. + + + +--enable-sim-opcode=which + + +Specify the file containing the rules for generating the instruction +decode and execute functions from the file ppc-instructions. + +The form of the instruction decode and execute functions is controlled +by an opcode table. It specifies: the combination of switch +statements and jump tables to use when decoding an instruction and how +much of each instruction should be decoded before calling the +instruction execute function. + +PSIM includes a number of opcode tables: + + psim-opcode-simple + Generates a small compact two level switch statement + that will compile quickly and run reasonably fast. + + This may be useful on a small machine. + + psim-opcode-complex + (the default) A fairly aggressive instruction decode + table that includes the breaking out of a number + of special instruction cases (eg RA==0 vs RA!=0). + + psim-opcode-flat + Identical to complex except a switch statement + is used. Ideal for when the icache is being + disabled. + + psim-opcode-stupid + In addition to the instruction decodes performed + by psim-opcode-complex, this also full decodes mtspr, + mfspr, and branch instructions. The table generated + is very large and, as a consequence, only performs + well on machines with large caches. + + ppc-opcode-test-1 + ppc-opcode-test-2 + Generate test (but workable) tables. These exercise + PSIM's ability to generate instruction decode functions + that are a combination of jump-tables and switch statements. + +The program igen generates the instruction tables from the opcode +table and the ppc-instruction table. + + + +--enable-sim-switch + + +Enable/disable the use of a switch statement when looking up the +attributes of a SPR register. + +The PowerPC architecture defines a number of Special Purpose Registers +(SPR's). Associated with each of these registers are a number of +attributes (such as validity or size) which the instructions +mtspr/mfspr query as part of their execution. + +For PSIM, this information is kept in a table (ppc-spr-table). The +program dgen converts this table into lookup routines (contained in +the generated files spreg.h spreg.c) that can be used to query an +SPR's attributes. Those lookup routines are either implemented as +a table or alternatively as a number of switch statements: + + spr_table spr_info[] = { .... }; + int spr_length(sprs spr) { return spr_info[spr].length; } + +vs + + int spr_length(sprs spr) { switch (spr) { case ..: return ..; } } + +In general the first implementation (a table) is the most efficient. +It may, however, prove that when performing an aggressive optimization +where both the SPR is known and the above function is being inlined +(with the consequence that GCC can eliminate the switch statement) +that the second choice is improves performance. + +In practice, only a marginal (if any benefit) has ever been seen. + + + +--enable-sim-duplicate + + +Create a duplicate copy of each instruction function hardwiring +instruction fields that would have otherwise have been variable. + +As discussed above, igen outputs a C function generated from the file +ppc-instructions (using the opcode rules) for each of the +instructions. Thus multiple entries in the instruction decode tables +may be pointing back at the same function. Enabling duplicate, will +result in psim creating a duplicate of the instruction's function for +each different entry in the instruction decode tables. + +For instance, given the branch instruction: + + 0.19,6.BO,11.BI,16./,21.528,31.LK + ... + if (LK) LR = (spreg)IEA(CIA + 4); + ... + +igen as part of its instruction lookup table may have generated two +different entries - one for LK=0 and one for LK=1. With duplicate +enabled, igen outputs (almost) duplicate copies of branch function, +one with LK hardwired to 0 and one with LK hardwired to 1. + +By doing this the compiler is provided with additional information that +will allow it possibly eliminate dead code. (such as the assignment +to LK if LR==0). + +Ex: default + +Because this feature is such a big win, --enable-sim-duplicate is +turned on by default. + +Ex: A small machine + +Only rarely (eg on a very small host) would this feature need to be +disabled (using: --disable-sim-duplicate). + + + +--enable-sim-filter=rule + + +Include/exclude PowerPC instructions that are specific to a particular +implementation. + +Some of the PowerPC instructions included in the file ppc-instructions +are limited to certain specific PPC implementations. For instance, +the instruction: + + 0.58,6.RT,11.RA,16.DS,30.2:DS:64::Load Word Algebraic + +Is only valid for the 64bit architecture. The enable-sim-filter flag +is passed to igen so that it can `filter out' any invalid +instructions. The filter rule has the form: + + -f <name> + +thus: + + --enable-sim-filter='-f 64' + +(the default) would filter out all 64bit instructions. + +Ex: Remove floating point instructions + +A given 32bit PowerPC implementation may not include floating point +hardware. Consequently there is little point in including floating +point instructions in the instruction table. The option: + + --enable-sim-filter='-f 64 -f f' + +will eliminate all floating point instructions from the instruction +table. + + + +--enable-sim-icache=size + + +Set the size of the cache used to hold decoded instructions. + +Psim executes instructions in two separate steps: + + o instruction fetch/decode + + o instruction execution + +For a given instruction, the first stage need only be executed once +(the first time the instruction is encountered) while the second stage +must be executed every time the program `executes' that instruction. + +Exploiting this, PSIM can maintain a cache of decoded instructions. +It will then use the decoded instruction from the cache in preference +to fetching/decoding the real instruction from memory. + +Ex: default + +Because this feature is normally such a big win, it is enabled by +default (with the cache size set to 1024 entries). + +The 1024 entries equals 4096 bytes (or one page) of instructions. +Larger caches can be used but with caution - PSIM does not check for +address aliasing within its instruction cache. + +Ex: disable the cache + +There may be cases (for instance where the cache has a low hit rate) +where the psim performs better with no instruction cache. For such +situations, the cache can be disabled vis: --disable-sim-icache. + + + +--enable-sim-inline[=module] + + +Specify the inlining of one or more modules. + +Many architectures (in particular the x86) suffer from a large +function call overhead. By eliminating function calls (through +inlining of functions) a large performance gain can be achieved. + +In PSIM, modules are inlined in one of two possible ways. Some +modules (such as the byte swapping code) can be inlined into any +module that calls them. Other modules, due to complex +interdependencies, are only inlined as a group when compiling the +external interface module psim.c. + +Ex: default + +By default the modules endian (handle be/le), bits (manipulate +bit-fields within words), cpu (the processor object) and events +(timers) are inlined in any module that calls them. This gives a +reasonable performance gain with little additional compilation +overhead. + +Ex: recommended --enable-sim-inline + +Assuming you machine is reasonably well configured, this option is +highly recommended. On the x86 several orders of magnitude +improvement in performance is possible. + +Ex: fine tuning + +The file std-config.h contains a detailed description of how the +inlining works. Individual modules can be inlined by specifying them. +For if you have a very large cache the model module could be inlined +with: + + --enable-sim-inline=MODEL + + + +--enable-sim-bswap + + +(x86 specific) Use the i486/P5/P6 byte swap instruction. + +PSIM contains generic byte swapping code. For the x86 (P[4-6]) PSIM +can be built so that it uses the bswap instruction instead of relying +on the compiler to generate byte swap code. + +Ex: default + +By default, when compiling with GCC-2 on an i486/P5/P6 the bswap +instruction is used. + + + +--enable-sim-endian=endian + + +Specify the byte order of the target. + +By default, PSIM is able to execute both big and little endian +executables. As a consequence, every byte swap routine includes a +test to see if the byte swap is really needed. By specifying the byte +order of the target (and the host below) the need for this test can be +eliminated. + +Clearly setting the byte order of the target is only useful when known +before hand. + + + +--enable-sim-hostendain=end + + +As above but for the host. + +Normally this option should not be needed. configure (autoconf) should +determine the byte order of the host automatically. However if for +some reason there is a problem, this option can be used to override +autoconf. + + + +--enable-sim-smp=n + + +Set the maximum number of processors that PSIM can model. + +Psim can model (with small limitation discussed else where) a +multi-processor PowerPC environment. While the overhead of +co-ordinating the execution of a number of processors is relatively +small it is still significant when compared to handling only one +processor. + +This option only sets the maximum number of processors that can be +simulated. The number active during a given simulation run us +determined at run time. + +Ex: default + +By default 5 processors are configured but only one is enabled. +Additional processors can be enabled with the runtime option: + + -o '/openprom/options/smp 5' + +Ex: recommended + +Unless you intend studying multi-processor systems there is little reason for +having PSIM configured with SMP support. Specifying: + + --disable-sim-smp +or --enable-sim-smp=0 + +will eliminate any SMP such as: + + for (cpu = 0; cpu < nr_cpus; cpu++) + ... + + + +--enable-sim-xor-endian=n + + +Set the byte-size of the bus involved in the PowerPC's xor endian byte +swapping. + +The PowerPC's implementation of BE/LE mode is different to what a +programmer may first expect. The details of this implementation are +discussed at length in PowerPC documentation. + +Ex: default + +By default this is configured with a value of 8 (the bus size of most +60x processors). + +Ex: recommended + +Unless you are expecting to test/debug PowerPC be/le switching code +this option is of little use and should be disabled: + + --disable-sim-xor-endian + + + +--enable-sim-bitsize=n + + +Specify the bit size (32/64) of the PowerPC to be modelled. + +Note: By default 32 is specified. The implementation of the 64bit +architecture is still under development. + + +--enable-sim-hostbitsize=32|64 + +As above but for the host. + +NOTE: Psim has yet to be built on a 64bit host. + + + +--enable-sim-env=env + + +Hardwire the PowerPC environment being modelled (user, virtual or +operating). + +The PowerPC architecture defines three different levels of compliance to its +architectural specification. These environments are discussed in detail in +PowerPC publications. + + user - normal user programs + virtual - an extension of the user environment (includes timers) + operating - kernel code + +Ex: default + +By default all three environments are supported. + +Ex: recommended + +If you only intend running psim with user (or operating) code then +PSIM should be configured accordingly. For user code, it eliminates: +support for timers and events and redundant VM calls. + + + +--enable-sim-timebase + + +Enable/disable the time base register. + +The PowerPC architecture (virtual environment) includes a time base +register. Maintaining that register incurs an overhead in +performance that can be eliminated by eliminating time-base register +support. + +Ex: default + +Normally this option is not used. Instead --enable-sim-env (above) us +used to disable/enable features such as the timebase register. + + + +--enable-sim-alignment=align + + +Control the PowerPC's memory access alignment restrictions. + +The PowerPC in LE mode only allows memory transfers of a correctly +aligned size/address. The above option controls how misaligned +accesses are handled. + + strict All accesses must be correctly aligned + + nonstrict Unaligned access allowed (the are split + into a number of aligned accesses). + +Ex: default + +Unless otherwise specified PSIM will auto configure a BE program to +allow miss-aligned accesses while a LE program will not. + +Ex: 604e + +The recently announced 604e processor allows miss-aligned accesses in both +BE and LE modes. If modeling the 604e then you should specify: + + --enable-sim-alignment=nonstrict + + + +--enable-sim-trace + + +Include code to trace PSIM's internal progress (also controlled by the +-t option). + +Checking to see if a trace message should be output slows down a +simulation. Disabling this option (--disable-sim-trace) eliminates +completely that code. + + + +--enable-sim-assert + + +Include the code that checks the correctness of parts of PSIM. + +Eliminating such code (--disable-sim-assert) eliminates internal +consistency tests and their overhead. + + + +--enable-sim-reserved-bits + + +Include code to check that the reserved fields of the instruction are +zero. + +The PowerPC architecture defines certain fields of some instructions +as reserved (`/'). By default, for each instruction, PSIM will check +the reserved fields causing an invalid instruction exception if a +field is invalid. Disabling this option eliminates this test. This +is at the slight risk of PSIM treating an invalid instruction as +valid. + + + +--enable-sim-float + + +Include support for hardware floating point. + + + +--enable-sim-monitor=mon + + +Include support for basic instruction counting. + +If you are not interested in the performance of either you program or +the simulator then you can disable this option. + + + +--enable-sim-model=which + +Hardwire the processor that will be used as a reference when modeling +execution units. + + + +--enable-sim-default-model=which + + +Specify the processor of choice for the execution unit model. + + + +--enable-sim-model-issue + + +Include support for the modeling of processor execution units. + + ---------------------------------------------------------------------- + +TYPICAL CONFIGURATION OPTIONS: + + + VEA CODE ONLY: + + Here of note are: + + o ramp up the compiler options (some + of the below are P5 specific). + + o disable anything not used + + CC=gcc ./configure \ + --prefix=/applications/psim \ + --target=powerpc-unknown-eabi \ + --enable-sim-powerpc \ + --enable-sim-warnings \ + --enable-sim-inline \ + --disable-sim-smp \ + --enable-sim-duplicate \ + --enable-sim-endian=big \ + --disable-sim-xor-endian \ + --enable-sim-env=user \ + --disable-sim-reserved-bits \ + --disable-sim-assert \ + --disable-sim-trace \ + --enable-sim-cflags='-g0,-O2,-fno-strength-reduce,-fomit-frame-pointer' + + + OEA CODE ONLY: + + The key configuration changes are: + + o turn off the instruction cache. The overhead + of flushing and reloading it is greater than + not having a cache. + + o use a switch statement (ppc-opcode-flat) for + the instruction decode and then (-O3) fully + inline all functions. + + o --enable-sim-warnings is not present. GCC (2.7.2) + gets confused by the instruction decode table + generated by igen (contains a perfect switch) + and, as a consequence, generates a bogus warning. + + CC=gcc ./configure \ + --prefix=/applications/psim \ + --target=powerpc-unknown-eabi \ + --enable-sim-powerpc \ + --enable-sim-inline \ + --disable-sim-smp \ + --enable-sim-duplicate \ + --enable-sim-endian=big \ + --disable-sim-xor-endian \ + --enable-sim-env=operating \ + --disable-sim-reserved-bits \ + --disable-sim-assert \ + --disable-sim-trace \ + --enable-sim-opcode=ppc-opcode-flat \ + --disable-sim-icache \ + --enable-sim-cflags='-g0,-O3,-fno-strength-reduce,-fomit-frame-pointer' diff --git a/sim/ppc/Makefile.in b/sim/ppc/Makefile.in new file mode 100644 index 00000000000..6f2b3102f66 --- /dev/null +++ b/sim/ppc/Makefile.in @@ -0,0 +1,660 @@ +# +# This file is part of the program psim. +# +# Copyright (C) 1994-1997, 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. +# + +default: all + +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 = @bindir@ +libdir = @libdir@ +tooldir = $(libdir)/$(target_alias) + +datadir = @datadir@ +mandir = @mandir@ +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 = @infodir@ +includedir = @includedir@ + +# This can be referenced by the gettext configuration code. +top_builddir = .. + +EXEEXT = @EXEEXT@ +SHELL = /bin/sh + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ + +AR = @AR@ +AR_FLAGS = rc +CC = @CC@ +CFLAGS = @CFLAGS@ +CC_FOR_BUILD = @CC_FOR_BUILD@ +BISON = bison +MAKEINFO = makeinfo +RANLIB = @RANLIB@ + +SIM_CFLAGS = @sim_cflags@ +INLINE_CFLAGS = @sim_inline@ +BSWAP_CFLAGS = @sim_bswap@ +ENDIAN_CFLAGS = @sim_endian@ +REGPARM_CFLAGS = @sim_regparm@ +STDCALL_CFLAGS = @sim_stdcall@ +HOSTENDIAN_CFLAGS = @sim_hostendian@ +SMP_CFLAGS = @sim_smp@ +XOR_ENDIAN_CFLAGS = @sim_xor_endian@ +BITSIZE_CFLAGS = @sim_bitsize@ +HOSTBITSIZE_CFLAGS = @sim_hostbitsize@ +ENV_CFLAGS = @sim_env@ +TIMEBASE_CFLAGS = @sim_timebase@ +ALIGNMENT_CFLAGS = @sim_alignment@ +FLOAT_CFLAGS = @sim_float@ +TRACE_CFLAGS = @sim_trace@ +ASSERT_CFLAGS = @sim_assert@ +RESERVED_CFLAGS = @sim_reserved@ +MONITOR_CFLAGS = @sim_monitor@ +MODEL_CFLAGS = @sim_model@ @sim_default_model@ @sim_model_issue@ +STDIO_CFLAGS = @sim_stdio@ +TERMIO_CFLAGS = @sim_termio@ +WARNING_CFLAGS = @sim_warnings@ +DEVZERO_CFLAGS = @sim_devzero@ +CONFIG_CFLAGS = $(BSWAP_CFLAGS) \ + $(ENDIAN_CFLAGS) \ + $(REGPARM_CFLAGS) \ + $(STDCALL_CFLAGS) \ + $(HOSTENDIAN_CFLAGS) \ + $(SMP_CFLAGS) \ + $(XOR_ENDIAN_CFLAGS) \ + $(BITSIZE_CFLAGS) \ + $(HOSTBITSIZE_CFLAGS) \ + $(ENV_CFLAGS) \ + $(TIMEBASE_CFLAGS) \ + $(ALIGNMENT_CFLAGS) \ + $(FLOAT_CFLAGS) \ + $(TRACE_CFLAGS) \ + $(ASSERT_CFLAGS) \ + $(RESERVED_CFLAGS) \ + $(MONITOR_CFLAGS) \ + $(MODEL_CFLAGS) \ + $(STDIO_CFLAGS) \ + $(TERMIO_CFLAGS) \ + $(DEVZERO_CFLAGS) + +STD_CFLAGS = $(CFLAGS) $(INLINE_CFLAGS) $(CONFIG_CFLAGS) $(WARNING_CFLAGS) $(SIM_CFLAGS) $(HDEFINES) $(TDEFINES) $(INCLUDES) +NOWARN_CFLAGS = $(CFLAGS) $(INLINE_CFLAGS) $(CONFIG_CFLAGS) $(SIM_CFLAGS) $(HDEFINES) $(TDEFINES) $(INCLUDES) +BUILD_CFLAGS = -g -O $(INCLUDES) $(WARNING_CFLAGS) + +BUILD_LDFLAGS = + +CONFIG_FILE = @sim_config@ +IGEN_OPCODE_RULES = @sim_opcode@ +IGEN_DECODE_MECHANISM = @sim_decode_mechanism@ +IGEN_DUPLICATE = @sim_dup@ +IGEN_JUMP = @sim_jump@ +IGEN_FILTER = @sim_filter@ +IGEN_ICACHE = @sim_icache@ +IGEN_SMP = @sim_igen_smp@ +IGEN_LINE_NR = @sim_line_nr@ +DGEN_FLAGS = @sim_switch@ + +HDEFINES = @HDEFINES@ +TDEFINES = +IGEN_FLAGS = \ + $(IGEN_DECODE_MECHANISM) \ + $(IGEN_DUPLICATE) \ + $(IGEN_JUMP) \ + $(IGEN_FILTER) \ + $(IGEN_ICACHE) \ + $(IGEN_SMP) \ + $(IGEN_LINE_NR) + +.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) + +LIBIBERTY_LIB = ../../libiberty/libiberty.a +BFD_LIB = ../../bfd/libbfd.a + +INTLLIBS = @INTLLIBS@ +INTLDEPS = @INTLDEPS@ + +TARGETLIB = libsim.a + +all: run $(TARGETLIB) $(GDB_OBJ) + +.c.o: + $(CC) -c $(STD_CFLAGS) $< + + + +BASICS_H = \ + basics.h \ + config.h \ + ppc-config.h \ + inline.h \ + sim_callbacks.h \ + debug.h filter_filename.h \ + words.h \ + bits.h \ + sim-endian.h + +PSIM_H = \ + psim.h \ + $(BASICS_H) + +IDECODE_H = \ + idecode.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) \ + $(IDECODE_H) \ + device.h \ + corefile.h \ + vm.h \ + events.h \ + interrupts.h \ + psim.h \ + itable.h \ + mon.h \ + model.h + +DEVICE_TABLE_H = \ + $(BASICS_H) \ + device_table.h \ + device.h \ + tree.h \ + hw.h + +EMUL_GENERIC_H = \ + $(CPU_H) \ + $(IDECODE_H) \ + emul_generic.h \ + tree.h \ + os_emul.h + + +INLINE = \ + inline.h \ + inline.c + +BUILT_SRC_WO_CONFIG = \ + icache.h icache.c \ + support.h support.c \ + idecode.h idecode.c \ + semantics.h semantics.c \ + itable.h itable.c \ + spreg.h spreg.c \ + model.h model.c \ + support.h support.c \ + pk.h \ + hw.h hw.c \ + filter_host.c \ + @sim_targ_vals@ + +BUILT_SRC = \ + $(BUILT_SRC_WO_CONFIG) \ + config.h \ + ppc-config.h + +LIB_INLINE_SRC = \ + psim.c \ + bits.c \ + debug.c \ + sim-endian.c \ + sim-endian.h \ + sim-endian-n.h \ + vm.c \ + vm_n.h \ + corefile.c \ + events.c \ + os_emul.c \ + registers.c \ + cpu.c \ + interrupts.c \ + device.c \ + tree.c \ + device_table.c \ + cap.c \ + mon.c \ + options.c + +LIB_SRC = \ + $(PACKAGE_SRC) \ + $(HW_SRC) \ + $(LIB_INLINE_SRC) + +MAIN_SRC = \ + main.c \ + sim_calls.c + + +# NOTE: semantics, idecode and psim put last so smaller files are compiled +# first +LIB_OBJ = \ + debug.o \ + filter_filename.o \ + bits.o \ + sim-endian.o \ + os_emul.o \ + emul_generic.o \ + emul_bugapi.o \ + emul_chirp.o \ + emul_netbsd.o \ + emul_unix.o \ + registers.o \ + vm.o \ + corefile.o \ + model.o \ + spreg.o \ + cpu.o \ + interrupts.o \ + events.o \ + cap.o \ + device.o \ + tree.o \ + device_table.o \ + itable.o \ + mon.o \ + icache.o \ + semantics.o \ + idecode.o \ + support.o \ + psim.o \ + $(PACKAGE_OBJ) \ + $(HW_OBJ) \ + options.o + + +GDB_OBJ = sim_calls.o @sim_callback@ + +HW_SRC = @sim_hw_src@ +HW_OBJ = @sim_hw_obj@ + +PACKAGE_SRC = @sim_pk_src@ +PACKAGE_OBJ = @sim_pk_obj@ + + +psim: $(TARGETLIB) main.o $(LIBIBERTY_LIB) $(BFD_LIB) $(LIBS) $(INTLDEPS) + $(CC) $(CFLAGS) $(SIM_CFLAGS) $(LDFLAGS) -o psim$(EXEEXT) main.o $(TARGETLIB) $(BFD_LIB) $(INTLLIBS) $(LIBIBERTY_LIB) $(LIBS) + +run: psim + rm -f run$(EXEEXT) + ln psim$(EXEEXT) run$(EXEEXT) + +$(TARGETLIB): tmp-igen tmp-dgen tmp-hw tmp-pk tmp-defines $(LIB_OBJ) $(GDB_OBJ) + rm -f $(TARGETLIB) + $(AR) $(AR_FLAGS) $(TARGETLIB) $(LIB_OBJ) $(GDB_OBJ) + $(RANLIB) $(TARGETLIB) + +psim.o: psim.c psim.h tree.h $(CPU_H) $(IDECODE_H) + +bits.o: bits.c $(BASICS_H) + +debug.o: debug.c $(BASICS_H) +filter_filename.o: filter_filename.c filter_filename.h config.h ppc-config.h + +sim-endian.o: sim-endian.c sim-endian-n.h $(BASICS_H) + +os_emul.o: os_emul.c emul_netbsd.h emul_unix.h emul_chirp.h emul_bugapi.h $(EMUL_GENERIC_H) +emul_generic.o: emul_generic.c $(EMUL_GENERIC_H) + +emul_bugapi.o: emul_bugapi.c emul_bugapi.h $(EMUL_GENERIC_H) +emul_chirp.o: emul_chirp.c emul_chirp.h $(EMUL_GENERIC_H) +emul_netbsd.o: emul_netbsd.c emul_netbsd.h $(EMUL_GENERIC_H) +emul_unix.o: emul_unix.c emul_unix.h $(EMUL_GENERIC_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) os_emul.h + +# Given that inlines are turned on now, rebuild idecode whenever +# anything changes. +idecode.o: idecode.c $(CPU_H) $(IDECODE_H) semantics.h $(LIB_INLINE_SRC) $(BUILT_SRC) + $(CC) -c $(NOWARN_CFLAGS) $< + +# double.o: double.c dp-bit.c + +vm.o: vm.c vm.h vm_n.h $(CPU_H) + +corefile.o: corefile.c corefile.h corefile-n.h $(BASICS_H) $(DEVICE_TABLE_H) + +model.o: model.c $(CPU_H) + +events.o: events.c events.h $(BASICS_H) + +sim_calls.o: sim_calls.c $(PSIM_H) itable.h ../../gdb/tm.h options.h + +spreg.o: spreg.c spreg.h spreg.c $(BASICS_H) + +main.o: main.c $(PSIM_H) itable.h options.h + +device.o: device.c $(DEVICE_TABLE_H) cap.h + +tree.o: tree.c tree.h device.h $(DEVICE_TABLE_H) + +device_table.o: device_table.c $(DEVICE_TABLE_H) events.h hw.c + +cap.o: cap.c cap.h $(BASICS_H) + +semantics.o: semantics.c semantics.h $(CPU_H) $(IDECODE_H) + $(CC) -c $(NOWARN_CFLAGS) $< + +icache.o: icache.c icache.h $(IDECODE_H) $(CPU_H) + $(CC) -c $(NOWARN_CFLAGS) $< + +support.o: support.c support.h $(IDECODE_H) $(CPU_H) + +itable.o: itable.c itable.h + +mon.o: mon.c $(CPU_H) + +# GDB after 4.16 expects the default_callback structure to be setup. +# As a kludge, build the common stuff here for now. +gentmap: ../common/gentmap.c Makefile targ-vals.def + $(CC_FOR_BUILD) $(BUILD_FLAGS) -I. -I../common -I$(srcdir)/../common -o gentmap $< $(BUILD_LIBS) + +targ-vals.def: $(srcdir)/../common/nltvals.def + rm -f targ-vals.def tmp-def + cat $(srcdir)/../common/nltvals.def > tmp-vals.def + $(srcdir)/../../move-if-change tmp-vals.def targ-vals.def + +targ-vals.h: Makefile gentmap $(srcdir)/../../move-if-change + rm -f tmp-vals.h + ./gentmap -h > tmp-vals.h + $(srcdir)/../../move-if-change tmp-vals.h targ-vals.h + +targ-map.c: Makefile gentmap $(srcdir)/../../move-if-change + rm -f tmp-map.c + ./gentmap -c > tmp-map.c + $(srcdir)/../../move-if-change tmp-map.c targ-map.c + +callback.o: ../common/callback.c targ-vals.h config.h + $(CC) -c $(STD_CFLAGS) -DHAVE_CONFIG_H $< + +targ-map.o: targ-map.c targ-vals.h + +# Rebuild options whenever something changes so the date/time is up to date. +options.o: options.c $(BASICS_H) $(CPU_H) $(IDECODE_H) $(INLINE) $(LIB_SRC) $(BUILT_SRC) config.status Makefile defines.h + $(CC) -c $(STD_CFLAGS) '-DOPCODE_RULES="@sim_opcode@"' '-DIGEN_FLAGS="$(IGEN_FLAGS)"' '-DDGEN_FLAGS="$(DGEN_FLAGS)"' $< + +tmp-defines: config.h Makefile + sed -n -e '/^#define HAVE_/s/ 1$$/",/' -e '/^#define HAVE_/s//"HAVE_/p' < config.h > tmp-defines.h + $(srcdir)/../../move-if-change tmp-defines.h defines.h + touch tmp-defines + +# +# Rules to create the built c source code files +# + +ppc-config.h: $(CONFIG_FILE) + cp $(srcdir)/$(CONFIG_FILE) ppc-config.h + + +tmp-dgen: dgen ppc-spr-table $(srcdir)/../../move-if-change + ./dgen $(DGEN_FLAGS) \ + -r $(srcdir)/ppc-spr-table \ + -n spreg.h -hp tmp-spreg.h \ + -n spreg.c -p tmp-spreg.c + $(srcdir)/../../move-if-change tmp-spreg.h spreg.h + $(srcdir)/../../move-if-change tmp-spreg.c spreg.c + touch tmp-dgen + + +tmp-igen: igen ppc-instructions $(IGEN_OPCODE_RULES) ppc-cache-rules $(srcdir)/../../move-if-change tmp-ld-decode tmp-ld-cache tmp-ld-insn tmp-filter + ./igen $(IGEN_FLAGS) \ + -o $(srcdir)/$(IGEN_OPCODE_RULES) \ + -k $(srcdir)/ppc-cache-rules \ + -i $(srcdir)/ppc-instructions \ + -n icache.h -hc tmp-icache.h \ + -n icache.c -c tmp-icache.c \ + -n semantics.h -hs tmp-semantics.h \ + -n semantics.c -s tmp-semantics.c \ + -n idecode.h -hd tmp-idecode.h \ + -n idecode.c -d tmp-idecode.c \ + -n itable.h -ht tmp-itable.h \ + -n itable.c -t tmp-itable.c \ + -n model.h -hm tmp-model.h \ + -n model.c -m tmp-model.c \ + -n support.h -hf tmp-support.h \ + -n support.c -f tmp-support.c + $(srcdir)/../../move-if-change tmp-icache.h icache.h + $(srcdir)/../../move-if-change tmp-icache.c icache.c + $(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-itable.h itable.h + $(srcdir)/../../move-if-change tmp-itable.c itable.c + $(srcdir)/../../move-if-change tmp-model.h model.h + $(srcdir)/../../move-if-change tmp-model.c model.c + $(srcdir)/../../move-if-change tmp-support.h support.h + $(srcdir)/../../move-if-change tmp-support.c support.c + touch tmp-igen + +# NOTE: Some versions of make don't handle files created as side-effects +# uncomment the below if that is the case. + +$(TARGETLIB): tmp-igen tmp-dgen +itable.h itable.c icache.h icache.c idecode.h idecode.c semantics.h semantics.c model.h model.c support.h support.c: tmp-igen +spreg.h spreg.c: tmp-dgen + +dgen: dgen.o table.o lf.o misc.o filter_host.o + $(CC_FOR_BUILD) $(BUILD_CFLAGS) -o dgen dgen.o table.o lf.o misc.o filter_host.o $(BUILD_LIBS) + +igen: igen.o table.o lf.o misc.o filter_host.o ld-decode.o ld-cache.o filter.o ld-insn.o gen-model.o gen-itable.o gen-icache.o gen-semantics.o gen-idecode.o gen-support.o + $(CC_FOR_BUILD) $(BUILD_CFLAGS) $(BUILD_LDFLAGS) -o igen igen.o table.o lf.o misc.o filter_host.o ld-decode.o ld-cache.o filter.o ld-insn.o gen-model.o gen-itable.o gen-icache.o gen-semantics.o gen-idecode.o gen-support.o $(BUILD_LIBS) + +filter_host.c: filter_filename.c + cat $(srcdir)/filter_filename.c > filter_host.c + +filter_host.o: filter_host.c filter_filename.h config.h ppc-config.h + $(CC_FOR_BUILD) $(BUILD_CFLAGS) -c filter_host.c + +table.o: table.c misc.h filter_filename.h lf.h table.h + $(CC_FOR_BUILD) $(BUILD_CFLAGS) -c $(srcdir)/table.c + +lf.o: lf.c misc.h filter_filename.h lf.h + $(CC_FOR_BUILD) $(BUILD_CFLAGS) -c $(srcdir)/lf.c + +filter.o: filter.c misc.h lf.h table.h filter.h + $(CC_FOR_BUILD) $(BUILD_CFLAGS) -c $(srcdir)/filter.c +tmp-filter: filter.c misc.h misc.o + $(CC_FOR_BUILD) $(BUILD_CFLAGS) $(BUILD_LDFLAGS) -o tmp-filter -DMAIN $(srcdir)/filter.c misc.o $(BUILD_LIBS) + +ld-decode.o: ld-decode.c misc.h lf.h table.h ld-decode.h + $(CC_FOR_BUILD) $(BUILD_CFLAGS) -c $(srcdir)/ld-decode.c +tmp-ld-decode: ld-decode.o misc.o lf.o table.o filter_host.o + $(CC_FOR_BUILD) $(BUILD_CFLAGS) $(BUILD_LDFLAGS) -o tmp-ld-decode -DMAIN $(srcdir)/ld-decode.c misc.o lf.o table.o filter_host.o $(BUILD_LIBS) + +ld-cache.o: ld-cache.c misc.h lf.h table.h ld-cache.h + $(CC_FOR_BUILD) $(BUILD_CFLAGS) -c $(srcdir)/ld-cache.c +tmp-ld-cache: ld-cache.o misc.o lf.o table.o filter_host.o + $(CC_FOR_BUILD) $(BUILD_CFLAGS) $(BUILD_LDFLAGS) -o tmp-ld-cache -DMAIN $(srcdir)/ld-cache.c misc.o lf.o table.o filter_host.o $(BUILD_LIBS) + +ld-insn.o: ld-insn.c misc.h lf.h table.h ld-insn.h ld-decode.h igen.h + $(CC_FOR_BUILD) $(BUILD_CFLAGS) -c $(srcdir)/ld-insn.c +tmp-ld-insn: ld-insn.o misc.o lf.o table.o ld-decode.o filter_host.o filter.o + $(CC_FOR_BUILD) $(BUILD_CFLAGS) $(BUILD_LDFLAGS) -o tmp-ld-insn -DMAIN $(srcdir)/ld-insn.c misc.o lf.o table.o ld-decode.o filter_host.o filter.o $(BUILD_LIBS) + +gen-model.o: gen-model.c misc.h lf.h table.h gen-model.h ld-decode.h igen.h ld-insn.h + $(CC_FOR_BUILD) $(BUILD_CFLAGS) -c $(srcdir)/gen-model.c + +gen-itable.o: gen-itable.c misc.h lf.h table.h gen-itable.h ld-decode.h igen.h ld-insn.h igen.h + $(CC_FOR_BUILD) $(BUILD_CFLAGS) -c $(srcdir)/gen-itable.c + +gen-icache.o: gen-icache.c misc.h lf.h table.h gen-icache.h ld-decode.h igen.h ld-insn.h gen-semantics.h gen-idecode.h + $(CC_FOR_BUILD) $(BUILD_CFLAGS) -c $(srcdir)/gen-icache.c + +gen-semantics.o: gen-semantics.c misc.h lf.h table.h gen-semantics.h ld-decode.h igen.h ld-insn.h + $(CC_FOR_BUILD) $(BUILD_CFLAGS) -c $(srcdir)/gen-semantics.c + +gen-idecode.o: gen-idecode.c misc.h lf.h table.h gen-idecode.h gen-icache.h gen-semantics.h ld-decode.h igen.h ld-insn.h + $(CC_FOR_BUILD) $(BUILD_CFLAGS) -c $(srcdir)/gen-idecode.c + +gen-support.o: gen-support.c misc.h lf.h table.h gen-support.h ld-decode.h igen.h ld-insn.h + $(CC_FOR_BUILD) $(BUILD_CFLAGS) -c $(srcdir)/gen-support.c + +dgen.o: dgen.c misc.h filter_filename.h lf.h table.h + $(CC_FOR_BUILD) $(BUILD_CFLAGS) -c $(srcdir)/dgen.c + +igen.o: igen.c misc.h filter_filename.h lf.h table.h ld-decode.h ld-cache.h ld-insn.h filter.h gen-model.h gen-itable.h gen-icache.h gen-idecode.h gen-semantics.h gen-support.h igen.h + $(CC_FOR_BUILD) $(BUILD_CFLAGS) -c $(srcdir)/igen.c + +misc.o: misc.c misc.h filter_filename.h + $(CC_FOR_BUILD) $(BUILD_CFLAGS) -c $(srcdir)/misc.c + + + +# real hardware +tmp-hw: Makefile $(HW_SRC) $(srcdir)/../../move-if-change + # The first for loop is to remove duplicates. + f=""; \ + for i in $(HW_SRC) ; do \ + case " $$f " in \ + *" $$i "*) ;; \ + *) f="$$f $$i" ;; \ + esac ; \ + done ; \ + for hw in $$f ; do echo $$hw ; done \ + | sed -e 's/^.*\(hw_.*\)\.c/\1/' \ + -e 's/^/extern const device_descriptor /' \ + -e 's/$$/_device_descriptor\[\];/' \ + > tmp-hw.h + f=""; \ + for i in $(HW_SRC) ; do \ + case " $$f " in \ + *" $$i "*) ;; \ + *) f="$$f $$i" ;; \ + esac ; \ + done ; \ + for hw in $$f ; do echo $$hw ; done \ + | sed -e 's/^.*\(hw_.*\)\.c/\1/' \ + -e 's/^/ /' \ + -e 's/$$/_device_descriptor,/' \ + > tmp-hw.c + $(srcdir)/../../move-if-change tmp-hw.h hw.h + $(srcdir)/../../move-if-change tmp-hw.c hw.c + touch tmp-hw + +hw_cpu.o: hw_cpu.c $(DEVICE_TABLE_H) +hw_core.o: hw_core.c $(DEVICE_TABLE_H) +hw_disk.o: hw_disk.c $(DEVICE_TABLE_H) pk.h +hw_glue.o: hw_glue.c $(DEVICE_TABLE_H) +hw_htab.o: hw_htab.c $(DEVICE_TABLE_H) +hw_ide.o: hw_ide.c $(DEVICE_TABLE_H) +hw_init.o: hw_init.c $(DEVICE_TABLE_H) +hw_iobus.o: hw_iobus.c $(DEVICE_TABLE_H) +hw_memory.o: hw_memory.c $(DEVICE_TABLE_H) +hw_nvram.o: hw_nvram.c $(DEVICE_TABLE_H) +hw_opic.o: hw_opic.c $(DEVICE_TABLE_H) +hw_pal.o: hw_pal.c $(DEVICE_TABLE_H) +hw_phb.o: hw_phb.c $(DEVICE_TABLE_H) hw_phb.h +hw_register.o: hw_register.c $(DEVICE_TABLE_H) +hw_trace.o: hw_trace.c $(DEVICE_TABLE_H) +hw_vm.o: hw_vm.c $(DEVICE_TABLE_H) +# ignore this line, it stops make from getting confused + + + +# real packages +tmp-pk: Makefile $(PACKAGE_SRC) $(srcdir)/../../move-if-change + # The first for loop is to remove duplicates. + f=""; \ + for i in $(PACKAGE_SRC) ; do \ + case " $$f " in \ + *" $$i "*) ;; \ + *) f="$$f $$i" ;; \ + esac ; \ + done ; \ + for pk in $$f ; do echo $$pk ; done \ + | sed -e 's/^.*pk_\(.*\)\.c/\1/' \ + -e 's/^/extern package_create_instance_callback pk_/' \ + -e 's/$$/_create_instance;/' \ + > tmp-pk.h + $(srcdir)/../../move-if-change tmp-pk.h pk.h + touch tmp-pk + +pk_disklabel.o: pk.h $(DEVICE_TABLE_H) +# ignore this line, it stops make from getting confused + + + +tags etags: TAGS + +TAGS: $(BUILT_SRC) + etags $(srcdir)/*.h $(srcdir)/*.c $(BUILT_SRC) + +clean mostlyclean: + rm -f tmp-* *.[oasi] core psim$(EXEEXT) run$(EXEEXT) igen dgen $(BUILT_SRC_WO_CONFIG) gentmap + +distclean realclean: clean + rm -f TAGS Makefile config.cache config.status config.h defines.h stamp-h config.log + +maintainer-clean: distclean + rm -f *~ *.log ppc-config.h core *.core + +Makefile: Makefile.in config.status + CONFIG_FILES=Makefile CONFIG_HEADERS= $(SHELL) ./config.status + +config.h: stamp-h ; @true +stamp-h: config.in config.status + CONFIG_FILES= CONFIG_HEADERS=config.h:config.in $(SHELL) ./config.status + +config.status: configure + $(SHELL) ./config.status --recheck + +install: installdirs + n=`echo run | sed '$(program_transform_name)'`; \ + $(INSTALL_PROGRAM) run$(EXEEXT) $(bindir)/$$n$(EXEEXT) + +installdirs: + $(SHELL) $(srcdir)/../../mkinstalldirs $(bindir) diff --git a/sim/ppc/README b/sim/ppc/README new file mode 100644 index 00000000000..ff3fbd2b349 --- /dev/null +++ b/sim/ppc/README @@ -0,0 +1,353 @@ + + + PSIM 1.0.1 - Model of the PowerPC Environments + + + Copyright (C) 1994-1996, 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. + + + ---------------------------------------------------------------------- + + +PSIM is a program written in extended ANSI-C that implements an +instruction level simulation of the PowerPC environment. It is freely +available in source code form under the terms of the GNU General +Public License (version 2 or later). + +The PowerPC Architecture is described as having three levels of +compliance: + + UEA - User Environment Architecture + VEA - Virtual Environment Architecture + OEA - Operating Environment Architecture + +PSIM both implements all three levels of the PowerPC and includes (for +each level) a corresponding simulated run-time environment. + +In addition, PSIM, to the execution unit level, models the performance +of most of the current PowerPC implementations (contributed by Michael +Meissner). This detailed performance monitoring (unlike many other +simulators) resulting in only a relatively marginal reduction in the +simulators performance. + + +A description of how to build PSIM is contained in the file: + + ftp://ftp.ci.com.au/pub/psim/INSTALL + or ftp://cambridge.cygnus.com/pub/psim/INSTALL + +while an overview of how to use PSIM is in: + + ftp://ftp.ci.com.au/pub/psim/RUN +or ftp://cambridge.cygnus.com/pub/psim/RUN + +This file is found in: + + ftp://ftp.ci.com.au/pub/psim/README +or ftp://cambridge.cygnus.com/pub/psim/README + + +Thanks goes firstly to: + + Corinthian Engineering Pty Ltd + Cygnus Support + Highland Logic Pty Ltd + +who provided the resources needed for making this software available +on the Internet. + +More importantly I'd like to thank the following individuals who each +contributed in their own unique way: + + Allen Briggs, Bett Koch, David Edelsohn, Gordon Irlam, + Michael Meissner, Bob Mercier, Richard Perini, Dale Rahn, + Richard Stallman, Mitchele Walker + + + Andrew Cagney + Feb, 1995 + + + ---------------------------------------------------------------------- + + + What features does PSIM include? + + Monitoring and modeling + + PSIM includes (thanks to Michael Meissner) + a detailed model of most of the PowerPC + implementations to the functional unit level. + + + SMP + + The PowerPC ISA defines SMP synchronizing instructions. + This simulator implements a limited, but functional, + subset of the PowerPC synchronization instructions + behaviour. Programs that restrict their synchronization + primitives to those that work with this functional + sub-set (eg P() and V()) are able to run on the SMP + version of PSIM. + + People intending to use this system should study + the code implementing the lwarx instruction. + + ENDIAN SUPPORT + + PSIM implements the PowerPC's big and little (xor + endian) modes and correctly simulates code that + switches between these two modes. + + In addition, psim can model a true little-endian + machine. + + ISA (Instruction Set Architecture) models + + PSIM includes a model of the UEA, VEA and OEA. This + includes the time base registers (VEA) and HTAB + and BATS (OEA). + + In addition, a preliminary model of the 64 bit + PowerPC architecture is implemented. + + IO Hardware + + PSIM's internals are based around the concept + of a Device Tree. This tree intentionally + resembles that of the Device Tree found in + OpenBoot firmware. PSIM is flexible enough + to allow the user to fully configure this device + tree (and consequently the hardware model) at + run time. + + Run-time environments: + + PSIM's UEA model includes emulation for BSD + based UNIX system calls. + + PSIM's OEA model includes emulation of either: + + o OpenBoot client interface + + o MOTO's BUG interface. + + + Floating point + + Preliminary support for floating point is included. + + + Who would be interested in PSIM? + + o the curious + + Using psim, gdb, gcc and binutils the curious + user can construct an environment that allows + them to play with PowerPC Environment without + the need for real hardware. + + + o the analyst + + PSIM includes many (contributed) monitoring + features which (unlike many other simulators) + do not come with a great penalty in performance. + + Thus the performance analyst is able to use + this simulator to analyse the performance of + the system under test. + + If PSIM doesn't monitor a components of interest, + the source code is freely available, and hence + there is no hinderance to changing things + to meet a specific analysts needs. + + + o the serious SW developer + + PSIM models all three levels of the PowerPC + Architecture: UEA, VEA and OEA. Further, + the internal design is such that PSIM can + be extended to support additional requirements. + + + What performance analysis measurements can PSIM perform? + + Below is the output from a recent analysis run + (contributed by Michael Meissner): + + For the following program: + + long + simple_rand () + { + static unsigned long seed = 47114711; + unsigned long this = seed * 1103515245 + 12345; + seed = this; + /* cut-cut-cut - see the file RUN.psim */ + } + + Here is the current output generated with the -I switch on a P90 + (the compiler used is the development version of GCC with a new + scheduler replacing the old one): + + CPU #1 executed 41,994 AND instructions. + CPU #1 executed 519,785 AND Immediate instructions. + . + . + . + CPU #1 executed 1 System Call instruction. + CPU #1 executed 207,746 XOR instructions. + + CPU #1 executed 23,740,856 cycles. + CPU #1 executed 10,242,780 stalls waiting for data. + CPU #1 executed 1 stall waiting for a function unit. + . + . + . + CPU #1 executed 3,136,229 branch functional unit instructions. + CPU #1 executed 16,949,396 instructions that were accounted for in timing info. + CPU #1 executed 871,920 data reads. + CPU #1 executed 971,926 data writes. + CPU #1 executed 221 icache misses. + CPU #1 executed 16,949,396 instructions in total. + + Simulator speed was 250,731 instructions/second + + + What motivated PSIM? + + As an idea, psim was first discussed seriously during mid + 1994. At that time its main objectives were: + + + o good performance + + Many simulators loose out by only providing + a binary interface to the internals. This + interface eventually becomes a bottle neck + in the simulators performance. + + It was intended that PSIM would avoid this + problem by giving the user access to the + full source code. + + Further, by exploiting the power of modern + compilers it was hoped that PSIM would achieve + good performance with out having to compromise + its internal design. + + + o practical portability + + Rather than try to be portable to every + C compiler on every platform, it was decided + that PSIM would restrict its self to supporting + ANSI compilers that included the extension + of a long long type. + + GCC is one such compiler, consequently PSIM + should be portable to any machine running GCC. + + + o flexibility in its design + + PSIM should allow the user to select the + features required and customise the build + accordingly. By having the source code, + the compiler is able to eliminate any un + used features of the simulator. + + After all, let the compiler do the work. + + + o SMP + + A model that allowed the simulation of + SMP platforms with out the large overhead + often encountered with such models. + + + PSIM achieves each of these objectives. + + + Is PSIM PowerPC Platform (PPCP) (nee CHRP) Compliant? + + No. + + Among other things it does not have an Apple ROM socket. + + + Could PSIM be extended so that it models a CHRP machine? + + Yes. + + PSIM has been designed with the CHRP spec in mind. To model + a CHRP desktop the following would need to be added: + + o An apple ROM socket :-) + + o Model of each of the desktop IO devices + + o An OpenPIC device. + + o RTAS (Run Time Abstraction Services). + + o A fully populated device tree. + + + Is the source code available? + + Yes. + + The source code to PSIM is available under the terms of + the GNU Public Licence. This allows you to distribute + the source code for free but with certain conditions. + + See the file: + + ftp://archie.au/gnu/COPYING + + For details of the terms and conditions. + + + Where do I send bugs or report problems? + + There is a mailing list (subscribe through majordomo@ci.com.au) at: + + powerpc-psim@ci.com.au + + If I get the ftp archive updated I post a note to that mailing list. + In addition your welcome to send bugs or problems either to me or to + that e-mail list. + + This list currently averages zero articles a day. + + + Does PSIM have any limitations or problems? + + PSIM can't run rs6000/AIX binaries - At present PSIM can only + simulate static executables. Since an AIX executable is + never static, PSIM is unable to simulate its execution. + + PSIM is still under development - consequently there are going + to be bugs. + + See the file BUGS (included in the distribution) for any + other outstanding issues. + diff --git a/sim/ppc/RUN b/sim/ppc/RUN new file mode 100644 index 00000000000..6c3cfefcb25 --- /dev/null +++ b/sim/ppc/RUN @@ -0,0 +1,949 @@ + + PSIM - model the PowerPC environment + + Copyright (C) 1994-1996, Andrew Cagney <cagney@highland.com.au>. + + ---------------------------------------------------------------------- + + Running PSIM + + This file describes how to run the program PSIM. + + o Walk through a number of examples from the + pre-built tar archive psim-test. + + o Looks at the device tree used by PSIM. + + o Notes on building a programmer environment to + use with PSIM (BSD/UEA and BUG/OEA) + + + ---------------------------------------------------------------------- + + +RUNNING PSIM: + + +The compressed tar archive psim-test available from: + + ftp://ftp.ci.com.au/pub/psim/psim-test-1.0.1.tar.gz +or ftp://cambridge.cygnus.com/pub/psim/psim-test-1.0.1.tar.gz + +contains a number of pre-built programs for running under PSIM. Each +pre-built binary is built both big and little endian. The suffixes +.be/.le (executables) .bo/.lo (object files) and .ba/.la (libraries) +are used. + + +To run one of these programs, use: + + powerpc-unknown-eabi-run <image> + +for instance: + + powerpc-unknown-eabi-run psim-test/uea/envp + +The program envp prints out your shells environment - very useful! +More generally psim is run as (this is part of the output from the -h +option): + + psim [ <psim-option> ... ] <image> [ <image-arg> ... ] + +Where + + <image> Name of the PowerPC program to run. + This can either be a PowerPC binary or + a text file containing a device tree + specification. + PSIM will attempt to determine from the + specified <image> the intended emulation + environment. + If PSIM gets it wrong, the emulation + environment can be specified using the + `-e' option (described below). + + <image-arg> Argument to be passed to <image> + These arguments will be passed to + <image> (as standard C argv, argc) + when <image> is started. + + <psim-option> See below + +The following are valid <psim-option>s: + + -m <model> Specify the processor to model (604) + Selects the processor to use when + modeling execution units. Includes: + 604, 603 and 603e + + -e <os-emul> specify an OS or platform to model + Can be any of the following: + bug - OEA + MOTO BUG ROM calls + netbsd - UEA + NetBSD system calls + chirp - OEA + a few OpenBoot calls + + -i Print instruction counting statistics + + -I Print execution unit statistics + + -r <size> Set RAM size in bytes (OEA environments) + + -t [!]<trace> Enable (disable) <trace> option + + -o <spec> add device <spec> to the device tree + + -h -? -H give more detailed usage + + +The `-H' option gives a long usage output. This includes a complete +list of all the pre-configured devices. + + + ---------------------------------------------------------------------- + + +RUNNING GDB: + + +If you built PSIM with gdb then the following is a quick start +tutorial. + +At present GDB, if configured big-endian (say) unlike PSIM, does not +support the debugging of little endian binaries. If you find that +your program won't run at all, make certain that GDB and your +program's endianness match. + + +The most important thing is that before you can run the simulator you +must enable it. For the simulator, gdb is started like any program: + + $ powerpc-unknown-eabi-gdb psim-test/uea/envp.be + +Next the simulator is enabled. The command `target sim' accepts the +same options as can be specified on the PSIM command line. + + (gdb) target sim + +To trace the communication between psim and gdb specify `target sim -t +gdb'. Once enabled, the binary needs to be loaded, any breakpoints of +interest set, and the program run: + + (gdb) load + (gdb) break main + (gdb) run + . + . + . + +In addition, if you are wanting to run a program described by a device +tree you can `attach' to the simulation using (I assume that you have +applied the attach patch): + + $ cd psim-test/tree + $ powerpc-unknown-eabi-gdb + (gdb) target sim + (gdb) attach device-tree + (gdb) run + +Here GDB takes the programs initial state from the attached +device-tree instead of forcing initialisation. + + + ---------------------------------------------------------------------- + + +PROFILING: + + +PSIM includes a number of performance monitoring (profiling) +facilities: + + o instruction frequency counting + + o execution unit modeling (records + effective usage of units). + + o instruction cache performance + +As discussed in the file INSTALL, each can be configured to individual +requirements. + + + -i Enable instruction counting. + + The frequency of all instructions is tabulated. In + addition (f configured) the hit/miss rate of the + instruction cache is output. + + + -I Enable execution unit analysis. + + In addition to counting basic instructions also model + the performance of the processors execution units + + + -m <processor> + + Select the processor to be modelled. + + For execution unit analysis specify the processor that + is to be analysed. By default the 604 is modelled + however, support for other processors such as the + 603 and 603e is included. + +The output from a performance run (on a P90) for the program +psim-test/profile/bench is below. In this run psim was fairly +agressively configured (see the file INSTALL for compile time +configuration). + + CPU #1 executed 41,994 AND instructions. + CPU #1 executed 519,785 AND Immediate instructions. + CPU #1 executed 680,058 Add instructions. + CPU #1 executed 41,994 Add Extended instructions. + CPU #1 executed 921,916 Add Immediate instructions. + CPU #1 executed 221,199 Add Immediate Carrying instructions. + CPU #1 executed 943,823 Add Immediate Shifted instructions. + CPU #1 executed 471,909 Add to Zero Extended instructions. + CPU #1 executed 571,915 Branch instructions. + CPU #1 executed 1,992,403 Branch Conditional instructions. + CPU #1 executed 571,910 Branch Conditional to Link Register instructions. + CPU #1 executed 320,431 Compare instructions. + CPU #1 executed 471,911 Compare Immediate instructions. + CPU #1 executed 145,867 Compare Logical instructions. + CPU #1 executed 442,414 Compare Logical Immediate instructions. + CPU #1 executed 1 Condition Register XOR instruction. + CPU #1 executed 103,873 Divide Word instructions. + CPU #1 executed 104,275 Divide Word Unsigned instructions. + CPU #1 executed 132,510 Extend Sign Byte instructions. + CPU #1 executed 178,895 Extend Sign Half Word instructions. + CPU #1 executed 871,920 Load Word and Zero instructions. + CPU #1 executed 41,994 Move From Condition Register instructions. + CPU #1 executed 100,005 Move from Special Purpose Register instructions. + CPU #1 executed 100,002 Move to Special Purpose Register instructions. + CPU #1 executed 804,619 Multiply Low Word instructions. + CPU #1 executed 421,201 OR instructions. + CPU #1 executed 471,910 OR Immediate instructions. + CPU #1 executed 1,292,020 Rotate Left Word Immediate then AND with Mask instructions. + CPU #1 executed 663,613 Shift Left Word instructions. + CPU #1 executed 1,151,564 Shift Right Algebraic Word Immediate instructions. + CPU #1 executed 871,922 Store Word instructions. + CPU #1 executed 100,004 Store Word with Update instructions. + CPU #1 executed 887,804 Subtract From instructions. + CPU #1 executed 83,988 Subtract From Immediate Carrying instructions. + CPU #1 executed 1 System Call instruction. + CPU #1 executed 207,746 XOR instructions. + + CPU #1 executed 23,740,856 cycles. + CPU #1 executed 10,242,780 stalls waiting for data. + CPU #1 executed 1 stall waiting for a function unit. + CPU #1 executed 1 stall waiting for serialization. + CPU #1 executed 1,757,900 times a write-back slot was unavailable. + CPU #1 executed 1,088,135 branches. + CPU #1 executed 2,048,093 conditional branches fell through. + CPU #1 executed 1,088,135 successful branch predictions. + CPU #1 executed 904,268 unsuccessful branch predictions. + CPU #1 executed 742,557 branch if the condition is FALSE conditional branches. + CPU #1 executed 1,249,846 branch if the condition is TRUE conditional branches. + CPU #1 executed 571,910 branch always conditional branches. + CPU #1 executed 9,493,653 1st single cycle integer functional unit instructions. + CPU #1 executed 1,220,900 2nd single cycle integer functional unit instructions. + CPU #1 executed 1,254,768 multiple cycle integer functional unit instructions. + CPU #1 executed 1,843,846 load/store functional unit instructions. + CPU #1 executed 3,136,229 branch functional unit instructions. + CPU #1 executed 16,949,396 instructions that were accounted for in timing info. + CPU #1 executed 871,920 data reads. + CPU #1 executed 971,926 data writes. + CPU #1 executed 221 icache misses. + CPU #1 executed 16,949,396 instructions in total. + + Simulator speed was 250,731 instructions/second + + + ---------------------------------------------------------------------- + + +PSIM CONFIGURATION - THE DEVICE TREE + + +Internally PSIM's configuration is controlled by a tree data +structure. This structure, created at run-time, intentionally +resembles the device tree used by OpenBoot firmware to describe a +machines hardware configuration. + +PSIM can either create its device tree using a builtin emulation or +from one read in from a file. + +During startup, the device tree is created using the following steps: + + o Initial empty tree is created + + o Any tree entry options specified on the + command line are merged in (the -o <entry> + option is used). + + It should be pointed out that most of the + command line options (eg -r, -e, -m, -t + are all just short hand for corresponding + -o options). + + o If the specified program is a device tree spec, that + is loaded. + + If the specified program is a text file it is assumed + that that file contains a further specification of the + simulators device tree. That tree is loaded and + merged with the current tree options. + + o The selected emulation fills out any remaining details. + + By this stage the emulation environment that the program + needs will either be specified in the device tree + (through the -e option) or determined from the + characteristics of the binary. + + The selected emulation will then fill out any missing + nodes in the device tree. + +Most importantly earlier additions to the tree are not overridden by +later additions. Thus, command line options override information +found in the program file and both override any builtin emulation +entries. + +The following is a summary of the most useful runtime configuration +options: + + -e <os-emul> + -o '/openprom/options/os-emul <os-emul>' + + Run program using the <emulation> run-time + environment. + + -r <ram-size> + -o '/openprom/options/oea-memory-size <ram-size>' + + Set the size of the first bank of memory + (RAM from address 0 up). + + -t print-device-tree + -o '/openprom/trace/print-device-tree 1' + + -t dump-device-tree + -o '/openprom/trace/dump-device-tree 1' + + Print out the device tree once it has been fully + populated. For dump-device-tree, exit simulator after + dumping the tree. + + PSIM is able to reload the dumped device tree. + + The format of the dumped tree is under development. + + -o '/openprom/options/smp <N>' + + Enable <N> processors for the simulation run. + See the directory psim-test/oea for an example. + + -o '/openprom/options/alignment <N>' + + Where <N> is 1 - nonstrict or 2 - strict. + Specify if the missaligned access are allowed + (non-strict) or result in an alignment exception + (strict). + +Devices (if included in the file device_table.c) can also be specified +in a similar way. For instance, to add a second serial port, a +command like: + + -o '/iobus@0x400000/console@0x000010' + +would create a `console' device at offset 0x10 within the `iobus' at +memory address 0x400000. + +For more detailed information on device specifiers see the notes on +the function dump_device_tree in the file device.c (found in the +source code). + + + ---------------------------------------------------------------------- + + +BUILDING A BUG/OEA DEVELOPMENT ENVIRONMENT + + +Background: + + +Included in many PowerPC systems is Motorola's BUG monitor. This +monitor includes, for client programs, a set of services that allow +that program to interact with hardware devices such as the console using +a simple system call interface. + +PSIM is able to emulate a number of the services (including the +console IO calls). If additional services are needed they can easily +be added. + +Cygnus support's newlib library includes includes an interface to the +MOTO BUG services. The notes below discuss how I both built and run +programs compiled using this library on PSIM. + +The only confusing part about building a development environment based +around newlib/binutils/gcc is a chicken/egg problem with include +files: + + For GCC to build, a fairly complete set of include + files must be installed but newlib won't install its + include files until it has been built with gcc ... + +I get around this by installing the problematic include files by hand. + + +Preparation: + + +The following files are needed: + +From your favorite FTP site, the sources to gas/ld and gcc - mine +happens to be archie.au : + + ftp://archie.au/gnu/binutils-2.6.tar.gz + ftp://archie.au/gnu/gcc-2.7.2.tar.gz + +From ftp://ftp.cygnus.com/pub/newlib the source code to a library: + + ftp://ftp.cygnus.com/pub/newlib/newlib-1.7.0.tar.gz + +From ftp://ftp.ci.com.au/pub/psim some minor patches and updates to +the above library: + + ftp://ftp.ci.com.au/pub/psim/newlib-1.7.0+float+ppc-asm.tar.gz + ftp://ftp.ci.com.au/pub/psim/newlib-1.7.0+ppc-fix.diff.gz + ftp://ftp.ci.com.au/pub/psim/binutils-2.6+note.diff.gz + +In addition you'll need to decide where you will be installing the +development environment. You will notice that in the below I install +things well away /usr/local instead installing everything under its +own directory in /applications. + + +Method: + + +These notes are based on an installation performed on a Sun-OS-4/SPARC +host. For other hosts and other configurations, the below should be +considered as a guideline only. + + + o Sanity check + + $ cd .../scratch # your scratch directory + $ ls -1 + binutils-2.6.tar.gz + binutils-2.6+note.diff.gz + gcc-2.7.2,tar.gz + newlib-1.7.0+float+ppc-asm.tar.gz + newlib-1.7.0+ppc-fix.diff.gz + newlib-1.7.0.tar.gz + + + o Unpack/build/install binutils + + This is done first so that there is a gas/ld ready + for the building of GCC and NEWLIB. + + $ cd .../scratch + $ gunzip < binutils-2.6.tar.gz | tar xf - + $ cd binutils-2.6 + + Optionally apply the note patch + + $ gunzip ../binutils-2.6+note.diff.gz | patch + + Then continue with the build + + $ ./configure --target=powerpc-unknown-eabi \ + --prefix=/applications/psim + $ make + $ make install + $ cd .. + $ rm -rf binutils-2.6 + + This also creates much of the installation directory + tree. + + + o Unpack newlib, install the include files so that they + are ready for GCC's build. + + $ cd .../scratch + $ gunzip < newlib-1.7.0.tar.gz | tar xf - + + New lib-1.7.0 had a few minor bugs (fixed in current): + the header files float.h and ppc-asm.h were missing; + the configure and Makefile's for the rs6000 (ppc) directory + contained typos: + + $ cd .../scratch + $ cd newlib-1.7.0 + $ gunzip < ../newlib-1.7.0+float+ppc-asm.tar.gz | tar xvf - + $ gunzip < ../newlib-1.7.0+ppc-fix.diff.gz | patch -p1 + + Finally copy the include files to where GCC will see them: + + $ cd .../scratch + $ cd newlib-1.7.0/newlib/libc + $ tar cf - include | \ + ( cd /applications/psim/powerpc-unknown-eabi && tar xf - ) + + + o Unpack/build gcc + + $ cd .../scratch + $ gunzip < gcc-2.7.2,tar.gz | tar xf - + $ cd gcc-2.7.2 + $ ./configure --target=powerpc-unknown-eabi \ + --prefix=/applications/psim + $ make + $ make install + $ cd .. + $ rm -rf gcc-2.7.2 + + Gcc likes to install its own dummy version of float that + just returns an error. + + $ more /applications/psim/lib/gcc-lib/powerpc-unknown-eabi/2.7.2/include/float.h + $ rm /applications/psim/lib/gcc-lib/powerpc-unknown-eabi/2.7.2/include/float.h + + + o Finish building/installing newlib + + $ cd .../scratch + $ cd newlib-1.7.0 + $ ./configure --target=powerpc-unknown-eabi \ + --prefix=/applications/psim + + Your path will need to include the recently installed + gas/gcc when building. Either add it to your path or + use: + + $ PATH=/applications/psim/bin:$PATH make + $ PATH=/applications/psim/bin:$PATH make install + + + o Finally, test out the build + + $ cat hello.c + main() + { + printf("hello world\n"); + } + + The binary is linked with an entry point less than 0x100000 + (1mb) so that psim will recognize the binary as needing + the BUG/OEA instead of the BSD/UEA runtime environment. + + $ powerpc-unknown-eabi-gcc -v -o hello \ + -Wl,-Ttext,0x4000,-Tdata,0x10000 \ + /applications/psim/powerpc-unknown-eabi/lib/mvme-crt0.o \ + hello.c \ + -lc -lmvme + $ powerpc-unknown-eabi-objdump -h hello + $ powerpc-unknown-eabi-run hello + + It is also possible to force psim to use a specific + run-time environment using the -e option vis: + + $ powerpc-unknown-eabi-run -e bug hello + + + + + ---------------------------------------------------------------------- + + +BUILDING A BSD/UEA DEVELOPMENT ENVIRONMENT + + +Background: + + +For a UEA to be useful it needs a supporting run-time environment. +PSIM implements a runtime environment based on the NetBSD system call +interface. + +More than any thing, this user level emulation was the first +implemented because I happened to have the NetBSD source code lying +around. + + +Preparation: + + +This requires the NetBSD-1.1 source tree online. It can either be +obtained vi ftp: + + try http://www.netbsd.org or ftp://ftp.netbsd.org + +Alternatively obtain one of the NetBSD cdrom's. Patches to this source +tree that fill out much of the PowerPC code are available in: + + ftp://ftp.ci.com.au/pub/clayton + +Fetch everything in that directory - diffs, tar archives and scripts. +In addition patches to the bintuils and gcc are in: + + ftp://ftp.ci.com.au/pub/psim/binutils-2.6+note.diff.gz + ftp://ftp.ci.com.au/pub/psim/gcc-2.7.2+sys-types.diff.gz + +while the compiler (gcc) and assember (binutils) can be found at your +favorite gnu ftp site. I used versions: + + gcc-2.7.2.tar.gz + binutils-2.6.tar.gz + + + + +Method: + + +These notes are based on an installation performed on a Solaris2/x86 +host. For other hosts and other configurations, the below should be +considered as a guideline only. + + + o Sanity check + + I assume that you have already obtained the NetBSD-1.1 source + code and unpacked it into the directory bsd-src. While the + full NetBSD source tree may not be needed, things are easier + if it is all online. + + $ cd .../scratch + $ ls -1 + binutils-2.6.tar.gz + binutils-2.6+note.diff.gz + clayton-include-960203.diff.gz + clayton-lib-960203.diff.gz + clayton-lib-960203.tar.gz + clayton-sys-960203.diff.gz + clayton-sys-960203.tar.gz + clayton.chown.sh + clayton.install.sh + clayton.lorder.sh + clayton.make.sh + gcc-2.7.2.tar.gz + gcc-2.7.2+sys-types.diff.gz + make.tar.gz + make.diff.gz + + + o Prepare the destination directory ready for installation. + + Firstly create many of the needed directories (some are + created automatically later): + + $ for d in \ + /applications/psim \ + /applications/psim/bsd-root \ + /applications/psim/bsd-root/usr \ + /applications/psim/bsd-root/usr/share \ + /applications/psim/bsd-root/usr/share/doc \ + /applications/psim/bsd-root/usr/share/doc/psd \ + /applications/psim/bsd-root/usr/share/doc/psd/19.curses \ + /applications/psim/bsd-root/usr/include \ + /applications/psim/bsd-root/usr/lib \ + /applications/psim/powerpc-unknown-eabi \ + /applications/psim/powerpc-unknown-eabi/bin \ + ; \ + do test -d $d || mkdir $d ; done + + Next, link the BSD and GNU include directories together. + GCC expects include files to be in one location while the + bsd install expects them in a second. The link is in + the direction below because bsd's install also insists on + a directory (not a link) for its install destination. + + $ rm -rf /applications/psim/powerpc-unknown-eabi/include + $ ln -s /applications/psim/bsd-root/usr/include \ + /applications/psim/powerpc-unknown-eabi/include + + $ ls -l /applications/psim/powerpc-unknown-eabi/include + lrwxr-xr-x 1 cagney wheel 39 Mar 21 18:09 + /applications/psim/powerpc-unknown-eabi/include + -> /applications/psim/bsd-root/usr/include + + + o Build/install Berkeley make + + The tar archive make.tar.gz contains a recent snapshot + of bmake from the NetBSD source tree. The notes below + describe how to build/install it. If you have access + to an even more recent version of bmake, use that. + + Unpack the source code: + + $ cd .../scratch + $ gunzip < make.tar.gz | tar xf - + $ cd make + + Apply the patch in make.diff.gz that fixes a minor + problem with a build under Solaris (by now it should + be fixed in the NetBSD-current source tree). + + $ gunzip < ../make.diff.gz | more + $ gunzip < ../make.diff.gz | patch + + Build it + + $ make -f Makefile.boot 'CC=gcc -g -DPOSIX' + + With bmake built, install it into the target specific bin + directory: + + $ cp bmake /applications/psim/powerpc-unknown-eabi/bin/make + $ cd .. + $ rm -rf make + + + o Set up a number of wrapper scripts for bmake so that it works. + + In addition to needing BSD make the build process assumes + a number of BSD specific commands. To get around this + several wrapper scripts are available. + + powerpc-unknown-eabi-make (clayton.make.sh) + + Front end to Berkeley make setting it up for a + cross compilation + + $ cp clayton.make.sh \ + /applications/psim/bin/powerpc-unknown-eabi-make + $ chmod a+x \ + /applications/psim/bin/powerpc-unknown-eabi-make + + chown (clayton.chown.sh) + + Wrapper that does not do any thing. + Avoids the need to be root when installing. + + $ cp clayton.chown.sh \ + /applications/psim/powerpc-unknown-eabi/bin/chown + $ chmod a+x \ + /applications/psim/powerpc-unknown-eabi/bin/chown + + install (clayton.install.sh) + + Wrapper to strip away a number of bsd specific install + arguments. + + $ cp clayton.install.sh \ + /applications/psim/powerpc-unknown-eabi/bin/install + $ chmod a+x \ + /applications/psim/powerpc-unknown-eabi/bin/install + + lorder (clayton.lorder.sh) + + Tweaked lorder script that will use nm etc from + binutils. + + $ cp clayton.lorder.sh \ + /applications/psim/powerpc-unknown-eabi/bin/lorder + $ chmod a+x \ + /applications/psim/powerpc-unknown-eabi/bin/lorder + + + printf (?) + + Some operating systems don't include the program + printf. If you host doesn't have one, then a + good source is the gnu sh-utils version. + + Again, if that program is missing, then I suggest + installing it onto the powerpc specific program + directory: + + /applications/psim/powerpc-unknown-eabi/bin + + + o Unpack the bsd source code (if you haven't already) + + If you're short on disk space (like me) just unpack: + + sys, lib, share/mk, include, usr.sbin/config, + usr.sbin/dbsym, gnu/lib/libg++/g++-include, + usr.bin/lex + + Otherwize, assuming you have a CD-DRIVE: + + $ cd .../scratch + $ mkdir bsd-src + $ cd bsd-src + $ for d in /cdrom/bsdisc_12_95_disc2/NetBSD-1.1/source/*11 + do + echo $d + cat $d/*.?? | gunzip | tar xf - + done + + Flatten the directory structure a little. + + $ mv usr/src/* . + $ rmdir usr/src usr + $ cd .. + + + o Apply the clayton (PowerPC) patches to your constructed + tree. + + $ cd .../scratch + $ cd bsd-src + + Diffs are applied using something like: + + $ gunzip < ../clayton-include-960312.diff.gz | patch -p1 + $ gunzip < ../clayton-lib-960203.diff.gz | patch -p1 + $ gunzip < ../clayton-sys-960203.diff.gz | patch -p1 + + The patch to sys/dev/pci/ncr.c.rej might fail. + + The tar archives have a different problem, you need + to remove the `src' prefix. I used + + $ ln -s . src + $ gunzip < ../clayton-lib-960203.tar.gz | tar xvf - + $ gunzip < ../clayton-sys-960203.tar.gz | tar xvf - + + So that src/xxx unpacked into ./xxx + + $ cd .. + + + o install Berkeley make's include (mk) files. + + $ cd .../scrath + $ cd bsd-src/share + $ tar cf - mk | ( cd /applications/psim/bsd-root/usr/share \ + && tar xvf - ) + $ cd ../.. + + + o Install the include files + + $ cd .../scratch + $ cd bsd-src/include + $ powerpc-unknown-eabi-make install + $ cd ../.. + + + o Install a few other include files. + + As discussed above in the section on building libnew, + the build process can have chicken/egg problems. In the + case of BSD's libc, it wants to use several include files + (from the installed include directory) before they are + installed. Just copy them in as seen below: + + $ cd .../scratch + $ cd bsd-src + $ cp gnu/lib/libg++/g++-include/values.h \ + /applications/psim/powerpc-unknown-eabi/include + $ cp lib/libcurses/curses.h \ + /applications/psim/powerpc-unknown-eabi/include + $ cd .. + + + o Unpack/patch/build/install BINUTILS + + $ cd .../scratch + $ gunzip < binutils-2.6.tar.gz | tar xf - + + gas (bfd) 2.6 didn't support the reading and writing of + note sections. The patch binutils-2.6+note.diff.gz + adds support for this. PowerPC/ELF boot files being loaded + by OpenBoot ROM's should contain a PowerPC note section. + + $ cd .../scratch + $ cd binutils-2.6/bfd + $ gunzip < ../../binutils-2.6+note.diff.gz | more + $ gunzip < ../../binutils-2.6+note.diff.gz | patch + $ cd ../.. + + Then continue with the build + + $ cd .../scratch + $ cd binutils-2.6 + $ ./configure --target=powerpc-unknown-eabi \ + --prefix=/applications/psim + $ make + $ make install + $ cd .. + $ rm -rf binutils-2.6 + + This has the intended side effect of partially populating + the psim directory tree which makes follow on steps easier. + + + o Unpack/patch/build/install GCC + + $ cd .../scratch + $ gunzip < gcc-2.7.2.tar.gz | tar xf - + $ cd gcc-2.7.2 + + GCC-2.7.2 and the BSD include files have a conflicting type + declaration. The patch below gets around this problem + (it may still be applicable to more recent versions of + GCC): + + $ gunzip < ../gcc-2.7.2+sys-types.diff.gz | more + $ gunzip < ../gcc-2.7.2+sys-types.diff.gz | patch + + If your version of GCC includes the file ginclude/ppc-asm.h + then you should install that header file into the directory: + /applications/psim/powerpc-unknown-eabi/include. More + recent versions of GCC expect this file to be installed: + + $ test -r ginclude/ppc-asm.h \ + && cp ginclude/ppc-asm.h \ + /applications/psim/powerpc-unknown-eabi/include + + Other than that, assuming the include files installed + okay, the rest should be fine .... + + $ ./configure --target=powerpc-unknown-eabi \ + --prefix=/applications/psim + $ make CC=gcc + $ make CC=gcc install + $ cd .. + $ rm -rf gcc-2.7.2 + + + o Build/install the Berkeley library: + + $ cd .../scratch + $ cd bsd-src/lib + $ powerpc-unknown-eabi-make + $ powerpc-unknown-eabi-make install + $ cd ../.. + + If you encounter problems check the following (each + discussed above): + + o GCC and BSD have a common include + directory + + o all the missing include files installed + + o all the wrapper programs installed + + + o Build/run a simple BSD program + + $ cd .../scratch + $ cd bsd-src/usr.bin/printenv + $ powerpc-unknown-eabi-make + $ powerpc-unknown-eabi-run printenv + . + . + . + + + ---------------------------------------------------------------------- diff --git a/sim/ppc/acconfig.h b/sim/ppc/acconfig.h new file mode 100644 index 00000000000..f9b87a10c60 --- /dev/null +++ b/sim/ppc/acconfig.h @@ -0,0 +1,15 @@ + +/* Define to 1 if NLS is requested. */ +#undef ENABLE_NLS + +/* Define as 1 if you have catgets and don't want to use GNU gettext. */ +#undef HAVE_CATGETS + +/* Define as 1 if you have gettext and don't want to use GNU gettext. */ +#undef HAVE_GETTEXT + +/* Define as 1 if you have the stpcpy function. */ +#undef HAVE_STPCPY + +/* Define if your locale.h file contains LC_MESSAGES. */ +#undef HAVE_LC_MESSAGES diff --git a/sim/ppc/aclocal.m4 b/sim/ppc/aclocal.m4 new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/sim/ppc/aclocal.m4 diff --git a/sim/ppc/basics.h b/sim/ppc/basics.h new file mode 100644 index 00000000000..1520023178c --- /dev/null +++ b/sim/ppc/basics.h @@ -0,0 +1,139 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1997, 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 _BASICS_H_ +#define _BASICS_H_ + + +/* from Michael Meissner, macro's to handle concating symbols better */ + +#if defined(__STDC__) || defined(ALMOST_STDC) +#define CONCAT2(a,b) a##b +#define CONCAT3(a,b,c) a##b##c +#define CONCAT4(a,b,c,d) a##b##c##d +#else +#define CONCAT2(a,b) a/**/b +#define CONCAT3(a,b,c) a/**/b/**/c +#define CONCAT4(a,b,c,d) a/**/b/**/c/**/d +#endif + +#define XCONCAT2(a,b) CONCAT2(a,b) +#define XCONCAT3(a,b,c) CONCAT3(a,b,c) +#define XCONCAT4(a,b,c,d) CONCAT4(a,b,c,d) + + +/* many things pass around the cpu and psim object with out knowing + what it is */ + +typedef struct _cpu cpu; +typedef struct _psim psim; +typedef struct _device device; +typedef struct _device_instance device_instance; +typedef struct _event_queue event_queue; +typedef struct _event_entry_tag *event_entry_tag; + + +/* many things are moving data between the host and target */ + +typedef enum { + cooked_transfer, + raw_transfer, +} transfer_mode; + + +/* possible exit statuses */ + +typedef enum { + was_continuing, was_trap, was_exited, was_signalled +} stop_reason; + + +/* disposition of an object when things are next restarted */ + +typedef enum { + permenant_object, + tempoary_object, +} object_disposition; + + +/* directions */ + +typedef enum { + bidirect_port, + input_port, + output_port, +} port_direction; + + + +/* Basic configuration */ + +#include "config.h" +#include "ppc-config.h" +#include "inline.h" + + +/* Basic host dependant mess - hopefully <stdio.h> + <stdarg.h> will + bring potential conflicts out in the open */ + +#include <stdarg.h> +#include <stdio.h> + + +#ifndef NORETURN +#define NORETURN +#endif + +#ifndef NULL +#define NULL 0 +#endif + +#if !defined (__attribute__) +#if (!defined(__GNUC__) \ + || (__GNUC__ < 2) \ + || (__GNUC__ == 2 && __GNUC_MINOR__ < 6)) +#define __attribute__(arg) +#endif +#endif + +#if !defined (UNUSED) +#if (!defined(__GNUC__) \ + || (__GNUC__ < 2) \ + || (__GNUC__ == 2 && __GNUC_MINOR__ < 7)) +#define UNUSED +#else +#define UNUSED __attribute__((__unused__)) +#endif +#endif + + +/* Basic definitions - ordered so that nothing calls what comes after + it */ + +#include "sim_callbacks.h" + +#include "debug.h" + +#include "words.h" +#include "bits.h" +#include "sim-endian.h" + +#endif /* _BASICS_H_ */ diff --git a/sim/ppc/bits.c b/sim/ppc/bits.c new file mode 100644 index 00000000000..00bba518aac --- /dev/null +++ b/sim/ppc/bits.c @@ -0,0 +1,114 @@ +/* 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_C_ +#define _BITS_C_ + +#include "basics.h" + + +INLINE_BITS\ +(unsigned32) +MASKED32(unsigned32 word, + unsigned start, + unsigned stop) +{ + return (word & MASK32(start, stop)); +} + +INLINE_BITS\ +(unsigned64) +MASKED64(unsigned64 word, + unsigned start, + unsigned stop) +{ + return (word & MASK64(start, stop)); +} + +INLINE_BITS\ +(unsigned_word) +MASKED(unsigned_word word, + unsigned start, + unsigned stop) +{ + return ((word) & MASK(start, stop)); +} + + + +INLINE_BITS\ +(unsigned_word) +EXTRACTED(unsigned_word word, + unsigned start, + unsigned stop) +{ + ASSERT(start <= stop); +#if (WITH_TARGET_WORD_BITSIZE == 64) + return _EXTRACTEDn(64, word, start, stop); +#else + if (stop < 32) + return 0; + else + return ((word >> (63 - stop)) + & MASK(start+(63-stop), 63)); +#endif +} + + +INLINE_BITS\ +(unsigned_word) +INSERTED(unsigned_word word, + unsigned start, + unsigned stop) +{ + ASSERT(start <= stop); +#if (WITH_TARGET_WORD_BITSIZE == 64) + return _INSERTEDn(64, word, start, stop); +#else + if (stop < 32) + return 0; + else + return ((word & MASK(start+(63-stop), 63)) + << (63 - stop)); +#endif +} + + +INLINE_BITS\ +(unsigned32) +ROTL32(unsigned32 val, + long shift) +{ + ASSERT(shift >= 0 && shift <= 32); + return _ROTLn(32, val, shift); +} + + +INLINE_BITS\ +(unsigned64) +ROTL64(unsigned64 val, + long shift) +{ + ASSERT(shift >= 0 && shift <= 64); + return _ROTLn(64, val, shift); +} + +#endif /* _BITS_C_ */ diff --git a/sim/ppc/bits.h b/sim/ppc/bits.h new file mode 100644 index 00000000000..06898e07e9b --- /dev/null +++ b/sim/ppc/bits.h @@ -0,0 +1,257 @@ +/* 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). + + SHUFFLED**(VALUE, OLD, NEW): Mask then move a single bit from OLD + new NEW. + + MOVED**(VALUE, OLD_FIRST, OLD_LAST, NEW_FIRST, NEW_LAST): Moves + things around so that bits OLD_FIRST..OLD_LAST are masked then + moved to NEW_FIRST..NEW_LAST. + + INSERTED*(VALUE, FIRST, LAST): Takes VALUE and `inserts' the (LAST + - FIRST + 1) least significant bits into bit positions [ FIRST + .. LAST ]. This is almost the complement to EXTRACTED. + + 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. + + ALIGN_*(VALUE): Round upwards the value so that it is aligned. + + FLOOR_*(VALUE): Truncate the value so that it is aligned. + + ROTL*(VALUE, NR_BITS): Return the value rotated by NR_BITS + + */ + +#define _MAKE_SHIFT(WIDTH, pos) ((WIDTH) - 1 - (pos)) + + +/* MakeBit */ +#define _BITn(WIDTH, pos) (((natural##WIDTH)(1)) \ + << _MAKE_SHIFT(WIDTH, pos)) + +#define BIT4(POS) (1 << _MAKE_SHIFT(4, POS)) +#define BIT5(POS) (1 << _MAKE_SHIFT(5, POS)) +#define BIT8(POS) (1 << _MAKE_SHIFT(8, POS)) +#define BIT10(POS) (1 << _MAKE_SHIFT(10, POS)) +#define BIT32(POS) _BITn(32, POS) +#define BIT64(POS) _BITn(64, POS) + +#if (WITH_TARGET_WORD_BITSIZE == 64) +#define BIT(POS) BIT64(POS) +#else +#define BIT(POS) (((POS) < 32) ? 0 : _BITn(32, (POS)-32)) +#endif + + +/* multi bit mask */ +#define _MASKn(WIDTH, START, STOP) \ +(((((unsigned##WIDTH)0) - 1) \ + >> (WIDTH - ((STOP) - (START) + 1))) \ + << (WIDTH - 1 - (STOP))) + +#define MASK32(START, STOP) _MASKn(32, START, STOP) +#define MASK64(START, STOP) _MASKn(64, START, STOP) + +#if (WITH_TARGET_WORD_BITSIZE == 64) +#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 + + +/* mask the required bits, leaving them in place */ + +INLINE_BITS\ +(unsigned32) MASKED32 +(unsigned32 word, + unsigned start, + unsigned stop); + +INLINE_BITS\ +(unsigned64) MASKED64 +(unsigned64 word, + unsigned start, + unsigned stop); + +INLINE_BITS\ +(unsigned_word) MASKED +(unsigned_word word, + unsigned start, + unsigned 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)) + +/* #define EXTRACTED10(WORD, START, STOP) _EXTRACTEDn(10, WORD, START, STOP) */ +#define EXTRACTED32(WORD, START, STOP) _EXTRACTEDn(32, WORD, START, STOP) +#define EXTRACTED64(WORD, START, STOP) _EXTRACTEDn(64, WORD, START, STOP) + +INLINE_BITS\ +(unsigned_word) EXTRACTED +(unsigned_word val, + unsigned start, + unsigned stop); + + +/* move a single bit around */ +/* NB: the wierdness (N>O?N-O:0) is to stop a warning from GCC */ +#define _SHUFFLEDn(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)))) + +#define SHUFFLED32(WORD, OLD, NEW) _SHUFFLEDn(32, WORD, OLD, NEW) +#define SHUFFLED64(WORD, OLD, NEW) _SHUFFLEDn(64, WORD, OLD, NEW) + +#define SHUFFLED(WORD, OLD, NEW) _SHUFFLEDn(_word, WORD, OLD, NEW) + + +/* move a group of bits around */ +#define _INSERTEDn(N, WORD, START, STOP) \ +(((natural##N)(WORD) << _MAKE_SHIFT(N, STOP)) & _MASKn(N, START, STOP)) + +#define INSERTED32(WORD, START, STOP) _INSERTEDn(32, WORD, START, STOP) +#define INSERTED64(WORD, START, STOP) _INSERTEDn(64, WORD, START, STOP) + +INLINE_BITS\ +(unsigned_word) INSERTED +(unsigned_word val, + unsigned start, + unsigned stop); + + +/* depending on MODE return a 64bit or 32bit (sign extended) value */ +#if (WITH_TARGET_WORD_BITSIZE == 64) +#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 _FLOORa(A,X) ((X) & ~((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 MBLIT32(V, LO, HI, VAL) \ +do { \ + (V) = (((V) & ~MASK32((LO), (HI))) \ + | INSERTED32(VAL, LO, HI)); \ +} while (0) + + +/* some rotate functions to make things easier + + NOTE: These are functions not macro's as the latter tickles bugs in + gcc-2.6.3 */ + +#define _ROTLn(N, VAL, SHIFT) \ +(((VAL) << (SHIFT)) | ((VAL) >> ((N)-(SHIFT)))) + +INLINE_BITS\ +(unsigned32) ROTL32 +(unsigned32 val, + long shift); + +INLINE_BITS\ +(unsigned64) ROTL64 +(unsigned64 val, + long shift); + + +#if (BITS_INLINE & INCLUDE_MODULE) +#include "bits.c" +#endif + +#endif /* _BITS_H_ */ diff --git a/sim/ppc/cap.c b/sim/ppc/cap.c new file mode 100644 index 00000000000..a3be304c81f --- /dev/null +++ b/sim/ppc/cap.c @@ -0,0 +1,134 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1995,1997, 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 _CAP_C_ +#define _CAP_C_ + +#include "cap.h" + +typedef struct _cap_mapping cap_mapping; +struct _cap_mapping { + unsigned_cell external; + void *internal; + cap_mapping *next; +}; + +struct _cap { + int nr_mappings; + cap_mapping *mappings; +}; + +INLINE_CAP\ +(cap *) +cap_create(const char *key) +{ + return ZALLOC(cap); +} + +INLINE_CAP\ +(void) +cap_init(cap *db) +{ + cap_mapping *current_map = db->mappings; + if (current_map != NULL) { + db->nr_mappings = db->mappings->external; + /* verify that the mappings that were not removed are in sequence + down to nr 1 */ + while (current_map->next != NULL) { + if (current_map->external != current_map->next->external + 1) + error("cap: cap database possibly corrupt"); + current_map = current_map->next; + } + ASSERT(current_map->next == NULL); + if (current_map->external != 1) + error("cap: cap database possibly currupt"); + } + else { + db->nr_mappings = 0; + } +} + +INLINE_CAP\ +(void *) +cap_internal(cap *db, + signed_cell external) +{ + cap_mapping *current_map = db->mappings; + while (current_map != NULL) { + if (current_map->external == external) + return current_map->internal; + current_map = current_map->next; + } + return (void*)0; +} + +INLINE_CAP\ +(signed_cell) +cap_external(cap *db, + void *internal) +{ + cap_mapping *current_map = db->mappings; + while (current_map != NULL) { + if (current_map->internal == internal) + return current_map->external; + current_map = current_map->next; + } + return 0; +} + +INLINE_CAP\ +(void) +cap_add(cap *db, + void *internal) +{ + if (cap_external(db, internal) != 0) { + error("cap: attempting to add an object already in the data base"); + } + else { + /* insert at the front making things in decending order */ + cap_mapping *new_map = ZALLOC(cap_mapping); + new_map->next = db->mappings; + new_map->internal = internal; + db->nr_mappings += 1; + new_map->external = db->nr_mappings; + db->mappings = new_map; + } +} + +INLINE_CAP\ +(void) +cap_remove(cap *db, + void *internal) +{ + cap_mapping **current_map = &db->mappings; + while (*current_map != NULL) { + if ((*current_map)->internal == internal) { + cap_mapping *delete = *current_map; + *current_map = delete->next; + zfree(delete); + return; + } + current_map = &(*current_map)->next; + } + error("cap: attempt to remove nonexistant internal object"); +} + +#endif diff --git a/sim/ppc/cap.h b/sim/ppc/cap.h new file mode 100644 index 00000000000..6c91752af8d --- /dev/null +++ b/sim/ppc/cap.h @@ -0,0 +1,60 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1995,1997, 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. + + */ + + +/* Export a capability data base that maps between internal data + values and those given to a simulation */ + +#ifndef _CAP_H_ +#define _CAP_H_ + +#include "basics.h" + +typedef struct _cap cap; + +INLINE_CAP\ +(cap *) cap_create +(const char *name); + +INLINE_CAP\ +(void) cap_init +(cap *db); + +INLINE_CAP\ +(signed_cell) cap_external +(cap *db, + void *internal); + +INLINE_CAP\ +(void *) cap_internal +(cap *db, + signed_cell external); + +INLINE_CAP\ +(void) cap_add +(cap *db, + void *internal); + +INLINE_CAP\ +(void) cap_remove +(cap *db, + void *internal); + +#endif diff --git a/sim/ppc/config.in b/sim/ppc/config.in new file mode 100644 index 00000000000..c53a833c01c --- /dev/null +++ b/sim/ppc/config.in @@ -0,0 +1,362 @@ +/* config.in. Generated automatically from configure.in by autoheader. */ + +/* Define if using alloca.c. */ +#undef C_ALLOCA + +/* Define to empty if the keyword does not work. */ +#undef const + +/* Define to one of _getb67, GETB67, getb67 for Cray-2 and Cray-YMP systems. + This function is required for alloca.c support on those systems. */ +#undef CRAY_STACKSEG_END + +/* Define to the type of elements in the array set by `getgroups'. + Usually this is either `int' or `gid_t'. */ +#undef GETGROUPS_T + +/* Define to `int' if <sys/types.h> doesn't define. */ +#undef gid_t + +/* Define if you have alloca, as a function or macro. */ +#undef HAVE_ALLOCA + +/* Define if you have <alloca.h> and it should be used (not on Ultrix). */ +#undef HAVE_ALLOCA_H + +/* Define if you have a working `mmap' system call. */ +#undef HAVE_MMAP + +/* Define if your struct stat has st_blksize. */ +#undef HAVE_ST_BLKSIZE + +/* Define if your struct stat has st_blocks. */ +#undef HAVE_ST_BLOCKS + +/* Define if your struct stat has st_rdev. */ +#undef HAVE_ST_RDEV + +/* Define if your struct tm has tm_zone. */ +#undef HAVE_TM_ZONE + +/* Define if you don't have tm_zone but do have the external array + tzname. */ +#undef HAVE_TZNAME + +/* Define as __inline if that's what the C compiler calls it. */ +#undef inline + +/* Define to `int' if <sys/types.h> doesn't define. */ +#undef mode_t + +/* Define to `long' if <sys/types.h> doesn't define. */ +#undef off_t + +/* Define to `int' if <sys/types.h> doesn't define. */ +#undef pid_t + +/* Define if you need to in order for stat and other things to work. */ +#undef _POSIX_SOURCE + +/* Define as the return type of signal handlers (int or void). */ +#undef RETSIGTYPE + +/* Define to `unsigned' if <sys/types.h> doesn't define. */ +#undef size_t + +/* If using the C implementation of alloca, define if you know the + direction of stack growth for your system; otherwise it will be + automatically deduced at run-time. + STACK_DIRECTION > 0 => grows toward higher addresses + STACK_DIRECTION < 0 => grows toward lower addresses + STACK_DIRECTION = 0 => direction of growth unknown + */ +#undef STACK_DIRECTION + +/* Define if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define if your <sys/time.h> declares struct tm. */ +#undef TM_IN_SYS_TIME + +/* Define to `int' if <sys/types.h> doesn't define. */ +#undef uid_t + +/* Define if your processor stores words with the most significant + byte first (like Motorola and SPARC, unlike Intel and VAX). */ +#undef WORDS_BIGENDIAN + +/* Define to 1 if NLS is requested. */ +#undef ENABLE_NLS + +/* Define as 1 if you have gettext and don't want to use GNU gettext. */ +#undef HAVE_GETTEXT + +/* Define as 1 if you have the stpcpy function. */ +#undef HAVE_STPCPY + +/* Define if your locale.h file contains LC_MESSAGES. */ +#undef HAVE_LC_MESSAGES + +/* Define if you have the __argz_count function. */ +#undef HAVE___ARGZ_COUNT + +/* Define if you have the __argz_next function. */ +#undef HAVE___ARGZ_NEXT + +/* Define if you have the __argz_stringify function. */ +#undef HAVE___ARGZ_STRINGIFY + +/* Define if you have the access function. */ +#undef HAVE_ACCESS + +/* Define if you have the cfgetispeed function. */ +#undef HAVE_CFGETISPEED + +/* Define if you have the cfgetospeed function. */ +#undef HAVE_CFGETOSPEED + +/* Define if you have the cfsetispeed function. */ +#undef HAVE_CFSETISPEED + +/* Define if you have the cfsetospeed function. */ +#undef HAVE_CFSETOSPEED + +/* Define if you have the chdir function. */ +#undef HAVE_CHDIR + +/* Define if you have the chmod function. */ +#undef HAVE_CHMOD + +/* Define if you have the chown function. */ +#undef HAVE_CHOWN + +/* Define if you have the dcgettext function. */ +#undef HAVE_DCGETTEXT + +/* Define if you have the dup function. */ +#undef HAVE_DUP + +/* Define if you have the dup2 function. */ +#undef HAVE_DUP2 + +/* Define if you have the fchmod function. */ +#undef HAVE_FCHMOD + +/* Define if you have the fchown function. */ +#undef HAVE_FCHOWN + +/* Define if you have the fcntl function. */ +#undef HAVE_FCNTL + +/* Define if you have the fstat function. */ +#undef HAVE_FSTAT + +/* Define if you have the fstatfs function. */ +#undef HAVE_FSTATFS + +/* Define if you have the getcwd function. */ +#undef HAVE_GETCWD + +/* Define if you have the getdirentries function. */ +#undef HAVE_GETDIRENTRIES + +/* Define if you have the getegid function. */ +#undef HAVE_GETEGID + +/* Define if you have the geteuid function. */ +#undef HAVE_GETEUID + +/* Define if you have the getgid function. */ +#undef HAVE_GETGID + +/* Define if you have the getpagesize function. */ +#undef HAVE_GETPAGESIZE + +/* Define if you have the getpid function. */ +#undef HAVE_GETPID + +/* Define if you have the getppid function. */ +#undef HAVE_GETPPID + +/* Define if you have the getrusage function. */ +#undef HAVE_GETRUSAGE + +/* Define if you have the gettimeofday function. */ +#undef HAVE_GETTIMEOFDAY + +/* Define if you have the getuid function. */ +#undef HAVE_GETUID + +/* Define if you have the ioctl function. */ +#undef HAVE_IOCTL + +/* Define if you have the kill function. */ +#undef HAVE_KILL + +/* Define if you have the link function. */ +#undef HAVE_LINK + +/* Define if you have the lseek function. */ +#undef HAVE_LSEEK + +/* Define if you have the lstat function. */ +#undef HAVE_LSTAT + +/* Define if you have the mkdir function. */ +#undef HAVE_MKDIR + +/* Define if you have the munmap function. */ +#undef HAVE_MUNMAP + +/* Define if you have the pipe function. */ +#undef HAVE_PIPE + +/* Define if you have the putenv function. */ +#undef HAVE_PUTENV + +/* Define if you have the readlink function. */ +#undef HAVE_READLINK + +/* Define if you have the rmdir function. */ +#undef HAVE_RMDIR + +/* Define if you have the setenv function. */ +#undef HAVE_SETENV + +/* Define if you have the setlocale function. */ +#undef HAVE_SETLOCALE + +/* Define if you have the setregid function. */ +#undef HAVE_SETREGID + +/* Define if you have the setreuid function. */ +#undef HAVE_SETREUID + +/* Define if you have the sigprocmask function. */ +#undef HAVE_SIGPROCMASK + +/* Define if you have the stat function. */ +#undef HAVE_STAT + +/* Define if you have the stpcpy function. */ +#undef HAVE_STPCPY + +/* Define if you have the strcasecmp function. */ +#undef HAVE_STRCASECMP + +/* Define if you have the strchr function. */ +#undef HAVE_STRCHR + +/* Define if you have the symlink function. */ +#undef HAVE_SYMLINK + +/* Define if you have the tcdrain function. */ +#undef HAVE_TCDRAIN + +/* Define if you have the tcflow function. */ +#undef HAVE_TCFLOW + +/* Define if you have the tcflush function. */ +#undef HAVE_TCFLUSH + +/* Define if you have the tcgetattr function. */ +#undef HAVE_TCGETATTR + +/* Define if you have the tcgetpgrp function. */ +#undef HAVE_TCGETPGRP + +/* Define if you have the tcsendbreak function. */ +#undef HAVE_TCSENDBREAK + +/* Define if you have the tcsetattr function. */ +#undef HAVE_TCSETATTR + +/* Define if you have the tcsetpgrp function. */ +#undef HAVE_TCSETPGRP + +/* Define if you have the time function. */ +#undef HAVE_TIME + +/* Define if you have the umask function. */ +#undef HAVE_UMASK + +/* Define if you have the unlink function. */ +#undef HAVE_UNLINK + +/* Define if you have the <argz.h> header file. */ +#undef HAVE_ARGZ_H + +/* Define if you have the <dirent.h> header file. */ +#undef HAVE_DIRENT_H + +/* Define if you have the <fcntl.h> header file. */ +#undef HAVE_FCNTL_H + +/* Define if you have the <limits.h> header file. */ +#undef HAVE_LIMITS_H + +/* Define if you have the <locale.h> header file. */ +#undef HAVE_LOCALE_H + +/* Define if you have the <malloc.h> header file. */ +#undef HAVE_MALLOC_H + +/* Define if you have the <ndir.h> header file. */ +#undef HAVE_NDIR_H + +/* Define if you have the <nl_types.h> header file. */ +#undef HAVE_NL_TYPES_H + +/* Define if you have the <stdlib.h> header file. */ +#undef HAVE_STDLIB_H + +/* Define if you have the <string.h> header file. */ +#undef HAVE_STRING_H + +/* Define if you have the <strings.h> header file. */ +#undef HAVE_STRINGS_H + +/* Define if you have the <sys/dir.h> header file. */ +#undef HAVE_SYS_DIR_H + +/* Define if you have the <sys/ioctl.h> header file. */ +#undef HAVE_SYS_IOCTL_H + +/* Define if you have the <sys/mount.h> header file. */ +#undef HAVE_SYS_MOUNT_H + +/* Define if you have the <sys/ndir.h> header file. */ +#undef HAVE_SYS_NDIR_H + +/* Define if you have the <sys/param.h> header file. */ +#undef HAVE_SYS_PARAM_H + +/* Define if you have the <sys/resource.h> header file. */ +#undef HAVE_SYS_RESOURCE_H + +/* Define if you have the <sys/stat.h> header file. */ +#undef HAVE_SYS_STAT_H + +/* Define if you have the <sys/termio.h> header file. */ +#undef HAVE_SYS_TERMIO_H + +/* Define if you have the <sys/termios.h> header file. */ +#undef HAVE_SYS_TERMIOS_H + +/* Define if you have the <sys/time.h> header file. */ +#undef HAVE_SYS_TIME_H + +/* Define if you have the <sys/times.h> header file. */ +#undef HAVE_SYS_TIMES_H + +/* Define if you have the <sys/types.h> header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define if you have the <time.h> header file. */ +#undef HAVE_TIME_H + +/* Define if you have the <unistd.h> header file. */ +#undef HAVE_UNISTD_H + +/* Define if you have the <values.h> header file. */ +#undef HAVE_VALUES_H diff --git a/sim/ppc/configure b/sim/ppc/configure new file mode 100755 index 00000000000..440662af86e --- /dev/null +++ b/sim/ppc/configure @@ -0,0 +1,5197 @@ +#! /bin/sh + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +sim_inline="-DDEFAULT_INLINE=0" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +# This file is derived from `gettext.m4'. The difference is that the +# included macros assume Cygnus-style source and build trees. + +# Macro to add for using GNU gettext. +# Ulrich Drepper <drepper@cygnus.com>, 1995. +# +# This file file be copied and used freely without restrictions. It can +# be used in projects which are not available under the GNU Public License +# but which still want to provide support for the GNU gettext functionality. +# Please note that the actual code is *not* freely available. + +# serial 3 + + + + + +# Search path for a program which passes the given test. +# Ulrich Drepper <drepper@cygnus.com>, 1996. +# +# This file file be copied and used freely without restrictions. It can +# be used in projects which are not available under the GNU Public License +# but which still want to provide support for the GNU gettext functionality. +# Please note that the actual code is *not* freely available. + +# serial 1 + + + +# Check whether LC_MESSAGES is available in <locale.h>. +# Ulrich Drepper <drepper@cygnus.com>, 1995. +# +# This file file be copied and used freely without restrictions. It can +# be used in projects which are not available under the GNU Public License +# but which still want to provide support for the GNU gettext functionality. +# Please note that the actual code is *not* freely available. + +# serial 1 + + + +# Check to see if we're running under Cygwin32, without using +# AC_CANONICAL_*. If so, set output variable CYGWIN32 to "yes". +# Otherwise set it to "no". + + + +# Check to see if we're running under Win32, without using +# AC_CANONICAL_*. If so, set output variable EXEEXT to ".exe". +# Otherwise set it to "". + + + + +# Guess values for system-dependent variables and create Makefiles. +# Generated automatically using autoconf version 2.12.2 +# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc. +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. + +# Defaults: +ac_help= +ac_default_prefix=/usr/local +# Any additions from configure.in: +ac_help="$ac_help + --disable-nls do not use Native Language Support" +ac_help="$ac_help + --with-included-gettext use the GNU gettext library included here" +ac_help="$ac_help + --enable-sim-alignment=align Specify strict or nonstrict alignment." +ac_help="$ac_help + --enable-sim-assert Specify whether to perform random assertions." +ac_help="$ac_help + --enable-sim-bitsize=n Specify target bitsize (32 or 64)." +ac_help="$ac_help + --enable-sim-bswap Use the BSWAP instruction on Intel 486s and Pentiums." +ac_help="$ac_help + --enable-sim-cflags=opts Extra CFLAGS for use in building simulator" +ac_help="$ac_help + --enable-sim-config=file Override default config file" +ac_help="$ac_help + --enable-sim-decode-mechanism=which Specify the instruction decode mechanism." +ac_help="$ac_help + --enable-sim-default-model=which Specify default PowerPC to model." +ac_help="$ac_help + --enable-sim-duplicate Expand (duplicate) semantic functions." +ac_help="$ac_help + --enable-sim-endian=endian Specify target byte endian orientation." +ac_help="$ac_help + --enable-sim-env=env Specify target environment (operating, virtual, user)." +ac_help="$ac_help + --enable-sim-filter=rule Specify filter rules." +ac_help="$ac_help + --enable-sim-float Specify whether to use host floating point or simulate." +ac_help="$ac_help + --enable-sim-hardware=list Specify the hardware to be included in the build." +ac_help="$ac_help + --enable-sim-hostbitsize=32|64 Specify host bitsize (32 or 64)." +ac_help="$ac_help + --enable-sim-hostendian=end Specify host byte endian orientation." +ac_help="$ac_help + --enable-sim-icache=size Specify instruction-decode cache size and type." +ac_help="$ac_help + --enable-sim-inline=inlines Specify which functions should be inlined." +ac_help="$ac_help + --enable-sim-jump Jump between semantic code (instead of call/return)." +ac_help="$ac_help + --enable-sim-line-nr=opts Generate extra CPP code that references source rather than generated code" +ac_help="$ac_help + --enable-sim-model=which Specify PowerPC to model." +ac_help="$ac_help + --enable-sim-model-issue Specify whether to simulate model specific actions" +ac_help="$ac_help + --enable-sim-monitor=mon Specify whether to enable monitoring events." +ac_help="$ac_help + --enable-sim-opcode=which Override default opcode lookup." +ac_help="$ac_help + --enable-sim-packages=list Specify the packages to be included in the build." +ac_help="$ac_help + --enable-sim-regparm=nr-parm Pass parameters in registers instead of on the stack - x86/GCC specific." +ac_help="$ac_help + --enable-sim-reserved-bits Specify whether to check reserved bits in instruction." +ac_help="$ac_help + --enable-sim-smp=n Specify number of processors to configure for." +ac_help="$ac_help + --enable-sim-stdcall=type Use an alternative function call/return mechanism - x86/GCC specific." +ac_help="$ac_help + --enable-sim-stdio Specify whether to use stdio for console input/output." +ac_help="$ac_help + --enable-sim-switch Use a switch instead of a table for instruction call." +ac_help="$ac_help + --enable-sim-timebase Specify whether the PPC timebase is supported." +ac_help="$ac_help + --enable-sim-trace Specify whether tracing is supported." +ac_help="$ac_help + --enable-sim-warnings=opts Extra CFLAGS for turning on compiler warnings except for idecode.o, semantics.o and psim.o" +ac_help="$ac_help + --enable-sim-xor-endian=n Specify number bytes involved in PowerPC XOR bi-endian mode (default 8)." + +# Initialize some variables set by options. +# The variables have the same names as the options, with +# dashes changed to underlines. +build=NONE +cache_file=./config.cache +exec_prefix=NONE +host=NONE +no_create= +nonopt=NONE +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +target=NONE +verbose= +x_includes=NONE +x_libraries=NONE +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +# Initialize some other variables. +subdirs= +MFLAGS= MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} +# Maximum number of lines to put in a shell here document. +ac_max_here_lines=12 + +ac_prev= +for ac_option +do + + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + case "$ac_option" in + -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;; + *) ac_optarg= ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case "$ac_option" in + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir="$ac_optarg" ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build="$ac_optarg" ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file="$ac_optarg" ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir="$ac_optarg" ;; + + -disable-* | --disable-*) + ac_feature=`echo $ac_option|sed -e 's/-*disable-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + eval "enable_${ac_feature}=no" ;; + + -enable-* | --enable-*) + ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "enable_${ac_feature}='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix="$ac_optarg" ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he) + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat << EOF +Usage: configure [options] [host] +Options: [defaults in brackets after descriptions] +Configuration: + --cache-file=FILE cache test results in FILE + --help print this message + --no-create do not create output files + --quiet, --silent do not print \`checking...' messages + --version print the version of autoconf that created configure +Directory and file names: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [same as prefix] + --bindir=DIR user executables in DIR [EPREFIX/bin] + --sbindir=DIR system admin executables in DIR [EPREFIX/sbin] + --libexecdir=DIR program executables in DIR [EPREFIX/libexec] + --datadir=DIR read-only architecture-independent data in DIR + [PREFIX/share] + --sysconfdir=DIR read-only single-machine data in DIR [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data in DIR + [PREFIX/com] + --localstatedir=DIR modifiable single-machine data in DIR [PREFIX/var] + --libdir=DIR object code libraries in DIR [EPREFIX/lib] + --includedir=DIR C header files in DIR [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc in DIR [/usr/include] + --infodir=DIR info documentation in DIR [PREFIX/info] + --mandir=DIR man documentation in DIR [PREFIX/man] + --srcdir=DIR find the sources in DIR [configure dir or ..] + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM + run sed PROGRAM on installed program names +EOF + cat << EOF +Host type: + --build=BUILD configure for building on BUILD [BUILD=HOST] + --host=HOST configure for HOST [guessed] + --target=TARGET configure for TARGET [TARGET=HOST] +Features and packages: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --x-includes=DIR X include files are in DIR + --x-libraries=DIR X library files are in DIR +EOF + if test -n "$ac_help"; then + echo "--enable and --with options recognized:$ac_help" + fi + exit 0 ;; + + -host | --host | --hos | --ho) + ac_prev=host ;; + -host=* | --host=* | --hos=* | --ho=*) + host="$ac_optarg" ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir="$ac_optarg" ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir="$ac_optarg" ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir="$ac_optarg" ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir="$ac_optarg" ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + localstatedir="$ac_optarg" ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir="$ac_optarg" ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir="$ac_optarg" ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix="$ac_optarg" ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix="$ac_optarg" ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix="$ac_optarg" ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name="$ac_optarg" ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir="$ac_optarg" ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir="$ac_optarg" ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site="$ac_optarg" ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir="$ac_optarg" ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir="$ac_optarg" ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target="$ac_optarg" ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers) + echo "configure generated by autoconf version 2.12.2" + exit 0 ;; + + -with-* | --with-*) + ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "with_${ac_package}='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`echo $ac_option|sed -e 's/-*without-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + eval "with_${ac_package}=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes="$ac_optarg" ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries="$ac_optarg" ;; + + -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; } + ;; + + *) + if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then + echo "configure: warning: $ac_option: invalid host type" 1>&2 + fi + if test "x$nonopt" != xNONE; then + { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; } + fi + nonopt="$ac_option" + ;; + + esac +done + +if test -n "$ac_prev"; then + { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; } +fi + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +# File descriptor usage: +# 0 standard input +# 1 file creation +# 2 errors and warnings +# 3 some systems may open it to /dev/tty +# 4 used on the Kubota Titan +# 6 checking for... messages and results +# 5 compiler messages saved in config.log +if test "$silent" = yes; then + exec 6>/dev/null +else + exec 6>&1 +fi +exec 5>./config.log + +echo "\ +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. +" 1>&5 + +# Strip out --no-create and --no-recursion so they do not pile up. +# Also quote any args containing shell metacharacters. +ac_configure_args= +for ac_arg +do + case "$ac_arg" in + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) ;; + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;; + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*) + ac_configure_args="$ac_configure_args '$ac_arg'" ;; + *) ac_configure_args="$ac_configure_args $ac_arg" ;; + esac +done + +# NLS nuisances. +# Only set these to C if already set. These must not be set unconditionally +# because not all systems understand e.g. LANG=C (notably SCO). +# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'! +# Non-C LC_CTYPE values break the ctype check. +if test "${LANG+set}" = set; then LANG=C; export LANG; fi +if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi +if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi +if test "${LC_CTYPE+set}" = set; then LC_CTYPE=C; export LC_CTYPE; fi + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo > confdefs.h + +# A filename unique to this package, relative to the directory that +# configure is in, which we can look for to find out if srcdir is correct. +ac_unique_file=Makefile.in + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_prog=$0 + ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'` + test "x$ac_confdir" = "x$ac_prog" && ac_confdir=. + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; } + else + { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; } + fi +fi +srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'` + +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + echo "loading site script $ac_site_file" + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + echo "loading cache $cache_file" + . $cache_file +else + echo "creating cache $cache_file" + > $cache_file +fi + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +ac_exeext= +ac_objext=o +if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then + # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu. + if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then + ac_n= ac_c=' +' ac_t=' ' + else + ac_n=-n ac_c= ac_t= + fi +else + ac_n= ac_c='\c' ac_t= +fi + + + +ac_aux_dir= +for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do + if test -f $ac_dir/install-sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f $ac_dir/install.sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; } +fi +ac_config_guess=$ac_aux_dir/config.guess +ac_config_sub=$ac_aux_dir/config.sub +ac_configure=$ac_aux_dir/configure # This should be Cygnus configure. + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# ./install, which can be erroneously created by make from ./install.sh. +echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6 +echo "configure:763: checking for a BSD compatible install" >&5 +if test -z "$INSTALL"; then +if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + IFS="${IFS= }"; ac_save_IFS="$IFS"; IFS=":" + for ac_dir in $PATH; do + # Account for people who put trailing slashes in PATH elements. + case "$ac_dir/" in + /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + if test -f $ac_dir/$ac_prog; then + if test $ac_prog = install && + grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + else + ac_cv_path_install="$ac_dir/$ac_prog -c" + break 2 + fi + fi + done + ;; + esac + done + IFS="$ac_save_IFS" + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL="$ac_cv_path_install" + else + # As a last resort, use the slow shell script. We don't cache a + # path for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the path is relative. + INSTALL="$ac_install_sh" + fi +fi +echo "$ac_t""$INSTALL" 1>&6 + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +# Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:816: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CC="gcc" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:845: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_prog_rejected=no + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + break + fi + done + IFS="$ac_save_ifs" +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# -gt 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + set dummy "$ac_dir/$ac_word" "$@" + shift + ac_cv_prog_CC="$@" + fi +fi +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + if test -z "$CC"; then + case "`uname -s`" in + *win32* | *WIN32*) + # Extract the first word of "cl", so it can be a program name with args. +set dummy cl; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:895: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CC="cl" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + ;; + esac + fi + test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; } +fi + +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6 +echo "configure:926: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +cat > conftest.$ac_ext <<EOF +#line 936 "configure" +#include "confdefs.h" +main(){return(0);} +EOF +if { (eval echo configure:940: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + ac_cv_prog_cc_works=yes + # If we can't run a trivial program, we are probably using a cross compiler. + if (./conftest; exit) 2>/dev/null; then + ac_cv_prog_cc_cross=no + else + ac_cv_prog_cc_cross=yes + fi +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + ac_cv_prog_cc_works=no +fi +rm -fr conftest* + +echo "$ac_t""$ac_cv_prog_cc_works" 1>&6 +if test $ac_cv_prog_cc_works = no; then + { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; } +fi +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6 +echo "configure:960: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 +echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6 +cross_compiling=$ac_cv_prog_cc_cross + +echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6 +echo "configure:965: checking whether we are using GNU C" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.c <<EOF +#ifdef __GNUC__ + yes; +#endif +EOF +if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:974: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then + ac_cv_prog_gcc=yes +else + ac_cv_prog_gcc=no +fi +fi + +echo "$ac_t""$ac_cv_prog_gcc" 1>&6 + +if test $ac_cv_prog_gcc = yes; then + GCC=yes +else + GCC= +fi + +ac_test_CFLAGS="${CFLAGS+set}" +ac_save_CFLAGS="$CFLAGS" +CFLAGS= +echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6 +echo "configure:993: checking whether ${CC-cc} accepts -g" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + echo 'void f(){}' > conftest.c +if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then + ac_cv_prog_cc_g=yes +else + ac_cv_prog_cc_g=no +fi +rm -f conftest* + +fi + +echo "$ac_t""$ac_cv_prog_cc_g" 1>&6 +if test "$ac_test_CFLAGS" = set; then + CFLAGS="$ac_save_CFLAGS" +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi + + +# Put a plausible default for CC_FOR_BUILD in Makefile. +if test "x$cross_compiling" = "xno"; then + CC_FOR_BUILD='$(CC)' +else + CC_FOR_BUILD=gcc +fi + +ALL_LINGUAS= +echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6 +echo "configure:1034: checking how to run the C preprocessor" >&5 +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then +if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + # This must be in double quotes, not single quotes, because CPP may get + # substituted into the Makefile and "${CC-cc}" will confuse make. + CPP="${CC-cc} -E" + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. + cat > conftest.$ac_ext <<EOF +#line 1049 "configure" +#include "confdefs.h" +#include <assert.h> +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1055: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP="${CC-cc} -E -traditional-cpp" + cat > conftest.$ac_ext <<EOF +#line 1066 "configure" +#include "confdefs.h" +#include <assert.h> +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1072: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP="${CC-cc} -nologo -E" + cat > conftest.$ac_ext <<EOF +#line 1083 "configure" +#include "confdefs.h" +#include <assert.h> +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1089: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP=/lib/cpp +fi +rm -f conftest* +fi +rm -f conftest* +fi +rm -f conftest* + ac_cv_prog_CPP="$CPP" +fi + CPP="$ac_cv_prog_CPP" +else + ac_cv_prog_CPP="$CPP" +fi +echo "$ac_t""$CPP" 1>&6 + +echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6 +echo "configure:1114: checking whether ${MAKE-make} sets \${MAKE}" >&5 +set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftestmake <<\EOF +all: + @echo 'ac_maketemp="${MAKE}"' +EOF +# GNU make sometimes prints "make[1]: Entering...", which would confuse us. +eval `${MAKE-make} -f conftestmake 2>/dev/null | grep temp=` +if test -n "$ac_maketemp"; then + eval ac_cv_prog_make_${ac_make}_set=yes +else + eval ac_cv_prog_make_${ac_make}_set=no +fi +rm -f conftestmake +fi +if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then + echo "$ac_t""yes" 1>&6 + SET_MAKE= +else + echo "$ac_t""no" 1>&6 + SET_MAKE="MAKE=${MAKE-make}" +fi + +# Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:1143: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_RANLIB="ranlib" + break + fi + done + IFS="$ac_save_ifs" + test -z "$ac_cv_prog_RANLIB" && ac_cv_prog_RANLIB=":" +fi +fi +RANLIB="$ac_cv_prog_RANLIB" +if test -n "$RANLIB"; then + echo "$ac_t""$RANLIB" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +echo $ac_n "checking for POSIXized ISC""... $ac_c" 1>&6 +echo "configure:1170: checking for POSIXized ISC" >&5 +if test -d /etc/conf/kconfig.d && + grep _POSIX_VERSION /usr/include/sys/unistd.h >/dev/null 2>&1 +then + echo "$ac_t""yes" 1>&6 + ISC=yes # If later tests want to check for ISC. + cat >> confdefs.h <<\EOF +#define _POSIX_SOURCE 1 +EOF + + if test "$GCC" = yes; then + CC="$CC -posix" + else + CC="$CC -Xp" + fi +else + echo "$ac_t""no" 1>&6 + ISC= +fi + +echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6 +echo "configure:1191: checking for ANSI C header files" >&5 +if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1196 "configure" +#include "confdefs.h" +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <float.h> +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1204: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + ac_cv_header_stdc=yes +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. +cat > conftest.$ac_ext <<EOF +#line 1221 "configure" +#include "confdefs.h" +#include <string.h> +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "memchr" >/dev/null 2>&1; then + : +else + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. +cat > conftest.$ac_ext <<EOF +#line 1239 "configure" +#include "confdefs.h" +#include <stdlib.h> +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "free" >/dev/null 2>&1; then + : +else + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. +if test "$cross_compiling" = yes; then + : +else + cat > conftest.$ac_ext <<EOF +#line 1260 "configure" +#include "confdefs.h" +#include <ctype.h> +#define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int main () { int i; for (i = 0; i < 256; i++) +if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2); +exit (0); } + +EOF +if { (eval echo configure:1271: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +then + : +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_header_stdc=no +fi +rm -fr conftest* +fi + +fi +fi + +echo "$ac_t""$ac_cv_header_stdc" 1>&6 +if test $ac_cv_header_stdc = yes; then + cat >> confdefs.h <<\EOF +#define STDC_HEADERS 1 +EOF + +fi + +echo $ac_n "checking for working const""... $ac_c" 1>&6 +echo "configure:1295: checking for working const" >&5 +if eval "test \"`echo '$''{'ac_cv_c_const'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1300 "configure" +#include "confdefs.h" + +int main() { + +/* Ultrix mips cc rejects this. */ +typedef int charset[2]; const charset x; +/* SunOS 4.1.1 cc rejects this. */ +char const *const *ccp; +char **p; +/* NEC SVR4.0.2 mips cc rejects this. */ +struct point {int x, y;}; +static struct point const zero = {0,0}; +/* AIX XL C 1.02.0.0 rejects this. + It does not let you subtract one const X* pointer from another in an arm + of an if-expression whose if-part is not a constant expression */ +const char *g = "string"; +ccp = &g + (g ? g-g : 0); +/* HPUX 7.0 cc rejects these. */ +++ccp; +p = (char**) ccp; +ccp = (char const *const *) p; +{ /* SCO 3.2v4 cc rejects this. */ + char *t; + char const *s = 0 ? (char *) 0 : (char const *) 0; + + *t++ = 0; +} +{ /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ + int x[] = {25, 17}; + const int *foo = &x[0]; + ++foo; +} +{ /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ + typedef const int *iptr; + iptr p = 0; + ++p; +} +{ /* AIX XL C 1.02.0.0 rejects this saying + "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ + struct s { int j; const int *ap[3]; }; + struct s *b; b->j = 5; +} +{ /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; +} + +; return 0; } +EOF +if { (eval echo configure:1349: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_c_const=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_c_const=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_c_const" 1>&6 +if test $ac_cv_c_const = no; then + cat >> confdefs.h <<\EOF +#define const +EOF + +fi + +echo $ac_n "checking for inline""... $ac_c" 1>&6 +echo "configure:1370: checking for inline" >&5 +if eval "test \"`echo '$''{'ac_cv_c_inline'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_cv_c_inline=no +for ac_kw in inline __inline__ __inline; do + cat > conftest.$ac_ext <<EOF +#line 1377 "configure" +#include "confdefs.h" + +int main() { +} $ac_kw foo() { +; return 0; } +EOF +if { (eval echo configure:1384: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_c_inline=$ac_kw; break +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 +fi +rm -f conftest* +done + +fi + +echo "$ac_t""$ac_cv_c_inline" 1>&6 +case "$ac_cv_c_inline" in + inline | yes) ;; + no) cat >> confdefs.h <<\EOF +#define inline +EOF + ;; + *) cat >> confdefs.h <<EOF +#define inline $ac_cv_c_inline +EOF + ;; +esac + +echo $ac_n "checking for off_t""... $ac_c" 1>&6 +echo "configure:1410: checking for off_t" >&5 +if eval "test \"`echo '$''{'ac_cv_type_off_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1415 "configure" +#include "confdefs.h" +#include <sys/types.h> +#if STDC_HEADERS +#include <stdlib.h> +#include <stddef.h> +#endif +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "off_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_off_t=yes +else + rm -rf conftest* + ac_cv_type_off_t=no +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_type_off_t" 1>&6 +if test $ac_cv_type_off_t = no; then + cat >> confdefs.h <<\EOF +#define off_t long +EOF + +fi + +echo $ac_n "checking for size_t""... $ac_c" 1>&6 +echo "configure:1443: checking for size_t" >&5 +if eval "test \"`echo '$''{'ac_cv_type_size_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1448 "configure" +#include "confdefs.h" +#include <sys/types.h> +#if STDC_HEADERS +#include <stdlib.h> +#include <stddef.h> +#endif +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "size_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_size_t=yes +else + rm -rf conftest* + ac_cv_type_size_t=no +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_type_size_t" 1>&6 +if test $ac_cv_type_size_t = no; then + cat >> confdefs.h <<\EOF +#define size_t unsigned +EOF + +fi + +# The Ultrix 4.2 mips builtin alloca declared by alloca.h only works +# for constant arguments. Useless! +echo $ac_n "checking for working alloca.h""... $ac_c" 1>&6 +echo "configure:1478: checking for working alloca.h" >&5 +if eval "test \"`echo '$''{'ac_cv_header_alloca_h'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1483 "configure" +#include "confdefs.h" +#include <alloca.h> +int main() { +char *p = alloca(2 * sizeof(int)); +; return 0; } +EOF +if { (eval echo configure:1490: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + ac_cv_header_alloca_h=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_header_alloca_h=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_header_alloca_h" 1>&6 +if test $ac_cv_header_alloca_h = yes; then + cat >> confdefs.h <<\EOF +#define HAVE_ALLOCA_H 1 +EOF + +fi + +echo $ac_n "checking for alloca""... $ac_c" 1>&6 +echo "configure:1511: checking for alloca" >&5 +if eval "test \"`echo '$''{'ac_cv_func_alloca_works'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1516 "configure" +#include "confdefs.h" + +#ifdef __GNUC__ +# define alloca __builtin_alloca +#else +# ifdef _MSC_VER +# include <malloc.h> +# define alloca _alloca +# else +# if HAVE_ALLOCA_H +# include <alloca.h> +# else +# ifdef _AIX + #pragma alloca +# else +# ifndef alloca /* predefined by HP cc +Olibcalls */ +char *alloca (); +# endif +# endif +# endif +# endif +#endif + +int main() { +char *p = (char *) alloca(1); +; return 0; } +EOF +if { (eval echo configure:1544: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + ac_cv_func_alloca_works=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_func_alloca_works=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_func_alloca_works" 1>&6 +if test $ac_cv_func_alloca_works = yes; then + cat >> confdefs.h <<\EOF +#define HAVE_ALLOCA 1 +EOF + +fi + +if test $ac_cv_func_alloca_works = no; then + # The SVR3 libPW and SVR4 libucb both contain incompatible functions + # that cause trouble. Some versions do not even contain alloca or + # contain a buggy version. If you still want to use their alloca, + # use ar to extract alloca.o from them instead of compiling alloca.c. + ALLOCA=alloca.${ac_objext} + cat >> confdefs.h <<\EOF +#define C_ALLOCA 1 +EOF + + +echo $ac_n "checking whether alloca needs Cray hooks""... $ac_c" 1>&6 +echo "configure:1576: checking whether alloca needs Cray hooks" >&5 +if eval "test \"`echo '$''{'ac_cv_os_cray'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1581 "configure" +#include "confdefs.h" +#if defined(CRAY) && ! defined(CRAY2) +webecray +#else +wenotbecray +#endif + +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "webecray" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_os_cray=yes +else + rm -rf conftest* + ac_cv_os_cray=no +fi +rm -f conftest* + +fi + +echo "$ac_t""$ac_cv_os_cray" 1>&6 +if test $ac_cv_os_cray = yes; then +for ac_func in _getb67 GETB67 getb67; do + echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:1606: checking for $ac_func" >&5 +if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1611 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func(); below. */ +#include <assert.h> +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +$ac_func(); +#endif + +; return 0; } +EOF +if { (eval echo configure:1634: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_$ac_func=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_$ac_func=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then + echo "$ac_t""yes" 1>&6 + cat >> confdefs.h <<EOF +#define CRAY_STACKSEG_END $ac_func +EOF + + break +else + echo "$ac_t""no" 1>&6 +fi + +done +fi + +echo $ac_n "checking stack direction for C alloca""... $ac_c" 1>&6 +echo "configure:1661: checking stack direction for C alloca" >&5 +if eval "test \"`echo '$''{'ac_cv_c_stack_direction'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test "$cross_compiling" = yes; then + ac_cv_c_stack_direction=0 +else + cat > conftest.$ac_ext <<EOF +#line 1669 "configure" +#include "confdefs.h" +find_stack_direction () +{ + static char *addr = 0; + auto char dummy; + if (addr == 0) + { + addr = &dummy; + return find_stack_direction (); + } + else + return (&dummy > addr) ? 1 : -1; +} +main () +{ + exit (find_stack_direction() < 0); +} +EOF +if { (eval echo configure:1688: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +then + ac_cv_c_stack_direction=1 +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_c_stack_direction=-1 +fi +rm -fr conftest* +fi + +fi + +echo "$ac_t""$ac_cv_c_stack_direction" 1>&6 +cat >> confdefs.h <<EOF +#define STACK_DIRECTION $ac_cv_c_stack_direction +EOF + +fi + +for ac_hdr in unistd.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:1713: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1718 "configure" +#include "confdefs.h" +#include <$ac_hdr> +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1723: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <<EOF +#define $ac_tr_hdr 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi +done + +for ac_func in getpagesize +do +echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:1752: checking for $ac_func" >&5 +if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1757 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func(); below. */ +#include <assert.h> +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +$ac_func(); +#endif + +; return 0; } +EOF +if { (eval echo configure:1780: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_$ac_func=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_$ac_func=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` + cat >> confdefs.h <<EOF +#define $ac_tr_func 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi +done + +echo $ac_n "checking for working mmap""... $ac_c" 1>&6 +echo "configure:1805: checking for working mmap" >&5 +if eval "test \"`echo '$''{'ac_cv_func_mmap_fixed_mapped'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_mmap_fixed_mapped=no +else + cat > conftest.$ac_ext <<EOF +#line 1813 "configure" +#include "confdefs.h" + +/* Thanks to Mike Haertel and Jim Avera for this test. + Here is a matrix of mmap possibilities: + mmap private not fixed + mmap private fixed at somewhere currently unmapped + mmap private fixed at somewhere already mapped + mmap shared not fixed + mmap shared fixed at somewhere currently unmapped + mmap shared fixed at somewhere already mapped + For private mappings, we should verify that changes cannot be read() + back from the file, nor mmap's back from the file at a different + address. (There have been systems where private was not correctly + implemented like the infamous i386 svr4.0, and systems where the + VM page cache was not coherent with the filesystem buffer cache + like early versions of FreeBSD and possibly contemporary NetBSD.) + For shared mappings, we should conversely verify that changes get + propogated back to all the places they're supposed to be. + + Grep wants private fixed already mapped. + The main things grep needs to know about mmap are: + * does it exist and is it safe to write into the mmap'd area + * how to use it (BSD variants) */ +#include <sys/types.h> +#include <fcntl.h> +#include <sys/mman.h> + +/* This mess was copied from the GNU getpagesize.h. */ +#ifndef HAVE_GETPAGESIZE +# ifdef HAVE_UNISTD_H +# include <unistd.h> +# endif + +/* Assume that all systems that can run configure have sys/param.h. */ +# ifndef HAVE_SYS_PARAM_H +# define HAVE_SYS_PARAM_H 1 +# endif + +# ifdef _SC_PAGESIZE +# define getpagesize() sysconf(_SC_PAGESIZE) +# else /* no _SC_PAGESIZE */ +# ifdef HAVE_SYS_PARAM_H +# include <sys/param.h> +# ifdef EXEC_PAGESIZE +# define getpagesize() EXEC_PAGESIZE +# else /* no EXEC_PAGESIZE */ +# ifdef NBPG +# define getpagesize() NBPG * CLSIZE +# ifndef CLSIZE +# define CLSIZE 1 +# endif /* no CLSIZE */ +# else /* no NBPG */ +# ifdef NBPC +# define getpagesize() NBPC +# else /* no NBPC */ +# ifdef PAGESIZE +# define getpagesize() PAGESIZE +# endif /* PAGESIZE */ +# endif /* no NBPC */ +# endif /* no NBPG */ +# endif /* no EXEC_PAGESIZE */ +# else /* no HAVE_SYS_PARAM_H */ +# define getpagesize() 8192 /* punt totally */ +# endif /* no HAVE_SYS_PARAM_H */ +# endif /* no _SC_PAGESIZE */ + +#endif /* no HAVE_GETPAGESIZE */ + +#ifdef __cplusplus +extern "C" { void *malloc(unsigned); } +#else +char *malloc(); +#endif + +int +main() +{ + char *data, *data2, *data3; + int i, pagesize; + int fd; + + pagesize = getpagesize(); + + /* + * First, make a file with some known garbage in it. + */ + data = malloc(pagesize); + if (!data) + exit(1); + for (i = 0; i < pagesize; ++i) + *(data + i) = rand(); + umask(0); + fd = creat("conftestmmap", 0600); + if (fd < 0) + exit(1); + if (write(fd, data, pagesize) != pagesize) + exit(1); + close(fd); + + /* + * Next, try to mmap the file at a fixed address which + * already has something else allocated at it. If we can, + * also make sure that we see the same garbage. + */ + fd = open("conftestmmap", O_RDWR); + if (fd < 0) + exit(1); + data2 = malloc(2 * pagesize); + if (!data2) + exit(1); + data2 += (pagesize - ((int) data2 & (pagesize - 1))) & (pagesize - 1); + if (data2 != mmap(data2, pagesize, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_FIXED, fd, 0L)) + exit(1); + for (i = 0; i < pagesize; ++i) + if (*(data + i) != *(data2 + i)) + exit(1); + + /* + * Finally, make sure that changes to the mapped area + * do not percolate back to the file as seen by read(). + * (This is a bug on some variants of i386 svr4.0.) + */ + for (i = 0; i < pagesize; ++i) + *(data2 + i) = *(data2 + i) + 1; + data3 = malloc(pagesize); + if (!data3) + exit(1); + if (read(fd, data3, pagesize) != pagesize) + exit(1); + for (i = 0; i < pagesize; ++i) + if (*(data + i) != *(data3 + i)) + exit(1); + close(fd); + unlink("conftestmmap"); + exit(0); +} + +EOF +if { (eval echo configure:1953: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +then + ac_cv_func_mmap_fixed_mapped=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_func_mmap_fixed_mapped=no +fi +rm -fr conftest* +fi + +fi + +echo "$ac_t""$ac_cv_func_mmap_fixed_mapped" 1>&6 +if test $ac_cv_func_mmap_fixed_mapped = yes; then + cat >> confdefs.h <<\EOF +#define HAVE_MMAP 1 +EOF + +fi + + + for ac_hdr in argz.h limits.h locale.h nl_types.h malloc.h string.h \ +unistd.h values.h sys/param.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:1981: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1986 "configure" +#include "confdefs.h" +#include <$ac_hdr> +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1991: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <<EOF +#define $ac_tr_hdr 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi +done + + for ac_func in getcwd munmap putenv setenv setlocale strchr strcasecmp \ +__argz_count __argz_stringify __argz_next +do +echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:2021: checking for $ac_func" >&5 +if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 2026 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func(); below. */ +#include <assert.h> +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +$ac_func(); +#endif + +; return 0; } +EOF +if { (eval echo configure:2049: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_$ac_func=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_$ac_func=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` + cat >> confdefs.h <<EOF +#define $ac_tr_func 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi +done + + + if test "${ac_cv_func_stpcpy+set}" != "set"; then + for ac_func in stpcpy +do +echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:2078: checking for $ac_func" >&5 +if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 2083 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func(); below. */ +#include <assert.h> +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +$ac_func(); +#endif + +; return 0; } +EOF +if { (eval echo configure:2106: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_$ac_func=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_$ac_func=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` + cat >> confdefs.h <<EOF +#define $ac_tr_func 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi +done + + fi + if test "${ac_cv_func_stpcpy}" = "yes"; then + cat >> confdefs.h <<\EOF +#define HAVE_STPCPY 1 +EOF + + fi + + if test $ac_cv_header_locale_h = yes; then + echo $ac_n "checking for LC_MESSAGES""... $ac_c" 1>&6 +echo "configure:2140: checking for LC_MESSAGES" >&5 +if eval "test \"`echo '$''{'am_cv_val_LC_MESSAGES'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 2145 "configure" +#include "confdefs.h" +#include <locale.h> +int main() { +return LC_MESSAGES +; return 0; } +EOF +if { (eval echo configure:2152: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + am_cv_val_LC_MESSAGES=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + am_cv_val_LC_MESSAGES=no +fi +rm -f conftest* +fi + +echo "$ac_t""$am_cv_val_LC_MESSAGES" 1>&6 + if test $am_cv_val_LC_MESSAGES = yes; then + cat >> confdefs.h <<\EOF +#define HAVE_LC_MESSAGES 1 +EOF + + fi + fi + echo $ac_n "checking whether NLS is requested""... $ac_c" 1>&6 +echo "configure:2173: checking whether NLS is requested" >&5 + # Check whether --enable-nls or --disable-nls was given. +if test "${enable_nls+set}" = set; then + enableval="$enable_nls" + USE_NLS=$enableval +else + USE_NLS=yes +fi + + echo "$ac_t""$USE_NLS" 1>&6 + + + USE_INCLUDED_LIBINTL=no + + if test "$USE_NLS" = "yes"; then + cat >> confdefs.h <<\EOF +#define ENABLE_NLS 1 +EOF + + echo $ac_n "checking whether included gettext is requested""... $ac_c" 1>&6 +echo "configure:2193: checking whether included gettext is requested" >&5 + # Check whether --with-included-gettext or --without-included-gettext was given. +if test "${with_included_gettext+set}" = set; then + withval="$with_included_gettext" + nls_cv_force_use_gnu_gettext=$withval +else + nls_cv_force_use_gnu_gettext=no +fi + + echo "$ac_t""$nls_cv_force_use_gnu_gettext" 1>&6 + + nls_cv_use_gnu_gettext="$nls_cv_force_use_gnu_gettext" + if test "$nls_cv_force_use_gnu_gettext" != "yes"; then + nls_cv_header_intl= + nls_cv_header_libgt= + CATOBJEXT=NONE + + ac_safe=`echo "libintl.h" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for libintl.h""... $ac_c" 1>&6 +echo "configure:2212: checking for libintl.h" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 2217 "configure" +#include "confdefs.h" +#include <libintl.h> +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:2222: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + echo $ac_n "checking for gettext in libc""... $ac_c" 1>&6 +echo "configure:2239: checking for gettext in libc" >&5 +if eval "test \"`echo '$''{'gt_cv_func_gettext_libc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 2244 "configure" +#include "confdefs.h" +#include <libintl.h> +int main() { +return (int) gettext ("") +; return 0; } +EOF +if { (eval echo configure:2251: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + gt_cv_func_gettext_libc=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + gt_cv_func_gettext_libc=no +fi +rm -f conftest* +fi + +echo "$ac_t""$gt_cv_func_gettext_libc" 1>&6 + + if test "$gt_cv_func_gettext_libc" != "yes"; then + echo $ac_n "checking for bindtextdomain in -lintl""... $ac_c" 1>&6 +echo "configure:2267: checking for bindtextdomain in -lintl" >&5 +ac_lib_var=`echo intl'_'bindtextdomain | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lintl $LIBS" +cat > conftest.$ac_ext <<EOF +#line 2275 "configure" +#include "confdefs.h" +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char bindtextdomain(); + +int main() { +bindtextdomain() +; return 0; } +EOF +if { (eval echo configure:2286: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + echo $ac_n "checking for gettext in libintl""... $ac_c" 1>&6 +echo "configure:2302: checking for gettext in libintl" >&5 +if eval "test \"`echo '$''{'gt_cv_func_gettext_libintl'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 2307 "configure" +#include "confdefs.h" + +int main() { +return (int) gettext ("") +; return 0; } +EOF +if { (eval echo configure:2314: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + gt_cv_func_gettext_libintl=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + gt_cv_func_gettext_libintl=no +fi +rm -f conftest* +fi + +echo "$ac_t""$gt_cv_func_gettext_libintl" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + fi + + if test "$gt_cv_func_gettext_libc" = "yes" \ + || test "$gt_cv_func_gettext_libintl" = "yes"; then + cat >> confdefs.h <<\EOF +#define HAVE_GETTEXT 1 +EOF + + # Extract the first word of "msgfmt", so it can be a program name with args. +set dummy msgfmt; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:2342: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_path_MSGFMT'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + case "$MSGFMT" in + /*) + ac_cv_path_MSGFMT="$MSGFMT" # Let the user override the test with a path. + ;; + *) + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + if test -z "`$ac_dir/$ac_word -h 2>&1 | grep 'dv '`"; then + ac_cv_path_MSGFMT="$ac_dir/$ac_word" + break + fi + fi + done + IFS="$ac_save_ifs" + test -z "$ac_cv_path_MSGFMT" && ac_cv_path_MSGFMT="no" + ;; +esac +fi +MSGFMT="$ac_cv_path_MSGFMT" +if test -n "$MSGFMT"; then + echo "$ac_t""$MSGFMT" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + if test "$MSGFMT" != "no"; then + for ac_func in dcgettext +do +echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:2376: checking for $ac_func" >&5 +if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 2381 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func(); below. */ +#include <assert.h> +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +$ac_func(); +#endif + +; return 0; } +EOF +if { (eval echo configure:2404: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_$ac_func=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_$ac_func=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` + cat >> confdefs.h <<EOF +#define $ac_tr_func 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi +done + + # Extract the first word of "gmsgfmt", so it can be a program name with args. +set dummy gmsgfmt; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:2431: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_path_GMSGFMT'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + case "$GMSGFMT" in + /*) + ac_cv_path_GMSGFMT="$GMSGFMT" # Let the user override the test with a path. + ;; + ?:/*) + ac_cv_path_GMSGFMT="$GMSGFMT" # Let the user override the test with a dos path. + ;; + *) + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_path_GMSGFMT="$ac_dir/$ac_word" + break + fi + done + IFS="$ac_save_ifs" + test -z "$ac_cv_path_GMSGFMT" && ac_cv_path_GMSGFMT="$MSGFMT" + ;; +esac +fi +GMSGFMT="$ac_cv_path_GMSGFMT" +if test -n "$GMSGFMT"; then + echo "$ac_t""$GMSGFMT" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + # Extract the first word of "xgettext", so it can be a program name with args. +set dummy xgettext; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:2466: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_path_XGETTEXT'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + case "$XGETTEXT" in + /*) + ac_cv_path_XGETTEXT="$XGETTEXT" # Let the user override the test with a path. + ;; + *) + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + if test -z "`$ac_dir/$ac_word -h 2>&1 | grep '(HELP)'`"; then + ac_cv_path_XGETTEXT="$ac_dir/$ac_word" + break + fi + fi + done + IFS="$ac_save_ifs" + test -z "$ac_cv_path_XGETTEXT" && ac_cv_path_XGETTEXT=":" + ;; +esac +fi +XGETTEXT="$ac_cv_path_XGETTEXT" +if test -n "$XGETTEXT"; then + echo "$ac_t""$XGETTEXT" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + cat > conftest.$ac_ext <<EOF +#line 2498 "configure" +#include "confdefs.h" + +int main() { +extern int _nl_msg_cat_cntr; + return _nl_msg_cat_cntr +; return 0; } +EOF +if { (eval echo configure:2506: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + CATOBJEXT=.gmo + DATADIRNAME=share +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CATOBJEXT=.mo + DATADIRNAME=lib +fi +rm -f conftest* + INSTOBJEXT=.mo + fi + fi + +else + echo "$ac_t""no" 1>&6 +fi + + + + if test "$CATOBJEXT" = "NONE"; then + nls_cv_use_gnu_gettext=yes + fi + fi + + if test "$nls_cv_use_gnu_gettext" = "yes"; then + INTLOBJS="\$(GETTOBJS)" + # Extract the first word of "msgfmt", so it can be a program name with args. +set dummy msgfmt; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:2538: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_path_MSGFMT'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + case "$MSGFMT" in + /*) + ac_cv_path_MSGFMT="$MSGFMT" # Let the user override the test with a path. + ;; + *) + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + if test -z "`$ac_dir/$ac_word -h 2>&1 | grep 'dv '`"; then + ac_cv_path_MSGFMT="$ac_dir/$ac_word" + break + fi + fi + done + IFS="$ac_save_ifs" + test -z "$ac_cv_path_MSGFMT" && ac_cv_path_MSGFMT="msgfmt" + ;; +esac +fi +MSGFMT="$ac_cv_path_MSGFMT" +if test -n "$MSGFMT"; then + echo "$ac_t""$MSGFMT" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + # Extract the first word of "gmsgfmt", so it can be a program name with args. +set dummy gmsgfmt; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:2572: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_path_GMSGFMT'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + case "$GMSGFMT" in + /*) + ac_cv_path_GMSGFMT="$GMSGFMT" # Let the user override the test with a path. + ;; + ?:/*) + ac_cv_path_GMSGFMT="$GMSGFMT" # Let the user override the test with a dos path. + ;; + *) + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_path_GMSGFMT="$ac_dir/$ac_word" + break + fi + done + IFS="$ac_save_ifs" + test -z "$ac_cv_path_GMSGFMT" && ac_cv_path_GMSGFMT="$MSGFMT" + ;; +esac +fi +GMSGFMT="$ac_cv_path_GMSGFMT" +if test -n "$GMSGFMT"; then + echo "$ac_t""$GMSGFMT" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + # Extract the first word of "xgettext", so it can be a program name with args. +set dummy xgettext; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:2607: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_path_XGETTEXT'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + case "$XGETTEXT" in + /*) + ac_cv_path_XGETTEXT="$XGETTEXT" # Let the user override the test with a path. + ;; + *) + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + if test -z "`$ac_dir/$ac_word -h 2>&1 | grep '(HELP)'`"; then + ac_cv_path_XGETTEXT="$ac_dir/$ac_word" + break + fi + fi + done + IFS="$ac_save_ifs" + test -z "$ac_cv_path_XGETTEXT" && ac_cv_path_XGETTEXT=":" + ;; +esac +fi +XGETTEXT="$ac_cv_path_XGETTEXT" +if test -n "$XGETTEXT"; then + echo "$ac_t""$XGETTEXT" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + + USE_INCLUDED_LIBINTL=yes + CATOBJEXT=.gmo + INSTOBJEXT=.mo + DATADIRNAME=share + INTLDEPS='$(top_builddir)/../intl/libintl.a' + INTLLIBS=$INTLDEPS + LIBS=`echo $LIBS | sed -e 's/-lintl//'` + nls_cv_header_intl=libintl.h + nls_cv_header_libgt=libgettext.h + fi + + if test "$XGETTEXT" != ":"; then + if $XGETTEXT --omit-header /dev/null 2> /dev/null; then + : ; + else + echo "$ac_t""found xgettext programs is not GNU xgettext; ignore it" 1>&6 + XGETTEXT=":" + fi + fi + + # We need to process the po/ directory. + POSUB=po + else + DATADIRNAME=share + nls_cv_header_intl=libintl.h + nls_cv_header_libgt=libgettext.h + fi + + # If this is used in GNU gettext we have to set USE_NLS to `yes' + # because some of the sources are only built for this goal. + if test "$PACKAGE" = gettext; then + USE_NLS=yes + USE_INCLUDED_LIBINTL=yes + fi + + for lang in $ALL_LINGUAS; do + GMOFILES="$GMOFILES $lang.gmo" + POFILES="$POFILES $lang.po" + done + + + + + + + + + + + + + + + if test "x$CATOBJEXT" != "x"; then + if test "x$ALL_LINGUAS" = "x"; then + LINGUAS= + else + echo $ac_n "checking for catalogs to be installed""... $ac_c" 1>&6 +echo "configure:2697: checking for catalogs to be installed" >&5 + NEW_LINGUAS= + for lang in ${LINGUAS=$ALL_LINGUAS}; do + case "$ALL_LINGUAS" in + *$lang*) NEW_LINGUAS="$NEW_LINGUAS $lang" ;; + esac + done + LINGUAS=$NEW_LINGUAS + echo "$ac_t""$LINGUAS" 1>&6 + fi + + if test -n "$LINGUAS"; then + for lang in $LINGUAS; do CATALOGS="$CATALOGS $lang$CATOBJEXT"; done + fi + fi + + if test $ac_cv_header_locale_h = yes; then + INCLUDE_LOCALE_H="#include <locale.h>" + else + INCLUDE_LOCALE_H="\ +/* The system does not provide the header <locale.h>. Take care yourself. */" + fi + + + if test -f $srcdir/po2tbl.sed.in; then + if test "$CATOBJEXT" = ".cat"; then + ac_safe=`echo "linux/version.h" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for linux/version.h""... $ac_c" 1>&6 +echo "configure:2725: checking for linux/version.h" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 2730 "configure" +#include "confdefs.h" +#include <linux/version.h> +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:2735: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + msgformat=linux +else + echo "$ac_t""no" 1>&6 +msgformat=xopen +fi + + + sed -e '/^#/d' $srcdir/$msgformat-msg.sed > po2msg.sed + fi + sed -e '/^#.*[^\\]$/d' -e '/^#$/d' \ + $srcdir/po2tbl.sed.in > po2tbl.sed + fi + + if test "$PACKAGE" = "gettext"; then + GT_NO="#NO#" + GT_YES= + else + GT_NO= + GT_YES="#YES#" + fi + + + + MKINSTALLDIRS="\$(srcdir)/../../mkinstalldirs" + + + l= + + + if test -d $srcdir/po; then + test -d po || mkdir po + if test "x$srcdir" != "x."; then + if test "x`echo $srcdir | sed 's@/.*@@'`" = "x"; then + posrcprefix="$srcdir/" + else + posrcprefix="../$srcdir/" + fi + else + posrcprefix="../" + fi + rm -f po/POTFILES + sed -e "/^#/d" -e "/^\$/d" -e "s,.*, $posrcprefix& \\\\," -e "\$s/\(.*\) \\\\/\1/" \ + < $srcdir/po/POTFILES.in > po/POTFILES + fi + + + +# Check whether --enable-sim-alignment or --disable-sim-alignment was given. +if test "${enable_sim_alignment+set}" = set; then + enableval="$enable_sim_alignment" + case "${enableval}" in + yes | strict | STRICT) sim_alignment="-DWITH_ALIGNMENT=STRICT_ALIGNMENT";; + no | nonstrict | NONSTRICT) sim_alignment="-DWITH_ALIGNMENT=NONSTRICT_ALIGNMENT";; + 0 | default | DEFAULT) sim_alignment="-DWITH_ALIGNMENT=0";; + *) { echo "configure: error: "Unknown value $enableval passed to --enable-sim-alignment"" 1>&2; exit 1; }; sim_alignment="";; +esac +if test x"$silent" != x"yes" && test x"$sim_alignment" != x""; then + echo "Setting alignment flags = $sim_alignment" 6>&1 +fi +else + sim_alignment="" +fi + + +# Check whether --enable-sim-assert or --disable-sim-assert was given. +if test "${enable_sim_assert+set}" = set; then + enableval="$enable_sim_assert" + case "${enableval}" in + yes) sim_assert="-DWITH_ASSERT=1";; + no) sim_assert="-DWITH_ASSERT=0";; + *) { echo "configure: error: "--enable-sim-assert does not take a value"" 1>&2; exit 1; }; sim_assert="";; +esac +if test x"$silent" != x"yes" && test x"$sim_assert" != x""; then + echo "Setting assert flags = $sim_assert" 6>&1 +fi +else + sim_assert="" +fi + + +# Check whether --enable-sim-bitsize or --disable-sim-bitsize was given. +if test "${enable_sim_bitsize+set}" = set; then + enableval="$enable_sim_bitsize" + case "${enableval}" in + 32|64) sim_bitsize="-DWITH_TARGET_WORD_BITSIZE=$enableval";; + *) { echo "configure: error: "--enable-sim-bitsize was given $enableval. Expected 32 or 64"" 1>&2; exit 1; }; sim_bitsize="";; +esac +if test x"$silent" != x"yes" && test x"$sim_bitsize" != x""; then + echo "Setting bitsize flags = $sim_bitsize" 6>&1 +fi +else + sim_bitsize="" +fi + + +# Check whether --enable-sim-bswap or --disable-sim-bswap was given. +if test "${enable_sim_bswap+set}" = set; then + enableval="$enable_sim_bswap" + case "${enableval}" in + yes) sim_bswap="-DWITH_BSWAP=1";; + no) sim_bswap="-DWITH_BSWAP=0";; + *) { echo "configure: error: "--enable-sim-bswap does not take a value"" 1>&2; exit 1; }; sim_bswap="";; +esac +if test x"$silent" != x"yes" && test x"$sim_bswap" != x""; then + echo "Setting bswap flags = $sim_bswap" 6>&1 +fi +else + sim_bswap="" +fi + + +# Check whether --enable-sim-cflags or --disable-sim-cflags was given. +if test "${enable_sim_cflags+set}" = set; then + enableval="$enable_sim_cflags" + case "${enableval}" in + yes) sim_cflags="-O2 -fomit-frame-pointer";; + no) sim_cflags="";; + *) sim_cflags=`echo "${enableval}" | sed -e "s/,/ /g"`;; +esac +if test x"$silent" != x"yes" && test x"$sim_cflags" != x""; then + echo "Setting sim cflags = $sim_cflags" 6>&1 +fi +else + sim_cflags="" +fi + + +# Check whether --enable-sim-config or --disable-sim-config was given. +if test "${enable_sim_config+set}" = set; then + enableval="$enable_sim_config" + case "${enableval}" in + yes|no) { echo "configure: error: "No value supplied for --enable-sim-config=file"" 1>&2; exit 1; };; + *) if test -f "${srcdir}/${enableval}"; then + sim_config="${enableval}"; + elif test -f "${srcdir}/${enableval}-config.h"; then + sim_config="${enableval}-config.h" + else + { echo "configure: error: "Config file $enableval was not found"" 1>&2; exit 1; }; + sim_config=std-config.h + fi;; +esac +if test x"$silent" != x"yes" && test x"$sim_config" != x""; then + echo "Setting config flags = $sim_config" 6>&1 +fi +else + sim_config="std-config.h" +if test x"$silent" != x"yes"; then + echo "Setting config flags = $sim_config" 6>&1 +fi +fi + + +# Check whether --enable-sim-decode-mechanism or --disable-sim-decode-mechanism was given. +if test "${enable_sim_decode_mechanism+set}" = set; then + enableval="$enable_sim_decode_mechanism" + case "${enableval}" in + yes|no) { echo "configure: error: "No value supplied for --enable-sim-decode-mechanism=file"" 1>&2; exit 1; };; + array|switch|padded-switch|goto-switch) sim_decode_mechanism="-T ${enableval}";; + *) { echo "configure: error: "File $enableval is not an opcode rules file"" 1>&2; exit 1; }; + sim_decode_mechanism="switch";; +esac +if test x"$silent" != x"yes" && test x"$sim_decode_mechanism" != x""; then + echo "Setting decode mechanism flags = $sim_decode_mechanism" 6>&1 +fi +else + sim_decode_mechanism="" +if test x"$silent" != x"yes"; then + echo "Setting decode mechanism flags = $sim_decode_mechanism" +fi +fi + + +# Check whether --enable-sim-default-model or --disable-sim-default-model was given. +if test "${enable_sim_default_model+set}" = set; then + enableval="$enable_sim_default_model" + case "${enableval}" in + yes|no) { echo "configure: error: "No value supplied for --enable-sim-default-model=model"" 1>&2; exit 1; };; + *) sim_default_model="-DWITH_DEFAULT_MODEL=${enableval}";; +esac +if test x"$silent" != x"yes" && test x"$sim_default_model" != x""; then + echo "Setting default-model flags = $sim_default_model" 6>&1 +fi +else + sim_default_model="" +fi + + +# Check whether --enable-sim-duplicate or --disable-sim-duplicate was given. +if test "${enable_sim_duplicate+set}" = set; then + enableval="$enable_sim_duplicate" + case "${enableval}" in + yes) sim_dup="-E";; + no) sim_dup="";; + *) { echo "configure: error: "--enable-sim-duplicate does not take a value"" 1>&2; exit 1; }; sim_dup="";; +esac +if test x"$silent" != x"yes" && test x"$sim_dup" != x""; then + echo "Setting duplicate flags = $sim_dup" 6>&1 +fi +else + sim_dup="-E" +if test x"$silent" != x"yes"; then + echo "Setting duplicate flags = $sim_dup" 6>&1 +fi +fi + + +# Check whether --enable-sim-endian or --disable-sim-endian was given. +if test "${enable_sim_endian+set}" = set; then + enableval="$enable_sim_endian" + case "${enableval}" in + yes) case "$target" in + *powerpc-*) sim_endian="-DWITH_TARGET_BYTE_ORDER=BIG_ENDIAN";; + *powerpcle-*) sim_endian="-DWITH_TARGET_BYTE_ORDER=LITTLE_ENDIAN";; + *) echo "Unknown target $target" 1>&6; sim_endian="-DWITH_TARGET_BYTE_ORDER=0";; + esac;; + no) sim_endian="-DWITH_TARGET_BYTE_ORDER=0";; + b*|B*) sim_endian="-DWITH_TARGET_BYTE_ORDER=BIG_ENDIAN";; + l*|L*) sim_endian="-DWITH_TARGET_BYTE_ORDER=LITTLE_ENDIAN";; + *) { echo "configure: error: "Unknown value $enableval for --enable-sim-endian"" 1>&2; exit 1; }; sim_endian="";; +esac +if test x"$silent" != x"yes" && test x"$sim_endian" != x""; then + echo "Setting endian flags = $sim_endian" 6>&1 +fi +else + sim_endian="" +fi + + +# Check whether --enable-sim-env or --disable-sim-env was given. +if test "${enable_sim_env+set}" = set; then + enableval="$enable_sim_env" + case "${enableval}" in + operating | os | oea) sim_env="-DWITH_ENVIRONMENT=OPERATING_ENVIRONMENT";; + virtual | vea) sim_env="-DWITH_ENVIRONMENT=VIRTUAL_ENVIRONMENT";; + user | uea) sim_env="-DWITH_ENVIRONMENT=USER_ENVIRONMENT";; + no) sim_env="-DWITH_ENVIRONMENT=0";; + *) { echo "configure: error: "Unknown value $enableval passed to --enable-sim-env"" 1>&2; exit 1; }; sim_env="";; +esac +if test x"$silent" != x"yes" && test x"$sim_env" != x""; then + echo "Setting env flags = $sim_env" 6>&1 +fi +else + sim_env="" +fi + + +# Check whether --enable-sim-filter or --disable-sim-filter was given. +if test "${enable_sim_filter+set}" = set; then + enableval="$enable_sim_filter" + case "${enableval}" in + yes) { echo "configure: error: "--enable-sim-filter must be specified with a rule to filter or no"" 1>&2; exit 1; }; sim_filter="";; + no) sim_filter="";; + *) sim_filter="-F $enableval";; +esac +if test x"$silent" != x"yes" && test x"$sim_filter" != x""; then + echo "Setting filter flags = $sim_filter" 6>&1 +fi +else + sim_filter="-F 32,f,o" +if test x"$silent" != x"yes"; then + echo "Setting filter flags = $sim_filter" 6>&1 +fi +fi + + +# Check whether --enable-sim-float or --disable-sim-float was given. +if test "${enable_sim_float+set}" = set; then + enableval="$enable_sim_float" + case "${enableval}" in + yes | hard) sim_float="-DWITH_FLOATING_POINT=HARD_FLOATING_POINT";; + no | soft) sim_float="-DWITH_FLOATING_POINT=SOFT_FLOATING_POINT";; + *) { echo "configure: error: "Unknown value $enableval passed to --enable-sim-float"" 1>&2; exit 1; }; sim_float="";; +esac +if test x"$silent" != x"yes" && test x"$sim_float" != x""; then + echo "Setting float flags = $sim_float" 6>&1 +fi +else + sim_float="" +fi + + +# Check whether --enable-sim-hardware or --disable-sim-hardware was given. +if test "${enable_sim_hardware+set}" = set; then + enableval="$enable_sim_hardware" + hardware="cpu,memory,nvram,iobus,htab,disk,trace,register,vm,init,core,pal,com,eeprom,opic,glue,phb,ide" +case "${enableval}" in + yes) ;; + no) { echo "configure: error: "List of hardware must be specified for --enable-sim-hardware"" 1>&2; exit 1; }; hardware="";; + ,*) hardware="${hardware}${enableval}";; + *,) hardware="${enableval}${hardware}";; + *) hardware="${enableval}"'';; +esac +sim_hw_src=`echo $hardware | sed -e 's/,/.c hw_/g' -e 's/^/hw_/' -e s'/$/.c/'` +sim_hw_obj=`echo $sim_hw_src | sed -e 's/\.c/.o/g'` +if test x"$silent" != x"yes" && test x"$hardware" != x""; then + echo "Setting hardware to $sim_hw_src, $sim_hw_obj" +fi +else + hardware="cpu,memory,nvram,iobus,htab,disk,trace,register,vm,init,core,pal,com,eeprom,opic,glue,phb,ide" +sim_hw_src=`echo $hardware | sed -e 's/,/.c hw_/g' -e 's/^/hw_/' -e s'/$/.c/'` +sim_hw_obj=`echo $sim_hw_src | sed -e 's/\.c/.o/g'` +if test x"$silent" != x"yes"; then + echo "Setting hardware to $sim_hw_src, $sim_hw_obj" +fi +fi + + +# Check whether --enable-sim-hostbitsize or --disable-sim-hostbitsize was given. +if test "${enable_sim_hostbitsize+set}" = set; then + enableval="$enable_sim_hostbitsize" + case "${enableval}" in + 32|64) sim_hostbitsize="-DWITH_HOST_WORD_BITSIZE=$enableval";; + *) { echo "configure: error: "--enable-sim-hostbitsize was given $enableval. Expected 32 or 64"" 1>&2; exit 1; }; sim_hostbitsize="";; +esac +if test x"$silent" != x"yes" && test x"$sim_hostbitsize" != x""; then + echo "Setting hostbitsize flags = $sim_hostbitsize" 6>&1 +fi +else + sim_hostbitsize="" +fi + + +# Check whether --enable-sim-hostendian or --disable-sim-hostendian was given. +if test "${enable_sim_hostendian+set}" = set; then + enableval="$enable_sim_hostendian" + case "${enableval}" in + no) sim_hostendian="-DWITH_HOST_BYTE_ORDER=0";; + b*|B*) sim_hostendian="-DWITH_HOST_BYTE_ORDER=BIG_ENDIAN";; + l*|L*) sim_hostendian="-DWITH_HOST_BYTE_ORDER=LITTLE_ENDIAN";; + *) { echo "configure: error: "Unknown value $enableval for --enable-sim-hostendian"" 1>&2; exit 1; }; sim_hostendian="";; +esac +if test x"$silent" != x"yes" && test x"$sim_hostendian" != x""; then + echo "Setting hostendian flags = $sim_hostendian" 6>&1 +fi +else + +if test "x$cross_compiling" = "xno"; then + echo $ac_n "checking whether byte ordering is bigendian""... $ac_c" 1>&6 +echo "configure:3089: checking whether byte ordering is bigendian" >&5 +if eval "test \"`echo '$''{'ac_cv_c_bigendian'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_cv_c_bigendian=unknown +# See if sys/param.h defines the BYTE_ORDER macro. +cat > conftest.$ac_ext <<EOF +#line 3096 "configure" +#include "confdefs.h" +#include <sys/types.h> +#include <sys/param.h> +int main() { + +#if !BYTE_ORDER || !BIG_ENDIAN || !LITTLE_ENDIAN + bogus endian macros +#endif +; return 0; } +EOF +if { (eval echo configure:3107: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + # It does; now see whether it defined to BIG_ENDIAN or not. +cat > conftest.$ac_ext <<EOF +#line 3111 "configure" +#include "confdefs.h" +#include <sys/types.h> +#include <sys/param.h> +int main() { + +#if BYTE_ORDER != BIG_ENDIAN + not big endian +#endif +; return 0; } +EOF +if { (eval echo configure:3122: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_c_bigendian=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_c_bigendian=no +fi +rm -f conftest* +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 +fi +rm -f conftest* +if test $ac_cv_c_bigendian = unknown; then +if test "$cross_compiling" = yes; then + { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; } +else + cat > conftest.$ac_ext <<EOF +#line 3142 "configure" +#include "confdefs.h" +main () { + /* Are we little or big endian? From Harbison&Steele. */ + union + { + long l; + char c[sizeof (long)]; + } u; + u.l = 1; + exit (u.c[sizeof (long) - 1] == 1); +} +EOF +if { (eval echo configure:3155: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +then + ac_cv_c_bigendian=no +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_c_bigendian=yes +fi +rm -fr conftest* +fi + +fi +fi + +echo "$ac_t""$ac_cv_c_bigendian" 1>&6 +if test $ac_cv_c_bigendian = yes; then + cat >> confdefs.h <<\EOF +#define WORDS_BIGENDIAN 1 +EOF + +fi + + if test $ac_cv_c_bigendian = yes; then + sim_hostendian="-DWITH_HOST_BYTE_ORDER=BIG_ENDIAN" + else + sim_hostendian="-DWITH_HOST_BYTE_ORDER=LITTLE_ENDIAN" + fi +else + sim_hostendian="-DWITH_HOST_BYTE_ORDER=0" +fi +fi + + +# Check whether --enable-sim-icache or --disable-sim-icache was given. +if test "${enable_sim_icache+set}" = set; then + enableval="$enable_sim_icache" + icache="-R" + case "${enableval}" in + yes) icache="1024"; sim_icache="-I $icache";; + no) sim_icache="-R";; + *) icache=1024 + sim_icache="-" + for x in `echo "${enableval}" | sed -e "s/,/ /g"`; do + case "$x" in + define) sim_icache="${sim_icache}R";; + semantic) sim_icache="${sim_icache}C";; + insn) sim_icache="${sim_icache}S";; + 0*|1*|2*|3*|4*|5*|6*|7*|8*|9*) icache=$x;; + *) { echo "configure: error: "Unknown value $x for --enable-sim-icache"" 1>&2; exit 1; }; sim_icache="";; + esac + done + sim_icache="${sim_icache}I $icache";; +esac +if test x"$silent" != x"yes" && test x"$icache" != x""; then + echo "Setting instruction cache size to $icache ($sim_icache)" +fi +else + sim_icache="-CSRI 1024" +if test x"$silent" != x"yes"; then + echo "Setting instruction cache size to 1024 ($sim_icache)" +fi +fi + + +# Check whether --enable-sim-inline or --disable-sim-inline was given. +if test "${enable_sim_inline+set}" = set; then + enableval="$enable_sim_inline" + sim_inline="" +case "$enableval" in + no) sim_inline="-DDEFAULT_INLINE=0";; + 0) sim_inline="-DDEFAULT_INLINE=0";; + yes | 2) sim_inline="-DDEFAULT_INLINE=ALL_INLINE";; + 1) sim_inline="-DDEFAULT_INLINE=INLINE_LOCALS";; + *) for x in `echo "$enableval" | sed -e "s/,/ /g"`; do + new_flag="" + case "$x" in + *_INLINE=*) new_flag="-D$x";; + *=*) new_flag=`echo "$x" | sed -e "s/=/_INLINE=/" -e "s/^/-D/"`;; + *_INLINE) new_flag="-D$x=ALL_INLINE";; + *) new_flag="-D$x""_INLINE=ALL_INLINE";; + esac + if test x"$sim_inline" = x""; then + sim_inline="$new_flag" + else + sim_inline="$sim_inline $new_flag" + fi + done;; +esac +if test x"$silent" != x"yes" && test x"$sim_inline" != x""; then + echo "Setting inline flags = $sim_inline" 6>&1 +fi +else + if test x"$GCC" != ""; then + sim_inline="-DDEFAULT_INLINE=INLINE_LOCALS" + if test x"$silent" != x"yes"; then + echo "Setting inline flags = $sim_inline" 6>&1 + fi +else + sim_inline="" +fi +fi + + +# Check whether --enable-sim-jump or --disable-sim-jump was given. +if test "${enable_sim_jump+set}" = set; then + enableval="$enable_sim_jump" + case "${enableval}" in + yes) sim_jump="-J";; + no) sim_jump="";; + *) { echo "configure: error: "--enable-sim-jump does not take a value"" 1>&2; exit 1; }; sim_jump="";; +esac +if test x"$silent" != x"yes" && test x"$sim_jump" != x""; then + echo "Setting jump flag = $sim_jump" 6>&1 +fi +else + sim_jump="" +if test x"$silent" != x"yes"; then + echo "Setting jump flag = $sim_jump" 6>&1 +fi +fi + + +# Check whether --enable-sim-line-nr or --disable-sim-line-nr was given. +if test "${enable_sim_line_nr+set}" = set; then + enableval="$enable_sim_line_nr" + case "${enableval}" in + yes) sim_line_nr="";; + no) sim_line_nr="-L";; + *) { echo "configure: error: "--enable-sim-line-nr does not take a value"" 1>&2; exit 1; }; sim_line_nr="";; +esac +if test x"$silent" != x"yes" && test x"$sim_line_nr" != x""; then + echo "Setting warning flags = $sim_line_nr" 6>&1 +fi +else + sim_line_nr="" +fi + + +# Check whether --enable-sim-model or --disable-sim-model was given. +if test "${enable_sim_model+set}" = set; then + enableval="$enable_sim_model" + case "${enableval}" in + yes|no) { echo "configure: error: "No value supplied for --enable-sim-model=model"" 1>&2; exit 1; };; + *) sim_model="-DWITH_MODEL=${enableval}";; +esac +if test x"$silent" != x"yes" && test x"$sim_model" != x""; then + echo "Setting model flags = $sim_model" 6>&1 +fi +else + sim_model="" +fi + + +# Check whether --enable-sim-model-issue or --disable-sim-model-issue was given. +if test "${enable_sim_model_issue+set}" = set; then + enableval="$enable_sim_model_issue" + case "${enableval}" in + yes) sim_model_issue="-DWITH_MODEL_ISSUE=MODEL_ISSUE_PROCESS";; + no) sim_model_issue="-DWITH_MODEL_ISSUE=MODEL_ISSUE_IGNORE";; + *) { echo "configure: error: "--enable-sim-model-issue does not take a value"" 1>&2; exit 1; }; sim_model_issue="";; +esac +if test x"$silent" != x"yes"; then + echo "Setting model-issue flags = $sim_model_issue" 6>&1 +fi +else + sim_model_issue="" +fi + + +# Check whether --enable-sim-monitor or --disable-sim-monitor was given. +if test "${enable_sim_monitor+set}" = set; then + enableval="$enable_sim_monitor" + case "${enableval}" in + yes) sim_monitor="-DWITH_MON='MONITOR_INSTRUCTION_ISSUE | MONITOR_LOAD_STORE_UNIT'";; + no) sim_monitor="-DWITH_MON=0";; + instruction) sim_monitor="-DWITH_MON=MONITOR_INSTRUCTION_ISSUE";; + memory) sim_monitor="-DWITH_MON=MONITOR_LOAD_STORE_UNIT";; + *) { echo "configure: error: "Unknown value $enableval passed to --enable-sim-mon"" 1>&2; exit 1; }; sim_env="";; +esac +if test x"$silent" != x"yes" && test x"$sim_monitor" != x""; then + echo "Setting monitor flags = $sim_monitor" 6>&1 +fi +else + sim_monitor="" +fi + + +# Check whether --enable-sim-opcode or --disable-sim-opcode was given. +if test "${enable_sim_opcode+set}" = set; then + enableval="$enable_sim_opcode" + case "${enableval}" in + yes|no) { echo "configure: error: "No value supplied for --enable-sim-opcode=file"" 1>&2; exit 1; };; + *) if test -f "${srcdir}/${enableval}"; then + sim_opcode="${enableval}" + elif test -f "${srcdir}/dc-${enableval}"; then + sim_opcode="dc-${enableval}" + else + { echo "configure: error: "File $enableval is not an opcode rules file"" 1>&2; exit 1; }; + sim_opcode="dc-complex" + fi;; +esac +if test x"$silent" != x"yes" && test x"$sim_opcode" != x""; then + echo "Setting opcode flags = $sim_opcode" 6>&1 +fi +else + sim_opcode="dc-complex" +if test x"$silent" != x"yes"; then + echo "Setting opcode flags = $sim_opcode" +fi +fi + + +# Check whether --enable-sim-packages or --disable-sim-packages was given. +if test "${enable_sim_packages+set}" = set; then + enableval="$enable_sim_packages" + packages=disklabel +case "${enableval}" in + yes) ;; + no) { echo "configure: error: "List of packages must be specified for --enable-sim-packages"" 1>&2; exit 1; }; packages="";; + ,*) packages="${packages}${enableval}";; + *,) packages="${enableval}${packages}";; + *) packages="${enableval}"'';; +esac +sim_pk_src=`echo $packages | sed -e 's/,/.c pk_/g' -e 's/^/pk_/' -e 's/$/.c/'` +sim_pk_obj=`echo $sim_pk_src | sed -e 's/\.c/.o/g'` +if test x"$silent" != x"yes" && test x"$packages" != x""; then + echo "Setting packages to $sim_pk_src, $sim_pk_obj" +fi +else + packages=disklabel +sim_pk_src=`echo $packages | sed -e 's/,/.c pk_/g' -e 's/^/pk_/' -e 's/$/.c/'` +sim_pk_obj=`echo $sim_pk_src | sed -e 's/\.c/.o/g'` +if test x"$silent" != x"yes"; then + echo "Setting packages to $sim_pk_src, $sim_pk_obj" +fi +fi + + +# Check whether --enable-sim-regparm or --disable-sim-regparm was given. +if test "${enable_sim_regparm+set}" = set; then + enableval="$enable_sim_regparm" + case "${enableval}" in + 0*|1*|2*|3*|4*|5*|6*|7*|8*|9*) sim_regparm="-DWITH_REGPARM=${enableval}";; + no) sim_regparm="" ;; + yes) sim_regparm="-DWITH_REGPARM=3";; + *) { echo "configure: error: "Unknown value $enableval for --enable-sim-regparm"" 1>&2; exit 1; }; sim_regparm="";; +esac +if test x"$silent" != x"yes" && test x"$sim_regparm" != x""; then + echo "Setting regparm flags = $sim_regparm" 6>&1 +fi +else + sim_regparm="" +fi + + +# Check whether --enable-sim-reserved-bits or --disable-sim-reserved-bits was given. +if test "${enable_sim_reserved_bits+set}" = set; then + enableval="$enable_sim_reserved_bits" + case "${enableval}" in + yes) sim_reserved="-DWITH_RESERVED_BITS=1";; + no) sim_reserved="-DWITH_RESERVED_BITS=0";; + *) { echo "configure: error: "--enable-sim-reserved-bits does not take a value"" 1>&2; exit 1; }; sim_reserved="";; +esac +if test x"$silent" != x"yes" && test x"$sim_reserved" != x""; then + echo "Setting reserved flags = $sim_reserved" 6>&1 +fi +else + sim_reserved="" +fi + + +# Check whether --enable-sim-smp or --disable-sim-smp was given. +if test "${enable_sim_smp+set}" = set; then + enableval="$enable_sim_smp" + case "${enableval}" in + yes) sim_smp="-DWITH_SMP=5" ; sim_igen_smp="-N 5";; + no) sim_smp="-DWITH_SMP=0" ; sim_igen_smp="-N 0";; + *) sim_smp="-DWITH_SMP=$enableval" ; sim_igen_smp="-N $enableval";; +esac +if test x"$silent" != x"yes" && test x"$sim_smp" != x""; then + echo "Setting smp flags = $sim_smp" 6>&1 +fi +else + sim_smp="-DWITH_SMP=5" ; sim_igen_smp="-N 5" +if test x"$silent" != x"yes"; then + echo "Setting smp flags = $sim_smp" 6>&1 +fi +fi + + +# Check whether --enable-sim-stdcall or --disable-sim-stdcall was given. +if test "${enable_sim_stdcall+set}" = set; then + enableval="$enable_sim_stdcall" + case "${enableval}" in + no) sim_stdcall="" ;; + std*) sim_stdcall="-DWITH_STDCALL=1";; + yes) sim_stdcall="-DWITH_STDCALL=1";; + *) { echo "configure: error: "Unknown value $enableval for --enable-sim-stdcall"" 1>&2; exit 1; }; sim_stdcall="";; +esac +if test x"$silent" != x"yes" && test x"$sim_stdcall" != x""; then + echo "Setting function call flags = $sim_stdcall" 6>&1 +fi +else + sim_stdcall="" +fi + + +# Check whether --enable-sim-stdio or --disable-sim-stdio was given. +if test "${enable_sim_stdio+set}" = set; then + enableval="$enable_sim_stdio" + case "${enableval}" in + yes) sim_stdio="-DWITH_STDIO=DO_USE_STDIO";; + no) sim_stdio="-DWITH_STDIO=DONT_USE_STDIO";; + *) { echo "configure: error: "Unknown value $enableval passed to --enable-sim-stdio"" 1>&2; exit 1; }; sim_stdio="";; +esac +if test x"$silent" != x"yes" && test x"$sim_stdio" != x""; then + echo "Setting stdio flags = $sim_stdio" 6>&1 +fi +else + sim_stdio="" +fi + + +# Check whether --enable-sim-switch or --disable-sim-switch was given. +if test "${enable_sim_switch+set}" = set; then + enableval="$enable_sim_switch" + case "${enableval}" in + yes) sim_switch="-s";; + no) sim_switch="";; + *) { echo "configure: error: "--enable-sim-switch does not take a value"" 1>&2; exit 1; }; sim_switch="";; +esac +if test x"$silent" != x"yes" && test x"$sim_switch" != x""; then + echo "Setting switch flags = $sim_switch" 6>&1 +fi +else + sim_switch=""; +if test x"$silent" != x"yes"; then + echo "Setting switch flags = $sim_switch" 6>&1 +fi +fi + + +# Check whether --enable-sim-timebase or --disable-sim-timebase was given. +if test "${enable_sim_timebase+set}" = set; then + enableval="$enable_sim_timebase" + case "${enableval}" in + yes) sim_timebase="-DWITH_TIME_BASE=1";; + no) sim_timebase="-DWITH_TIME_BASE=0";; + *) { echo "configure: error: "--enable-sim-timebase does not take a value"" 1>&2; exit 1; }; sim_timebase="";; +esac +if test x"$silent" != x"yes" && test x"$sim_timebase" != x""; then + echo "Setting timebase flags = $sim_timebase" 6>&1 +fi +else + sim_timebase="" +fi + + +# Check whether --enable-sim-trace or --disable-sim-trace was given. +if test "${enable_sim_trace+set}" = set; then + enableval="$enable_sim_trace" + case "${enableval}" in + yes) sim_trace="-DWITH_TRACE=1";; + no) sim_trace="-DWITH_TRACE=0";; + *) { echo "configure: error: "--enable-sim-trace does not take a value"" 1>&2; exit 1; }; sim_trace="";; +esac +if test x"$silent" != x"yes" && test x"$sim_trace" != x""; then + echo "Setting trace flags = $sim_trace" 6>&1 +fi +else + sim_trace="" +fi + + +# Check whether --enable-sim-warnings or --disable-sim-warnings was given. +if test "${enable_sim_warnings+set}" = set; then + enableval="$enable_sim_warnings" + case "${enableval}" in + yes) sim_warnings="-Werror -Wall -Wpointer-arith -Wmissing-prototypes -Wmissing-declarations ";; + no) sim_warnings="-w";; + *) sim_warnings=`echo "${enableval}" | sed -e "s/,/ /g"`;; +esac +if test x"$silent" != x"yes" && test x"$sim_warnings" != x""; then + echo "Setting warning flags = $sim_warnings" 6>&1 +fi +else + sim_warnings="" +fi + + +# Check whether --enable-sim-xor-endian or --disable-sim-xor-endian was given. +if test "${enable_sim_xor_endian+set}" = set; then + enableval="$enable_sim_xor_endian" + case "${enableval}" in + yes) sim_xor_endian="-DWITH_XOR_ENDIAN=8";; + no) sim_xor_endian="-DWITH_XOR_ENDIAN=0";; + *) sim_xor_endian="-DWITH_XOR_ENDIAN=$enableval";; +esac +if test x"$silent" != x"yes" && test x"$sim_xor_endian" != x""; then + echo "Setting xor-endian flag = $sim_xor_endian" 6>&1 +fi +else + sim_xor_endian="" +fi + + +ac_aux_dir= +for ac_dir in `cd $srcdir;pwd`/../.. $srcdir/`cd $srcdir;pwd`/../..; do + if test -f $ac_dir/install-sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f $ac_dir/install.sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { echo "configure: error: can not find install-sh or install.sh in `cd $srcdir;pwd`/../.. $srcdir/`cd $srcdir;pwd`/../.." 1>&2; exit 1; } +fi +ac_config_guess=$ac_aux_dir/config.guess +ac_config_sub=$ac_aux_dir/config.sub +ac_configure=$ac_aux_dir/configure # This should be Cygnus configure. + + +# Do some error checking and defaulting for the host and target type. +# The inputs are: +# configure --host=HOST --target=TARGET --build=BUILD NONOPT +# +# The rules are: +# 1. You are not allowed to specify --host, --target, and nonopt at the +# same time. +# 2. Host defaults to nonopt. +# 3. If nonopt is not specified, then host defaults to the current host, +# as determined by config.guess. +# 4. Target and build default to nonopt. +# 5. If nonopt is not specified, then target and build default to host. + +# The aliases save the names the user supplied, while $host etc. +# will get canonicalized. +case $host---$target---$nonopt in +NONE---*---* | *---NONE---* | *---*---NONE) ;; +*) { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; } ;; +esac + + +# Make sure we can run config.sub. +if ${CONFIG_SHELL-/bin/sh} $ac_config_sub sun4 >/dev/null 2>&1; then : +else { echo "configure: error: can not run $ac_config_sub" 1>&2; exit 1; } +fi + +echo $ac_n "checking host system type""... $ac_c" 1>&6 +echo "configure:3609: checking host system type" >&5 + +host_alias=$host +case "$host_alias" in +NONE) + case $nonopt in + NONE) + if host_alias=`${CONFIG_SHELL-/bin/sh} $ac_config_guess`; then : + else { echo "configure: error: can not guess host type; you must specify one" 1>&2; exit 1; } + fi ;; + *) host_alias=$nonopt ;; + esac ;; +esac + +host=`${CONFIG_SHELL-/bin/sh} $ac_config_sub $host_alias` +host_cpu=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +host_vendor=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +host_os=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` +echo "$ac_t""$host" 1>&6 + +echo $ac_n "checking target system type""... $ac_c" 1>&6 +echo "configure:3630: checking target system type" >&5 + +target_alias=$target +case "$target_alias" in +NONE) + case $nonopt in + NONE) target_alias=$host_alias ;; + *) target_alias=$nonopt ;; + esac ;; +esac + +target=`${CONFIG_SHELL-/bin/sh} $ac_config_sub $target_alias` +target_cpu=`echo $target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +target_vendor=`echo $target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +target_os=`echo $target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` +echo "$ac_t""$target" 1>&6 + +echo $ac_n "checking build system type""... $ac_c" 1>&6 +echo "configure:3648: checking build system type" >&5 + +build_alias=$build +case "$build_alias" in +NONE) + case $nonopt in + NONE) build_alias=$host_alias ;; + *) build_alias=$nonopt ;; + esac ;; +esac + +build=`${CONFIG_SHELL-/bin/sh} $ac_config_sub $build_alias` +build_cpu=`echo $build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +build_vendor=`echo $build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +build_os=`echo $build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` +echo "$ac_t""$build" 1>&6 + +test "$host_alias" != "$target_alias" && + test "$program_prefix$program_suffix$program_transform_name" = \ + NONENONEs,x,x, && + program_prefix=${target_alias}- + +if test "$program_transform_name" = s,x,x,; then + program_transform_name= +else + # Double any \ or $. echo might interpret backslashes. + cat <<\EOF_SED > conftestsed +s,\\,\\\\,g; s,\$,$$,g +EOF_SED + program_transform_name="`echo $program_transform_name|sed -f conftestsed`" + rm -f conftestsed +fi +test "$program_prefix" != NONE && + program_transform_name="s,^,${program_prefix},; $program_transform_name" +# Use a double $ so make ignores it. +test "$program_suffix" != NONE && + program_transform_name="s,\$\$,${program_suffix},; $program_transform_name" + +# sed with no file args requires a program. +test "$program_transform_name" = "" && program_transform_name="s,x,x," + + +. ${srcdir}/../../bfd/configure.host + + + +echo $ac_n "checking for st_blksize in struct stat""... $ac_c" 1>&6 +echo "configure:3695: checking for st_blksize in struct stat" >&5 +if eval "test \"`echo '$''{'ac_cv_struct_st_blksize'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 3700 "configure" +#include "confdefs.h" +#include <sys/types.h> +#include <sys/stat.h> +int main() { +struct stat s; s.st_blksize; +; return 0; } +EOF +if { (eval echo configure:3708: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_struct_st_blksize=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_struct_st_blksize=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_struct_st_blksize" 1>&6 +if test $ac_cv_struct_st_blksize = yes; then + cat >> confdefs.h <<\EOF +#define HAVE_ST_BLKSIZE 1 +EOF + +fi + +echo $ac_n "checking for st_blocks in struct stat""... $ac_c" 1>&6 +echo "configure:3729: checking for st_blocks in struct stat" >&5 +if eval "test \"`echo '$''{'ac_cv_struct_st_blocks'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 3734 "configure" +#include "confdefs.h" +#include <sys/types.h> +#include <sys/stat.h> +int main() { +struct stat s; s.st_blocks; +; return 0; } +EOF +if { (eval echo configure:3742: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_struct_st_blocks=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_struct_st_blocks=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_struct_st_blocks" 1>&6 +if test $ac_cv_struct_st_blocks = yes; then + cat >> confdefs.h <<\EOF +#define HAVE_ST_BLOCKS 1 +EOF + +else + LIBOBJS="$LIBOBJS fileblocks.${ac_objext}" +fi + +echo $ac_n "checking for st_rdev in struct stat""... $ac_c" 1>&6 +echo "configure:3765: checking for st_rdev in struct stat" >&5 +if eval "test \"`echo '$''{'ac_cv_struct_st_rdev'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 3770 "configure" +#include "confdefs.h" +#include <sys/types.h> +#include <sys/stat.h> +int main() { +struct stat s; s.st_rdev; +; return 0; } +EOF +if { (eval echo configure:3778: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_struct_st_rdev=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_struct_st_rdev=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_struct_st_rdev" 1>&6 +if test $ac_cv_struct_st_rdev = yes; then + cat >> confdefs.h <<\EOF +#define HAVE_ST_RDEV 1 +EOF + +fi + +echo $ac_n "checking whether struct tm is in sys/time.h or time.h""... $ac_c" 1>&6 +echo "configure:3799: checking whether struct tm is in sys/time.h or time.h" >&5 +if eval "test \"`echo '$''{'ac_cv_struct_tm'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 3804 "configure" +#include "confdefs.h" +#include <sys/types.h> +#include <time.h> +int main() { +struct tm *tp; tp->tm_sec; +; return 0; } +EOF +if { (eval echo configure:3812: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_struct_tm=time.h +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_struct_tm=sys/time.h +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_struct_tm" 1>&6 +if test $ac_cv_struct_tm = sys/time.h; then + cat >> confdefs.h <<\EOF +#define TM_IN_SYS_TIME 1 +EOF + +fi + +echo $ac_n "checking for tm_zone in struct tm""... $ac_c" 1>&6 +echo "configure:3833: checking for tm_zone in struct tm" >&5 +if eval "test \"`echo '$''{'ac_cv_struct_tm_zone'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 3838 "configure" +#include "confdefs.h" +#include <sys/types.h> +#include <$ac_cv_struct_tm> +int main() { +struct tm tm; tm.tm_zone; +; return 0; } +EOF +if { (eval echo configure:3846: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_struct_tm_zone=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_struct_tm_zone=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_struct_tm_zone" 1>&6 +if test "$ac_cv_struct_tm_zone" = yes; then + cat >> confdefs.h <<\EOF +#define HAVE_TM_ZONE 1 +EOF + +else + echo $ac_n "checking for tzname""... $ac_c" 1>&6 +echo "configure:3866: checking for tzname" >&5 +if eval "test \"`echo '$''{'ac_cv_var_tzname'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 3871 "configure" +#include "confdefs.h" +#include <time.h> +#ifndef tzname /* For SGI. */ +extern char *tzname[]; /* RS6000 and others reject char **tzname. */ +#endif +int main() { +atoi(*tzname); +; return 0; } +EOF +if { (eval echo configure:3881: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + ac_cv_var_tzname=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_var_tzname=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_var_tzname" 1>&6 + if test $ac_cv_var_tzname = yes; then + cat >> confdefs.h <<\EOF +#define HAVE_TZNAME 1 +EOF + + fi +fi + + +echo $ac_n "checking for uid_t in sys/types.h""... $ac_c" 1>&6 +echo "configure:3904: checking for uid_t in sys/types.h" >&5 +if eval "test \"`echo '$''{'ac_cv_type_uid_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 3909 "configure" +#include "confdefs.h" +#include <sys/types.h> +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "uid_t" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_uid_t=yes +else + rm -rf conftest* + ac_cv_type_uid_t=no +fi +rm -f conftest* + +fi + +echo "$ac_t""$ac_cv_type_uid_t" 1>&6 +if test $ac_cv_type_uid_t = no; then + cat >> confdefs.h <<\EOF +#define uid_t int +EOF + + cat >> confdefs.h <<\EOF +#define gid_t int +EOF + +fi + +echo $ac_n "checking type of array argument to getgroups""... $ac_c" 1>&6 +echo "configure:3938: checking type of array argument to getgroups" >&5 +if eval "test \"`echo '$''{'ac_cv_type_getgroups'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test "$cross_compiling" = yes; then + ac_cv_type_getgroups=cross +else + cat > conftest.$ac_ext <<EOF +#line 3946 "configure" +#include "confdefs.h" + +/* Thanks to Mike Rendell for this test. */ +#include <sys/types.h> +#define NGID 256 +#undef MAX +#define MAX(x, y) ((x) > (y) ? (x) : (y)) +main() +{ + gid_t gidset[NGID]; + int i, n; + union { gid_t gval; long lval; } val; + + val.lval = -1; + for (i = 0; i < NGID; i++) + gidset[i] = val.gval; + n = getgroups (sizeof (gidset) / MAX (sizeof (int), sizeof (gid_t)) - 1, + gidset); + /* Exit non-zero if getgroups seems to require an array of ints. This + happens when gid_t is short but getgroups modifies an array of ints. */ + exit ((n > 0 && gidset[n] != val.gval) ? 1 : 0); +} + +EOF +if { (eval echo configure:3971: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +then + ac_cv_type_getgroups=gid_t +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_type_getgroups=int +fi +rm -fr conftest* +fi + +if test $ac_cv_type_getgroups = cross; then + cat > conftest.$ac_ext <<EOF +#line 3985 "configure" +#include "confdefs.h" +#include <unistd.h> +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "getgroups.*int.*gid_t" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_getgroups=gid_t +else + rm -rf conftest* + ac_cv_type_getgroups=int +fi +rm -f conftest* + +fi +fi + +echo "$ac_t""$ac_cv_type_getgroups" 1>&6 +cat >> confdefs.h <<EOF +#define GETGROUPS_T $ac_cv_type_getgroups +EOF + + +echo $ac_n "checking for mode_t""... $ac_c" 1>&6 +echo "configure:4009: checking for mode_t" >&5 +if eval "test \"`echo '$''{'ac_cv_type_mode_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 4014 "configure" +#include "confdefs.h" +#include <sys/types.h> +#if STDC_HEADERS +#include <stdlib.h> +#include <stddef.h> +#endif +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "mode_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_mode_t=yes +else + rm -rf conftest* + ac_cv_type_mode_t=no +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_type_mode_t" 1>&6 +if test $ac_cv_type_mode_t = no; then + cat >> confdefs.h <<\EOF +#define mode_t int +EOF + +fi + +echo $ac_n "checking for off_t""... $ac_c" 1>&6 +echo "configure:4042: checking for off_t" >&5 +if eval "test \"`echo '$''{'ac_cv_type_off_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 4047 "configure" +#include "confdefs.h" +#include <sys/types.h> +#if STDC_HEADERS +#include <stdlib.h> +#include <stddef.h> +#endif +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "off_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_off_t=yes +else + rm -rf conftest* + ac_cv_type_off_t=no +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_type_off_t" 1>&6 +if test $ac_cv_type_off_t = no; then + cat >> confdefs.h <<\EOF +#define off_t long +EOF + +fi + +echo $ac_n "checking for pid_t""... $ac_c" 1>&6 +echo "configure:4075: checking for pid_t" >&5 +if eval "test \"`echo '$''{'ac_cv_type_pid_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 4080 "configure" +#include "confdefs.h" +#include <sys/types.h> +#if STDC_HEADERS +#include <stdlib.h> +#include <stddef.h> +#endif +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "pid_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_pid_t=yes +else + rm -rf conftest* + ac_cv_type_pid_t=no +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_type_pid_t" 1>&6 +if test $ac_cv_type_pid_t = no; then + cat >> confdefs.h <<\EOF +#define pid_t int +EOF + +fi + +echo $ac_n "checking return type of signal handlers""... $ac_c" 1>&6 +echo "configure:4108: checking return type of signal handlers" >&5 +if eval "test \"`echo '$''{'ac_cv_type_signal'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 4113 "configure" +#include "confdefs.h" +#include <sys/types.h> +#include <signal.h> +#ifdef signal +#undef signal +#endif +#ifdef __cplusplus +extern "C" void (*signal (int, void (*)(int)))(int); +#else +void (*signal ()) (); +#endif + +int main() { +int i; +; return 0; } +EOF +if { (eval echo configure:4130: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_type_signal=void +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_type_signal=int +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_type_signal" 1>&6 +cat >> confdefs.h <<EOF +#define RETSIGTYPE $ac_cv_type_signal +EOF + + +echo $ac_n "checking for size_t""... $ac_c" 1>&6 +echo "configure:4149: checking for size_t" >&5 +if eval "test \"`echo '$''{'ac_cv_type_size_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 4154 "configure" +#include "confdefs.h" +#include <sys/types.h> +#if STDC_HEADERS +#include <stdlib.h> +#include <stddef.h> +#endif +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "size_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_size_t=yes +else + rm -rf conftest* + ac_cv_type_size_t=no +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_type_size_t" 1>&6 +if test $ac_cv_type_size_t = no; then + cat >> confdefs.h <<\EOF +#define size_t unsigned +EOF + +fi + +echo $ac_n "checking for uid_t in sys/types.h""... $ac_c" 1>&6 +echo "configure:4182: checking for uid_t in sys/types.h" >&5 +if eval "test \"`echo '$''{'ac_cv_type_uid_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 4187 "configure" +#include "confdefs.h" +#include <sys/types.h> +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "uid_t" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_uid_t=yes +else + rm -rf conftest* + ac_cv_type_uid_t=no +fi +rm -f conftest* + +fi + +echo "$ac_t""$ac_cv_type_uid_t" 1>&6 +if test $ac_cv_type_uid_t = no; then + cat >> confdefs.h <<\EOF +#define uid_t int +EOF + + cat >> confdefs.h <<\EOF +#define gid_t int +EOF + +fi + + +for ac_func in access cfgetispeed cfgetospeed cfsetispeed cfsetospeed chdir chmod chown dup dup2 fchmod fchown fcntl fstat fstatfs getdirentries getegid geteuid getgid getpid getppid getrusage gettimeofday getuid ioctl kill link lseek lstat mkdir pipe readlink rmdir setreuid setregid stat sigprocmask stat symlink tcgetattr tcsetattr tcsendbreak tcdrain tcflush tcflow tcgetpgrp tcsetpgrp time umask unlink +do +echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:4219: checking for $ac_func" >&5 +if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 4224 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func(); below. */ +#include <assert.h> +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +$ac_func(); +#endif + +; return 0; } +EOF +if { (eval echo configure:4247: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_$ac_func=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_$ac_func=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` + cat >> confdefs.h <<EOF +#define $ac_tr_func 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi +done + + +for ac_hdr in fcntl.h stdlib.h string.h strings.h sys/ioctl.h sys/param.h sys/resource.h sys/stat.h sys/termio.h sys/termios.h sys/time.h sys/times.h sys/types.h time.h unistd.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:4276: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 4281 "configure" +#include "confdefs.h" +#include <$ac_hdr> +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:4286: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <<EOF +#define $ac_tr_hdr 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi +done + +ac_header_dirent=no +for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr that defines DIR""... $ac_c" 1>&6 +echo "configure:4317: checking for $ac_hdr that defines DIR" >&5 +if eval "test \"`echo '$''{'ac_cv_header_dirent_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 4322 "configure" +#include "confdefs.h" +#include <sys/types.h> +#include <$ac_hdr> +int main() { +DIR *dirp = 0; +; return 0; } +EOF +if { (eval echo configure:4330: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + eval "ac_cv_header_dirent_$ac_safe=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_dirent_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_dirent_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <<EOF +#define $ac_tr_hdr 1 +EOF + ac_header_dirent=$ac_hdr; break +else + echo "$ac_t""no" 1>&6 +fi +done +# Two versions of opendir et al. are in -ldir and -lx on SCO Xenix. +if test $ac_header_dirent = dirent.h; then +echo $ac_n "checking for opendir in -ldir""... $ac_c" 1>&6 +echo "configure:4355: checking for opendir in -ldir" >&5 +ac_lib_var=`echo dir'_'opendir | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-ldir $LIBS" +cat > conftest.$ac_ext <<EOF +#line 4363 "configure" +#include "confdefs.h" +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char opendir(); + +int main() { +opendir() +; return 0; } +EOF +if { (eval echo configure:4374: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + LIBS="$LIBS -ldir" +else + echo "$ac_t""no" 1>&6 +fi + +else +echo $ac_n "checking for opendir in -lx""... $ac_c" 1>&6 +echo "configure:4396: checking for opendir in -lx" >&5 +ac_lib_var=`echo x'_'opendir | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lx $LIBS" +cat > conftest.$ac_ext <<EOF +#line 4404 "configure" +#include "confdefs.h" +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char opendir(); + +int main() { +opendir() +; return 0; } +EOF +if { (eval echo configure:4415: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + LIBS="$LIBS -lx" +else + echo "$ac_t""no" 1>&6 +fi + +fi + + +sim_termio="" +echo $ac_n "checking for struct termios""... $ac_c" 1>&6 +echo "configure:4440: checking for struct termios" >&5 +if eval "test \"`echo '$''{'ac_cv_termios_struct'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 4445 "configure" +#include "confdefs.h" +#include <sys/types.h> +#include <sys/termios.h> +int main() { +static struct termios x; + x.c_iflag = 0; + x.c_oflag = 0; + x.c_cflag = 0; + x.c_lflag = 0; + x.c_cc[NCCS] = 0; +; return 0; } +EOF +if { (eval echo configure:4458: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_termios_struct=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_termios_struct=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_termios_struct" 1>&6 +if test $ac_cv_termios_struct = yes; then + sim_termio="$sim_termio -DHAVE_TERMIOS_STRUCTURE" +fi + +if test "$ac_cv_termios_struct" = "yes"; then + echo $ac_n "checking for c_line field in struct termios""... $ac_c" 1>&6 +echo "configure:4477: checking for c_line field in struct termios" >&5 + if eval "test \"`echo '$''{'ac_cv_termios_cline'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 4482 "configure" +#include "confdefs.h" +#include <sys/types.h> +#include <sys/termios.h> +int main() { +static struct termios x; x.c_line = 0; +; return 0; } +EOF +if { (eval echo configure:4490: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_termios_cline=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_termios_cline=no +fi +rm -f conftest* +fi + + + echo "$ac_t""$ac_cv_termios_cline" 1>&6 + if test $ac_cv_termios_cline = yes; then + sim_termio="$sim_termio -DHAVE_TERMIOS_CLINE" + fi +else + ac_cv_termios_cline=no +fi + +if test "$ac_cv_termios_struct" != "yes"; then + echo $ac_n "checking for struct termio""... $ac_c" 1>&6 +echo "configure:4513: checking for struct termio" >&5 + if eval "test \"`echo '$''{'ac_cv_termio_struct'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 4518 "configure" +#include "confdefs.h" +#include <sys/types.h> +#include <sys/termio.h> +int main() { +static struct termio x; + x.c_iflag = 0; + x.c_oflag = 0; + x.c_cflag = 0; + x.c_lflag = 0; + x.c_cc[NCC] = 0; +; return 0; } +EOF +if { (eval echo configure:4531: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_termio_struct=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_termio_struct=no +fi +rm -f conftest* +fi + + echo "$ac_t""$ac_cv_termio_struct" 1>&6 + if test $ac_cv_termio_struct = yes; then + sim_termio="$sim_termio -DHAVE_TERMIO_STRUCTURE" + fi +else + ac_cv_termio_struct=no +fi + +if test "$ac_cv_termio_struct" = "yes"; then + echo $ac_n "checking for c_line field in struct termio""... $ac_c" 1>&6 +echo "configure:4553: checking for c_line field in struct termio" >&5 + if eval "test \"`echo '$''{'ac_cv_termio_cline'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 4558 "configure" +#include "confdefs.h" +#include <sys/types.h> +#include <sys/termio.h> +int main() { +static struct termio x; x.c_line = 0; +; return 0; } +EOF +if { (eval echo configure:4566: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_termio_cline=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_termio_cline=no +fi +rm -f conftest* +fi + + + echo "$ac_t""$ac_cv_termio_cline" 1>&6 + if test $ac_cv_termio_cline = yes; then + sim_termio="$sim_termio -DHAVE_TERMIO_CLINE" + fi +else + ac_cv_termio_cline=no +fi + +sim_devzero="" +echo $ac_n "checking for /dev/zero""... $ac_c" 1>&6 +echo "configure:4589: checking for /dev/zero" >&5 +if eval "test \"`echo '$''{'ac_cv_devzero'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test "$cross_compiling" = yes; then + ac_cv_devzero=no +else + cat > conftest.$ac_ext <<EOF +#line 4597 "configure" +#include "confdefs.h" +#include <fcntl.h> +main () { + char buf[2048]; + int i; + int fd = open ("/dev/zero", O_RDONLY); + if (fd < 0) + return 1; + for (i = 0; i < sizeof (buf); i++) + buf[i] = 1; + if (read (fd, buf, sizeof (buf)) != sizeof (buf)) + return 1; + for (i = 0; i < sizeof (buf); i++) + if (buf[i]) + return 1; + return 0; +} +EOF +if { (eval echo configure:4616: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +then + ac_cv_devzero=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_devzero=no +fi +rm -fr conftest* +fi + +fi + +echo "$ac_t""$ac_cv_devzero" 1>&6 +if test $ac_cv_devzero = yes; then + sim_devzero="-DHAVE_DEVZERO" +else + sim_devzero="" +fi + +echo $ac_n "checking for common simulator directory""... $ac_c" 1>&6 +echo "configure:4638: checking for common simulator directory" >&5 +if test -f "${srcdir}/../common/callback.c"; then + echo "$ac_t""yes" 1>&6 + sim_callback="callback.o targ-map.o" + sim_targ_vals="targ-vals.h targ-map.c targ-vals.def" +else + echo "$ac_t""no" 1>&6 + sim_callback="" + sim_targ_vals="" +fi + +echo $ac_n "checking for executable suffix""... $ac_c" 1>&6 +echo "configure:4650: checking for executable suffix" >&5 +if eval "test \"`echo '$''{'am_cv_exeext'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test "$CYGWIN32" = yes; then +am_cv_exeext=.exe +else +cat > am_c_test.c << 'EOF' +int main() { +/* Nothing needed here */ +} +EOF +${CC-cc} -o am_c_test $CFLAGS $CPPFLAGS $LDFLAGS am_c_test.c $LIBS 1>&5 +am_cv_exeext=`ls am_c_test.* | grep -v am_c_test.c | sed -e s/am_c_test//` +rm -f am_c_test* +fi + +test x"${am_cv_exeext}" = x && am_cv_exeext=no +fi +EXEEXT="" +test x"${am_cv_exeext}" != xno && EXEEXT=${am_cv_exeext} +echo "$ac_t""${am_cv_exeext}" 1>&6 + + + + + +AR=${AR-ar} + +# Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:4682: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_RANLIB="ranlib" + break + fi + done + IFS="$ac_save_ifs" + test -z "$ac_cv_prog_RANLIB" && ac_cv_prog_RANLIB=":" +fi +fi +RANLIB="$ac_cv_prog_RANLIB" +if test -n "$RANLIB"; then + echo "$ac_t""$RANLIB" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +trap '' 1 2 15 +cat > confcache <<\EOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs. It is not useful on other systems. +# If it contains results you don't want to keep, you may remove or edit it. +# +# By default, configure uses ./config.cache as the cache file, +# creating it if it does not exist already. You can give configure +# the --cache-file=FILE option to use a different cache file; that is +# what configure does when it calls configure scripts in +# subdirectories, so they share the cache. +# Giving --cache-file=/dev/null disables caching, for debugging configure. +# config.status only pays attention to the cache file if you give it the +# --recheck option to rerun configure. +# +EOF +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, don't put newlines in cache variables' values. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +(set) 2>&1 | + case `(ac_space=' '; set) 2>&1 | grep ac_space` in + *ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote substitution + # turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + -e "s/'/'\\\\''/g" \ + -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p' + ;; + esac >> confcache +if cmp -s $cache_file confcache; then + : +else + if test -w $cache_file; then + echo "updating cache $cache_file" + cat confcache > $cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# Any assignment to VPATH causes Sun make to only execute +# the first set of double-colon rules, so remove it if not needed. +# If there is a colon in the path, we need to keep it. +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d' +fi + +trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15 + +DEFS=-DHAVE_CONFIG_H + +# Without the "./", some shells look in PATH for config.status. +: ${CONFIG_STATUS=./config.status} + +echo creating $CONFIG_STATUS +rm -f $CONFIG_STATUS +cat > $CONFIG_STATUS <<EOF +#! /bin/sh +# Generated automatically by configure. +# Run this file to recreate the current configuration. +# This directory was configured as follows, +# on host `(hostname || uname -n) 2>/dev/null | sed 1q`: +# +# $0 $ac_configure_args +# +# Compiler output produced by configure, useful for debugging +# configure, is in ./config.log if it exists. + +ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]" +for ac_option +do + case "\$ac_option" in + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + 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.12.2" + exit 0 ;; + -help | --help | --hel | --he | --h) + echo "\$ac_cs_usage"; exit 0 ;; + *) echo "\$ac_cs_usage"; exit 1 ;; + esac +done + +ac_given_srcdir=$srcdir +ac_given_INSTALL="$INSTALL" + +trap 'rm -fr `echo "Makefile config.h:config.in" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15 +EOF +cat >> $CONFIG_STATUS <<EOF + +# Protect against being on the right side of a sed subst in config.status. +sed 's/%@/@@/; s/@%/@@/; s/%g\$/@g/; /@g\$/s/[\\\\&%]/\\\\&/g; + s/@@/%@/; s/@@/@%/; s/@g\$/%g/' > conftest.subs <<\\CEOF +$ac_vpsub +$extrasub +s%@sim_environment@%$sim_environment%g +s%@sim_alignment@%$sim_alignment%g +s%@sim_assert@%$sim_assert%g +s%@sim_bitsize@%$sim_bitsize%g +s%@sim_endian@%$sim_endian%g +s%@sim_hostendian@%$sim_hostendian%g +s%@sim_float@%$sim_float%g +s%@sim_scache@%$sim_scache%g +s%@sim_default_model@%$sim_default_model%g +s%@sim_hw_cflags@%$sim_hw_cflags%g +s%@sim_hw_objs@%$sim_hw_objs%g +s%@sim_hw@%$sim_hw%g +s%@sim_inline@%$sim_inline%g +s%@sim_packages@%$sim_packages%g +s%@sim_regparm@%$sim_regparm%g +s%@sim_reserved_bits@%$sim_reserved_bits%g +s%@sim_smp@%$sim_smp%g +s%@sim_stdcall@%$sim_stdcall%g +s%@sim_xor_endian@%$sim_xor_endian%g +s%@build_warnings@%$build_warnings%g +s%@SHELL@%$SHELL%g +s%@CFLAGS@%$CFLAGS%g +s%@CPPFLAGS@%$CPPFLAGS%g +s%@CXXFLAGS@%$CXXFLAGS%g +s%@DEFS@%$DEFS%g +s%@LDFLAGS@%$LDFLAGS%g +s%@LIBS@%$LIBS%g +s%@exec_prefix@%$exec_prefix%g +s%@prefix@%$prefix%g +s%@program_transform_name@%$program_transform_name%g +s%@bindir@%$bindir%g +s%@sbindir@%$sbindir%g +s%@libexecdir@%$libexecdir%g +s%@datadir@%$datadir%g +s%@sysconfdir@%$sysconfdir%g +s%@sharedstatedir@%$sharedstatedir%g +s%@localstatedir@%$localstatedir%g +s%@libdir@%$libdir%g +s%@includedir@%$includedir%g +s%@oldincludedir@%$oldincludedir%g +s%@infodir@%$infodir%g +s%@mandir@%$mandir%g +s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g +s%@INSTALL_DATA@%$INSTALL_DATA%g +s%@CC@%$CC%g +s%@SET_MAKE@%$SET_MAKE%g +s%@RANLIB@%$RANLIB%g +s%@CPP@%$CPP%g +s%@ALLOCA@%$ALLOCA%g +s%@USE_NLS@%$USE_NLS%g +s%@MSGFMT@%$MSGFMT%g +s%@GMSGFMT@%$GMSGFMT%g +s%@XGETTEXT@%$XGETTEXT%g +s%@USE_INCLUDED_LIBINTL@%$USE_INCLUDED_LIBINTL%g +s%@CATALOGS@%$CATALOGS%g +s%@CATOBJEXT@%$CATOBJEXT%g +s%@DATADIRNAME@%$DATADIRNAME%g +s%@GMOFILES@%$GMOFILES%g +s%@INSTOBJEXT@%$INSTOBJEXT%g +s%@INTLDEPS@%$INTLDEPS%g +s%@INTLLIBS@%$INTLLIBS%g +s%@INTLOBJS@%$INTLOBJS%g +s%@POFILES@%$POFILES%g +s%@POSUB@%$POSUB%g +s%@INCLUDE_LOCALE_H@%$INCLUDE_LOCALE_H%g +s%@GT_NO@%$GT_NO%g +s%@GT_YES@%$GT_YES%g +s%@MKINSTALLDIRS@%$MKINSTALLDIRS%g +s%@l@%$l%g +s%@host@%$host%g +s%@host_alias@%$host_alias%g +s%@host_cpu@%$host_cpu%g +s%@host_vendor@%$host_vendor%g +s%@host_os@%$host_os%g +s%@target@%$target%g +s%@target_alias@%$target_alias%g +s%@target_cpu@%$target_cpu%g +s%@target_vendor@%$target_vendor%g +s%@target_os@%$target_os%g +s%@build@%$build%g +s%@build_alias@%$build_alias%g +s%@build_cpu@%$build_cpu%g +s%@build_vendor@%$build_vendor%g +s%@build_os@%$build_os%g +s%@LIBOBJS@%$LIBOBJS%g +s%@EXEEXT@%$EXEEXT%g +s%@CC_FOR_BUILD@%$CC_FOR_BUILD%g +s%@HDEFINES@%$HDEFINES%g +s%@AR@%$AR%g +s%@sim_cflags@%$sim_cflags%g +s%@sim_warnings@%$sim_warnings%g +s%@sim_line_nr@%$sim_line_nr%g +s%@sim_config@%$sim_config%g +s%@sim_opcode@%$sim_opcode%g +s%@sim_switch@%$sim_switch%g +s%@sim_dup@%$sim_dup%g +s%@sim_decode_mechanism@%$sim_decode_mechanism%g +s%@sim_jump@%$sim_jump%g +s%@sim_filter@%$sim_filter%g +s%@sim_icache@%$sim_icache%g +s%@sim_hw_src@%$sim_hw_src%g +s%@sim_hw_obj@%$sim_hw_obj%g +s%@sim_pk_src@%$sim_pk_src%g +s%@sim_pk_obj@%$sim_pk_obj%g +s%@sim_bswap@%$sim_bswap%g +s%@sim_igen_smp@%$sim_igen_smp%g +s%@sim_hostbitsize@%$sim_hostbitsize%g +s%@sim_env@%$sim_env%g +s%@sim_timebase@%$sim_timebase%g +s%@sim_trace@%$sim_trace%g +s%@sim_reserved@%$sim_reserved%g +s%@sim_monitor@%$sim_monitor%g +s%@sim_model@%$sim_model%g +s%@sim_model_issue@%$sim_model_issue%g +s%@sim_stdio@%$sim_stdio%g +s%@sim_termio@%$sim_termio%g +s%@sim_devzero@%$sim_devzero%g +s%@sim_callback@%$sim_callback%g +s%@sim_targ_vals@%$sim_targ_vals%g + +CEOF +EOF + +cat >> $CONFIG_STATUS <<\EOF + +# Split the substitutions into bite-sized pieces for seds with +# small command number limits, like on Digital OSF/1 and HP-UX. +ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script. +ac_file=1 # Number of current file. +ac_beg=1 # First line for current file. +ac_end=$ac_max_sed_cmds # Line after last line for current file. +ac_more_lines=: +ac_sed_cmds="" +while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file + else + sed "${ac_end}q" conftest.subs > conftest.s$ac_file + fi + if test ! -s conftest.s$ac_file; then + ac_more_lines=false + rm -f conftest.s$ac_file + else + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f conftest.s$ac_file" + else + ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file" + fi + ac_file=`expr $ac_file + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_cmds` + fi +done +if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat +fi +EOF + +cat >> $CONFIG_STATUS <<EOF + +CONFIG_FILES=\${CONFIG_FILES-"Makefile"} +EOF +cat >> $CONFIG_STATUS <<\EOF +for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case "$ac_file" in + *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` + ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + *) ac_file_in="${ac_file}.in" ;; + esac + + # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories. + + # Remove last slash and all that follows it. Not all systems have dirname. + ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + # The file is in a subdirectory. + test ! -d "$ac_dir" && mkdir "$ac_dir" + ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`" + # A "../" for each directory in $ac_dir_suffix. + ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'` + else + ac_dir_suffix= ac_dots= + fi + + case "$ac_given_srcdir" in + .) srcdir=. + if test -z "$ac_dots"; then top_srcdir=. + else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;; + /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;; + *) # Relative path. + srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix" + top_srcdir="$ac_dots$ac_given_srcdir" ;; + esac + + case "$ac_given_INSTALL" in + [/$]*) INSTALL="$ac_given_INSTALL" ;; + *) INSTALL="$ac_dots$ac_given_INSTALL" ;; + esac + + echo creating "$ac_file" + rm -f "$ac_file" + configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure." + case "$ac_file" in + *Makefile*) ac_comsub="1i\\ +# $configure_input" ;; + *) ac_comsub= ;; + esac + + ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"` + sed -e "$ac_comsub +s%@configure_input@%$configure_input%g +s%@srcdir@%$srcdir%g +s%@top_srcdir@%$top_srcdir%g +s%@INSTALL@%$INSTALL%g +" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file +fi; done +rm -f conftest.s* + +# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where +# NAME is the cpp macro being defined and VALUE is the value it is being given. +# +# ac_d sets the value in "#define NAME VALUE" lines. +ac_dA='s%^\([ ]*\)#\([ ]*define[ ][ ]*\)' +ac_dB='\([ ][ ]*\)[^ ]*%\1#\2' +ac_dC='\3' +ac_dD='%g' +# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE". +ac_uA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_uB='\([ ]\)%\1#\2define\3' +ac_uC=' ' +ac_uD='\4%g' +# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE". +ac_eA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_eB='$%\1#\2define\3' +ac_eC=' ' +ac_eD='%g' + +if test "${CONFIG_HEADERS+set}" != set; then +EOF +cat >> $CONFIG_STATUS <<EOF + CONFIG_HEADERS="config.h:config.in" +EOF +cat >> $CONFIG_STATUS <<\EOF +fi +for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case "$ac_file" in + *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` + ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + *) ac_file_in="${ac_file}.in" ;; + esac + + echo creating $ac_file + + rm -f conftest.frag conftest.in conftest.out + ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"` + cat $ac_file_inputs > conftest.in + +EOF + +# Transform confdefs.h into a sed script conftest.vals that substitutes +# the proper values into config.h.in to produce config.h. And first: +# Protect against being on the right side of a sed subst in config.status. +# Protect against being in an unquoted here document in config.status. +rm -f conftest.vals +cat > conftest.hdr <<\EOF +s/[\\&%]/\\&/g +s%[\\$`]%\\&%g +s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp +s%ac_d%ac_u%gp +s%ac_u%ac_e%gp +EOF +sed -n -f conftest.hdr confdefs.h > conftest.vals +rm -f conftest.hdr + +# This sed command replaces #undef with comments. This is necessary, for +# example, in the case of _POSIX_SOURCE, which is predefined and required +# on some systems where configure will not decide to define it. +cat >> conftest.vals <<\EOF +s%^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */% +EOF + +# Break up conftest.vals because some shells have a limit on +# the size of here documents, and old seds have small limits too. + +rm -f conftest.tail +while : +do + ac_lines=`grep -c . conftest.vals` + # grep -c gives empty output for an empty file on some AIX systems. + if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi + # Write a limited-size here document to conftest.frag. + echo ' cat > conftest.frag <<CEOF' >> $CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS + echo 'CEOF + sed -f conftest.frag conftest.in > conftest.out + rm -f conftest.in + mv conftest.out conftest.in +' >> $CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail + rm -f conftest.vals + mv conftest.tail conftest.vals +done +rm -f conftest.vals + +cat >> $CONFIG_STATUS <<\EOF + rm -f conftest.frag conftest.h + echo "/* $ac_file. Generated automatically by configure. */" > conftest.h + cat conftest.in >> conftest.h + rm -f conftest.in + if cmp -s $ac_file conftest.h 2>/dev/null; then + echo "$ac_file is unchanged" + rm -f conftest.h + else + # Remove last slash and all that follows it. Not all systems have dirname. + ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + # The file is in a subdirectory. + test ! -d "$ac_dir" && mkdir "$ac_dir" + fi + rm -f $ac_file + mv conftest.h $ac_file + fi +fi; done + +EOF +cat >> $CONFIG_STATUS <<EOF + +EOF +cat >> $CONFIG_STATUS <<\EOF +case x$CONFIG_HEADERS in xconfig.h:config.in) echo > stamp-h ;; esac +exit 0 +EOF +chmod +x $CONFIG_STATUS +rm -fr confdefs* $ac_clean_files +test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1 + diff --git a/sim/ppc/configure.in b/sim/ppc/configure.in new file mode 100644 index 00000000000..8e530b6f5f1 --- /dev/null +++ b/sim/ppc/configure.in @@ -0,0 +1,745 @@ +dnl Process this file with autoconf to produce a configure script. +sinclude(../common/aclocal.m4) +AC_PREREQ(2.5)dnl +AC_INIT(Makefile.in) + +AC_PROG_INSTALL +AC_PROG_CC + +# Put a plausible default for CC_FOR_BUILD in Makefile. +if test "x$cross_compiling" = "xno"; then + CC_FOR_BUILD='$(CC)' +else + CC_FOR_BUILD=gcc +fi + +dnl We don't use gettext, but bfd does. So we do the appropriate checks +dnl to see if there are intl libraries we should link against. +ALL_LINGUAS= +CY_GNU_GETTEXT + + +AC_ARG_ENABLE(sim-alignment, +[ --enable-sim-alignment=align Specify strict or nonstrict alignment.], +[case "${enableval}" in + yes | strict | STRICT) sim_alignment="-DWITH_ALIGNMENT=STRICT_ALIGNMENT";; + no | nonstrict | NONSTRICT) sim_alignment="-DWITH_ALIGNMENT=NONSTRICT_ALIGNMENT";; + 0 | default | DEFAULT) sim_alignment="-DWITH_ALIGNMENT=0";; + *) AC_MSG_ERROR("Unknown value $enableval passed to --enable-sim-alignment"); sim_alignment="";; +esac +if test x"$silent" != x"yes" && test x"$sim_alignment" != x""; then + echo "Setting alignment flags = $sim_alignment" 6>&1 +fi],[sim_alignment=""])dnl + + +AC_ARG_ENABLE(sim-assert, +[ --enable-sim-assert Specify whether to perform random assertions.], +[case "${enableval}" in + yes) sim_assert="-DWITH_ASSERT=1";; + no) sim_assert="-DWITH_ASSERT=0";; + *) AC_MSG_ERROR("--enable-sim-assert does not take a value"); sim_assert="";; +esac +if test x"$silent" != x"yes" && test x"$sim_assert" != x""; then + echo "Setting assert flags = $sim_assert" 6>&1 +fi],[sim_assert=""])dnl + + +AC_ARG_ENABLE(sim-bitsize, +[ --enable-sim-bitsize=n Specify target bitsize (32 or 64).], +[case "${enableval}" in + 32|64) sim_bitsize="-DWITH_TARGET_WORD_BITSIZE=$enableval";; + *) AC_MSG_ERROR("--enable-sim-bitsize was given $enableval. Expected 32 or 64"); sim_bitsize="";; +esac +if test x"$silent" != x"yes" && test x"$sim_bitsize" != x""; then + echo "Setting bitsize flags = $sim_bitsize" 6>&1 +fi],[sim_bitsize=""])dnl + + +AC_ARG_ENABLE(sim-bswap, +[ --enable-sim-bswap Use the BSWAP instruction on Intel 486s and Pentiums.], +[case "${enableval}" in + yes) sim_bswap="-DWITH_BSWAP=1";; + no) sim_bswap="-DWITH_BSWAP=0";; + *) AC_MSG_ERROR("--enable-sim-bswap does not take a value"); sim_bswap="";; +esac +if test x"$silent" != x"yes" && test x"$sim_bswap" != x""; then + echo "Setting bswap flags = $sim_bswap" 6>&1 +fi],[sim_bswap=""])dnl + + +AC_ARG_ENABLE(sim-cflags, +[ --enable-sim-cflags=opts Extra CFLAGS for use in building simulator], +[case "${enableval}" in + yes) sim_cflags="-O2 -fomit-frame-pointer";; + no) sim_cflags="";; + *) sim_cflags=`echo "${enableval}" | sed -e "s/,/ /g"`;; +esac +if test x"$silent" != x"yes" && test x"$sim_cflags" != x""; then + echo "Setting sim cflags = $sim_cflags" 6>&1 +fi],[sim_cflags=""])dnl + + +AC_ARG_ENABLE(sim-config, +[ --enable-sim-config=file Override default config file], +[case "${enableval}" in + yes|no) AC_MSG_ERROR("No value supplied for --enable-sim-config=file");; + *) if test -f "${srcdir}/${enableval}"; then + sim_config="${enableval}"; + elif test -f "${srcdir}/${enableval}-config.h"; then + sim_config="${enableval}-config.h" + else + AC_MSG_ERROR("Config file $enableval was not found"); + sim_config=std-config.h + fi;; +esac +if test x"$silent" != x"yes" && test x"$sim_config" != x""; then + echo "Setting config flags = $sim_config" 6>&1 +fi],[sim_config="std-config.h" +if test x"$silent" != x"yes"; then + echo "Setting config flags = $sim_config" 6>&1 +fi])dnl + + +AC_ARG_ENABLE(sim-decode-mechanism, +[ --enable-sim-decode-mechanism=which Specify the instruction decode mechanism.], +[case "${enableval}" in + yes|no) AC_MSG_ERROR("No value supplied for --enable-sim-decode-mechanism=file");; + array|switch|padded-switch|goto-switch) sim_decode_mechanism="-T ${enableval}";; + *) AC_MSG_ERROR("File $enableval is not an opcode rules file"); + sim_decode_mechanism="switch";; +esac +if test x"$silent" != x"yes" && test x"$sim_decode_mechanism" != x""; then + echo "Setting decode mechanism flags = $sim_decode_mechanism" 6>&1 +fi],[sim_decode_mechanism="" +if test x"$silent" != x"yes"; then + echo "Setting decode mechanism flags = $sim_decode_mechanism" +fi])dnl + + +AC_ARG_ENABLE(sim-default-model, +[ --enable-sim-default-model=which Specify default PowerPC to model.], +[case "${enableval}" in + yes|no) AC_MSG_ERROR("No value supplied for --enable-sim-default-model=model");; + *) sim_default_model="-DWITH_DEFAULT_MODEL=${enableval}";; +esac +if test x"$silent" != x"yes" && test x"$sim_default_model" != x""; then + echo "Setting default-model flags = $sim_default_model" 6>&1 +fi],[sim_default_model=""])dnl + + +AC_ARG_ENABLE(sim-duplicate, +[ --enable-sim-duplicate Expand (duplicate) semantic functions.], +[case "${enableval}" in + yes) sim_dup="-E";; + no) sim_dup="";; + *) AC_MSG_ERROR("--enable-sim-duplicate does not take a value"); sim_dup="";; +esac +if test x"$silent" != x"yes" && test x"$sim_dup" != x""; then + echo "Setting duplicate flags = $sim_dup" 6>&1 +fi],[sim_dup="-E" +if test x"$silent" != x"yes"; then + echo "Setting duplicate flags = $sim_dup" 6>&1 +fi])dnl + + +AC_ARG_ENABLE(sim-endian, +[ --enable-sim-endian=endian Specify target byte endian orientation.], +[case "${enableval}" in + yes) case "$target" in + *powerpc-*) sim_endian="-DWITH_TARGET_BYTE_ORDER=BIG_ENDIAN";; + *powerpcle-*) sim_endian="-DWITH_TARGET_BYTE_ORDER=LITTLE_ENDIAN";; + *) echo "Unknown target $target" 1>&6; sim_endian="-DWITH_TARGET_BYTE_ORDER=0";; + esac;; + no) sim_endian="-DWITH_TARGET_BYTE_ORDER=0";; + b*|B*) sim_endian="-DWITH_TARGET_BYTE_ORDER=BIG_ENDIAN";; + l*|L*) sim_endian="-DWITH_TARGET_BYTE_ORDER=LITTLE_ENDIAN";; + *) AC_MSG_ERROR("Unknown value $enableval for --enable-sim-endian"); sim_endian="";; +esac +if test x"$silent" != x"yes" && test x"$sim_endian" != x""; then + echo "Setting endian flags = $sim_endian" 6>&1 +fi],[sim_endian=""])dnl + + +AC_ARG_ENABLE(sim-env, +[ --enable-sim-env=env Specify target environment (operating, virtual, user).], +[case "${enableval}" in + operating | os | oea) sim_env="-DWITH_ENVIRONMENT=OPERATING_ENVIRONMENT";; + virtual | vea) sim_env="-DWITH_ENVIRONMENT=VIRTUAL_ENVIRONMENT";; + user | uea) sim_env="-DWITH_ENVIRONMENT=USER_ENVIRONMENT";; + no) sim_env="-DWITH_ENVIRONMENT=0";; + *) AC_MSG_ERROR("Unknown value $enableval passed to --enable-sim-env"); sim_env="";; +esac +if test x"$silent" != x"yes" && test x"$sim_env" != x""; then + echo "Setting env flags = $sim_env" 6>&1 +fi],[sim_env=""])dnl + + +AC_ARG_ENABLE(sim-filter, +[ --enable-sim-filter=rule Specify filter rules.], +[case "${enableval}" in + yes) AC_MSG_ERROR("--enable-sim-filter must be specified with a rule to filter or no"); sim_filter="";; + no) sim_filter="";; + *) sim_filter="-F $enableval";; +esac +if test x"$silent" != x"yes" && test x"$sim_filter" != x""; then + echo "Setting filter flags = $sim_filter" 6>&1 +fi],[sim_filter="-F 32,f,o" +if test x"$silent" != x"yes"; then + echo "Setting filter flags = $sim_filter" 6>&1 +fi])dnl + + +AC_ARG_ENABLE(sim-float, +[ --enable-sim-float Specify whether to use host floating point or simulate.], +[case "${enableval}" in + yes | hard) sim_float="-DWITH_FLOATING_POINT=HARD_FLOATING_POINT";; + no | soft) sim_float="-DWITH_FLOATING_POINT=SOFT_FLOATING_POINT";; + *) AC_MSG_ERROR("Unknown value $enableval passed to --enable-sim-float"); sim_float="";; +esac +if test x"$silent" != x"yes" && test x"$sim_float" != x""; then + echo "Setting float flags = $sim_float" 6>&1 +fi],[sim_float=""])dnl + + +AC_ARG_ENABLE(sim-hardware, +[ --enable-sim-hardware=list Specify the hardware to be included in the build.], +[hardware="cpu,memory,nvram,iobus,htab,disk,trace,register,vm,init,core,pal,com,eeprom,opic,glue,phb,ide" +case "${enableval}" in + yes) ;; + no) AC_MSG_ERROR("List of hardware must be specified for --enable-sim-hardware"); hardware="";; + ,*) hardware="${hardware}${enableval}";; + *,) hardware="${enableval}${hardware}";; + *) hardware="${enableval}"'';; +esac +sim_hw_src=`echo $hardware | sed -e 's/,/.c hw_/g' -e 's/^/hw_/' -e s'/$/.c/'` +sim_hw_obj=`echo $sim_hw_src | sed -e 's/\.c/.o/g'` +if test x"$silent" != x"yes" && test x"$hardware" != x""; then + echo "Setting hardware to $sim_hw_src, $sim_hw_obj" +fi],[hardware="cpu,memory,nvram,iobus,htab,disk,trace,register,vm,init,core,pal,com,eeprom,opic,glue,phb,ide" +sim_hw_src=`echo $hardware | sed -e 's/,/.c hw_/g' -e 's/^/hw_/' -e s'/$/.c/'` +sim_hw_obj=`echo $sim_hw_src | sed -e 's/\.c/.o/g'` +if test x"$silent" != x"yes"; then + echo "Setting hardware to $sim_hw_src, $sim_hw_obj" +fi])dnl + + +AC_ARG_ENABLE(sim-hostbitsize, +[ --enable-sim-hostbitsize=32|64 Specify host bitsize (32 or 64).], +[case "${enableval}" in + 32|64) sim_hostbitsize="-DWITH_HOST_WORD_BITSIZE=$enableval";; + *) AC_MSG_ERROR("--enable-sim-hostbitsize was given $enableval. Expected 32 or 64"); sim_hostbitsize="";; +esac +if test x"$silent" != x"yes" && test x"$sim_hostbitsize" != x""; then + echo "Setting hostbitsize flags = $sim_hostbitsize" 6>&1 +fi],[sim_hostbitsize=""])dnl + + +AC_ARG_ENABLE(sim-hostendian, +[ --enable-sim-hostendian=end Specify host byte endian orientation.], +[case "${enableval}" in + no) sim_hostendian="-DWITH_HOST_BYTE_ORDER=0";; + b*|B*) sim_hostendian="-DWITH_HOST_BYTE_ORDER=BIG_ENDIAN";; + l*|L*) sim_hostendian="-DWITH_HOST_BYTE_ORDER=LITTLE_ENDIAN";; + *) AC_MSG_ERROR("Unknown value $enableval for --enable-sim-hostendian"); sim_hostendian="";; +esac +if test x"$silent" != x"yes" && test x"$sim_hostendian" != x""; then + echo "Setting hostendian flags = $sim_hostendian" 6>&1 +fi],[ +if test "x$cross_compiling" = "xno"; then + AC_C_BIGENDIAN + if test $ac_cv_c_bigendian = yes; then + sim_hostendian="-DWITH_HOST_BYTE_ORDER=BIG_ENDIAN" + else + sim_hostendian="-DWITH_HOST_BYTE_ORDER=LITTLE_ENDIAN" + fi +else + sim_hostendian="-DWITH_HOST_BYTE_ORDER=0" +fi])dnl + + +AC_ARG_ENABLE(sim-icache, +[ --enable-sim-icache=size Specify instruction-decode cache size and type.], +[icache="-R" + case "${enableval}" in + yes) icache="1024"; sim_icache="-I $icache";; + no) sim_icache="-R";; + *) icache=1024 + sim_icache="-" + for x in `echo "${enableval}" | sed -e "s/,/ /g"`; do + case "$x" in + define) sim_icache="${sim_icache}R";; + semantic) sim_icache="${sim_icache}C";; + insn) sim_icache="${sim_icache}S";; + 0*|1*|2*|3*|4*|5*|6*|7*|8*|9*) icache=$x;; + *) AC_MSG_ERROR("Unknown value $x for --enable-sim-icache"); sim_icache="";; + esac + done + sim_icache="${sim_icache}I $icache";; +esac +if test x"$silent" != x"yes" && test x"$icache" != x""; then + echo "Setting instruction cache size to $icache ($sim_icache)" +fi],[sim_icache="-CSRI 1024" +if test x"$silent" != x"yes"; then + echo "Setting instruction cache size to 1024 ($sim_icache)" +fi])dnl + + +AC_ARG_ENABLE(sim-inline, +[ --enable-sim-inline=inlines Specify which functions should be inlined.], +[sim_inline="" +case "$enableval" in + no) sim_inline="-DDEFAULT_INLINE=0";; + 0) sim_inline="-DDEFAULT_INLINE=0";; + yes | 2) sim_inline="-DDEFAULT_INLINE=ALL_INLINE";; + 1) sim_inline="-DDEFAULT_INLINE=INLINE_LOCALS";; + *) for x in `echo "$enableval" | sed -e "s/,/ /g"`; do + new_flag="" + case "$x" in + *_INLINE=*) new_flag="-D$x";; + *=*) new_flag=`echo "$x" | sed -e "s/=/_INLINE=/" -e "s/^/-D/"`;; + *_INLINE) new_flag="-D$x=ALL_INLINE";; + *) new_flag="-D$x""_INLINE=ALL_INLINE";; + esac + if test x"$sim_inline" = x""; then + sim_inline="$new_flag" + else + sim_inline="$sim_inline $new_flag" + fi + done;; +esac +if test x"$silent" != x"yes" && test x"$sim_inline" != x""; then + echo "Setting inline flags = $sim_inline" 6>&1 +fi],[if test x"$GCC" != ""; then + sim_inline="-DDEFAULT_INLINE=INLINE_LOCALS" + if test x"$silent" != x"yes"; then + echo "Setting inline flags = $sim_inline" 6>&1 + fi +else + sim_inline="" +fi])dnl + + +AC_ARG_ENABLE(sim-jump, +[ --enable-sim-jump Jump between semantic code (instead of call/return).], +[case "${enableval}" in + yes) sim_jump="-J";; + no) sim_jump="";; + *) AC_MSG_ERROR("--enable-sim-jump does not take a value"); sim_jump="";; +esac +if test x"$silent" != x"yes" && test x"$sim_jump" != x""; then + echo "Setting jump flag = $sim_jump" 6>&1 +fi],[sim_jump="" +if test x"$silent" != x"yes"; then + echo "Setting jump flag = $sim_jump" 6>&1 +fi])dnl + + +AC_ARG_ENABLE(sim-line-nr, +[ --enable-sim-line-nr=opts Generate extra CPP code that references source rather than generated code], +[case "${enableval}" in + yes) sim_line_nr="";; + no) sim_line_nr="-L";; + *) AC_MSG_ERROR("--enable-sim-line-nr does not take a value"); sim_line_nr="";; +esac +if test x"$silent" != x"yes" && test x"$sim_line_nr" != x""; then + echo "Setting warning flags = $sim_line_nr" 6>&1 +fi],[sim_line_nr=""])dnl + + +AC_ARG_ENABLE(sim-model, +[ --enable-sim-model=which Specify PowerPC to model.], +[case "${enableval}" in + yes|no) AC_MSG_ERROR("No value supplied for --enable-sim-model=model");; + *) sim_model="-DWITH_MODEL=${enableval}";; +esac +if test x"$silent" != x"yes" && test x"$sim_model" != x""; then + echo "Setting model flags = $sim_model" 6>&1 +fi],[sim_model=""])dnl + + +AC_ARG_ENABLE(sim-model-issue, +[ --enable-sim-model-issue Specify whether to simulate model specific actions], +[case "${enableval}" in + yes) sim_model_issue="-DWITH_MODEL_ISSUE=MODEL_ISSUE_PROCESS";; + no) sim_model_issue="-DWITH_MODEL_ISSUE=MODEL_ISSUE_IGNORE";; + *) AC_MSG_ERROR("--enable-sim-model-issue does not take a value"); sim_model_issue="";; +esac +if test x"$silent" != x"yes"; then + echo "Setting model-issue flags = $sim_model_issue" 6>&1 +fi],[sim_model_issue=""])dnl + + +AC_ARG_ENABLE(sim-monitor, +[ --enable-sim-monitor=mon Specify whether to enable monitoring events.], +[case "${enableval}" in + yes) sim_monitor="-DWITH_MON='MONITOR_INSTRUCTION_ISSUE | MONITOR_LOAD_STORE_UNIT'";; + no) sim_monitor="-DWITH_MON=0";; + instruction) sim_monitor="-DWITH_MON=MONITOR_INSTRUCTION_ISSUE";; + memory) sim_monitor="-DWITH_MON=MONITOR_LOAD_STORE_UNIT";; + *) AC_MSG_ERROR("Unknown value $enableval passed to --enable-sim-mon"); sim_env="";; +esac +if test x"$silent" != x"yes" && test x"$sim_monitor" != x""; then + echo "Setting monitor flags = $sim_monitor" 6>&1 +fi],[sim_monitor=""])dnl + + +AC_ARG_ENABLE(sim-opcode, +[ --enable-sim-opcode=which Override default opcode lookup.], +[case "${enableval}" in + yes|no) AC_MSG_ERROR("No value supplied for --enable-sim-opcode=file");; + *) if test -f "${srcdir}/${enableval}"; then + sim_opcode="${enableval}" + elif test -f "${srcdir}/dc-${enableval}"; then + sim_opcode="dc-${enableval}" + else + AC_MSG_ERROR("File $enableval is not an opcode rules file"); + sim_opcode="dc-complex" + fi;; +esac +if test x"$silent" != x"yes" && test x"$sim_opcode" != x""; then + echo "Setting opcode flags = $sim_opcode" 6>&1 +fi],[sim_opcode="dc-complex" +if test x"$silent" != x"yes"; then + echo "Setting opcode flags = $sim_opcode" +fi])dnl + + +AC_ARG_ENABLE(sim-packages, +[ --enable-sim-packages=list Specify the packages to be included in the build.], +[packages=disklabel +case "${enableval}" in + yes) ;; + no) AC_MSG_ERROR("List of packages must be specified for --enable-sim-packages"); packages="";; + ,*) packages="${packages}${enableval}";; + *,) packages="${enableval}${packages}";; + *) packages="${enableval}"'';; +esac +sim_pk_src=`echo $packages | sed -e 's/,/.c pk_/g' -e 's/^/pk_/' -e 's/$/.c/'` +sim_pk_obj=`echo $sim_pk_src | sed -e 's/\.c/.o/g'` +if test x"$silent" != x"yes" && test x"$packages" != x""; then + echo "Setting packages to $sim_pk_src, $sim_pk_obj" +fi],[packages=disklabel +sim_pk_src=`echo $packages | sed -e 's/,/.c pk_/g' -e 's/^/pk_/' -e 's/$/.c/'` +sim_pk_obj=`echo $sim_pk_src | sed -e 's/\.c/.o/g'` +if test x"$silent" != x"yes"; then + echo "Setting packages to $sim_pk_src, $sim_pk_obj" +fi])dnl + + +AC_ARG_ENABLE(sim-regparm, +[ --enable-sim-regparm=nr-parm Pass parameters in registers instead of on the stack - x86/GCC specific.], +[case "${enableval}" in + 0*|1*|2*|3*|4*|5*|6*|7*|8*|9*) sim_regparm="-DWITH_REGPARM=${enableval}";; + no) sim_regparm="" ;; + yes) sim_regparm="-DWITH_REGPARM=3";; + *) AC_MSG_ERROR("Unknown value $enableval for --enable-sim-regparm"); sim_regparm="";; +esac +if test x"$silent" != x"yes" && test x"$sim_regparm" != x""; then + echo "Setting regparm flags = $sim_regparm" 6>&1 +fi],[sim_regparm=""])dnl + + +AC_ARG_ENABLE(sim-reserved-bits, +[ --enable-sim-reserved-bits Specify whether to check reserved bits in instruction.], +[case "${enableval}" in + yes) sim_reserved="-DWITH_RESERVED_BITS=1";; + no) sim_reserved="-DWITH_RESERVED_BITS=0";; + *) AC_MSG_ERROR("--enable-sim-reserved-bits does not take a value"); sim_reserved="";; +esac +if test x"$silent" != x"yes" && test x"$sim_reserved" != x""; then + echo "Setting reserved flags = $sim_reserved" 6>&1 +fi],[sim_reserved=""])dnl + + +AC_ARG_ENABLE(sim-smp, +[ --enable-sim-smp=n Specify number of processors to configure for.], +[case "${enableval}" in + yes) sim_smp="-DWITH_SMP=5" ; sim_igen_smp="-N 5";; + no) sim_smp="-DWITH_SMP=0" ; sim_igen_smp="-N 0";; + *) sim_smp="-DWITH_SMP=$enableval" ; sim_igen_smp="-N $enableval";; +esac +if test x"$silent" != x"yes" && test x"$sim_smp" != x""; then + echo "Setting smp flags = $sim_smp" 6>&1 +fi],[sim_smp="-DWITH_SMP=5" ; sim_igen_smp="-N 5" +if test x"$silent" != x"yes"; then + echo "Setting smp flags = $sim_smp" 6>&1 +fi])dnl + + +AC_ARG_ENABLE(sim-stdcall, +[ --enable-sim-stdcall=type Use an alternative function call/return mechanism - x86/GCC specific.], +[case "${enableval}" in + no) sim_stdcall="" ;; + std*) sim_stdcall="-DWITH_STDCALL=1";; + yes) sim_stdcall="-DWITH_STDCALL=1";; + *) AC_MSG_ERROR("Unknown value $enableval for --enable-sim-stdcall"); sim_stdcall="";; +esac +if test x"$silent" != x"yes" && test x"$sim_stdcall" != x""; then + echo "Setting function call flags = $sim_stdcall" 6>&1 +fi],[sim_stdcall=""])dnl + + +AC_ARG_ENABLE(sim-stdio, +[ --enable-sim-stdio Specify whether to use stdio for console input/output.], +[case "${enableval}" in + yes) sim_stdio="-DWITH_STDIO=DO_USE_STDIO";; + no) sim_stdio="-DWITH_STDIO=DONT_USE_STDIO";; + *) AC_MSG_ERROR("Unknown value $enableval passed to --enable-sim-stdio"); sim_stdio="";; +esac +if test x"$silent" != x"yes" && test x"$sim_stdio" != x""; then + echo "Setting stdio flags = $sim_stdio" 6>&1 +fi],[sim_stdio=""])dnl + + +AC_ARG_ENABLE(sim-switch, +[ --enable-sim-switch Use a switch instead of a table for instruction call.], +[case "${enableval}" in + yes) sim_switch="-s";; + no) sim_switch="";; + *) AC_MSG_ERROR("--enable-sim-switch does not take a value"); sim_switch="";; +esac +if test x"$silent" != x"yes" && test x"$sim_switch" != x""; then + echo "Setting switch flags = $sim_switch" 6>&1 +fi],[sim_switch=""; +if test x"$silent" != x"yes"; then + echo "Setting switch flags = $sim_switch" 6>&1 +fi])dnl + + +AC_ARG_ENABLE(sim-timebase, +[ --enable-sim-timebase Specify whether the PPC timebase is supported.], +[case "${enableval}" in + yes) sim_timebase="-DWITH_TIME_BASE=1";; + no) sim_timebase="-DWITH_TIME_BASE=0";; + *) AC_MSG_ERROR("--enable-sim-timebase does not take a value"); sim_timebase="";; +esac +if test x"$silent" != x"yes" && test x"$sim_timebase" != x""; then + echo "Setting timebase flags = $sim_timebase" 6>&1 +fi],[sim_timebase=""])dnl + + +AC_ARG_ENABLE(sim-trace, +[ --enable-sim-trace Specify whether tracing is supported.], +[case "${enableval}" in + yes) sim_trace="-DWITH_TRACE=1";; + no) sim_trace="-DWITH_TRACE=0";; + *) AC_MSG_ERROR("--enable-sim-trace does not take a value"); sim_trace="";; +esac +if test x"$silent" != x"yes" && test x"$sim_trace" != x""; then + echo "Setting trace flags = $sim_trace" 6>&1 +fi],[sim_trace=""])dnl + + +AC_ARG_ENABLE(sim-warnings, +[ --enable-sim-warnings=opts Extra CFLAGS for turning on compiler warnings except for idecode.o, semantics.o and psim.o], +[case "${enableval}" in + yes) sim_warnings="-Werror -Wall -Wpointer-arith -Wmissing-prototypes -Wmissing-declarations ";; + no) sim_warnings="-w";; + *) sim_warnings=`echo "${enableval}" | sed -e "s/,/ /g"`;; +esac +if test x"$silent" != x"yes" && test x"$sim_warnings" != x""; then + echo "Setting warning flags = $sim_warnings" 6>&1 +fi],[sim_warnings=""])dnl + + +AC_ARG_ENABLE(sim-xor-endian, +[ --enable-sim-xor-endian=n Specify number bytes involved in PowerPC XOR bi-endian mode (default 8).], +[case "${enableval}" in + yes) sim_xor_endian="-DWITH_XOR_ENDIAN=8";; + no) sim_xor_endian="-DWITH_XOR_ENDIAN=0";; + *) sim_xor_endian="-DWITH_XOR_ENDIAN=$enableval";; +esac +if test x"$silent" != x"yes" && test x"$sim_xor_endian" != x""; then + echo "Setting xor-endian flag = $sim_xor_endian" 6>&1 +fi],[sim_xor_endian=""])dnl + + +AC_CONFIG_AUX_DIR(`cd $srcdir;pwd`/../..) +AC_CANONICAL_SYSTEM +AC_ARG_PROGRAM + +. ${srcdir}/../../bfd/configure.host + +AC_CONFIG_HEADER(config.h:config.in) + +AC_STRUCT_ST_BLKSIZE +AC_STRUCT_ST_BLOCKS +AC_STRUCT_ST_RDEV +AC_STRUCT_TIMEZONE + +AC_TYPE_GETGROUPS +AC_TYPE_MODE_T +AC_TYPE_OFF_T +AC_TYPE_PID_T +AC_TYPE_SIGNAL +AC_TYPE_SIZE_T +AC_TYPE_UID_T + +AC_CHECK_FUNCS(access cfgetispeed cfgetospeed cfsetispeed cfsetospeed chdir chmod chown dup dup2 fchmod fchown fcntl fstat fstatfs getdirentries getegid geteuid getgid getpid getppid getrusage gettimeofday getuid ioctl kill link lseek lstat mkdir pipe readlink rmdir setreuid setregid stat sigprocmask stat symlink tcgetattr tcsetattr tcsendbreak tcdrain tcflush tcflow tcgetpgrp tcsetpgrp time umask unlink) + +AC_CHECK_HEADERS(fcntl.h stdlib.h string.h strings.h sys/ioctl.h sys/param.h sys/resource.h sys/stat.h sys/termio.h sys/termios.h sys/time.h sys/times.h sys/types.h time.h unistd.h) +AC_HEADER_DIRENT + +dnl Figure out what type of termio/termios support there is +sim_termio="" +AC_MSG_CHECKING(for struct termios) +AC_CACHE_VAL(ac_cv_termios_struct, +[AC_TRY_COMPILE([#include <sys/types.h> +#include <sys/termios.h>], +[static struct termios x; + x.c_iflag = 0; + x.c_oflag = 0; + x.c_cflag = 0; + x.c_lflag = 0; + x.c_cc[NCCS] = 0;], +ac_cv_termios_struct=yes, ac_cv_termios_struct=no)]) +AC_MSG_RESULT($ac_cv_termios_struct) +if test $ac_cv_termios_struct = yes; then + sim_termio="$sim_termio -DHAVE_TERMIOS_STRUCTURE" +fi + +if test "$ac_cv_termios_struct" = "yes"; then + AC_MSG_CHECKING(for c_line field in struct termios) + AC_CACHE_VAL(ac_cv_termios_cline, + [AC_TRY_COMPILE([#include <sys/types.h> +#include <sys/termios.h>], +[static struct termios x; x.c_line = 0;], + ac_cv_termios_cline=yes, ac_cv_termios_cline=no)]) + + AC_MSG_RESULT($ac_cv_termios_cline) + if test $ac_cv_termios_cline = yes; then + sim_termio="$sim_termio -DHAVE_TERMIOS_CLINE" + fi +else + ac_cv_termios_cline=no +fi + +if test "$ac_cv_termios_struct" != "yes"; then + AC_MSG_CHECKING(for struct termio) + AC_CACHE_VAL(ac_cv_termio_struct, + [AC_TRY_COMPILE([#include <sys/types.h> +#include <sys/termio.h>], +[static struct termio x; + x.c_iflag = 0; + x.c_oflag = 0; + x.c_cflag = 0; + x.c_lflag = 0; + x.c_cc[NCC] = 0;], +ac_cv_termio_struct=yes, ac_cv_termio_struct=no)]) + AC_MSG_RESULT($ac_cv_termio_struct) + if test $ac_cv_termio_struct = yes; then + sim_termio="$sim_termio -DHAVE_TERMIO_STRUCTURE" + fi +else + ac_cv_termio_struct=no +fi + +if test "$ac_cv_termio_struct" = "yes"; then + AC_MSG_CHECKING(for c_line field in struct termio) + AC_CACHE_VAL(ac_cv_termio_cline, + [AC_TRY_COMPILE([#include <sys/types.h> +#include <sys/termio.h>], +[static struct termio x; x.c_line = 0;], + ac_cv_termio_cline=yes, ac_cv_termio_cline=no)]) + + AC_MSG_RESULT($ac_cv_termio_cline) + if test $ac_cv_termio_cline = yes; then + sim_termio="$sim_termio -DHAVE_TERMIO_CLINE" + fi +else + ac_cv_termio_cline=no +fi + +dnl Figure out if /dev/zero exists or not +sim_devzero="" +AC_MSG_CHECKING(for /dev/zero) +AC_CACHE_VAL(ac_cv_devzero, +[AC_TRY_RUN([#include <fcntl.h> +main () { + char buf[2048]; + int i; + int fd = open ("/dev/zero", O_RDONLY); + if (fd < 0) + return 1; + for (i = 0; i < sizeof (buf); i++) + buf[i] = 1; + if (read (fd, buf, sizeof (buf)) != sizeof (buf)) + return 1; + for (i = 0; i < sizeof (buf); i++) + if (buf[i]) + return 1; + return 0; +}],[ac_cv_devzero=yes],[ac_cv_devzero=no],[ac_cv_devzero=no])]) +AC_MSG_RESULT($ac_cv_devzero) +if test $ac_cv_devzero = yes; then + sim_devzero="-DHAVE_DEVZERO" +else + sim_devzero="" +fi + +dnl Figure out if we are in the new Cygnus tree with a common directory or not +AC_MSG_CHECKING(for common simulator directory) +if test -f "${srcdir}/../common/callback.c"; then + AC_MSG_RESULT(yes) + sim_callback="callback.o targ-map.o" + sim_targ_vals="targ-vals.h targ-map.c targ-vals.def" +else + AC_MSG_RESULT(no) + sim_callback="" + sim_targ_vals="" +fi + +dnl Check for exe extension +AM_EXEEXT + +AC_SUBST(CC_FOR_BUILD) +AC_SUBST(CFLAGS) +AC_SUBST(HDEFINES) +AR=${AR-ar} +AC_SUBST(AR) +AC_PROG_RANLIB +AC_SUBST(sim_cflags) +AC_SUBST(sim_warnings) +AC_SUBST(sim_line_nr) +AC_SUBST(sim_config) +AC_SUBST(sim_opcode) +AC_SUBST(sim_switch) +AC_SUBST(sim_dup) +AC_SUBST(sim_decode_mechanism) +AC_SUBST(sim_jump) +AC_SUBST(sim_filter) +AC_SUBST(sim_icache) +AC_SUBST(sim_hw_src) +AC_SUBST(sim_hw_obj) +AC_SUBST(sim_pk_src) +AC_SUBST(sim_pk_obj) +AC_SUBST(sim_inline) +AC_SUBST(sim_bswap) +AC_SUBST(sim_endian) +AC_SUBST(sim_regparm) +AC_SUBST(sim_stdcall) +AC_SUBST(sim_xor_endian) +AC_SUBST(sim_hostendian) +AC_SUBST(sim_smp) +AC_SUBST(sim_igen_smp) +AC_SUBST(sim_bitsize) +AC_SUBST(sim_hostbitsize) +AC_SUBST(sim_env) +AC_SUBST(sim_timebase) +AC_SUBST(sim_alignment) +AC_SUBST(sim_float) +AC_SUBST(sim_trace) +AC_SUBST(sim_assert) +AC_SUBST(sim_reserved) +AC_SUBST(sim_monitor) +AC_SUBST(sim_model) +AC_SUBST(sim_default_model) +AC_SUBST(sim_model_issue) +AC_SUBST(sim_stdio) +AC_SUBST(sim_termio) +AC_SUBST(sim_devzero) +AC_SUBST(sim_callback) +AC_SUBST(sim_targ_vals) + +AC_OUTPUT(Makefile, +[case x$CONFIG_HEADERS in xconfig.h:config.in) echo > stamp-h ;; esac]) diff --git a/sim/ppc/corefile-n.h b/sim/ppc/corefile-n.h new file mode 100644 index 00000000000..0e4cae8fcbc --- /dev/null +++ b/sim/ppc/corefile-n.h @@ -0,0 +1,97 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1996, 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 N +#error "N must be #defined" +#endif + +/* NOTE: see end of file for #undef of these macros */ +#define unsigned_N XCONCAT2(unsigned_,N) +#define T2H_N XCONCAT2(T2H_,N) +#define H2T_N XCONCAT2(H2T_,N) + +#define core_map_read_N XCONCAT2(core_map_read_,N) +#define core_map_write_N XCONCAT2(core_map_write_,N) + +INLINE_CORE(unsigned_N) +core_map_read_N(core_map *map, + unsigned_word addr, + cpu *processor, + unsigned_word cia) +{ + core_mapping *mapping = core_map_find_mapping(map, + addr, + sizeof(unsigned_N), + processor, + cia, + 1); /*abort*/ + if (WITH_CALLBACK_MEMORY && mapping->device != NULL) { + unsigned_N data; + if (device_io_read_buffer(mapping->device, + &data, + mapping->space, + addr, + sizeof(unsigned_N), /* nr_bytes */ + processor, + cia) != sizeof(unsigned_N)) + device_error(mapping->device, "internal error - core_read_N() - io_read_buffer should not fail"); + return T2H_N(data); + } + else + return T2H_N(*(unsigned_N*)core_translate(mapping, addr)); +} + + + +INLINE_CORE(void) +core_map_write_N(core_map *map, + unsigned_word addr, + unsigned_N val, + cpu *processor, + unsigned_word cia) +{ + core_mapping *mapping = core_map_find_mapping(map, + addr, + sizeof(unsigned_N), + processor, + cia, + 1); /*abort*/ + if (WITH_CALLBACK_MEMORY && mapping->device != NULL) { + unsigned_N data = H2T_N(val); + if (device_io_write_buffer(mapping->device, + &data, + mapping->space, + addr, + sizeof(unsigned_N), /* nr_bytes */ + processor, + cia) != sizeof(unsigned_N)) + device_error(mapping->device, "internal error - core_write_N() - io_write_buffer should not fail"); + } + else + *(unsigned_N*)core_translate(mapping, addr) = H2T_N(val); +} + +/* NOTE: see start of file for #define of these macros */ +#undef unsigned_N +#undef T2H_N +#undef H2T_N +#undef core_map_read_N +#undef core_map_write_N diff --git a/sim/ppc/corefile.c b/sim/ppc/corefile.c new file mode 100644 index 00000000000..00295bb2a62 --- /dev/null +++ b/sim/ppc/corefile.c @@ -0,0 +1,415 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1996, 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_ + +#include "basics.h" +#include "device_table.h" +#include "corefile.h" + +typedef struct _core_mapping core_mapping; +struct _core_mapping { + /* common */ + int level; + int space; + unsigned_word base; + unsigned_word bound; + unsigned nr_bytes; + /* memory map */ + void *free_buffer; + void *buffer; + /* callback map */ + device *device; + /* growth */ + core_mapping *next; +}; + +struct _core_map { + core_mapping *first; +}; + +typedef enum { + core_read_map, + core_write_map, + core_execute_map, + nr_core_map_types, +} core_map_types; + +struct _core { + core_map map[nr_core_map_types]; +}; + + +INLINE_CORE\ +(core *) +core_create(void) +{ + return ZALLOC(core); +} + + +INLINE_CORE\ +(core *) +core_from_device(device *root) +{ + root = device_root(root); + ASSERT(strcmp(device_name(root), "core") == 0); + return device_data(root); +} + + +INLINE_CORE\ +(void) +core_init(core *memory) +{ + core_map_types access_type; + for (access_type = 0; + access_type < nr_core_map_types; + access_type++) { + core_map *map = memory->map + access_type; + /* blow away old mappings */ + core_mapping *curr = map->first; + while (curr != NULL) { + core_mapping *tbd = curr; + curr = curr->next; + if (tbd->free_buffer != NULL) { + ASSERT(tbd->buffer != NULL); + zfree(tbd->free_buffer); + } + zfree(tbd); + } + map->first = NULL; + } +} + + + +/* the core has three sub mappings that the more efficient + read/write fixed quantity functions use */ + +INLINE_CORE\ +(core_map *) +core_readable(core *memory) +{ + return memory->map + core_read_map; +} + +INLINE_CORE\ +(core_map *) +core_writeable(core *memory) +{ + return memory->map + core_write_map; +} + +INLINE_CORE\ +(core_map *) +core_executable(core *memory) +{ + return memory->map + core_execute_map; +} + + + +STATIC_INLINE_CORE\ +(core_mapping *) +new_core_mapping(attach_type attach, + int space, + unsigned_word addr, + unsigned nr_bytes, + device *device, + void *buffer, + void *free_buffer) +{ + core_mapping *new_mapping = ZALLOC(core_mapping); + /* common */ + new_mapping->level = attach; + new_mapping->space = space; + new_mapping->base = addr; + new_mapping->nr_bytes = nr_bytes; + new_mapping->bound = addr + (nr_bytes - 1); + if (attach == attach_raw_memory) { + new_mapping->buffer = buffer; + new_mapping->free_buffer = free_buffer; + } + else if (attach >= attach_callback) { + new_mapping->device = device; + } + else { + error("new_core_mapping() - internal error - unknown attach type %d\n", + attach); + } + return new_mapping; +} + + +STATIC_INLINE_CORE\ +(void) +core_map_attach(core_map *access_map, + attach_type attach, + int space, + unsigned_word addr, + unsigned nr_bytes, /* host limited */ + device *client, /*callback/default*/ + void *buffer, /*raw_memory*/ + void *free_buffer) /*raw_memory*/ +{ + /* find the insertion point for this additional mapping and insert */ + core_mapping *next_mapping; + core_mapping **last_mapping; + + /* actually do occasionally get a zero size map */ + if (nr_bytes == 0) { + device_error(client, "called on core_map_attach() with size zero"); + } + + /* find the insertion point (between last/next) */ + next_mapping = access_map->first; + last_mapping = &access_map->first; + while(next_mapping != NULL + && (next_mapping->level < attach + || (next_mapping->level == attach + && next_mapping->bound < addr))) { + /* provided levels are the same */ + /* assert: next_mapping->base > all bases before next_mapping */ + /* assert: next_mapping->bound >= all bounds before next_mapping */ + last_mapping = &next_mapping->next; + next_mapping = next_mapping->next; + } + + /* check insertion point correct */ + ASSERT(next_mapping == NULL || next_mapping->level >= attach); + if (next_mapping != NULL && next_mapping->level == attach + && next_mapping->base < (addr + (nr_bytes - 1))) { + device_error(client, "map overlap when attaching %d:0x%lx (%ld)", + space, (long)addr, (long)nr_bytes); + } + + /* create/insert the new mapping */ + *last_mapping = new_core_mapping(attach, + space, addr, nr_bytes, + client, buffer, free_buffer); + (*last_mapping)->next = next_mapping; +} + + +INLINE_CORE\ +(void) +core_attach(core *memory, + attach_type attach, + int space, + access_type access, + unsigned_word addr, + unsigned nr_bytes, /* host limited */ + device *client) /*callback/default*/ +{ + core_map_types access_map; + void *buffer; + void *free_buffer; + if (attach == attach_raw_memory) { + /* Padd out the raw buffer to ensure that ADDR starts on a + correctly aligned boundary */ + int padding = (addr % sizeof (unsigned64)); + free_buffer = zalloc(nr_bytes + padding); + buffer = (char*)free_buffer + padding; + } + else { + buffer = NULL; + free_buffer = &buffer; /* marker for assertion */ + } + for (access_map = 0; + access_map < nr_core_map_types; + access_map++) { + switch (access_map) { + case core_read_map: + if (access & access_read) + core_map_attach(memory->map + access_map, + attach, + space, addr, nr_bytes, + client, buffer, free_buffer); + free_buffer = NULL; + break; + case core_write_map: + if (access & access_write) + core_map_attach(memory->map + access_map, + attach, + space, addr, nr_bytes, + client, buffer, free_buffer); + free_buffer = NULL; + break; + case core_execute_map: + if (access & access_exec) + core_map_attach(memory->map + access_map, + attach, + space, addr, nr_bytes, + client, buffer, free_buffer); + free_buffer = NULL; + break; + default: + error("core_attach() internal error\n"); + break; + } + } + /* allocated buffer must attach to at least one thing */ + ASSERT(free_buffer == NULL); +} + + +STATIC_INLINE_CORE\ +(core_mapping *) +core_map_find_mapping(core_map *map, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia, + int abort) /*either 0 or 1 - helps inline */ +{ + core_mapping *mapping = map->first; + ASSERT((addr & (nr_bytes - 1)) == 0); /* must be aligned */ + ASSERT((addr + (nr_bytes - 1)) >= addr); /* must not wrap */ + while (mapping != NULL) { + if (addr >= mapping->base + && (addr + (nr_bytes - 1)) <= mapping->bound) + return mapping; + mapping = mapping->next; + } + if (abort) + error("core_find_mapping() - access to unmaped address, attach a default map to handle this - addr=0x%x nr_bytes=0x%x processor=0x%x cia=0x%x\n", + addr, nr_bytes, processor, cia); + return NULL; +} + + +STATIC_INLINE_CORE\ +(void *) +core_translate(core_mapping *mapping, + unsigned_word addr) +{ + return (void *)(((char *)mapping->buffer) + addr - mapping->base); +} + + +INLINE_CORE\ +(unsigned) +core_map_read_buffer(core_map *map, + void *buffer, + unsigned_word addr, + unsigned len) +{ + unsigned count = 0; + while (count < len) { + unsigned_word raddr = addr + count; + core_mapping *mapping = + core_map_find_mapping(map, + raddr, 1, + NULL, /*processor*/ + 0, /*cia*/ + 0); /*dont-abort*/ + if (mapping == NULL) + break; + if (mapping->device != NULL) { + int nr_bytes = len - count; + if (raddr + nr_bytes - 1> mapping->bound) + nr_bytes = mapping->bound - raddr + 1; + if (device_io_read_buffer(mapping->device, + (unsigned_1*)buffer + count, + mapping->space, + raddr, + nr_bytes, + 0, /*processor*/ + 0 /*cpu*/) != nr_bytes) + break; + count += nr_bytes; + } + else { + ((unsigned_1*)buffer)[count] = + *(unsigned_1*)core_translate(mapping, raddr); + count += 1; + } + } + return count; +} + + +INLINE_CORE\ +(unsigned) +core_map_write_buffer(core_map *map, + const void *buffer, + unsigned_word addr, + unsigned len) +{ + unsigned count = 0; + while (count < len) { + unsigned_word raddr = addr + count; + core_mapping *mapping = core_map_find_mapping(map, + raddr, 1, + NULL, /*processor*/ + 0, /*cia*/ + 0); /*dont-abort*/ + if (mapping == NULL) + break; + if (mapping->device != NULL) { + int nr_bytes = len - count; + if (raddr + nr_bytes - 1 > mapping->bound) + nr_bytes = mapping->bound - raddr + 1; + if (device_io_write_buffer(mapping->device, + (unsigned_1*)buffer + count, + mapping->space, + raddr, + nr_bytes, + 0, /*processor*/ + 0 /*cpu*/) != nr_bytes) + break; + count += nr_bytes; + } + else { + *(unsigned_1*)core_translate(mapping, raddr) = + ((unsigned_1*)buffer)[count]; + count += 1; + } + } + return count; +} + + +/* define the read/write 1/2/4/8/word functions */ + +#define N 1 +#include "corefile-n.h" +#undef N + +#define N 2 +#include "corefile-n.h" +#undef N + +#define N 4 +#include "corefile-n.h" +#undef N + +#define N 8 +#include "corefile-n.h" +#undef N + +#define N word +#include "corefile-n.h" +#undef N + +#endif /* _CORE_C_ */ diff --git a/sim/ppc/corefile.h b/sim/ppc/corefile.h new file mode 100644 index 00000000000..726470ca3f3 --- /dev/null +++ b/sim/ppc/corefile.h @@ -0,0 +1,227 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1996, 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_ + +/* Introduction: + + The core device, positioned at the top of the device tree that + models the architecure being simulated, acts as an interface + between the processor engines and the modeled devices. + + On the one side the processor engines issue read and write requests + to the core (each request further catagorised as being for an + instruction or data subunit) while on the other side, the core is + receiving address configuration and DMA requests from child + devices. + + In the below a synopsis of the core object and device in PSIM is + given, details of the object can be found in the files + <<corefile.h>> and <<corefile.c>>. + + */ + +/* Core:: + + At the heart of the interface between devices and processor engines + is a single core object. This object, in turn, has two children: + + o a core device which exists in the device tree and provides + an interface to the core object to child devices. + + o a set of access maps which provide an efficient + interface to the core object for the processor engines. + + */ + +/* basic types */ + +typedef struct _core core; +typedef struct _core_map core_map; + +/* constructor */ + +INLINE_CORE\ +(core *) core_create +(void); + +INLINE_CORE\ +(core *) core_from_device +(device *root); + +INLINE_CORE\ +(void) core_init +(core *memory); + +/* Core map management::: + + The core ojbect manages two different types of address maps: + + o raw-memory - the address range can be implemented using + a simple byte array. No device needs to be notifed of + any accesses to the specified memory range. + + o callback - Any access to the specified address range + should be passed on to the associated device. That device + can in turn resolve the access - handling or aborting or + restarting it. + + For callback maps it is possible to further order them by + specifiying specifying a callback level (eg callback + 1). + + When the core is resolving an access it searches each of the maps + in order. First raw-memory and then callback maps (in assending + order of level). This search order makes it possible for latter + maps to overlap earlier ones. For instance, a device that wants to + be notified of all accesses that are not covered by raw-memory maps + could attach its self with an address range of the entire address + space. + + In addition, each attached address map as an associated set of + access attributes (readable, writeable, executable) which are + verified as part of resolving each access. + + */ + +INLINE_CORE\ +(void) core_attach +(core *map, + attach_type attach, + int address_space, + access_type access, + unsigned_word addr, + unsigned nr_bytes, /* host limited */ + device *device); /*callback/default*/ + +/* Bugs::: + + At present there is no method for removing address maps. That will + be implemented in a future release. + + The operation of mapping between an address and its destination + device or memory array is currently implemented using a simple + linked list. The posibility of replacing this list with a more + powerfull data structure exists. + + */ + + +/* Device:: + + The device that corresponds to the core object is described + separatly in the devices section. + + */ + +/* Access maps:: + + Providing an interface between the processor engines and the core + object are the access maps (core_map). Three access maps are + provided, one for each of the possible access requests that can be + generated by a processor. + + o read + + o write + + o execute + + A processor being able to request a read (or write) or write + operation to any of the maps. Those operations can either be + highly efficient (by specifying a specific transfer size) or + generic (specifying a parameterized number of bytes). + + Internally the core object takes the request, determines the + approperiate attached address space that it should handle it passes + it on. + + */ + +INLINE_CORE\ +(core_map *) core_readable +(core *memory); + +INLINE_CORE\ +(core_map *) core_writeable +(core *memory); + +INLINE_CORE\ +(core_map *) core_executable +(core *memory); + +/* Variable sized read/write + + 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_CORE\ +(unsigned) core_map_read_buffer +(core_map *map, + void *buffer, + unsigned_word addr, + unsigned nr_bytes); + +INLINE_CORE\ +(unsigned) core_map_write_buffer +(core_map *map, + const void *buffer, + unsigned_word addr, + unsigned nr_bytes); + + +/* 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_CORE_WRITE_N(N) \ +INLINE_CORE\ +(void) core_map_write_##N \ +(core_map *map, \ + unsigned_word addr, \ + unsigned_##N val, \ + cpu *processor, \ + unsigned_word cia); + +DECLARE_CORE_WRITE_N(1) +DECLARE_CORE_WRITE_N(2) +DECLARE_CORE_WRITE_N(4) +DECLARE_CORE_WRITE_N(8) +DECLARE_CORE_WRITE_N(word) + +#define DECLARE_CORE_READ_N(N) \ +INLINE_CORE\ +(unsigned_##N) core_map_read_##N \ +(core_map *map, \ + unsigned_word addr, \ + cpu *processor, \ + unsigned_word cia); + +DECLARE_CORE_READ_N(1) +DECLARE_CORE_READ_N(2) +DECLARE_CORE_READ_N(4) +DECLARE_CORE_READ_N(8) +DECLARE_CORE_READ_N(word) + +#endif diff --git a/sim/ppc/cpu.c b/sim/ppc/cpu.c new file mode 100644 index 00000000000..bce82f1d141 --- /dev/null +++ b/sim/ppc/cpu.c @@ -0,0 +1,404 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1997, 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 _CPU_C_ +#define _CPU_C_ + +#include <setjmp.h> + +#include "cpu.h" +#include "idecode.h" + +#ifdef HAVE_STRING_H +#include <string.h> +#else +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif +#endif + +struct _cpu { + + /* the registers */ + registers regs; + + /* current instruction address */ + unsigned_word program_counter; + + /* the memory maps */ + core *physical; /* all of memory */ + vm *virtual; + vm_instruction_map *instruction_map; /* instructions */ + vm_data_map *data_map; /* data */ + + /* the system this processor is contained within */ + cpu_mon *monitor; + os_emul *os_emulation; + psim *system; + event_queue *events; + int cpu_nr; + + /* Current CPU model information */ + model_data *model_ptr; + +#if WITH_IDECODE_CACHE_SIZE + /* a cache to store cracked instructions */ + idecode_cache icache[WITH_IDECODE_CACHE_SIZE]; +#endif + + /* any interrupt state */ + interrupts ints; + + /* address reservation: keep the physical address and the contents + of memory at that address */ + memory_reservation reservation; + + /* offset from event time to this cpu's idea of the local time */ + signed64 time_base_local_time; + signed64 decrementer_local_time; + event_entry_tag decrementer_event; + +}; + +INLINE_CPU\ +(cpu *) +cpu_create(psim *system, + core *memory, + cpu_mon *monitor, + os_emul *os_emulation, + int cpu_nr) +{ + cpu *processor = ZALLOC(cpu); + + /* create the virtual memory map from the core */ + processor->physical = memory; + processor->virtual = vm_create(memory); + processor->instruction_map = vm_create_instruction_map(processor->virtual); + processor->data_map = vm_create_data_map(processor->virtual); + + if (CURRENT_MODEL_ISSUE > 0) + processor->model_ptr = model_create (processor); + + /* link back to core system */ + processor->system = system; + processor->events = psim_event_queue(system); + processor->cpu_nr = cpu_nr; + processor->monitor = monitor; + processor->os_emulation = os_emulation; + + return processor; +} + + +INLINE_CPU\ +(void) +cpu_init(cpu *processor) +{ + memset(&processor->regs, 0, sizeof(processor->regs)); + /* vm init is delayed until after the device tree has been init as + the devices may further init the cpu */ + if (CURRENT_MODEL_ISSUE > 0) + model_init (processor->model_ptr); +} + + +/* find ones way home */ + +INLINE_CPU\ +(psim *) +cpu_system(cpu *processor) +{ + return processor->system; +} + +INLINE_CPU\ +(int) +cpu_nr(cpu *processor) +{ + return processor->cpu_nr; +} + +INLINE_CPU\ +(cpu_mon *) +cpu_monitor(cpu *processor) +{ + return processor->monitor; +} + +INLINE_CPU\ +(os_emul *) +cpu_os_emulation(cpu *processor) +{ + return processor->os_emulation; +} + +INLINE_CPU\ +(model_data *) +cpu_model(cpu *processor) +{ + return processor->model_ptr; +} + + +/* program counter manipulation */ + +INLINE_CPU\ +(void) +cpu_set_program_counter(cpu *processor, + unsigned_word new_program_counter) +{ + processor->program_counter = new_program_counter; +} + +INLINE_CPU\ +(unsigned_word) +cpu_get_program_counter(cpu *processor) +{ + return processor->program_counter; +} + + +INLINE_CPU\ +(void) +cpu_restart(cpu *processor, + unsigned_word nia) +{ + ASSERT(processor != NULL); + cpu_set_program_counter(processor, nia); + psim_restart(processor->system, processor->cpu_nr); +} + +INLINE_CPU\ +(void) +cpu_halt(cpu *processor, + unsigned_word nia, + stop_reason reason, + int signal) +{ + ASSERT(processor != NULL); + if (CURRENT_MODEL_ISSUE > 0) + model_halt(processor->model_ptr); + cpu_set_program_counter(processor, nia); + psim_halt(processor->system, processor->cpu_nr, reason, signal); +} + +EXTERN_CPU\ +(void) +cpu_error(cpu *processor, + unsigned_word cia, + const char *fmt, + ...) +{ + char message[1024]; + va_list ap; + + /* format the message */ + va_start(ap, fmt); + vsprintf(message, fmt, ap); + va_end(ap); + + /* sanity check */ + if (strlen(message) >= sizeof(message)) + error("cpu_error: buffer overflow"); + + if (processor != NULL) { + printf_filtered("cpu %d, cia 0x%lx: %s\n", + processor->cpu_nr + 1, (unsigned long)cia, message); + cpu_halt(processor, cia, was_signalled, -1); + } + else { + error("cpu: %s", message); + } +} + + +/* The processors local concept of time */ + +INLINE_CPU\ +(signed64) +cpu_get_time_base(cpu *processor) +{ + return (event_queue_time(processor->events) + - processor->time_base_local_time); +} + +INLINE_CPU\ +(void) +cpu_set_time_base(cpu *processor, + signed64 time_base) +{ + processor->time_base_local_time = (event_queue_time(processor->events) + - time_base); +} + +INLINE_CPU\ +(signed32) +cpu_get_decrementer(cpu *processor) +{ + return (processor->decrementer_local_time + - event_queue_time(processor->events)); +} + +STATIC_INLINE_CPU\ +(void) +cpu_decrement_event(void *data) +{ + cpu *processor = (cpu*)data; + processor->decrementer_event = NULL; + decrementer_interrupt(processor); +} + +INLINE_CPU\ +(void) +cpu_set_decrementer(cpu *processor, + signed32 decrementer) +{ + signed64 old_decrementer = cpu_get_decrementer(processor); + event_queue_deschedule(processor->events, processor->decrementer_event); + processor->decrementer_event = NULL; + processor->decrementer_local_time = (event_queue_time(processor->events) + + decrementer); + if (decrementer < 0 && old_decrementer >= 0) + /* A decrementer interrupt occures if the sign of the decrement + register is changed from positive to negative by the load + instruction */ + decrementer_interrupt(processor); + else if (decrementer >= 0) + processor->decrementer_event = event_queue_schedule(processor->events, + decrementer, + cpu_decrement_event, + processor); +} + + +#if WITH_IDECODE_CACHE_SIZE +/* allow access to the cpu's instruction cache */ +INLINE_CPU\ +(idecode_cache *) +cpu_icache_entry(cpu *processor, + unsigned_word cia) +{ + return &processor->icache[cia / 4 % WITH_IDECODE_CACHE_SIZE]; +} + + +INLINE_CPU\ +(void) +cpu_flush_icache(cpu *processor) +{ + int i; + /* force all addresses to 0xff... so that they never hit */ + for (i = 0; i < WITH_IDECODE_CACHE_SIZE; i++) + processor->icache[i].address = MASK(0, 63); +} +#endif + + +/* address map revelation */ + +INLINE_CPU\ +(vm_instruction_map *) +cpu_instruction_map(cpu *processor) +{ + return processor->instruction_map; +} + +INLINE_CPU\ +(vm_data_map *) +cpu_data_map(cpu *processor) +{ + return processor->data_map; +} + +INLINE_CPU\ +(void) +cpu_page_tlb_invalidate_entry(cpu *processor, + unsigned_word ea) +{ + vm_page_tlb_invalidate_entry(processor->virtual, ea); +} + +INLINE_CPU\ +(void) +cpu_page_tlb_invalidate_all(cpu *processor) +{ + vm_page_tlb_invalidate_all(processor->virtual); +} + + +/* interrupt access */ + +INLINE_CPU\ +(interrupts *) +cpu_interrupts(cpu *processor) +{ + return &processor->ints; +} + + + +/* reservation access */ + +INLINE_CPU\ +(memory_reservation *) +cpu_reservation(cpu *processor) +{ + return &processor->reservation; +} + + +/* register access */ + +INLINE_CPU\ +(registers *) +cpu_registers(cpu *processor) +{ + return &processor->regs; +} + +INLINE_CPU\ +(void) +cpu_synchronize_context(cpu *processor, + unsigned_word cia) +{ +#if (WITH_IDECODE_CACHE_SIZE) + /* kill of the cache */ + cpu_flush_icache(processor); +#endif + + /* update virtual memory */ + vm_synchronize_context(processor->virtual, + processor->regs.spr, + processor->regs.sr, + processor->regs.msr, + processor, cia); +} + + +/* might again be useful one day */ + +INLINE_CPU\ +(void) +cpu_print_info(cpu *processor, int verbose) +{ +} + +#endif /* _CPU_C_ */ diff --git a/sim/ppc/cpu.h b/sim/ppc/cpu.h new file mode 100644 index 00000000000..840f5815742 --- /dev/null +++ b/sim/ppc/cpu.h @@ -0,0 +1,254 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1997, 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 _CPU_H_ +#define _CPU_H_ + +#include "basics.h" +#include "registers.h" +#include "device.h" +#include "corefile.h" +#include "vm.h" +#include "events.h" +#include "interrupts.h" +#include "psim.h" +#include "idecode.h" +#include "itable.h" +#include "os_emul.h" +#include "mon.h" +#include "model.h" + +#ifndef CONST_ATTRIBUTE +#define CONST_ATTRIBUTE __attribute__((__const__)) +#endif + +/* typedef struct _cpu cpu; + + Declared in basics.h because it is used opaquely throughout the + code */ + + +/* Create a cpu object */ + +INLINE_CPU\ +(cpu *) cpu_create +(psim *system, + core *memory, + cpu_mon *monitor, + os_emul *cpu_emulation, + int cpu_nr); + +INLINE_CPU\ +(void) cpu_init +(cpu *processor); + +/* Find our way home */ + +INLINE_CPU\ +(psim *) cpu_system +(cpu *processor) CONST_ATTRIBUTE; + +INLINE_CPU\ +(cpu_mon *) cpu_monitor +(cpu *processor) CONST_ATTRIBUTE; + +INLINE_CPU\ +(os_emul *) cpu_os_emulation +(cpu *processor); + +INLINE_CPU\ +(int) cpu_nr +(cpu *processor) CONST_ATTRIBUTE; + + +/* manipulate the processors program counter and execution state. + + The program counter is not included in the register file. Instead + it is extracted and then later restored (set, reset, halt). This + is to give the user of the cpu (and the compiler) the chance to + minimize the need to load/store the cpu's PC value. (Especially in + the case of a single processor) */ + +INLINE_CPU\ +(void) cpu_set_program_counter +(cpu *processor, + unsigned_word new_program_counter); + +INLINE_CPU\ +(unsigned_word) cpu_get_program_counter +(cpu *processor); + +INLINE_CPU\ +(void) cpu_restart +(cpu *processor, + unsigned_word nia); + +INLINE_CPU\ +(void) cpu_halt +(cpu *processor, + unsigned_word nia, + stop_reason reason, + int signal); + +EXTERN_CPU\ +(void) cpu_error +(cpu *processor, + unsigned_word cia, + const char *fmt, + ...) __attribute__ ((format (printf, 3, 4))); + + +/* The processors local concept of time */ + +INLINE_CPU\ +(signed64) cpu_get_time_base +(cpu *processor); + +INLINE_CPU\ +(void) cpu_set_time_base +(cpu *processor, + signed64 time_base); + +INLINE_CPU\ +(signed32) cpu_get_decrementer +(cpu *processor); + +INLINE_CPU\ +(void) cpu_set_decrementer +(cpu *processor, + signed32 decrementer); + + +#if WITH_IDECODE_CACHE_SIZE +/* Return the cache entry that matches the given CIA. No guarentee + that the cache entry actually contains the instruction for that + address */ + +INLINE_CPU\ +(idecode_cache) *cpu_icache_entry +(cpu *processor, + unsigned_word cia); + +INLINE_CPU\ +(void) cpu_flush_icache +(cpu *processor); +#endif + + +/* reveal the processors VM: + + At first sight it may seem better to, instead of exposing the cpu's + inner vm maps, to have the cpu its self provide memory manipulation + functions. (eg cpu_instruction_fetch() cpu_data_read_4()) + + Unfortunatly in addition to these functions is the need (for the + debugger) to be able to read/write to memory in ways that violate + the vm protection (eg store breakpoint instruction in the + instruction map). */ + +INLINE_CPU\ +(vm_data_map *) cpu_data_map +(cpu *processor); + +INLINE_CPU\ +(vm_instruction_map *) cpu_instruction_map +(cpu *processor); + +INLINE_CPU\ +(void) cpu_page_tlb_invalidate_entry +(cpu *processor, + unsigned_word ea); + +INLINE_CPU\ +(void) cpu_page_tlb_invalidate_all +(cpu *processor); + + +/* reveal the processors interrupt state */ + +INLINE_CPU\ +(interrupts *) cpu_interrupts +(cpu *processor); + + +/* grant access to the reservation information */ + +typedef struct _memory_reservation { + int valid; + unsigned_word addr; + unsigned_word data; +} memory_reservation; + +INLINE_CPU\ +(memory_reservation *) cpu_reservation +(cpu *processor); + + +/* Registers: + + This model exploits the PowerPC's requirement for a synchronization + to occure after (or before) the update of any context controlling + register. All context sync points must call the sync function + below to when ever a synchronization point is reached */ + +INLINE_CPU\ +(registers *) cpu_registers +(cpu *processor) CONST_ATTRIBUTE; + +INLINE_CPU\ +(void) cpu_synchronize_context +(cpu *processor, + unsigned_word cia); + +#define IS_PROBLEM_STATE(PROCESSOR) \ +(CURRENT_ENVIRONMENT == OPERATING_ENVIRONMENT \ + ? (cpu_registers(PROCESSOR)->msr & msr_problem_state) \ + : 1) + +#define IS_64BIT_MODE(PROCESSOR) \ +(WITH_TARGET_WORD_BITSIZE == 64 \ + ? (CURRENT_ENVIRONMENT == OPERATING_ENVIRONMENT \ + ? (cpu_registers(PROCESSOR)->msr & msr_64bit_mode) \ + : 1) \ + : 0) + +#define IS_FP_AVAILABLE(PROCESSOR) \ +(CURRENT_ENVIRONMENT == OPERATING_ENVIRONMENT \ + ? (cpu_registers(PROCESSOR)->msr & msr_floating_point_available) \ + : 1) + + + +INLINE_CPU\ +(void) cpu_print_info +(cpu *processor, + int verbose); + +INLINE_CPU\ +(model_data *) cpu_model +(cpu *processor) CONST_ATTRIBUTE; + + +#if (CPU_INLINE & INCLUDE_MODULE) +# include "cpu.c" +#endif + +#endif diff --git a/sim/ppc/dc-complex b/sim/ppc/dc-complex new file mode 100644 index 00000000000..13361ecd525 --- /dev/null +++ b/sim/ppc/dc-complex @@ -0,0 +1,58 @@ +# +# 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. +# +array,normal: 0: 5: 0: 5: +array,normal: 21:31:32:-1:OE,LR,AA,Rc,LK: +## +## Branch Conditional instruction - Expand BO{0:4} only, ignore BO{5} +## +array,expand-forced: 6: 9: 6: 9:BO: 0xfc000000:0x40000000 +## +## Expand RA on equality with 0 in Add instructions were if(RA==0) appears. +## +# Add Immediate +array,boolean: 11:15:11:15:RA: 0xfc000000:0x38000000:0 +# Add Immediate Shifted +array,boolean: 11:15:11:15:RA: 0xfc000000:0x3c000000:0 +## +## Ditto for high frequency load/store instructions. +## +# Store Byte +#array,boolean: 11:15:11:15:RA: 0xfc000000:0x98000000:0 +# Store Word +#array,boolean: 11:15:11:15:RA: 0xfc000000:0x90000000:0 +# Load Word and Zero +#array,boolean: 11:15:11:15:RA: 0xfc000000:0x80000000:0 +## +## Move to/from SPR instructions - LR=8 is munged into 0x100 == 256 +## +#array,boolean: 11:20:11:20:SPR: 0xfc0007ff:0x7c0003a6:256 +#array,boolean: 11:20:11:20:SPR: 0xfc0007ff:0x7c0002a6:256 +## +## Compare Immediate instruction - separate out L == 0 and L == 1 +## +# Compare Immediate +#array,normal: 10:11:10:11:L: 0xfc000000:0x2c000000:0 +## +## Move to/from SPR instructions - separate out LR case +## +# Move to SPR +array,boolean: 11:20:11:20:SPR: 0xfc0007ff:0x7c0003a6:256 +# Move from SPR +array,boolean: 11:20:11:20:SPR: 0xfc0007ff:0x7c0002a6:256 diff --git a/sim/ppc/dc-simple b/sim/ppc/dc-simple new file mode 100644 index 00000000000..9ea823e66c0 --- /dev/null +++ b/sim/ppc/dc-simple @@ -0,0 +1,25 @@ +# +# 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. +# +# +# Create a two level switch statement. The first level branches on bits +# 0..5 while the second level branches on bits 21..31 +# +switch: 0: 5: 0: 5 +switch:21:31 diff --git a/sim/ppc/dc-stupid b/sim/ppc/dc-stupid new file mode 100644 index 00000000000..4238bdb9c09 --- /dev/null +++ b/sim/ppc/dc-stupid @@ -0,0 +1,58 @@ +# +# 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. +# +array,normal: 0: 5: 0: 5: +array,normal:21:31:32:-1:OE,LR,AA,Rc,LK: +## +## Branch Conditional instruction - Expand BO{0:4} only, ignore BO{5} +## +array,expand-forced: 6: 9: 6: 9:BO: 0xfc000000:0x40000000 +## +## Expand RA on equality with 0 in Add instructions were if(RA==0) appears. +## +# Add Immediate +#array,boolean: 11:15:11:15:RA: 0xfc000000:0x38000000:0 +# Add Immediate Shifted +#array,boolean: 11:15:11:15:RA: 0xfc000000:0x3c000000:0 +## +## Ditto for high frequency load/store instructions. +## +# Store Byte +#array,boolean: 11:15:11:15:RA: 0xfc000000:0x98000000:0 +# Store Word +#array,boolean: 11:15:11:15:RA: 0xfc000000:0x90000000:0 +# Load Word and Zero +#array,boolean: 11:15:11:15:RA: 0xfc000000:0x80000000:0 +## +## Move to/from SPR instructions - LR=8 is munged into 0x100 == 256 +## +#array,boolean: 11:20:11:20:SPR: 0xfc0007ff:0x7c0003a6:256 +#array,boolean: 11:20:11:20:SPR: 0xfc0007ff:0x7c0002a6:256 +## +## Compare Immediate instruction - separate out L == 0 and L == 1 +## +# Compare Immediate +#array,boolean: 10:11:10:11:L: 0xfc000000:0x2c000000:0 +## +## Move to/from SPR instructions - separate out LR case +## +# Move to SPR +#array,boolean: 11:20:11:20:SPR: 0xfc0007ff:0x7c0003a6:256 +# Move from SPR +#array,boolean: 11:20:11:20:SPR: 0xfc0007ff:0x7c0002a6:256 diff --git a/sim/ppc/dc-test.01 b/sim/ppc/dc-test.01 new file mode 100644 index 00000000000..df8c175cc0d --- /dev/null +++ b/sim/ppc/dc-test.01 @@ -0,0 +1,24 @@ +# +# 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. +# + 0: 5: 0: 5:0:: 2:0x00000000:0x00000000:0 +21:31:32:-1:0:OE,LR,AA,Rc,LK:0:0x00000000:0x00000000:0 + 6: 9: 6: 9:0:BO: 0:0xfc000000:0x40000000:1 +11:15:11:15:0:RA: 1:0xfc000000:0x38000000:2 +11:15:11:15:0:RA: 1:0xfc000000:0x3c000000:2 diff --git a/sim/ppc/dc-test.02 b/sim/ppc/dc-test.02 new file mode 100644 index 00000000000..a98d0a78e2c --- /dev/null +++ b/sim/ppc/dc-test.02 @@ -0,0 +1,24 @@ +# +# 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. +# + 0: 5: 0: 5:0:: 0:0x00000000:0x00000000:0 +21:31:32:-1:0:OE,LR,AA,Rc,LK:0:0x00000000:0x00000000:0 + 6: 9: 6: 9:0:BO: 0:0xfc000000:0x40000000:1 +11:15:11:15:0:RA: 1:0xfc000000:0x38000000:2 +11:15:11:15:0:RA: 1:0xfc000000:0x3c000000:2 diff --git a/sim/ppc/debug.c b/sim/ppc/debug.c new file mode 100644 index 00000000000..5931d1e338a --- /dev/null +++ b/sim/ppc/debug.c @@ -0,0 +1,153 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1997, 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 _DEBUG_C_ +#define _DEBUG_C_ + +#include "config.h" +#include "basics.h" + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +int ppc_trace[nr_trace_options]; + +typedef struct _trace_option_descriptor { + trace_options option; + const char *name; + const char *description; +} trace_option_descriptor; + +static trace_option_descriptor trace_description[] = { + { trace_gdb, "gdb", "calls made by gdb to the sim_calls.c file" }, + { trace_os_emul, "os-emul", "VEA mode sytem calls - like strace" }, + { trace_events, "events", "event queue handling" }, + /* decode/issue */ + { trace_semantics, "semantics", "Instruction execution (issue)" }, + { trace_idecode, "idecode", "instruction decode (when miss in icache)" }, + { trace_alu, "alu", "results of integer ALU" }, + { trace_vm, "vm", "OEA address translation" }, + { trace_load_store, "load-store", "transfers between registers and memory" }, + { trace_model, "model", "model specific information" }, + { trace_interrupts, "interrupts", "interrupt handling" }, + /* devices */ + { trace_device_tree, "device-tree", }, + { trace_devices, "devices" }, + { trace_binary_device, "binary-device" }, + { trace_com_device, "com-device" }, + { trace_console_device, "console-device" }, + { trace_core_device, "core-device" }, + { trace_disk_device, "disk-device" }, + { trace_eeprom_device, "eeprom-device" }, + { trace_file_device, "file-device" }, + { trace_glue_device, "glue-device" }, + { trace_halt_device, "halt-device" }, + { trace_htab_device, "htab-device" }, + { trace_icu_device, "icu-device" }, + { trace_ide_device, "ide-device" }, + { trace_memory_device, "memory-device" }, + { trace_opic_device, "opic-device" }, + { trace_pal_device, "pal-device" }, + { trace_pass_device, "pass-device" }, + { trace_phb_device, "phb-device" }, + { trace_register_device, "register-device", "Device initializing registers" }, + { trace_stack_device, "stack-device" }, + { trace_vm_device, "vm-device" }, + /* packages */ + { trace_disklabel_package, "disklabel-package" }, + /* misc */ + { trace_print_info, "print-info", "Print performance analysis information" }, + { trace_opts, "options", "Print options simulator was compiled with" }, + /*{ trace_tbd, "tbd", "Trace any missing features" },*/ + { trace_print_device_tree, "print-device-tree", "Output the contents of the device tree" }, + { trace_dump_device_tree, "dump-device-tree", "Output the contents of the device tree then exit" }, + /* sentinal */ + { nr_trace_options, NULL }, +}; + +extern void +trace_option(const char *option, + int setting) +{ + if (strcmp(option, "all") == 0) { + trace_options i; + for (i = 0; i < nr_trace_options; i++) + if (i != trace_dump_device_tree) { + ppc_trace[i] = setting; + } + } + else { + int i = 0; + while (trace_description[i].option < nr_trace_options + && strcmp(option, trace_description[i].name) != 0) + i++; + if (trace_description[i].option < nr_trace_options) + ppc_trace[trace_description[i].option] = setting; + else { + i = strtoul(option, 0, 0); + if (i > 0 && i < nr_trace_options) + ppc_trace[i] = setting; + else + error("Unknown trace option: %s\n", option); + } + + } +} + + +extern void +trace_usage(int verbose) +{ + if (verbose) { + printf_filtered("\n"); + printf_filtered("The following are possible <trace> options:\n"); + printf_filtered("\n"); + } + if (verbose == 1) { + int pos; + int i; + printf_filtered(" all"); + pos = strlen("all") + 2; + for (i = 0; trace_description[i].option < nr_trace_options; i++) { + pos += strlen(trace_description[i].name) + 2; + if (pos > 75) { + pos = strlen(trace_description[i].name) + 2; + printf_filtered("\n"); + } + printf_filtered(" %s", trace_description[i].name); + } + printf_filtered("\n"); + } + if (verbose > 1) { + const char *format = "\t%-18s%s\n"; + int i; + printf_filtered(format, "all", "enable all the trace options"); + for (i = 0; trace_description[i].option < nr_trace_options; i++) + printf_filtered(format, + trace_description[i].name, + (trace_description[i].description + ? trace_description[i].description + : "")); + } +} + +#endif /* _DEBUG_C_ */ diff --git a/sim/ppc/debug.h b/sim/ppc/debug.h new file mode 100644 index 00000000000..1fdb36ebac3 --- /dev/null +++ b/sim/ppc/debug.h @@ -0,0 +1,172 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1997, 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 _DEBUG_H_ +#define _DEBUG_H_ + +#include "filter_filename.h" + +typedef enum { + trace_invalid, + trace_tbd, + /**/ + trace_gdb, + trace_os_emul, + /**/ + trace_events, + trace_device_tree, + trace_devices, + trace_binary_device, + trace_com_device, + trace_console_device, + trace_core_device, + trace_disk_device, + trace_eeprom_device, + trace_file_device, + trace_glue_device, + trace_halt_device, + trace_htab_device, + trace_icu_device, + trace_ide_device, + trace_memory_device, + trace_opic_device, + trace_pal_device, + trace_pass_device, + trace_phb_device, + trace_stack_device, + trace_register_device, + trace_vm_device, + /**/ + trace_disklabel_package, + /**/ + trace_semantics, + trace_idecode, + trace_alu, + trace_load_store, + trace_model, + /**/ + trace_vm, + trace_core, + trace_interrupts, + trace_psim, + trace_device_init, + trace_cpu, + trace_breakpoint, + trace_opts, + trace_print_info, + trace_print_device_tree, + trace_dump_device_tree, + nr_trace_options +} trace_options; + + + +extern int ppc_trace[nr_trace_options]; + +/* simple */ +#define TRACE(OBJECT, ARGS) \ +do { \ + if (WITH_TRACE) { \ + if (ppc_trace[OBJECT]) { \ + printf_filtered("%s:%d: ", filter_filename(__FILE__), __LINE__); \ + printf_filtered ARGS; \ + } \ + } \ +} while (0) + +/* issue */ +#define ITRACE(OBJECT, ARGS) \ +do { \ + if (WITH_TRACE) { \ + if (ppc_trace[OBJECT]) { \ + printf_filtered("%s:%d:0x%08lx:%s ", itable[MY_INDEX].file, itable[MY_INDEX].line_nr, (long)cia, itable[MY_INDEX].name); \ + printf_filtered ARGS; \ + } \ + } \ +} while (0) + +/* device */ +#define DTRACE(OBJECT, ARGS) \ +do { \ + if (WITH_TRACE) { \ + int trace_device = device_trace(me); \ + if (ppc_trace[trace_devices] \ + || ppc_trace[trace_##OBJECT##_device] \ + || trace_device) { \ + printf_filtered("%s:%d:%s:%s%s ", \ + filter_filename(__FILE__), __LINE__, #OBJECT, \ + trace_device ? device_path(me) : "", \ + trace_device ? ":" : ""); \ + printf_filtered ARGS; \ + } \ + } \ +} while (0) + +/* device instance */ +#define DITRACE(OBJECT, ARGS) \ +do { \ + if (WITH_TRACE) { \ + device *me = device_instance_device(instance); \ + int trace_device = device_trace(me); \ + if (ppc_trace[trace_devices] \ + || ppc_trace[trace_##OBJECT##_device] \ + || trace_device) { \ + printf_filtered("%s:%d:%s:%s%s ", \ + filter_filename(__FILE__), __LINE__, #OBJECT, \ + trace_device ? device_path(me) : "", \ + trace_device ? ":" : ""); \ + printf_filtered ARGS; \ + } \ + } \ +} while (0) + +/* package */ +#define PTRACE(OBJECT, ARGS) \ +do { \ + if (WITH_TRACE) { \ + if (ppc_trace[trace_##OBJECT##_package]) { \ + printf_filtered("%s:%d:%s: ", filter_filename(__FILE__), __LINE__, #OBJECT); \ + printf_filtered ARGS; \ + } \ + } \ +} while (0) + + +#define ASSERT(EXPRESSION) \ +do { \ + if (WITH_ASSERT) { \ + if (!(EXPRESSION)) { \ + error("%s:%d: assertion failed - %s\n", \ + filter_filename(__FILE__), __LINE__, #EXPRESSION); \ + } \ + } \ +} while (0) + +/* Parse OPTION updating the trace array */ +extern void +trace_option(const char *option, int setting); + +/* Output the list of trace options */ +extern void trace_usage +(int verbose); + + +#endif /* _DEBUG_H_ */ diff --git a/sim/ppc/device.c b/sim/ppc/device.c new file mode 100644 index 00000000000..ffb094324c4 --- /dev/null +++ b/sim/ppc/device.c @@ -0,0 +1,1998 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1997, 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_C_ +#define _DEVICE_C_ + +#include <stdio.h> + +#include "device_table.h" +#include "cap.h" + +#include "events.h" +#include "psim.h" + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#ifdef HAVE_STRING_H +#include <string.h> +#else +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif +#endif + +#include <ctype.h> + +STATIC_INLINE_DEVICE (void) clean_device_properties(device *); + +/* property entries */ + +typedef struct _device_property_entry device_property_entry; +struct _device_property_entry { + device_property_entry *next; + device_property *value; + const void *init_array; + unsigned sizeof_init_array; +}; + + +/* Interrupt edges */ + +typedef struct _device_interrupt_edge device_interrupt_edge; +struct _device_interrupt_edge { + int my_port; + device *dest; + int dest_port; + device_interrupt_edge *next; + object_disposition disposition; +}; + +STATIC_INLINE_DEVICE\ +(void) +attach_device_interrupt_edge(device_interrupt_edge **list, + int my_port, + device *dest, + int dest_port, + object_disposition disposition) +{ + device_interrupt_edge *new_edge = ZALLOC(device_interrupt_edge); + new_edge->my_port = my_port; + new_edge->dest = dest; + new_edge->dest_port = dest_port; + new_edge->next = *list; + new_edge->disposition = disposition; + *list = new_edge; +} + +STATIC_INLINE_DEVICE\ +(void) +detach_device_interrupt_edge(device *me, + device_interrupt_edge **list, + int my_port, + device *dest, + int dest_port) +{ + while (*list != NULL) { + device_interrupt_edge *old_edge = *list; + if (old_edge->dest == dest + && old_edge->dest_port == dest_port + && old_edge->my_port == my_port) { + if (old_edge->disposition == permenant_object) + device_error(me, "attempt to delete permenant interrupt"); + *list = old_edge->next; + zfree(old_edge); + return; + } + } + device_error(me, "attempt to delete unattached interrupt"); +} + +STATIC_INLINE_DEVICE\ +(void) +clean_device_interrupt_edges(device_interrupt_edge **list) +{ + while (*list != NULL) { + device_interrupt_edge *old_edge = *list; + switch (old_edge->disposition) { + case permenant_object: + list = &old_edge->next; + break; + case tempoary_object: + *list = old_edge->next; + zfree(old_edge); + break; + } + } +} + + +/* A device */ + +struct _device { + + /* my name is ... */ + const char *name; + device_unit unit_address; + const char *path; + int nr_address_cells; + int nr_size_cells; + + /* device tree */ + device *parent; + device *children; + device *sibling; + + /* its template methods */ + void *data; /* device specific data */ + const device_callbacks *callback; + + /* device properties */ + device_property_entry *properties; + + /* interrupts */ + device_interrupt_edge *interrupt_destinations; + + /* any open instances of this device */ + device_instance *instances; + + /* the internal/external mappings and other global requirements */ + cap *ihandles; + cap *phandles; + psim *system; + + /* debugging */ + int trace; +}; + + +/* an instance of a device */ +struct _device_instance { + void *data; + char *args; + char *path; + const device_instance_callbacks *callback; + /* the root instance */ + device *owner; + device_instance *next; + /* interposed instance */ + device_instance *parent; + device_instance *child; +}; + + + +/* creation */ + +STATIC_INLINE_DEVICE\ +(const char *) +device_full_name(device *leaf, + char *buf, + unsigned sizeof_buf) +{ + /* get a buffer */ + char full_name[1024]; + if (buf == (char*)0) { + buf = full_name; + sizeof_buf = sizeof(full_name); + } + + /* construct a name */ + if (leaf->parent == NULL) { + if (sizeof_buf < 1) + error("device_full_name: buffer overflow"); + *buf = '\0'; + } + else { + char unit[1024]; + device_full_name(leaf->parent, buf, sizeof_buf); + if (leaf->parent != NULL + && device_encode_unit(leaf->parent, + &leaf->unit_address, + unit+1, + sizeof(unit)-1) > 0) + unit[0] = '@'; + else + unit[0] = '\0'; + if (strlen(buf) + strlen("/") + strlen(leaf->name) + strlen(unit) + >= sizeof_buf) + error("device_full_name: buffer overflow"); + strcat(buf, "/"); + strcat(buf, leaf->name); + strcat (buf, unit); + } + + /* return it usefully */ + if (buf == full_name) + buf = (char *) strdup(full_name); + return buf; +} + +STATIC_INLINE_DEVICE\ +(device *) +device_create_from(const char *name, + const device_unit *unit_address, + void *data, + const device_callbacks *callbacks, + device *parent) +{ + device *new_device = ZALLOC(device); + + /* insert it into the device tree */ + new_device->parent = parent; + new_device->children = NULL; + if (parent != NULL) { + device **sibling = &parent->children; + while ((*sibling) != NULL) + sibling = &(*sibling)->sibling; + *sibling = new_device; + } + + /* give it a name */ + new_device->name = (char *) strdup(name); + new_device->unit_address = *unit_address; + new_device->path = device_full_name(new_device, NULL, 0); + + /* its template */ + new_device->data = data; + new_device->callback = callbacks; + + /* its properties - already null */ + /* interrupts - already null */ + + /* mappings - if needed */ + if (parent == NULL) { + new_device->ihandles = cap_create(name); + new_device->phandles = cap_create(name); + } + else { + new_device->ihandles = device_root(parent)->ihandles; + new_device->phandles = device_root(parent)->phandles; + } + + cap_add(new_device->phandles, new_device); + return new_device; +} + + + +INLINE_DEVICE\ +(device *) +device_create(device *parent, + const char *base, + const char *name, + const char *unit_address, + const char *args) +{ + const device_descriptor *const *table; + for (table = device_table; *table != NULL; table++) { + const device_descriptor *descr; + for (descr = *table; descr->name != NULL; descr++) { + if (strcmp(base, descr->name) == 0) { + device_unit address = { 0 }; + void *data = NULL; + if (parent != NULL) + if (device_decode_unit(parent, unit_address, &address) < 0) + device_error(parent, "invalid address %s for device %s", + unit_address, name); + if (descr->creator != NULL) + data = descr->creator(name, &address, args); + return device_create_from(name, &address, data, + descr->callbacks, parent); + } + } + } + device_error(parent, "attempt to attach unknown device %s", name); + return NULL; +} + + + +INLINE_DEVICE\ +(void) +device_usage(int verbose) +{ + const device_descriptor *const *table; + if (verbose == 1) { + int pos = 0; + for (table = device_table; *table != NULL; table++) { + const device_descriptor *descr; + for (descr = *table; descr->name != NULL; descr++) { + pos += strlen(descr->name) + 2; + if (pos > 75) { + pos = strlen(descr->name) + 2; + printf_filtered("\n"); + } + printf_filtered(" %s", descr->name); + } + printf_filtered("\n"); + } + } + if (verbose > 1) { + for (table = device_table; *table != NULL; table++) { + const device_descriptor *descr; + for (descr = *table; descr->name != NULL; descr++) { + printf_filtered(" %s:\n", descr->name); + /* interrupt ports */ + if (descr->callbacks->interrupt.ports != NULL) { + const device_interrupt_port_descriptor *ports = + descr->callbacks->interrupt.ports; + printf_filtered(" interrupt ports:"); + while (ports->name != NULL) { + printf_filtered(" %s", ports->name); + ports++; + } + printf_filtered("\n"); + } + /* general info */ + if (descr->callbacks->usage != NULL) + descr->callbacks->usage(verbose); + } + } + } +} + + + + + +/* Device node: */ + +INLINE_DEVICE\ +(device *) +device_parent(device *me) +{ + return me->parent; +} + +INLINE_DEVICE\ +(device *) +device_root(device *me) +{ + ASSERT(me != NULL); + while (me->parent != NULL) + me = me->parent; + return me; +} + +INLINE_DEVICE\ +(device *) +device_sibling(device *me) +{ + return me->sibling; +} + +INLINE_DEVICE\ +(device *) +device_child(device *me) +{ + return me->children; +} + +INLINE_DEVICE\ +(const char *) +device_name(device *me) +{ + return me->name; +} + +INLINE_DEVICE\ +(const char *) +device_path(device *me) +{ + return me->path; +} + +INLINE_DEVICE\ +(void *) +device_data(device *me) +{ + return me->data; +} + +INLINE_DEVICE\ +(psim *) +device_system(device *me) +{ + return me->system; +} + +INLINE_DEVICE\ +(const device_unit *) +device_unit_address(device *me) +{ + return &me->unit_address; +} + + +INLINE_DEVICE\ +(int) +device_address_to_attach_address(device *me, + const device_unit *address, + int *attach_space, + unsigned_word *attach_address, + device *client) +{ + if (me->callback->convert.address_to_attach_address == NULL) + device_error(me, "no convert.address_to_attach_address method"); + return me->callback->convert.address_to_attach_address(me, address, attach_space, attach_address, client); +} + + +INLINE_DEVICE\ +(int) +device_size_to_attach_size(device *me, + const device_unit *size, + unsigned *nr_bytes, + device *client) +{ + if (me->callback->convert.size_to_attach_size == NULL) + device_error(me, "no convert.size_to_attach_size method"); + return me->callback->convert.size_to_attach_size(me, size, nr_bytes, client); +} + + +INLINE_DEVICE\ +(int) +device_decode_unit(device *bus, + const char *unit, + device_unit *address) +{ + if (bus->callback->convert.decode_unit == NULL) + device_error(bus, "no convert.decode_unit method"); + return bus->callback->convert.decode_unit(bus, unit, address); +} + + +INLINE_DEVICE\ +(int) +device_encode_unit(device *bus, + const device_unit *unit_address, + char *buf, + int sizeof_buf) +{ + if (bus->callback->convert.encode_unit == NULL) + device_error(bus, "no convert.encode_unit method"); + return bus->callback->convert.encode_unit(bus, unit_address, buf, sizeof_buf); +} + +INLINE_DEVICE\ +(unsigned) +device_nr_address_cells(device *me) +{ + if (me->nr_address_cells == 0) { + if (device_find_property(me, "#address-cells") != NULL) + me->nr_address_cells = device_find_integer_property(me, "#address-cells"); + else + me->nr_address_cells = 2; + } + return me->nr_address_cells; +} + +INLINE_DEVICE\ +(unsigned) +device_nr_size_cells(device *me) +{ + if (me->nr_size_cells == 0) { + if (device_find_property(me, "#size-cells") != NULL) + me->nr_size_cells = device_find_integer_property(me, "#size-cells"); + else + me->nr_size_cells = 1; + } + return me->nr_size_cells; +} + + + +/* device-instance: */ + +INLINE_DEVICE\ +(device_instance *) +device_create_instance_from(device *me, + device_instance *parent, + void *data, + const char *path, + const char *args, + const device_instance_callbacks *callbacks) +{ + device_instance *instance = ZALLOC(device_instance); + if ((me == NULL) == (parent == NULL)) + device_error(me, "can't have both parent instance and parent device"); + /*instance->unit*/ + /* link this instance into the devices list */ + if (me != NULL) { + ASSERT(parent == NULL); + instance->owner = me; + instance->parent = NULL; + /* link this instance into the front of the devices instance list */ + instance->next = me->instances; + me->instances = instance; + } + if (parent != NULL) { + device_instance **previous; + ASSERT(parent->child == NULL); + parent->child = instance; + ASSERT(me == NULL); + instance->owner = parent->owner; + instance->parent = parent; + /* in the devices instance list replace the parent instance with + this one */ + instance->next = parent->next; + /* replace parent with this new node */ + previous = &instance->owner->instances; + while (*previous != parent) { + ASSERT(*previous != NULL); + previous = &(*previous)->next; + } + *previous = instance; + } + instance->data = data; + instance->args = (args == NULL ? NULL : (char *) strdup(args)); + instance->path = (path == NULL ? NULL : (char *) strdup(path)); + instance->callback = callbacks; + cap_add(instance->owner->ihandles, instance); + return instance; +} + + +INLINE_DEVICE\ +(device_instance *) +device_create_instance(device *me, + const char *path, + const char *args) +{ + /* create the instance */ + if (me->callback->instance_create == NULL) + device_error(me, "no instance_create method"); + return me->callback->instance_create(me, path, args); +} + + +STATIC_INLINE_DEVICE\ +(void) +clean_device_instances(device *me) +{ + device_instance **instance = &me->instances; + while (*instance != NULL) { + device_instance *old_instance = *instance; + device_instance_delete(old_instance); + instance = &me->instances; + } +} + + +INLINE_DEVICE\ +(void) +device_instance_delete(device_instance *instance) +{ + device *me = instance->owner; + if (instance->callback->delete == NULL) + device_error(me, "no delete method"); + instance->callback->delete(instance); + if (instance->args != NULL) + zfree(instance->args); + if (instance->path != NULL) + zfree(instance->path); + if (instance->child == NULL) { + /* only remove leaf nodes */ + device_instance **curr = &me->instances; + while (*curr != instance) { + ASSERT(*curr != NULL); + curr = &(*curr)->next; + } + *curr = instance->next; + } + else { + /* check it isn't in the instance list */ + device_instance *curr = me->instances; + while (curr != NULL) { + ASSERT(curr != instance); + curr = curr->next; + } + /* unlink the child */ + ASSERT(instance->child->parent == instance); + instance->child->parent = NULL; + } + cap_remove(me->ihandles, instance); + zfree(instance); +} + +INLINE_DEVICE\ +(int) +device_instance_read(device_instance *instance, + void *addr, + unsigned_word len) +{ + device *me = instance->owner; + if (instance->callback->read == NULL) + device_error(me, "no read method"); + return instance->callback->read(instance, addr, len); +} + +INLINE_DEVICE\ +(int) +device_instance_write(device_instance *instance, + const void *addr, + unsigned_word len) +{ + device *me = instance->owner; + if (instance->callback->write == NULL) + device_error(me, "no write method"); + return instance->callback->write(instance, addr, len); +} + +INLINE_DEVICE\ +(int) +device_instance_seek(device_instance *instance, + unsigned_word pos_hi, + unsigned_word pos_lo) +{ + device *me = instance->owner; + if (instance->callback->seek == NULL) + device_error(me, "no seek method"); + return instance->callback->seek(instance, pos_hi, pos_lo); +} + +INLINE_DEVICE\ +(int) +device_instance_call_method(device_instance *instance, + const char *method_name, + int n_stack_args, + unsigned_cell stack_args[/*n_stack_args*/], + int n_stack_returns, + unsigned_cell stack_returns[/*n_stack_args*/]) +{ + device *me = instance->owner; + const device_instance_methods *method = instance->callback->methods; + if (method == NULL) { + device_error(me, "no methods (want %s)", method_name); + } + while (method->name != NULL) { + if (strcmp(method->name, method_name) == 0) { + return method->method(instance, + n_stack_args, stack_args, + n_stack_returns, stack_returns); + } + method++; + } + device_error(me, "no %s method", method_name); + return 0; +} + + +INLINE_DEVICE\ +(device *) +device_instance_device(device_instance *instance) +{ + return instance->owner; +} + +INLINE_DEVICE\ +(const char *) +device_instance_path(device_instance *instance) +{ + return instance->path; +} + +INLINE_DEVICE\ +(void *) +device_instance_data(device_instance *instance) +{ + return instance->data; +} + + + +/* Device Properties: */ + +STATIC_INLINE_DEVICE\ +(device_property_entry *) +find_property_entry(device *me, + const char *property) +{ + device_property_entry *entry; + ASSERT(property != NULL); + entry = me->properties; + while (entry != NULL) { + if (strcmp(entry->value->name, property) == 0) + return entry; + entry = entry->next; + } + return NULL; +} + +STATIC_INLINE_DEVICE\ +(void) +device_add_property(device *me, + const char *property, + device_property_type type, + const void *init_array, + unsigned sizeof_init_array, + const void *array, + unsigned sizeof_array, + const device_property *original, + object_disposition disposition) +{ + device_property_entry *new_entry = NULL; + device_property *new_value = NULL; + + /* find the list end */ + device_property_entry **insertion_point = &me->properties; + while (*insertion_point != NULL) { + if (strcmp((*insertion_point)->value->name, property) == 0) + return; + insertion_point = &(*insertion_point)->next; + } + + /* create a new value */ + new_value = ZALLOC(device_property); + new_value->name = (char *) strdup(property); + new_value->type = type; + if (sizeof_array > 0) { + void *new_array = zalloc(sizeof_array); + memcpy(new_array, array, sizeof_array); + new_value->array = new_array; + new_value->sizeof_array = sizeof_array; + } + new_value->owner = me; + new_value->original = original; + new_value->disposition = disposition; + + /* insert the value into the list */ + new_entry = ZALLOC(device_property_entry); + *insertion_point = new_entry; + if (sizeof_init_array > 0) { + void *new_init_array = zalloc(sizeof_init_array); + memcpy(new_init_array, init_array, sizeof_init_array); + new_entry->init_array = new_init_array; + new_entry->sizeof_init_array = sizeof_init_array; + } + new_entry->value = new_value; +} + + +/* local - not available externally */ +STATIC_INLINE_DEVICE\ +(void) +device_set_property(device *me, + const char *property, + device_property_type type, + const void *array, + int sizeof_array) +{ + /* find the property */ + device_property_entry *entry = find_property_entry(me, property); + if (entry != NULL) { + /* existing property - update it */ + void *new_array = 0; + device_property *value = entry->value; + /* check the type matches */ + if (value->type != type) + device_error(me, "conflict between type of new and old value for property %s", property); + /* replace its value */ + if (value->array != NULL) + zfree((void*)value->array); + new_array = (sizeof_array > 0 + ? zalloc(sizeof_array) + : (void*)0); + value->array = new_array; + value->sizeof_array = sizeof_array; + if (sizeof_array > 0) + memcpy(new_array, array, sizeof_array); + return; + } + else { + /* new property - create it */ + device_add_property(me, property, type, + NULL, 0, array, sizeof_array, + NULL, tempoary_object); + } +} + + +STATIC_INLINE_DEVICE\ +(void) +clean_device_properties(device *me) +{ + device_property_entry **delete_point = &me->properties; + while (*delete_point != NULL) { + device_property_entry *current = *delete_point; + switch (current->value->disposition) { + case permenant_object: + /* zap the current value, will be initialized later */ + ASSERT(current->init_array != NULL); + if (current->value->array != NULL) { + zfree((void*)current->value->array); + current->value->array = NULL; + } + delete_point = &(*delete_point)->next; + break; + case tempoary_object: + /* zap the actual property, was created during simulation run */ + ASSERT(current->init_array == NULL); + *delete_point = current->next; + if (current->value->array != NULL) + zfree((void*)current->value->array); + zfree(current->value); + zfree(current); + break; + } + } +} + + +INLINE_DEVICE\ +(void) +device_init_static_properties(device *me, + void *data) +{ + device_property_entry *property; + for (property = me->properties; + property != NULL; + property = property->next) { + ASSERT(property->init_array != NULL); + ASSERT(property->value->array == NULL); + ASSERT(property->value->disposition == permenant_object); + switch (property->value->type) { + case array_property: + case boolean_property: + case range_array_property: + case reg_array_property: + case string_property: + case string_array_property: + case integer_property: + /* delete the property, and replace it with the original */ + device_set_property(me, property->value->name, + property->value->type, + property->init_array, + property->sizeof_init_array); + break; + case ihandle_property: + break; + } + } +} + + +INLINE_DEVICE\ +(void) +device_init_runtime_properties(device *me, + void *data) +{ + device_property_entry *property; + for (property = me->properties; + property != NULL; + property = property->next) { + switch (property->value->disposition) { + case permenant_object: + switch (property->value->type) { + case ihandle_property: + { + device_instance *ihandle; + ihandle_runtime_property_spec spec; + ASSERT(property->init_array != NULL); + ASSERT(property->value->array == NULL); + device_find_ihandle_runtime_property(me, property->value->name, &spec); + ihandle = tree_instance(me, spec.full_path); + device_set_ihandle_property(me, property->value->name, ihandle); + break; + } + case array_property: + case boolean_property: + case range_array_property: + case integer_property: + case reg_array_property: + case string_property: + case string_array_property: + ASSERT(property->init_array != NULL); + ASSERT(property->value->array != NULL); + break; + } + break; + case tempoary_object: + ASSERT(property->init_array == NULL); + ASSERT(property->value->array != NULL); + break; + } + } +} + + +INLINE_DEVICE\ +(const device_property *) +device_next_property(const device_property *property) +{ + /* find the property in the list */ + device *owner = property->owner; + device_property_entry *entry = owner->properties; + while (entry != NULL && entry->value != property) + entry = entry->next; + /* now return the following property */ + ASSERT(entry != NULL); /* must be a member! */ + if (entry->next != NULL) + return entry->next->value; + else + return NULL; +} + + +INLINE_DEVICE\ +(const device_property *) +device_find_property(device *me, + const char *property) +{ + if (me == NULL) { + return NULL; + } + else if (property == NULL || strcmp(property, "") == 0) { + if (me->properties == NULL) + return NULL; + else + return me->properties->value; + } + else { + device_property_entry *entry = find_property_entry(me, property); + if (entry != NULL) + return entry->value; + } + return NULL; +} + + +INLINE_DEVICE\ +(void) +device_add_array_property(device *me, + const char *property, + const void *array, + int sizeof_array) +{ + device_add_property(me, property, array_property, + array, sizeof_array, array, sizeof_array, + NULL, permenant_object); +} + +INLINE_DEVICE\ +(void) +device_set_array_property(device *me, + const char *property, + const void *array, + int sizeof_array) +{ + device_set_property(me, property, array_property, array, sizeof_array); +} + +INLINE_DEVICE\ +(const device_property *) +device_find_array_property(device *me, + const char *property) +{ + const device_property *node; + node = device_find_property(me, property); + if (node == (device_property*)0 + || node->type != array_property) + device_error(me, "property %s not found or of wrong type", property); + return node; +} + + +INLINE_DEVICE\ +(void) +device_add_boolean_property(device *me, + const char *property, + int boolean) +{ + signed32 new_boolean = (boolean ? -1 : 0); + device_add_property(me, property, boolean_property, + &new_boolean, sizeof(new_boolean), + &new_boolean, sizeof(new_boolean), + NULL, permenant_object); +} + +INLINE_DEVICE\ +(int) +device_find_boolean_property(device *me, + const char *property) +{ + const device_property *node; + unsigned_cell boolean; + node = device_find_property(me, property); + if (node == (device_property*)0 + || node->type != boolean_property) + device_error(me, "property %s not found or of wrong type", property); + ASSERT(sizeof(boolean) == node->sizeof_array); + memcpy(&boolean, node->array, sizeof(boolean)); + return boolean; +} + + +INLINE_DEVICE\ +(void) +device_add_ihandle_runtime_property(device *me, + const char *property, + const ihandle_runtime_property_spec *ihandle) +{ + /* enter the full path as the init array */ + device_add_property(me, property, ihandle_property, + ihandle->full_path, strlen(ihandle->full_path) + 1, + NULL, 0, + NULL, permenant_object); +} + +INLINE_DEVICE\ +(void) +device_find_ihandle_runtime_property(device *me, + const char *property, + ihandle_runtime_property_spec *ihandle) +{ + device_property_entry *entry = find_property_entry(me, property); + TRACE(trace_devices, + ("device_find_ihandle_runtime_property(me=0x%lx, property=%s)\n", + (long)me, property)); + if (entry == NULL + || entry->value->type != ihandle_property + || entry->value->disposition != permenant_object) + device_error(me, "property %s not found or of wrong type", property); + ASSERT(entry->init_array != NULL); + /* the full path */ + ihandle->full_path = entry->init_array; +} + + + +INLINE_DEVICE\ +(void) +device_set_ihandle_property(device *me, + const char *property, + device_instance *ihandle) +{ + unsigned_cell cells; + cells = H2BE_cell(device_instance_to_external(ihandle)); + device_set_property(me, property, ihandle_property, + &cells, sizeof(cells)); + +} + +INLINE_DEVICE\ +(device_instance *) +device_find_ihandle_property(device *me, + const char *property) +{ + const device_property *node; + unsigned_cell ihandle; + device_instance *instance; + + node = device_find_property(me, property); + if (node == NULL || node->type != ihandle_property) + device_error(me, "property %s not found or of wrong type", property); + if (node->array == NULL) + device_error(me, "runtime property %s not yet initialized", property); + + ASSERT(sizeof(ihandle) == node->sizeof_array); + memcpy(&ihandle, node->array, sizeof(ihandle)); + instance = external_to_device_instance(me, BE2H_cell(ihandle)); + ASSERT(instance != NULL); + return instance; +} + + +INLINE_DEVICE\ +(void) +device_add_integer_property(device *me, + const char *property, + signed_cell integer) +{ + H2BE(integer); + device_add_property(me, property, integer_property, + &integer, sizeof(integer), + &integer, sizeof(integer), + NULL, permenant_object); +} + +INLINE_DEVICE\ +(signed_cell) +device_find_integer_property(device *me, + const char *property) +{ + const device_property *node; + signed_cell integer; + TRACE(trace_devices, + ("device_find_integer(me=0x%lx, property=%s)\n", + (long)me, property)); + node = device_find_property(me, property); + if (node == (device_property*)0 + || node->type != integer_property) + device_error(me, "property %s not found or of wrong type", property); + ASSERT(sizeof(integer) == node->sizeof_array); + memcpy(&integer, node->array, sizeof(integer)); + return BE2H_cell(integer); +} + +INLINE_DEVICE\ +(int) +device_find_integer_array_property(device *me, + const char *property, + unsigned index, + signed_cell *integer) +{ + const device_property *node; + int sizeof_integer = sizeof(*integer); + signed_cell *cell; + TRACE(trace_devices, + ("device_find_integer(me=0x%lx, property=%s)\n", + (long)me, property)); + + /* check things sane */ + node = device_find_property(me, property); + if (node == (device_property*)0 + || (node->type != integer_property + && node->type != array_property)) + device_error(me, "property %s not found or of wrong type", property); + if ((node->sizeof_array % sizeof_integer) != 0) + device_error(me, "property %s contains an incomplete number of cells", property); + if (node->sizeof_array <= sizeof_integer * index) + return 0; + + /* Find and convert the value */ + cell = ((signed_cell*)node->array) + index; + *integer = BE2H_cell(*cell); + + return node->sizeof_array / sizeof_integer; +} + + +STATIC_INLINE_DEVICE\ +(unsigned_cell *) +unit_address_to_cells(const device_unit *unit, + unsigned_cell *cell, + int nr_cells) +{ + int i; + ASSERT(nr_cells == unit->nr_cells); + for (i = 0; i < unit->nr_cells; i++) { + *cell = H2BE_cell(unit->cells[i]); + cell += 1; + } + return cell; +} + + +STATIC_INLINE_DEVICE\ +(const unsigned_cell *) +cells_to_unit_address(const unsigned_cell *cell, + device_unit *unit, + int nr_cells) +{ + int i; + memset(unit, 0, sizeof(*unit)); + unit->nr_cells = nr_cells; + for (i = 0; i < unit->nr_cells; i++) { + unit->cells[i] = BE2H_cell(*cell); + cell += 1; + } + return cell; +} + + +STATIC_INLINE_DEVICE\ +(unsigned) +nr_range_property_cells(device *me, + int nr_ranges) +{ + return ((device_nr_address_cells(me) + + device_nr_address_cells(device_parent(me)) + + device_nr_size_cells(me)) + ) * nr_ranges; +} + +INLINE_DEVICE\ +(void) +device_add_range_array_property(device *me, + const char *property, + const range_property_spec *ranges, + unsigned nr_ranges) +{ + unsigned sizeof_cells = (nr_range_property_cells(me, nr_ranges) + * sizeof(unsigned_cell)); + unsigned_cell *cells = zalloc(sizeof_cells); + unsigned_cell *cell; + int i; + + /* copy the property elements over */ + cell = cells; + for (i = 0; i < nr_ranges; i++) { + const range_property_spec *range = &ranges[i]; + /* copy the child address */ + cell = unit_address_to_cells(&range->child_address, cell, + device_nr_address_cells(me)); + /* copy the parent address */ + cell = unit_address_to_cells(&range->parent_address, cell, + device_nr_address_cells(device_parent(me))); + /* copy the size */ + cell = unit_address_to_cells(&range->size, cell, + device_nr_size_cells(me)); + } + ASSERT(cell == &cells[nr_range_property_cells(me, nr_ranges)]); + + /* add it */ + device_add_property(me, property, range_array_property, + cells, sizeof_cells, + cells, sizeof_cells, + NULL, permenant_object); + + zfree(cells); +} + +INLINE_DEVICE\ +(int) +device_find_range_array_property(device *me, + const char *property, + unsigned index, + range_property_spec *range) +{ + const device_property *node; + unsigned sizeof_entry = (nr_range_property_cells(me, 1) + * sizeof(unsigned_cell)); + const unsigned_cell *cells; + + /* locate the property */ + node = device_find_property(me, property); + if (node == (device_property*)0 + || node->type != range_array_property) + device_error(me, "property %s not found or of wrong type", property); + + /* aligned ? */ + if ((node->sizeof_array % sizeof_entry) != 0) + device_error(me, "property %s contains an incomplete number of entries", + property); + + /* within bounds? */ + if (node->sizeof_array < sizeof_entry * (index + 1)) + return 0; + + /* find the range of interest */ + cells = (unsigned_cell*)((char*)node->array + sizeof_entry * index); + + /* copy the child address out - converting as we go */ + cells = cells_to_unit_address(cells, &range->child_address, + device_nr_address_cells(me)); + + /* copy the parent address out - converting as we go */ + cells = cells_to_unit_address(cells, &range->parent_address, + device_nr_address_cells(device_parent(me))); + + /* copy the size - converting as we go */ + cells = cells_to_unit_address(cells, &range->size, + device_nr_size_cells(me)); + + return node->sizeof_array / sizeof_entry; +} + + +STATIC_INLINE_DEVICE\ +(unsigned) +nr_reg_property_cells(device *me, + int nr_regs) +{ + return (device_nr_address_cells(device_parent(me)) + + device_nr_size_cells(device_parent(me)) + ) * nr_regs; +} + +INLINE_DEVICE\ +(void) +device_add_reg_array_property(device *me, + const char *property, + const reg_property_spec *regs, + unsigned nr_regs) +{ + unsigned sizeof_cells = (nr_reg_property_cells(me, nr_regs) + * sizeof(unsigned_cell)); + unsigned_cell *cells = zalloc(sizeof_cells); + unsigned_cell *cell; + int i; + + /* copy the property elements over */ + cell = cells; + for (i = 0; i < nr_regs; i++) { + const reg_property_spec *reg = ®s[i]; + /* copy the address */ + cell = unit_address_to_cells(®->address, cell, + device_nr_address_cells(device_parent(me))); + /* copy the size */ + cell = unit_address_to_cells(®->size, cell, + device_nr_size_cells(device_parent(me))); + } + ASSERT(cell == &cells[nr_reg_property_cells(me, nr_regs)]); + + /* add it */ + device_add_property(me, property, reg_array_property, + cells, sizeof_cells, + cells, sizeof_cells, + NULL, permenant_object); + + zfree(cells); +} + +INLINE_DEVICE\ +(int) +device_find_reg_array_property(device *me, + const char *property, + unsigned index, + reg_property_spec *reg) +{ + const device_property *node; + unsigned sizeof_entry = (nr_reg_property_cells(me, 1) + * sizeof(unsigned_cell)); + const unsigned_cell *cells; + + /* locate the property */ + node = device_find_property(me, property); + if (node == (device_property*)0 + || node->type != reg_array_property) + device_error(me, "property %s not found or of wrong type", property); + + /* aligned ? */ + if ((node->sizeof_array % sizeof_entry) != 0) + device_error(me, "property %s contains an incomplete number of entries", + property); + + /* within bounds? */ + if (node->sizeof_array < sizeof_entry * (index + 1)) + return 0; + + /* find the range of interest */ + cells = (unsigned_cell*)((char*)node->array + sizeof_entry * index); + + /* copy the address out - converting as we go */ + cells = cells_to_unit_address(cells, ®->address, + device_nr_address_cells(device_parent(me))); + + /* copy the size out - converting as we go */ + cells = cells_to_unit_address(cells, ®->size, + device_nr_size_cells(device_parent(me))); + + return node->sizeof_array / sizeof_entry; +} + + +INLINE_DEVICE\ +(void) +device_add_string_property(device *me, + const char *property, + const char *string) +{ + device_add_property(me, property, string_property, + string, strlen(string) + 1, + string, strlen(string) + 1, + NULL, permenant_object); +} + +INLINE_DEVICE\ +(const char *) +device_find_string_property(device *me, + const char *property) +{ + const device_property *node; + const char *string; + node = device_find_property(me, property); + if (node == (device_property*)0 + || node->type != string_property) + device_error(me, "property %s not found or of wrong type", property); + string = node->array; + ASSERT(strlen(string) + 1 == node->sizeof_array); + return string; +} + +INLINE_DEVICE\ +(void) +device_add_string_array_property(device *me, + const char *property, + const string_property_spec *strings, + unsigned nr_strings) +{ + int sizeof_array; + int string_nr; + char *array; + char *chp; + if (nr_strings == 0) + device_error(me, "property %s must be non-null", property); + /* total up the size of the needed array */ + for (sizeof_array = 0, string_nr = 0; + string_nr < nr_strings; + string_nr ++) { + sizeof_array += strlen(strings[string_nr]) + 1; + } + /* create the array */ + array = (char*)zalloc(sizeof_array); + chp = array; + for (string_nr = 0; + string_nr < nr_strings; + string_nr++) { + strcpy(chp, strings[string_nr]); + chp += strlen(chp) + 1; + } + ASSERT(chp == array + sizeof_array); + /* now enter it */ + device_add_property(me, property, string_array_property, + array, sizeof_array, + array, sizeof_array, + NULL, permenant_object); +} + +INLINE_DEVICE\ +(int) +device_find_string_array_property(device *me, + const char *property, + unsigned index, + string_property_spec *string) +{ + const device_property *node; + node = device_find_property(me, property); + if (node == (device_property*)0) + device_error(me, "property %s not found", property); + switch (node->type) { + default: + device_error(me, "property %s of wrong type", property); + break; + case string_property: + if (index == 0) { + *string = node->array; + ASSERT(strlen(*string) + 1 == node->sizeof_array); + return 1; + } + break; + case array_property: + if (node->sizeof_array == 0 + || ((char*)node->array)[node->sizeof_array - 1] != '\0') + device_error(me, "property %s invalid for string array", property); + /* FALL THROUGH */ + case string_array_property: + ASSERT(node->sizeof_array > 0); + ASSERT(((char*)node->array)[node->sizeof_array - 1] == '\0'); + { + const char *chp = node->array; + int nr_entries = 0; + /* count the number of strings, keeping an eye out for the one + we're looking for */ + *string = chp; + do { + if (*chp == '\0') { + /* next string */ + nr_entries++; + chp++; + if (nr_entries == index) + *string = chp; + } + else { + chp++; + } + } while (chp < (char*)node->array + node->sizeof_array); + if (index < nr_entries) + return nr_entries; + else { + *string = NULL; + return 0; + } + } + break; + } + return 0; +} + +INLINE_DEVICE\ +(void) +device_add_duplicate_property(device *me, + const char *property, + const device_property *original) +{ + device_property_entry *master; + TRACE(trace_devices, + ("device_add_duplicate_property(me=0x%lx, property=%s, ...)\n", + (long)me, property)); + if (original->disposition != permenant_object) + device_error(me, "Can only duplicate permenant objects"); + /* find the original's master */ + master = original->owner->properties; + while (master->value != original) { + master = master->next; + ASSERT(master != NULL); + } + /* now duplicate it */ + device_add_property(me, property, + original->type, + master->init_array, master->sizeof_init_array, + original->array, original->sizeof_array, + original, permenant_object); +} + + + +/* Device Hardware: */ + +INLINE_DEVICE\ +(unsigned) +device_io_read_buffer(device *me, + void *dest, + int space, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + if (me->callback->io.read_buffer == NULL) + device_error(me, "no io.read_buffer method"); + return me->callback->io.read_buffer(me, dest, space, + addr, nr_bytes, + processor, cia); +} + +INLINE_DEVICE\ +(unsigned) +device_io_write_buffer(device *me, + const void *source, + int space, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + if (me->callback->io.write_buffer == NULL) + device_error(me, "no io.write_buffer method"); + return me->callback->io.write_buffer(me, source, space, + addr, nr_bytes, + processor, cia); +} + +INLINE_DEVICE\ +(unsigned) +device_dma_read_buffer(device *me, + void *dest, + int space, + unsigned_word addr, + unsigned nr_bytes) +{ + if (me->callback->dma.read_buffer == NULL) + device_error(me, "no dma.read_buffer method"); + return me->callback->dma.read_buffer(me, dest, space, + addr, nr_bytes); +} + +INLINE_DEVICE\ +(unsigned) +device_dma_write_buffer(device *me, + const void *source, + int space, + unsigned_word addr, + unsigned nr_bytes, + int violate_read_only_section) +{ + if (me->callback->dma.write_buffer == NULL) + device_error(me, "no dma.write_buffer method"); + return me->callback->dma.write_buffer(me, source, space, + addr, nr_bytes, + violate_read_only_section); +} + +INLINE_DEVICE\ +(void) +device_attach_address(device *me, + attach_type attach, + int space, + unsigned_word addr, + unsigned nr_bytes, + access_type access, + device *client) /*callback/default*/ +{ + if (me->callback->address.attach == NULL) + device_error(me, "no address.attach method"); + me->callback->address.attach(me, attach, space, + addr, nr_bytes, access, client); +} + +INLINE_DEVICE\ +(void) +device_detach_address(device *me, + attach_type attach, + int space, + unsigned_word addr, + unsigned nr_bytes, + access_type access, + device *client) /*callback/default*/ +{ + if (me->callback->address.detach == NULL) + device_error(me, "no address.detach method"); + me->callback->address.detach(me, attach, space, + addr, nr_bytes, access, client); +} + + + +/* Interrupts: */ + +INLINE_DEVICE(void) +device_interrupt_event(device *me, + int my_port, + int level, + cpu *processor, + unsigned_word cia) +{ + int found_an_edge = 0; + device_interrupt_edge *edge; + /* device's interrupt lines directly connected */ + for (edge = me->interrupt_destinations; + edge != NULL; + edge = edge->next) { + if (edge->my_port == my_port) { + if (edge->dest->callback->interrupt.event == NULL) + device_error(me, "no interrupt method"); + edge->dest->callback->interrupt.event(edge->dest, + edge->dest_port, + me, + my_port, + level, + processor, cia); + found_an_edge = 1; + } + } + if (!found_an_edge) { + device_error(me, "No interrupt edge for port %d", my_port); + } +} + +INLINE_DEVICE\ +(void) +device_interrupt_attach(device *me, + int my_port, + device *dest, + int dest_port, + object_disposition disposition) +{ + attach_device_interrupt_edge(&me->interrupt_destinations, + my_port, + dest, + dest_port, + disposition); +} + +INLINE_DEVICE\ +(void) +device_interrupt_detach(device *me, + int my_port, + device *dest, + int dest_port) +{ + detach_device_interrupt_edge(me, + &me->interrupt_destinations, + my_port, + dest, + dest_port); +} + +INLINE_DEVICE\ +(void) +device_interrupt_traverse(device *me, + device_interrupt_traverse_function *handler, + void *data) +{ + device_interrupt_edge *interrupt_edge; + for (interrupt_edge = me->interrupt_destinations; + interrupt_edge != NULL; + interrupt_edge = interrupt_edge->next) { + handler(me, interrupt_edge->my_port, + interrupt_edge->dest, interrupt_edge->dest_port, + data); + } +} + +INLINE_DEVICE\ +(int) +device_interrupt_decode(device *me, + const char *port_name, + port_direction direction) +{ + if (port_name == NULL || port_name[0] == '\0') + return 0; + if (isdigit(port_name[0])) { + return strtoul(port_name, NULL, 0); + } + else { + const device_interrupt_port_descriptor *ports = + me->callback->interrupt.ports; + if (ports != NULL) { + while (ports->name != NULL) { + if (ports->direction == bidirect_port + || ports->direction == direction) { + if (ports->nr_ports > 0) { + int len = strlen(ports->name); + if (strncmp(port_name, ports->name, len) == 0) { + if (port_name[len] == '\0') + return ports->number; + else if(isdigit(port_name[len])) { + int port = ports->number + strtoul(&port_name[len], NULL, 0); + if (port >= ports->number + ports->nr_ports) + device_error(me, "Interrupt port %s out of range", + port_name); + return port; + } + } + } + else if (strcmp(port_name, ports->name) == 0) + return ports->number; + } + ports++; + } + } + } + device_error(me, "Unreconized interrupt port %s", port_name); + return 0; +} + +INLINE_DEVICE\ +(int) +device_interrupt_encode(device *me, + int port_number, + char *buf, + int sizeof_buf, + port_direction direction) +{ + const device_interrupt_port_descriptor *ports = NULL; + ports = me->callback->interrupt.ports; + if (ports != NULL) { + while (ports->name != NULL) { + if (ports->direction == bidirect_port + || ports->direction == direction) { + if (ports->nr_ports > 0) { + if (port_number >= ports->number + && port_number < ports->number + ports->nr_ports) { + strcpy(buf, ports->name); + sprintf(buf + strlen(buf), "%d", port_number - ports->number); + if (strlen(buf) >= sizeof_buf) + error("device_interrupt_encode: buffer overflow"); + return strlen(buf); + } + } + else { + if (ports->number == port_number) { + if (strlen(ports->name) >= sizeof_buf) + error("device_interrupt_encode: buffer overflow"); + strcpy(buf, ports->name); + return strlen(buf); + } + } + } + ports++; + } + } + sprintf(buf, "%d", port_number); + if (strlen(buf) >= sizeof_buf) + error("device_interrupt_encode: buffer overflow"); + return strlen(buf); +} + + + +/* IOCTL: */ + +EXTERN_DEVICE\ +(int) +device_ioctl(device *me, + cpu *processor, + unsigned_word cia, + device_ioctl_request request, + ...) +{ + int status; + va_list ap; + va_start(ap, request); + if (me->callback->ioctl == NULL) + device_error(me, "no ioctl method"); + status = me->callback->ioctl(me, processor, cia, request, ap); + va_end(ap); + return status; +} + + + +/* I/O */ + +EXTERN_DEVICE\ +(void volatile) +device_error(device *me, + const char *fmt, + ...) +{ + char message[1024]; + va_list ap; + /* format the message */ + va_start(ap, fmt); + vsprintf(message, fmt, ap); + va_end(ap); + /* sanity check */ + if (strlen(message) >= sizeof(message)) + error("device_error: buffer overflow"); + if (me == NULL) + error("device: %s", message); + else if (me->path != NULL && me->path[0] != '\0') + error("%s: %s", me->path, message); + else if (me->name != NULL && me->name[0] != '\0') + error("%s: %s", me->name, message); + else + error("device: %s", message); + while(1); +} + +INLINE_DEVICE\ +(int) +device_trace(device *me) +{ + return me->trace; +} + + +/* External representation */ + +INLINE_DEVICE\ +(device *) +external_to_device(device *tree_member, + unsigned_cell phandle) +{ + device *me = cap_internal(tree_member->phandles, phandle); + return me; +} + +INLINE_DEVICE\ +(unsigned_cell) +device_to_external(device *me) +{ + unsigned_cell phandle = cap_external(me->phandles, me); + return phandle; +} + +INLINE_DEVICE\ +(device_instance *) +external_to_device_instance(device *tree_member, + unsigned_cell ihandle) +{ + device_instance *instance = cap_internal(tree_member->ihandles, ihandle); + return instance; +} + +INLINE_DEVICE\ +(unsigned_cell) +device_instance_to_external(device_instance *instance) +{ + unsigned_cell ihandle = cap_external(instance->owner->ihandles, instance); + return ihandle; +} + + +/* Map onto the event functions */ + +INLINE_DEVICE\ +(event_entry_tag) +device_event_queue_schedule(device *me, + signed64 delta_time, + device_event_handler *handler, + void *data) +{ + return event_queue_schedule(psim_event_queue(me->system), + delta_time, + handler, + data); +} + +INLINE_DEVICE\ +(void) +device_event_queue_deschedule(device *me, + event_entry_tag event_to_remove) +{ + event_queue_deschedule(psim_event_queue(me->system), + event_to_remove); +} + +INLINE_DEVICE\ +(signed64) +device_event_queue_time(device *me) +{ + return event_queue_time(psim_event_queue(me->system)); +} + + +/* Initialization: */ + + +INLINE_DEVICE\ +(void) +device_clean(device *me, + void *data) +{ + psim *system; + system = (psim*)data; + TRACE(trace_device_init, ("device_clean - initializing %s", me->path)); + clean_device_interrupt_edges(&me->interrupt_destinations); + clean_device_instances(me); + clean_device_properties(me); +} + +/* Device initialization: */ + +INLINE_DEVICE\ +(void) +device_init_address(device *me, + void *data) +{ + psim *system = (psim*)data; + int nr_address_cells; + int nr_size_cells; + TRACE(trace_device_init, ("device_init_address - initializing %s", me->path)); + + /* ensure the cap database is valid */ + if (me->parent == NULL) { + cap_init(me->ihandles); + cap_init(me->phandles); + } + + /* some basics */ + me->system = system; /* misc things not known until now */ + me->trace = (device_find_property(me, "trace") + ? device_find_integer_property(me, "trace") + : 0); + + /* Ensure that the first address found in the reg property matches + anything that was specified as part of the devices name */ + if (device_find_property(me, "reg") != NULL) { + reg_property_spec unit; + device_find_reg_array_property(me, "reg", 0, &unit); + if (memcmp(device_unit_address(me), &unit.address, sizeof(unit.address)) + != 0) + device_error(me, "Unit address as specified by the reg property in conflict with the value previously specified in the devices path"); + } + + /* ensure that the devices #address/size-cells is consistent */ + nr_address_cells = device_nr_address_cells(me); + if (device_find_property(me, "#address-cells") != NULL + && (nr_address_cells + != device_find_integer_property(me, "#address-cells"))) + device_error(me, "#address-cells property used before defined"); + nr_size_cells = device_nr_size_cells(me); + if (device_find_property(me, "#size-cells") != NULL + && (nr_size_cells + != device_find_integer_property(me, "#size-cells"))) + device_error(me, "#size-cells property used before defined"); + + /* now init it */ + if (me->callback->init.address != NULL) + me->callback->init.address(me); +} + +INLINE_DEVICE\ +(void) +device_init_data(device *me, + void *data) +{ + TRACE(trace_device_init, ("device_init_data - initializing %s", me->path)); + if (me->callback->init.data != NULL) + me->callback->init.data(me); +} + +#endif /* _DEVICE_C_ */ diff --git a/sim/ppc/device.h b/sim/ppc/device.h new file mode 100644 index 00000000000..56c8ce54d99 --- /dev/null +++ b/sim/ppc/device.h @@ -0,0 +1,797 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1997, 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_H_ +#define _DEVICE_H_ + +#ifndef INLINE_DEVICE +#define INLINE_DEVICE +#endif + +/* declared in basics.h, this object is used everywhere */ +/* typedef struct _device device; */ + + +/* Introduction: + + As explained in earlier sections, the device, device instance, + property and interrupts lie at the heart of PSIM's device model. + + In the below a synopsis of the device object and the operations it + supports are given. Details of this object can be found in the + files <<device.h>> and <<device.c>>. + + */ + + +/* Device creation: */ + +INLINE_DEVICE\ +(device *) device_create +(device *parent, + const char *base, + const char *name, + const char *unit_address, + const char *args); + +INLINE_DEVICE\ +(void) device_usage +(int verbose); + + +/* Device initialization: */ + +INLINE_DEVICE\ +(void) device_clean +(device *root, + void *data); + +INLINE_DEVICE\ +(void) device_init_static_properties +(device *me, + void *data); + +INLINE_DEVICE\ +(void) device_init_address +(device *me, + void *data); + +INLINE_DEVICE\ +(void) device_init_runtime_properties +(device *me, + void *data); + +INLINE_DEVICE\ +(void) device_init_data +(device *me, + void *data); + + +/* Relationships: + + A device is able to determine its relationship to other devices + within the tree. Operations include querying for a devices parent, + sibling, child, name, and path (from the root). + + */ + +INLINE_DEVICE\ +(device *) device_parent +(device *me); + +INLINE_DEVICE\ +(device *) device_root +(device *me); + +INLINE_DEVICE\ +(device *) device_sibling +(device *me); + +INLINE_DEVICE\ +(device *) device_child +(device *me); + +INLINE_DEVICE\ +(const char *) device_name +(device *me); + +INLINE_DEVICE\ +(const char *) device_base +(device *me); + +INLINE_DEVICE\ +(const char *) device_path +(device *me); + +INLINE_DEVICE\ +(void *) device_data +(device *me); + +INLINE_DEVICE\ +(psim *) device_system +(device *me); + +typedef struct _device_unit { + int nr_cells; + unsigned_cell cells[4]; /* unused cells are zero */ +} device_unit; + +INLINE_DEVICE\ +(const device_unit *) device_unit_address +(device *me); + +INLINE_DEVICE\ +(int) device_decode_unit +(device *bus, + const char *unit, + device_unit *address); + +INLINE_DEVICE\ +(int) device_encode_unit +(device *bus, + const device_unit *unit_address, + char *buf, + int sizeof_buf); + + +/* Convert an Open Firmware size into a form suitable for attach + address calls. + + Return a zero result if the address should be ignored when looking + for attach addresses */ + +INLINE_DEVICE\ +(int) device_address_to_attach_address +(device *me, + const device_unit *address, + int *attach_space, + unsigned_word *attach_address, + device *client); + + +/* Convert an Open Firmware size into a form suitable for attach + address calls + + Return a zero result if the address should be ignored */ + +INLINE_DEVICE\ +(int) device_size_to_attach_size +(device *me, + const device_unit *size, + unsigned *nr_bytes, + device *client); + + +INLINE_DEVICE\ +(unsigned) device_nr_address_cells +(device *me); + +INLINE_DEVICE\ +(unsigned) device_nr_size_cells +(device *me); + + +/* Properties: + + Attached to a device are a number of properties. Each property has + a size and type (both of which can be queried). A device is able + to iterate over or query and set a properties value. + + */ + +/* The following are valid property types. The property `array' is + for generic untyped data. */ + +typedef enum { + array_property, + boolean_property, + ihandle_property, /*runtime*/ + integer_property, + range_array_property, + reg_array_property, + string_property, + string_array_property, +} device_property_type; + +typedef struct _device_property device_property; +struct _device_property { + device *owner; + const char *name; + device_property_type type; + unsigned sizeof_array; + const void *array; + const device_property *original; + object_disposition disposition; +}; + + +/* iterate through the properties attached to a device */ + +INLINE_DEVICE\ +(const device_property *) device_next_property +(const device_property *previous); + +INLINE_DEVICE\ +(const device_property *) device_find_property +(device *me, + const char *property); /* NULL for first property */ + + +/* Manipulate the properties belonging to a given device. + + SET on the other hand will force the properties value. The + simulation is aborted if the property was present but of a + conflicting type. + + FIND returns the specified properties value, aborting the + simulation if the property is missing. Code locating a property + should first check its type (using device_find_property above) and + then obtain its value using the below. + + void device_add_<type>_property(device *, const char *, <type>) + void device_add_*_array_property(device *, const char *, const <type>*, int) + void device_set_*_property(device *, const char *, <type>) + void device_set_*_array_property(device *, const char *, const <type>*, int) + <type> device_find_*_property(device *, const char *) + int device_find_*_array_property(device *, const char *, int, <type>*) + + */ + + +INLINE_DEVICE\ +(void) device_add_array_property +(device *me, + const char *property, + const void *array, + int sizeof_array); + +INLINE_DEVICE\ +(void) device_set_array_property +(device *me, + const char *property, + const void *array, + int sizeof_array); + +INLINE_DEVICE\ +(const device_property *) device_find_array_property +(device *me, + const char *property); + + + +INLINE_DEVICE\ +(void) device_add_boolean_property +(device *me, + const char *property, + int bool); + +INLINE_DEVICE\ +(int) device_find_boolean_property +(device *me, + const char *property); + + + +typedef struct _ihandle_runtime_property_spec { + const char *full_path; +} ihandle_runtime_property_spec; + +INLINE_DEVICE\ +(void) device_add_ihandle_runtime_property +(device *me, + const char *property, + const ihandle_runtime_property_spec *ihandle); + +INLINE_DEVICE\ +(void) device_find_ihandle_runtime_property +(device *me, + const char *property, + ihandle_runtime_property_spec *ihandle); + +INLINE_DEVICE\ +(void) device_set_ihandle_property +(device *me, + const char *property, + device_instance *ihandle); + +INLINE_DEVICE\ +(device_instance *) device_find_ihandle_property +(device *me, + const char *property); + + + +INLINE_DEVICE\ +(void) device_add_integer_property +(device *me, + const char *property, + signed_cell integer); + +INLINE_DEVICE\ +(signed_cell) device_find_integer_property +(device *me, + const char *property); + +INLINE_DEVICE\ +(int) device_find_integer_array_property +(device *me, + const char *property, + unsigned index, + signed_word *integer); + + + +typedef struct _range_property_spec { + device_unit child_address; + device_unit parent_address; + device_unit size; +} range_property_spec; + +INLINE_DEVICE\ +(void) device_add_range_array_property +(device *me, + const char *property, + const range_property_spec *ranges, + unsigned nr_ranges); + +INLINE_DEVICE\ +(int) device_find_range_array_property +(device *me, + const char *property, + unsigned index, + range_property_spec *range); + + + +typedef struct _reg_property_spec { + device_unit address; + device_unit size; +} reg_property_spec; + +INLINE_DEVICE\ +(void) device_add_reg_array_property +(device *me, + const char *property, + const reg_property_spec *reg, + unsigned nr_regs); + +INLINE_DEVICE\ +(int) device_find_reg_array_property +(device *me, + const char *property, + unsigned index, + reg_property_spec *reg); + + + +INLINE_DEVICE\ +(void) device_add_string_property +(device *me, + const char *property, + const char *string); + +INLINE_DEVICE\ +(const char *) device_find_string_property +(device *me, + const char *property); + + + +typedef const char *string_property_spec; + +INLINE_DEVICE\ +(void) device_add_string_array_property +(device *me, + const char *property, + const string_property_spec *strings, + unsigned nr_strings); + +INLINE_DEVICE\ +(int) device_find_string_array_property +(device *me, + const char *property, + unsigned index, + string_property_spec *string); + + + +INLINE_DEVICE\ +(void) device_add_duplicate_property +(device *me, + const char *property, + const device_property *original); + + + +/* Instances: + + As with IEEE1275, a device can be opened, creating an instance. + Instances provide more abstract interfaces to the underlying + hardware. For example, the instance methods for a disk may include + code that is able to interpret file systems found on disks. Such + methods would there for allow the manipulation of files on the + disks file system. The operations would be implemented using the + basic block I/O model provided by the disk. + + This model includes methods that faciliate the creation of device + instance and (should a given device support it) standard operations + on those instances. + + */ + +typedef struct _device_instance_callbacks device_instance_callbacks; + +INLINE_DEVICE\ +(device_instance *) device_create_instance_from +(device *me, /*OR*/ device_instance *parent, + void *data, + const char *path, + const char *args, + const device_instance_callbacks *callbacks); + +INLINE_DEVICE\ +(device_instance *) device_create_instance +(device *me, + const char *full_path, + const char *args); + +INLINE_DEVICE\ +(void) device_instance_delete +(device_instance *instance); + +INLINE_DEVICE\ +(int) device_instance_read +(device_instance *instance, + void *addr, + unsigned_word len); + +INLINE_DEVICE\ +(int) device_instance_write +(device_instance *instance, + const void *addr, + unsigned_word len); + +INLINE_DEVICE\ +(int) device_instance_seek +(device_instance *instance, + unsigned_word pos_hi, + unsigned_word pos_lo); + +INLINE_DEVICE\ +(int) device_instance_call_method +(device_instance *instance, + const char *method, + int n_stack_args, + unsigned_cell stack_args[/*n_stack_args*/], + int n_stack_returns, + unsigned_cell stack_returns[/*n_stack_returns*/]); + +INLINE_DEVICE\ +(device *) device_instance_device +(device_instance *instance); + +INLINE_DEVICE\ +(const char *) device_instance_path +(device_instance *instance); + +INLINE_DEVICE\ +(void *) device_instance_data +(device_instance *instance); + + +/* Interrupts: + + */ + +/* Interrupt Source + + A device drives its interrupt line using the call + + */ + +INLINE_DEVICE\ +(void) device_interrupt_event +(device *me, + int my_port, + int value, + cpu *processor, + unsigned_word cia); + +/* This interrupt event will then be propogated to any attached + interrupt destinations. + + Any interpretation of PORT and VALUE is model dependant. However + as guidelines the following are recommended: PCI interrupts a-d + correspond to lines 0-3; level sensative interrupts be requested + with a value of one and withdrawn with a value of 0; edge sensative + interrupts always have a value of 1, the event its self is treated + as the interrupt. + + + Interrupt Destinations + + Attached to each interrupt line of a device can be zero or more + desitinations. These destinations consist of a device/port pair. + A destination is attached/detached to a device line using the + attach and detach calls. */ + +INLINE_DEVICE\ +(void) device_interrupt_attach +(device *me, + int my_port, + device *dest, + int dest_port, + object_disposition disposition); + +INLINE_DEVICE\ +(void) device_interrupt_detach +(device *me, + int my_port, + device *dest, + int dest_port); + +typedef void (device_interrupt_traverse_function) + (device *me, + int my_port, + device *dest, + int my_dest, + void *data); + +INLINE_DEVICE\ +(void) device_interrupt_traverse +(device *me, + device_interrupt_traverse_function *handler, + void *data); + + +/* DESTINATION is attached (detached) to LINE of the device ME + + + Interrupt conversion + + Users refer to interrupt port numbers symbolically. For instance a + device may refer to its `INT' signal which is internally + represented by port 3. + + To convert to/from the symbolic and internal representation of a + port name/number. The following functions are available. */ + +INLINE_DEVICE\ +(int) device_interrupt_decode +(device *me, + const char *symbolic_name, + port_direction direction); + +INLINE_DEVICE\ +(int) device_interrupt_encode +(device *me, + int port_number, + char *buf, + int sizeof_buf, + port_direction direction); + + +/* Hardware operations: + + */ + +INLINE_DEVICE\ +(unsigned) device_io_read_buffer +(device *me, + void *dest, + int space, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia); + +INLINE_DEVICE\ +(unsigned) device_io_write_buffer +(device *me, + const void *source, + int space, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia); + + +/* Conversly, the device pci1000,1@1 my need to perform a dma transfer + into the cpu/memory core. Just as I/O moves towards the leaves, + dma transfers move towards the core via the initiating devices + parent nodes. The root device (special) converts the DMA transfer + into reads/writes to memory */ + +INLINE_DEVICE\ +(unsigned) device_dma_read_buffer +(device *me, + void *dest, + int space, + unsigned_word addr, + unsigned nr_bytes); + +INLINE_DEVICE\ +(unsigned) device_dma_write_buffer +(device *me, + const void *source, + int space, + unsigned_word addr, + unsigned nr_bytes, + int violate_read_only_section); + +/* To avoid the need for an intermediate (bridging) node to ask each + of its child devices in turn if an IO access is intended for them, + parent nodes maintain a table mapping addresses directly to + specific devices. When a device is `connected' to its bus it + attaches its self to its parent. */ + +/* Address access attributes */ +typedef enum _access_type { + access_invalid = 0, + access_read = 1, + access_write = 2, + access_read_write = 3, + access_exec = 4, + access_read_exec = 5, + access_write_exec = 6, + access_read_write_exec = 7, +} access_type; + +/* Address attachement types */ +typedef enum _attach_type { + attach_invalid, + attach_raw_memory, + attach_callback, + /* ... */ +} attach_type; + +INLINE_DEVICE\ +(void) device_attach_address +(device *me, + attach_type attach, + int space, + unsigned_word addr, + unsigned nr_bytes, + access_type access, + device *client); /*callback/default*/ + +INLINE_DEVICE\ +(void) device_detach_address +(device *me, + attach_type attach, + int space, + unsigned_word addr, + unsigned nr_bytes, + access_type access, + device *client); /*callback/default*/ + +/* Utilities: + + */ + +/* IOCTL:: + + Often devices require `out of band' operations to be performed. + For instance a pal device may need to notify a PCI bridge device + that an interrupt ack cycle needs to be performed on the PCI bus. + Within PSIM such operations are performed by using the generic + ioctl call <<device_ioctl()>>. + + */ + +typedef enum { + device_ioctl_break, /* unsigned_word requested_break */ + device_ioctl_set_trace, /* void */ + device_ioctl_create_stack, /* unsigned_word *sp, char **argv, char **envp */ + device_ioctl_change_media, /* const char *new_image (possibly NULL) */ + nr_device_ioctl_requests, +} device_ioctl_request; + +EXTERN_DEVICE\ +(int) device_ioctl +(device *me, + cpu *processor, + unsigned_word cia, + device_ioctl_request request, + ...); + + +/* Error reporting:: + + So that errors originating from devices appear in a consistent + format, the <<device_error()>> function can be used. Formats and + outputs the error message before aborting the simulation + + Devices should use this function to abort the simulation except + when the abort reason leaves the simulation in a hazardous + condition (for instance a failed malloc). + + */ + +EXTERN_DEVICE\ +(void volatile) device_error +(device *me, + const char *fmt, + ...) __attribute__ ((format (printf, 2, 3))); + +INLINE_DEVICE\ +(int) device_trace +(device *me); + + + +/* External representation: + + Both device nodes and device instances, in OpenBoot firmware have + an external representation (phandles and ihandles) and these values + are both stored in the device tree in property nodes and passed + between the client program and the simulator during emulation + calls. + + To limit the potential risk associated with trusing `data' from the + client program, the following mapping operators `safely' convert + between the two representations + + */ + +INLINE_DEVICE\ +(device *) external_to_device +(device *tree_member, + unsigned_cell phandle); + +INLINE_DEVICE\ +(unsigned_cell) device_to_external +(device *me); + +INLINE_DEVICE\ +(device_instance *) external_to_device_instance +(device *tree_member, + unsigned_cell ihandle); + +INLINE_DEVICE\ +(unsigned_cell) device_instance_to_external +(device_instance *me); + + +/* Event queue: + + The device inherets certain event queue operations from the main + simulation. */ + +typedef void device_event_handler(void *data); + +INLINE_DEVICE\ +(event_entry_tag) device_event_queue_schedule +(device *me, + signed64 delta_time, + device_event_handler *handler, + void *data); + +INLINE_EVENTS\ +(void) device_event_queue_deschedule +(device *me, + event_entry_tag event_to_remove); + +INLINE_EVENTS\ +(signed64) device_event_queue_time +(device *me); + +#endif /* _DEVICE_H_ */ diff --git a/sim/ppc/device_table.c b/sim/ppc/device_table.c new file mode 100644 index 00000000000..09772aba6b4 --- /dev/null +++ b/sim/ppc/device_table.c @@ -0,0 +1,310 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1996, 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_TABLE_C_ +#define _DEVICE_TABLE_C_ + +#include "device_table.h" + +#if HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#include <ctype.h> + + +/* Helper functions */ + + +/* Go through the devices various reg properties for those that + specify attach addresses */ + + +void +generic_device_init_address(device *me) +{ + static const char *(reg_property_names[]) = { + "attach-addresses", + "assigned-addresses", + "reg", + "alternate-reg" , + NULL + }; + const char **reg_property_name; + int nr_valid_reg_properties = 0; + for (reg_property_name = reg_property_names; + *reg_property_name != NULL; + reg_property_name++) { + if (device_find_property(me, *reg_property_name) != NULL) { + reg_property_spec reg; + int reg_entry; + for (reg_entry = 0; + device_find_reg_array_property(me, *reg_property_name, reg_entry, + ®); + reg_entry++) { + unsigned_word attach_address; + int attach_space; + unsigned attach_size; + if (!device_address_to_attach_address(device_parent(me), + ®.address, + &attach_space, &attach_address, + me)) + continue; + if (!device_size_to_attach_size(device_parent(me), + ®.size, + &attach_size, me)) + continue; + device_attach_address(device_parent(me), + attach_callback, + attach_space, attach_address, attach_size, + access_read_write_exec, + me); + nr_valid_reg_properties++; + } + /* if first option matches don't try for any others */ + if (reg_property_name == reg_property_names) + break; + } + } +} + +int +generic_device_unit_decode(device *bus, + const char *unit, + device_unit *phys) +{ + memset(phys, 0, sizeof(device_unit)); + if (unit == NULL) + return 0; + else { + int nr_cells = 0; + const int max_nr_cells = device_nr_address_cells(bus); + while (1) { + char *end = NULL; + unsigned long val; + val = strtoul(unit, &end, 0); + /* parse error? */ + if (unit == end) + return -1; + /* two many cells? */ + if (nr_cells >= max_nr_cells) + return -1; + /* save it */ + phys->cells[nr_cells] = val; + nr_cells++; + unit = end; + /* more to follow? */ + if (isspace(*unit) || *unit == '\0') + break; + if (*unit != ',') + return -1; + unit++; + } + if (nr_cells < max_nr_cells) { + /* shift everything to correct position */ + int i; + for (i = 1; i <= nr_cells; i++) + phys->cells[max_nr_cells - i] = phys->cells[nr_cells - i]; + for (i = 0; i < (max_nr_cells - nr_cells); i++) + phys->cells[i] = 0; + } + phys->nr_cells = max_nr_cells; + return max_nr_cells; + } +} + +int +generic_device_unit_encode(device *bus, + const device_unit *phys, + char *buf, + int sizeof_buf) +{ + int i; + int len; + char *pos = buf; + /* skip leading zero's */ + for (i = 0; i < phys->nr_cells; i++) { + if (phys->cells[i] != 0) + break; + } + /* don't output anything if empty */ + if (phys->nr_cells == 0) { + strcpy(pos, ""); + len = 0; + } + else if (i == phys->nr_cells) { + /* all zero */ + strcpy(pos, "0"); + len = 1; + } + else { + for (; i < phys->nr_cells; i++) { + if (pos != buf) { + strcat(pos, ","); + pos = strchr(pos, '\0'); + } + if (phys->cells[i] < 10) + sprintf(pos, "%ld", (unsigned long)phys->cells[i]); + else + sprintf(pos, "0x%lx", (unsigned long)phys->cells[i]); + pos = strchr(pos, '\0'); + } + len = pos - buf; + } + if (len >= sizeof_buf) + error("generic_unit_encode - buffer overflow\n"); + return len; +} + +int +generic_device_address_to_attach_address(device *me, + const device_unit *address, + int *attach_space, + unsigned_word *attach_address, + device *client) +{ + int i; + for (i = 0; i < address->nr_cells - 2; i++) { + if (address->cells[i] != 0) + device_error(me, "Only 32bit addresses supported"); + } + if (address->nr_cells >= 2) + *attach_space = address->cells[address->nr_cells - 2]; + else + *attach_space = 0; + *attach_address = address->cells[address->nr_cells - 1]; + return 1; +} + +int +generic_device_size_to_attach_size(device *me, + const device_unit *size, + unsigned *nr_bytes, + device *client) +{ + int i; + for (i = 0; i < size->nr_cells - 1; i++) { + if (size->cells[i] != 0) + device_error(me, "Only 32bit sizes supported"); + } + *nr_bytes = size->cells[0]; + return *nr_bytes; +} + + +/* ignore/passthrough versions of each function */ + +void +passthrough_device_address_attach(device *me, + attach_type attach, + int space, + unsigned_word addr, + unsigned nr_bytes, + access_type access, + device *client) /*callback/default*/ +{ + device_attach_address(device_parent(me), attach, + space, addr, nr_bytes, + access, + client); +} + +void +passthrough_device_address_detach(device *me, + attach_type attach, + int space, + unsigned_word addr, + unsigned nr_bytes, + access_type access, + device *client) /*callback/default*/ +{ + device_detach_address(device_parent(me), attach, + space, addr, nr_bytes, access, + client); +} + +unsigned +passthrough_device_dma_read_buffer(device *me, + void *dest, + int space, + unsigned_word addr, + unsigned nr_bytes) +{ + return device_dma_read_buffer(device_parent(me), dest, + space, addr, nr_bytes); +} + +unsigned +passthrough_device_dma_write_buffer(device *me, + const void *source, + int space, + unsigned_word addr, + unsigned nr_bytes, + int violate_read_only_section) +{ + return device_dma_write_buffer(device_parent(me), source, + space, addr, + nr_bytes, + violate_read_only_section); +} + +int +ignore_device_unit_decode(device *me, + const char *unit, + device_unit *phys) +{ + memset(phys, 0, sizeof(device_unit)); + return 0; +} + + +static const device_callbacks passthrough_callbacks = { + { NULL, }, /* init */ + { passthrough_device_address_attach, + passthrough_device_address_detach, }, + { NULL, }, /* IO */ + { passthrough_device_dma_read_buffer, passthrough_device_dma_write_buffer, }, + { NULL, }, /* interrupt */ + { generic_device_unit_decode, + generic_device_unit_encode, }, +}; + + +static const device_descriptor ob_device_table[] = { + /* standard OpenBoot devices */ + { "aliases", NULL, &passthrough_callbacks }, + { "options", NULL, &passthrough_callbacks }, + { "chosen", NULL, &passthrough_callbacks }, + { "packages", NULL, &passthrough_callbacks }, + { "cpus", NULL, &passthrough_callbacks }, + { "openprom", NULL, &passthrough_callbacks }, + { "init", NULL, &passthrough_callbacks }, + { NULL }, +}; + +const device_descriptor *const device_table[] = { + ob_device_table, +#include "hw.c" + NULL, +}; + + +#endif /* _DEVICE_TABLE_C_ */ diff --git a/sim/ppc/device_table.h b/sim/ppc/device_table.h new file mode 100644 index 00000000000..af6c28d697d --- /dev/null +++ b/sim/ppc/device_table.h @@ -0,0 +1,323 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1996, 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_TABLE_H_ +#define _DEVICE_TABLE_H_ + +#include "basics.h" +#include "device.h" +#include "tree.h" + +#ifdef HAVE_STRING_H +#include <string.h> +#else +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif +#endif + + +typedef struct _device_callbacks device_callbacks; + + +/* The creator, returns a pointer to any data that should be allocated + once during (multiple) simulation runs */ + +typedef void *(device_creator) + (const char *name, + const device_unit *unit_address, + const char *args); + + +/* two stages of initialization */ + +typedef void (device_init_callback) + (device *me); + +typedef struct _device_init_callbacks { + device_init_callback *address; /* NULL - ignore */ + device_init_callback *data; /* NULL - ignore */ +} device_init_callbacks; + + +/* attaching/detaching a devices address space to its parent */ + +typedef void (device_address_callback) + (device *me, + attach_type attach, + int space, + unsigned_word addr, + unsigned nr_bytes, + access_type access, + device *client); /*callback/default*/ + +typedef struct _device_address_callbacks { + device_address_callback *attach; + device_address_callback *detach; +} device_address_callbacks; + + +/* I/O operations - from parent */ + +typedef unsigned (device_io_read_buffer_callback) + (device *me, + void *dest, + int space, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia); + +typedef unsigned (device_io_write_buffer_callback) + (device *me, + const void *source, + int space, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia); + +typedef struct _device_io_callbacks { /* NULL - error */ + device_io_read_buffer_callback *read_buffer; + device_io_write_buffer_callback *write_buffer; +} device_io_callbacks; + + +/* DMA transfers by a device via its parent */ + +typedef unsigned (device_dma_read_buffer_callback) + (device *me, + void *dest, + int space, + unsigned_word addr, + unsigned nr_bytes); + +typedef unsigned (device_dma_write_buffer_callback) + (device *me, + const void *source, + int space, + unsigned_word addr, + unsigned nr_bytes, + int violate_read_only_section); + +typedef struct _device_dma_callbacks { /* NULL - error */ + device_dma_read_buffer_callback *read_buffer; + device_dma_write_buffer_callback *write_buffer; +} device_dma_callbacks; + + +/* Interrupts */ + +typedef void (device_interrupt_event_callback) + (device *me, + int my_port, + device *source, + int source_port, + int level, + cpu *processor, + unsigned_word cia); + +typedef void (device_child_interrupt_event_callback) + (device *me, + device *parent, + device *source, + int source_port, + int level, + cpu *processor, + unsigned_word cia); + +typedef struct _device_interrupt_port_descriptor { + const char *name; + int number; + int nr_ports; + port_direction direction; +} device_interrupt_port_descriptor; + +typedef struct _device_interrupt_callbacks { + device_interrupt_event_callback *event; + device_child_interrupt_event_callback *child_event; + const device_interrupt_port_descriptor *ports; +} device_interrupt_callbacks; + + +/* symbolic value decoding */ + +typedef int (device_unit_decode_callback) + (device *bus, + const char *unit, + device_unit *address); + +typedef int (device_unit_encode_callback) + (device *bus, + const device_unit *unit_address, + char *buf, + int sizeof_buf); + +typedef int (device_address_to_attach_address_callback) + (device *bus, + const device_unit *address, + int *attach_space, + unsigned_word *attach_address, + device *client); + +typedef int (device_size_to_attach_size_callback) + (device *bus, + const device_unit *size, + unsigned *nr_bytes, + device *client); + +typedef struct _device_convert_callbacks { + device_unit_decode_callback *decode_unit; + device_unit_encode_callback *encode_unit; + device_address_to_attach_address_callback *address_to_attach_address; + device_size_to_attach_size_callback *size_to_attach_size; +} device_convert_callbacks; + + +/* instances */ + +typedef void (device_instance_delete_callback) + (device_instance *instance); + +typedef int (device_instance_read_callback) + (device_instance *instance, + void *buf, + unsigned_word len); + +typedef int (device_instance_write_callback) + (device_instance *instance, + const void *buf, + unsigned_word len); + +typedef int (device_instance_seek_callback) + (device_instance *instance, + unsigned_word pos_hi, + unsigned_word pos_lo); + +typedef int (device_instance_method) + (device_instance *instance, + int n_stack_args, + unsigned_cell stack_args[/*n_stack_args*/], + int n_stack_returns, + unsigned_cell stack_returns[/*n_stack_returns*/]); + +typedef struct _device_instance_methods { + const char *name; + device_instance_method *method; +} device_instance_methods; + +struct _device_instance_callbacks { /* NULL - error */ + device_instance_delete_callback *delete; + device_instance_read_callback *read; + device_instance_write_callback *write; + device_instance_seek_callback *seek; + const device_instance_methods *methods; +}; + +typedef device_instance *(device_create_instance_callback) + (device *me, + const char *full_path, + const char *args); + +typedef device_instance *(package_create_instance_callback) + (device_instance *parent, + const char *args); + + +/* all else fails */ + +typedef int (device_ioctl_callback) + (device *me, + cpu *processor, + unsigned_word cia, + device_ioctl_request request, + va_list ap); + +typedef void (device_usage_callback) + (int verbose); + + +/* the callbacks */ + +struct _device_callbacks { + + /* initialization */ + device_init_callbacks init; + + /* address/data config - from child */ + device_address_callbacks address; + + /* address/data transfer - from parent */ + device_io_callbacks io; + + /* address/data transfer - from child */ + device_dma_callbacks dma; + + /* interrupt signalling */ + device_interrupt_callbacks interrupt; + + /* bus address decoding */ + device_convert_callbacks convert; + + /* instances */ + device_create_instance_callback *instance_create; + + /* back door to anything we've forgot */ + device_ioctl_callback *ioctl; + device_usage_callback *usage; +}; + + +/* Table of all the devices and a function to lookup/create a device + from its name */ + +typedef struct _device_descriptor device_descriptor; +struct _device_descriptor { + const char *name; + device_creator *creator; + const device_callbacks *callbacks; +}; + +extern const device_descriptor *const device_table[]; +#include "hw.h" + + +/* Pass through, ignore and generic callback functions. A call going + towards the root device are passed on up, local calls are ignored + and call downs abort */ + +extern device_address_callback passthrough_device_address_attach; +extern device_address_callback passthrough_device_address_detach; +extern device_dma_read_buffer_callback passthrough_device_dma_read_buffer; +extern device_dma_write_buffer_callback passthrough_device_dma_write_buffer; + +extern device_unit_decode_callback ignore_device_unit_decode; + +extern device_init_callback generic_device_init_address; +extern device_unit_decode_callback generic_device_unit_decode; +extern device_unit_encode_callback generic_device_unit_encode; +extern device_address_to_attach_address_callback generic_device_address_to_attach_address; +extern device_size_to_attach_size_callback generic_device_size_to_attach_size; + + +extern const device_callbacks passthrough_device_callbacks; + +#endif /* _DEVICE_TABLE_H_ */ diff --git a/sim/ppc/dgen.c b/sim/ppc/dgen.c new file mode 100644 index 00000000000..54e0e2657c9 --- /dev/null +++ b/sim/ppc/dgen.c @@ -0,0 +1,336 @@ +/* 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. + + */ + + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <getopt.h> +#include <stdio.h> +#include <ctype.h> +#include <stdarg.h> + +#include "config.h" +#include "misc.h" +#include "lf.h" +#include "table.h" + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#ifdef HAVE_STRING_H +#include <string.h> +#else +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif +#endif + +/****************************************************************/ + +int spreg_lookup_table = 1; +enum { + nr_of_sprs = 1024, +}; + +/****************************************************************/ + + +typedef enum { + spreg_name, + spreg_reg_nr, + spreg_readonly, + spreg_length, + nr_spreg_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; + table_entry *entry; + spreg_table_entry *next; +}; + +typedef struct _spreg_table spreg_table; +struct _spreg_table { + spreg_table_entry *sprs; +}; + +static void +spreg_table_insert(spreg_table *table, table_entry *entry) +{ + /* create a new spr entry */ + spreg_table_entry *new_spr = ZALLOC(spreg_table_entry); + 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*)zalloc(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; + } + +} + + +static spreg_table * +spreg_table_load(char *file_name) +{ + table *file = table_open(file_name, nr_spreg_fields, 0); + spreg_table *table = ZALLOC(spreg_table); + + { + table_entry *entry; + while ((entry = table_entry_read(file)) != NULL) { + spreg_table_insert(table, entry); + } + } + + return table; +} + + +/****************************************************************/ + +char *spreg_attributes[] = { + "is_valid", + "is_readonly", + "name", + "index", + "length", + 0 +}; + +static void +gen_spreg_h(spreg_table *table, lf *file) +{ + spreg_table_entry *entry; + char **attribute; + + lf_print__gnu_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, "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_print_function_type(file, "const char *", "INLINE_SPREG", " "); + lf_printf(file, "spr_%s(sprs spr);\n", *attribute); + } + else { + lf_print_function_type(file, "int", "INLINE_SPREG", " "); + lf_printf(file, "spr_%s(sprs spr);\n", *attribute); + } + } + lf_printf(file, "\n"); + lf_printf(file, "#endif /* _SPREG_H_ */\n"); +} + + +static void +gen_spreg_c(spreg_table *table, lf *file) +{ + spreg_table_entry *entry; + char **attribute; + int spreg_nr; + + lf_print__gnu_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 \"basics.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_print_function_type(file, "const char *", "INLINE_SPREG", "\n"); + } + else { + lf_print_function_type(file, "int", "INLINE_SPREG", "\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) +{ + lf_file_references file_references = lf_include_references; + spreg_table *sprs = NULL; + char *real_file_name = NULL; + int is_header = 0; + int ch; + + if (argc <= 1) { + printf("Usage: dgen ...\n"); + printf("-s Use switch instead of table\n"); + printf("-n <file-name> Use this as cpp line numbering name\n"); + printf("-h Output header file\n"); + printf("-p <spreg-file> Output spreg.h(P) or spreg.c(p)\n"); + printf("-L Suppress cpp line numbering in output files\n"); + } + + + while ((ch = getopt(argc, argv, "hLsn:r:p:")) != -1) { + fprintf(stderr, "\t-%c %s\n", ch, ( optarg ? optarg : "")); + switch(ch) { + case 's': + spreg_lookup_table = 0; + break; + case 'r': + sprs = spreg_table_load(optarg); + break; + case 'n': + real_file_name = strdup(optarg); + break; + case 'L': + file_references = lf_omit_references; + break; + case 'h': + is_header = 1; + break; + case 'p': + { + lf *file = lf_open(optarg, real_file_name, file_references, + (is_header ? lf_is_h : lf_is_c), + argv[0]); + if (is_header) + gen_spreg_h(sprs, file); + else + gen_spreg_c(sprs, file); + lf_close(file); + is_header = 0; + } + real_file_name = NULL; + break; + default: + error("unknown option\n"); + } + } + return 0; +} diff --git a/sim/ppc/double.c b/sim/ppc/double.c new file mode 100644 index 00000000000..85943010883 --- /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 00000000000..3313132a50f --- /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/emul_bugapi.c b/sim/ppc/emul_bugapi.c new file mode 100644 index 00000000000..97028bf9de9 --- /dev/null +++ b/sim/ppc/emul_bugapi.c @@ -0,0 +1,581 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1997, 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 _EMUL_BUGAPI_C_ +#define _EMUL_BUGAPI_C_ + +/* Note: this module is called via a table. There is no benefit in + making it inline */ + +#include "emul_generic.h" +#include "emul_bugapi.h" + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#ifdef HAVE_STRING_H +#include <string.h> +#else +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif +#endif + + +/* EMULATION + + BUG - Motorola's embeded firmware BUG interface + + DESCRIPTION + + + + */ + + +/* from PowerPCBug Debugging Package User's Manual, part 2 of 2 and also bug.S - Dale Rahn */ +#define _INCHR 0x000 /* Input character */ +#define _INSTAT 0x001 /* Input serial port status */ +#define _INLN 0x002 /* Input line (pointer / pointer format) */ +#define _READSTR 0x003 /* Input string (pointer / count format) */ +#define _READLN 0x004 /* Input line (pointer / count format) */ +#define _CHKBRK 0x005 /* Check for break */ +#define _DSKRD 0x010 /* Disk read */ +#define _DSKWR 0x011 /* Disk write */ +#define _DSKCFIG 0x012 /* Disk configure */ +#define _DSKFMT 0x014 /* Disk format */ +#define _DSKCTRL 0x015 /* Disk control */ +#define _NETRD 0x018 /* Read from host */ +#define _NETWR 0x019 /* Write to host */ +#define _NETCFIG 0x01a /* Configure network parameters */ +#define _NETOPN 0x01b /* Open file for reading */ +#define _NETFRD 0x01c /* Retreive specified file blocks */ +#define _NETCTRL 0x01d /* Implement special control functions */ +#define _OUTCHR 0x020 /* Output character (pointer / pointer format) */ +#define _OUTSTR 0x021 /* Output string (pointer / pointer format) */ +#define _OUTLN 0x022 /* Output line (pointer / pointer format) */ +#define _WRITE 0x023 /* Output string (pointer / count format) */ +#define _WRITELN 0x024 /* Output line (pointer / count format) */ +#define _WRITDLN 0x025 /* Output line with data (pointer / count format) */ +#define _PCRLF 0x026 /* Output carriage return and line feed */ +#define _ERASLN 0x027 /* Erase line */ +#define _WRITD 0x028 /* Output string with data (pointer / count format) */ +#define _SNDBRK 0x029 /* Send break */ +#define _DELAY 0x043 /* Timer delay */ +#define _RTC_TM 0x050 /* Time initialization for RTC */ +#define _RTC_DT 0x051 /* Date initialization for RTC */ +#define _RTC_DSP 0x052 /* Display RTC time and date */ +#define _RTC_RD 0x053 /* Read the RTC registers */ +#define _REDIR 0x060 /* Redirect I/O of a system call function */ +#define _REDIR_I 0x061 /* Redirect input */ +#define _REDIR_O 0x062 /* Redirect output */ +#define _RETURN 0x063 /* Return to PPCbug */ +#define _BINDEC 0x064 /* Convert binary to binary coded decimal (BCD) */ +#define _CHANGEV 0x067 /* Parse value */ +#define _STRCMP 0x068 /* Compare two strings (pointer / count format) */ +#define _MULU32 0x069 /* Multiply two 32-bit unsigned integers */ +#define _DIVU32 0x06a /* Divide two 32-bit unsigned integers */ +#define _CHK_SUM 0x06b /* Generate checksum */ +#define _BRD_ID 0x070 /* Return pointer to board ID packet */ +#define _ENVIRON 0x071 /* Access boot environment parameters */ +#define _DIAGFCN 0x074 /* Diagnostic function(s) */ +#define _SIOPEPS 0x090 /* Retrieve SCSI pointers */ +#define _IOINQ 0x120 /* Port inquire */ +#define _IOINFORM 0x124 /* Port inform */ +#define _IOCONFIG 0x128 /* Port configure */ +#define _IODELETE 0x12c /* Port delete */ +#define _SYMBOLTA 0x130 /* Attach symbol table */ +#define _SYMBOLDA 0x131 /* Detach symbol table */ + +struct bug_map { + int value; + const char *info; +}; + +static const struct bug_map bug_mapping[] = { + { _INCHR, ".INCHR -- Input character" }, + { _INSTAT, ".INSTAT -- Input serial port status" }, + { _INLN, ".INLN -- Input line (pointer / pointer format)" }, + { _READSTR, ".READSTR -- Input string (pointer / count format)" }, + { _READLN, ".READLN -- Input line (pointer / count format)" }, + { _CHKBRK, ".CHKBRK -- Check for break" }, + { _DSKRD, ".DSKRD -- Disk read" }, + { _DSKWR, ".DSKWR -- Disk write" }, + { _DSKCFIG, ".DSKCFIG -- Disk configure" }, + { _DSKFMT, ".DSKFMT -- Disk format" }, + { _DSKCTRL, ".DSKCTRL -- Disk control" }, + { _NETRD, ".NETRD -- Read from host" }, + { _NETWR, ".NETWR -- Write to host" }, + { _NETCFIG, ".NETCFIG -- Configure network parameters" }, + { _NETOPN, ".NETOPN -- Open file for reading" }, + { _NETFRD, ".NETFRD -- Retreive specified file blocks" }, + { _NETCTRL, ".NETCTRL -- Implement special control functions" }, + { _OUTCHR, ".OUTCHR -- Output character" }, + { _OUTSTR, ".OUTSTR -- Output string (pointer / pointer format)" }, + { _OUTLN, ".OUTLN -- Output line (pointer / pointer format)" }, + { _WRITE, ".WRITE -- Output string (pointer / count format)" }, + { _WRITELN, ".WRITELN -- Output line (pointer / count format)" }, + { _WRITDLN, ".WRITDLN -- Output line with data (pointer / count format)" }, + { _PCRLF, ".PCRLF -- Output carriage return and line feed" }, + { _ERASLN, ".ERASLN -- Erase line" }, + { _WRITD, ".WRITD -- Output string with data (pointer / count format)" }, + { _SNDBRK, ".SNDBRK -- Send break" }, + { _DELAY, ".DELAY -- Timer delay" }, + { _RTC_TM, ".RTC_TM -- Time initialization for RTC" }, + { _RTC_DT, ".RTC_DT -- Date initialization for RTC" }, + { _RTC_DSP, ".RTC_DSP -- Display RTC time and date" }, + { _RTC_RD, ".RTC_RD -- Read the RTC registers" }, + { _REDIR, ".REDIR -- Redirect I/O of a system call function" }, + { _REDIR, ".REDIR -- Redirect input" }, + { _REDIR, ".REDIR -- Redirect output" }, + { _RETURN, ".RETURN -- Return to PPCbug" }, + { _BINDEC, ".BINDEC -- Convert binary to binary coded decimal (BCD)" }, + { _CHANGEV, ".CHANGEV -- Parse value" }, + { _STRCMP, ".STRCMP -- Compare two strings (pointer / count format)" }, + { _MULU32, ".MULU32 -- Multiply two 32-bit unsigned integers" }, + { _DIVU32, ".DIVU32 -- Divide two 32-bit unsigned integers" }, + { _CHK_SUM, ".CHK_SUM -- Generate checksum" }, + { _BRD_ID, ".BRD_ID -- Return pointer to board ID packet" }, + { _ENVIRON, ".ENVIRON -- Access boot environment parameters" }, + { _DIAGFCN, ".DIAGFCN -- Diagnostic function(s)" }, + { _SIOPEPS, ".SIOPEPS -- Retrieve SCSI pointers" }, + { _IOINQ, ".IOINQ -- Port inquire" }, + { _IOINFORM, ".IOINFORM -- Port inform" }, + { _IOCONFIG, ".IOCONFIG -- Port configure" }, + { _IODELETE, ".IODELETE -- Port delete" }, + { _SYMBOLTA, ".SYMBOLTA -- Attach symbol table" }, + { _SYMBOLDA, ".SYMBOLDA -- Detach symbol table" }, +}; + +#ifndef BUGAPI_END_ADDRESS +#define BUGAPI_END_ADDRESS 0x100000 +#endif + +enum { + nr_bugapi_disks = 2, +}; + + +struct _os_emul_data { + device *root; + unsigned_word memory_size; + unsigned_word top_of_stack; + int interrupt_prefix; + unsigned_word interrupt_vector_address; + unsigned_word system_call_address; + unsigned_word stall_cpu_loop_address; + int little_endian; + int floating_point_available; + /* I/O devices */ + device_instance *output; + device_instance *input; + device_instance *(disk[nr_bugapi_disks]); +}; + + +static os_emul_data * +emul_bugapi_create(device *root, + bfd *image, + const char *name) +{ + device *node; + os_emul_data *bugapi; + + /* check it really is for us */ + if (name != NULL + && strcmp(name, "bugapi") != 0 + && strcmp(name, "bug") != 0) + return NULL; + if (image != NULL + && name == NULL + && bfd_get_start_address(image) >= BUGAPI_END_ADDRESS) + return NULL; + + bugapi = ZALLOC(os_emul_data); + + /* options */ + emul_add_tree_options(root, image, "bug", "oea", + 1 /*oea-interrupt-prefix*/); + + /* add some real hardware, include eeprom memory for the eeprom trap + addresses */ + emul_add_tree_hardware(root); + node = tree_parse(root, "/openprom/memory@0xfff00000"); + tree_parse(node, "./psim,description \"eeprom trap addresses"); + tree_parse(node, "./reg 0xfff00000 0x3000"); + + bugapi->root = root; + + bugapi->memory_size + = tree_find_integer_property(root, "/openprom/options/oea-memory-size"); + bugapi->interrupt_prefix = + tree_find_integer_property(root, "/openprom/options/oea-interrupt-prefix"); + bugapi->interrupt_vector_address = (bugapi->interrupt_prefix + ? MASK(0, 43) + : 0); + bugapi->system_call_address = (bugapi->interrupt_vector_address + 0x00c00); + bugapi->stall_cpu_loop_address = (bugapi->system_call_address + 0x000f0); + bugapi->top_of_stack = bugapi->memory_size - 0x1000; + bugapi->little_endian + = tree_find_boolean_property(root, "/options/little-endian?"); + bugapi->floating_point_available + = tree_find_boolean_property(root, "/openprom/options/floating-point?"); + bugapi->input = NULL; + bugapi->output = NULL; + + /* initialization */ + if (image != NULL) + tree_parse(root, "/openprom/init/register/0.pc 0x%lx", + (unsigned long)bfd_get_start_address(image)); + tree_parse(root, "/openprom/init/register/pc 0x%lx", + (unsigned long)bugapi->stall_cpu_loop_address); + tree_parse(root, "/openprom/init/register/sp 0x%lx", + (unsigned long)(bugapi->top_of_stack - 16)); + tree_parse(root, "/openprom/init/register/msr 0x%x", + (msr_recoverable_interrupt + | (bugapi->little_endian + ? (msr_little_endian_mode + | msr_interrupt_little_endian_mode) + : 0) + | (bugapi->floating_point_available + ? msr_floating_point_available + : 0) + | (bugapi->interrupt_prefix + ? msr_interrupt_prefix + : 0) + )); + + /* patch the system call instruction to call this emulation and then + do an rfi */ + node = tree_parse(root, "/openprom/init/data@0x%lx", + (unsigned long)bugapi->system_call_address); + tree_parse(node, "./psim,description \"system-call trap instruction"); + tree_parse(node, "./real-address 0x%lx", + (unsigned long)bugapi->system_call_address); + tree_parse(node, "./data 0x%x", emul_call_instruction); + node = tree_parse(root, "/openprom/init/data@0x%lx", + (unsigned long)bugapi->system_call_address + 4); + tree_parse(node, "./psim,description \"return from interrupt instruction"); + tree_parse(node, "./real-address 0x%lx", + (unsigned long)bugapi->system_call_address + 4); + tree_parse(node, "./data 0x%x", + emul_rfi_instruction); + + /* patch the end of the system call instruction so that it contains + a loop to self instruction and point all the cpu's at this */ + node = tree_parse(root, "/openprom/init/data@0x%lx", + (unsigned long)bugapi->stall_cpu_loop_address); + tree_parse(node, "./psim,description \"cpu-loop instruction"); + tree_parse(node, "./real-address 0x%lx", + (unsigned long)bugapi->stall_cpu_loop_address); + tree_parse(node, "./data 0x%lx", + (unsigned long)emul_loop_instruction); + + if (image != NULL) + tree_parse(root, "/openprom/init/stack/stack-type %s", + (image->xvec->flavour == bfd_target_elf_flavour + ? "ppc-elf" + : "ppc-xcoff")); + + if (image != NULL) + tree_parse(root, "/openprom/init/load-binary/file-name \"%s", + bfd_get_filename(image)); + + return bugapi; +} + +static void +emul_bugapi_init(os_emul_data *bugapi, + int nr_cpus) +{ + int i; + /* get the current input/output devices that were created during + device tree initialization */ + bugapi->input = tree_find_ihandle_property(bugapi->root, "/chosen/stdin"); + bugapi->output = tree_find_ihandle_property(bugapi->root, "/chosen/stdout"); + /* if present, extract the selected disk devices */ + for (i = 0; i < nr_bugapi_disks; i++) { + char disk[32]; + char *chp; + strcpy(disk, "/chosen/disk0"); + ASSERT(sizeof(disk) > strlen(disk)); + chp = strchr(disk, '0'); + *chp = *chp + i; + if (tree_find_property(bugapi->root, disk) != NULL) + bugapi->disk[i] = tree_find_ihandle_property(bugapi->root, disk); + } +} + +static const char * +emul_bugapi_instruction_name(int call_id) +{ + static char buffer[40]; + int i; + + for (i = 0; i < sizeof (bug_mapping) / sizeof (bug_mapping[0]); i++) + { + if (bug_mapping[i].value == call_id) + return bug_mapping[i].info; + } + + (void) sprintf (buffer, "Unknown bug call 0x%x", call_id); + return buffer; +} + +static int +emul_bugapi_do_read(os_emul_data *bugapi, + cpu *processor, + unsigned_word cia, + unsigned_word buf, + int nbytes) +{ + unsigned char *scratch_buffer; + int status; + + /* get a tempoary bufer */ + scratch_buffer = (unsigned char *) zalloc(nbytes); + + /* check if buffer exists by reading it */ + emul_read_buffer((void *)scratch_buffer, buf, nbytes, processor, cia); + + /* read */ + status = device_instance_read(bugapi->input, + (void *)scratch_buffer, nbytes); + + /* -1 = error, -2 = nothing available - see "serial" [IEEE1275] */ + if (status < 0) { + status = 0; + } + + if (status > 0) { + emul_write_buffer((void *)scratch_buffer, buf, status, processor, cia); + + /* Bugapi chops off the trailing n, but leaves it in the buffer */ + if (scratch_buffer[status-1] == '\n' || scratch_buffer[status-1] == '\r') + status--; + } + + zfree(scratch_buffer); + return status; +} + +static void +emul_bugapi_do_diskio(os_emul_data *bugapi, + cpu *processor, + unsigned_word cia, + unsigned_word descriptor_addr, + int call_id) +{ + struct dskio_descriptor { + unsigned_1 ctrl_lun; + unsigned_1 dev_lun; + unsigned_2 status; + unsigned_word pbuffer; + unsigned_4 blk_num; + unsigned_2 blk_cnt; + unsigned_1 flag; +#define BUG_FILE_MARK 0x80 +#define IGNORE_FILENUM 0x02 +#define END_OF_FILE 0x01 + unsigned_1 addr_mod; + } descriptor; + int block; + emul_read_buffer(&descriptor, descriptor_addr, sizeof(descriptor), + processor, cia); + T2H(descriptor.ctrl_lun); + T2H(descriptor.dev_lun); + T2H(descriptor.status); + T2H(descriptor.pbuffer); + T2H(descriptor.blk_num); + T2H(descriptor.blk_cnt); + T2H(descriptor.flag); + T2H(descriptor.addr_mod); + if (descriptor.dev_lun >= nr_bugapi_disks + || bugapi->disk[descriptor.dev_lun] == NULL) { + error("emul_bugapi_do_diskio: attempt to access unconfigured disk /chosen/disk%d", + descriptor.dev_lun); + } + else { + for (block = 0; block < descriptor.blk_cnt; block++) { + device_instance *disk = bugapi->disk[descriptor.dev_lun]; + unsigned_1 buf[512]; /*????*/ + unsigned_word block_nr = descriptor.blk_num + block; + unsigned_word byte_nr = block_nr * sizeof(buf); + unsigned_word block_addr = descriptor.pbuffer + block*sizeof(buf); + if (device_instance_seek(disk, 0, byte_nr) < 0) + error("emul_bugapi_do_diskio: bad seek\n"); + switch (call_id) { + case _DSKRD: + if (device_instance_read(disk, buf, sizeof(buf)) != sizeof(buf)) + error("emul_`bugapi_do_diskio: bad read\n"); + emul_write_buffer(buf, block_addr, sizeof(buf), processor, cia); + break; + case _DSKWR: + emul_read_buffer(buf, block_addr, sizeof(buf), processor, cia); + if (device_instance_write(disk, buf, sizeof(buf)) != sizeof(buf)) + error("emul_bugapi_do_diskio: bad write\n"); + break; + default: + error("emul_bugapi_do_diskio: bad switch\n"); + } + } + } +} + +static void +emul_bugapi_do_write(os_emul_data *bugapi, + cpu *processor, + unsigned_word cia, + unsigned_word buf, + int nbytes, + const char *suffix) +{ + void *scratch_buffer = NULL; + + /* get a tempoary bufer */ + if (nbytes > 0) + { + scratch_buffer = zalloc(nbytes); + + /* copy in */ + emul_read_buffer(scratch_buffer, buf, nbytes, + processor, cia); + + /* write */ + device_instance_write(bugapi->output, scratch_buffer, nbytes); + + zfree(scratch_buffer); + } + + if (suffix) + device_instance_write(bugapi->output, suffix, strlen(suffix)); + + flush_stdoutput (); +} + +static int +emul_bugapi_instruction_call(cpu *processor, + unsigned_word cia, + unsigned_word ra, + os_emul_data *bugapi) +{ + const int call_id = cpu_registers(processor)->gpr[10]; + unsigned char uc; + +#define MY_INDEX itable_instruction_call + ITRACE (trace_os_emul, + (" 0x%x %s, r3 = 0x%lx, r4 = 0x%lx\n", + call_id, emul_bugapi_instruction_name (call_id), + (long)cpu_registers(processor)->gpr[3], + (long)cpu_registers(processor)->gpr[4]));; + + /* check that this isn't an invalid instruction */ + if (cia != bugapi->system_call_address) + return 0; + + switch (call_id) { + default: + error("emul-bugapi: unimplemented bugapi %s from address 0x%lx\n", + emul_bugapi_instruction_name (call_id), SRR0); + break; + + /* read a single character, output r3 = byte */ + /* FIXME: Add support to unbuffer input */ + case _INCHR: + if (device_instance_read(bugapi->input, (void *)&uc, 1) <= 0) + uc = 0; + cpu_registers(processor)->gpr[3] = uc; + break; + + /* read a line of at most 256 bytes, r3 = ptr to 1st byte, output r3 = ptr to last byte+1 */ + case _INLN: + cpu_registers(processor)->gpr[3] += emul_bugapi_do_read(bugapi, + processor, cia, + cpu_registers(processor)->gpr[3], + 256); + break; + + /* output a character, r3 = character */ + case _OUTCHR: + { + char out = (char)cpu_registers(processor)->gpr[3]; + device_instance_write(bugapi->output, &out, 1); + break; + } + + /* output a string, r3 = ptr to 1st byte, r4 = ptr to last byte+1 */ + case _OUTSTR: + emul_bugapi_do_write(bugapi, + processor, cia, + cpu_registers(processor)->gpr[3], + cpu_registers(processor)->gpr[4] - cpu_registers(processor)->gpr[3], + (const char *)0); + break; + + /* output a string followed by \r\n, r3 = ptr to 1st byte, r4 = ptr to last byte+1 */ + case _OUTLN: + + emul_bugapi_do_write(bugapi, + processor, cia, + cpu_registers(processor)->gpr[3], + cpu_registers(processor)->gpr[4] - cpu_registers(processor)->gpr[3], + "\n"); + break; + + /* output a \r\n */ + case _PCRLF: + device_instance_write(bugapi->output, "\n", 1); + break; + + /* read/write blocks of data to/from the disk */ + case _DSKWR: + case _DSKRD: + emul_bugapi_do_diskio(bugapi, processor, cia, + cpu_registers(processor)->gpr[3], + call_id); + break; + + /* return to ppcbug monitor (exiting with gpr[3] as status is not + part of the bug monitor) */ + case _RETURN: + cpu_halt(processor, cia, was_exited, cpu_registers(processor)->gpr[3]); + break; + } + return 1; + /* the instruction following this one is a RFI. Thus by just + continuing the return from system call is performed */ +} + +const os_emul emul_bugapi = { + "bugapi", + emul_bugapi_create, + emul_bugapi_init, + 0, /*system_call*/ + emul_bugapi_instruction_call, + 0 /*data*/ +}; + +#endif diff --git a/sim/ppc/emul_bugapi.h b/sim/ppc/emul_bugapi.h new file mode 100644 index 00000000000..fd3e3838fbd --- /dev/null +++ b/sim/ppc/emul_bugapi.h @@ -0,0 +1,27 @@ +/* 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 _EMUL_BUGAPI_H_ +#define _EMUL_BUGAPI_H_ + +extern const os_emul emul_bugapi; + +#endif diff --git a/sim/ppc/emul_chirp.c b/sim/ppc/emul_chirp.c new file mode 100644 index 00000000000..c9bb89c5e0c --- /dev/null +++ b/sim/ppc/emul_chirp.c @@ -0,0 +1,2010 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1997, 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 _EMUL_CHIRP_C_ +#define _EMUL_CHIRP_C_ + +/* Note: this module is called via a table. There is no benefit in + making it inline */ + +#include "emul_generic.h" +#include "emul_chirp.h" + +#ifdef HAVE_STRING_H +#include <string.h> +#else +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif +#endif + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#ifndef STATIC_INLINE_EMUL_CHIRP +#define STATIC_INLINE_EMUL_CHIRP STATIC_INLINE +#endif + + +/* EMULATION + + + OpenFirmware - IEEE Standard for Boot (Initialization + Configuration) Firmware. + + + DESCRIPTION + + + BUGS + + + This code assumes that the memory node has #address-cells and + #size-cells set to one. For future implementations, this may not + be the case. + + */ + + + + +/* Descriptor of the open boot services being emulated */ + +typedef int (chirp_handler) + (os_emul_data *data, + cpu *processor, + unsigned_word cia); + +typedef struct _chirp_services { + const char *name; + chirp_handler *handler; +} chirp_services; + + +/* The OpenBoot emulation is, at any time either waiting for a client + request or waiting on a client callback */ +typedef enum { + serving, + emulating, + faulting, +} chirp_emul_state; + +struct _os_emul_data { + chirp_emul_state state; + unsigned_word return_address; + unsigned_word arguments; + unsigned_word n_args; + unsigned_word n_returns; + chirp_services *service; + device *root; + chirp_services *services; + /* configuration */ + unsigned_word memory_size; + unsigned_word real_base; + unsigned_word real_size; + unsigned_word virt_base; + unsigned_word virt_size; + int real_mode; + int little_endian; + int floating_point_available; + int interrupt_prefix; + unsigned_word load_base; + /* hash table */ + unsigned_word nr_page_table_entry_groups; + unsigned_word htab_offset; + unsigned_word htab_ra; + unsigned_word htab_va; + unsigned_word sizeof_htab; + /* virtual address of htab */ + unsigned_word stack_offset; + unsigned_word stack_ra; + unsigned_word stack_va; + unsigned_word sizeof_stack; + /* addresses of emulation instructions virtual/real */ + unsigned_word code_offset; + unsigned_word code_va; + unsigned_word code_ra; + unsigned_word sizeof_code; + unsigned_word code_client_va; + unsigned_word code_client_ra; + unsigned_word code_callback_va; + unsigned_word code_callback_ra; + unsigned_word code_loop_va; + unsigned_word code_loop_ra; +}; + + +/* returns the name of the corresponding Ihandle */ +static const char * +ihandle_name(device_instance *ihandle) +{ + if (ihandle == NULL) + return ""; + else + return device_name(device_instance_device(ihandle)); +} + + + +/* Read/write the argument list making certain that all values are + converted to/from host byte order. + + In the below only n_args+n_returns is read/written */ + +static int +chirp_read_t2h_args(void *args, + int sizeof_args, + int n_args, + int n_returns, + os_emul_data *data, + cpu *processor, + unsigned_word cia) +{ + unsigned_cell *words; + int i; + /* check against the number of arguments specified by the client + program */ + if ((n_args >= 0 && data->n_args != n_args) + || (n_returns >= 0 && data->n_returns != n_returns)) { + TRACE(trace_os_emul, ("%s - invalid nr of args - n_args=%ld, n_returns=%ld\n", + data->service->name, + (long)data->n_args, + (long)data->n_returns)); + return -1; + } + /* check that there is enough space */ + if (sizeof(unsigned_cell) * (data->n_args + data->n_returns) > sizeof_args) + return -1; + /* bring in the data */ + memset(args, 0, sizeof_args); + emul_read_buffer(args, data->arguments + 3 * sizeof(unsigned_cell), + sizeof(unsigned_cell) * (data->n_args + data->n_returns), + processor, cia); + /* convert all words to host format */ + words = args; + for (i = 0; i < (sizeof_args / sizeof(unsigned_cell)); i++) + words[i] = T2H_cell(words[i]); + return 0; +} + +static void +chirp_write_h2t_args(void *args, + int sizeof_args, + os_emul_data *data, + cpu *processor, + unsigned_word cia) +{ + int i; + unsigned_cell *words; + /* convert to target everything */ + words = args; + for (i = 0; i < (sizeof_args / sizeof(unsigned_cell)); i++) + words[i] = H2T_cell(words[i]); + /* bring in the data */ + emul_write_buffer(args, data->arguments + 3 * sizeof(unsigned_cell), + sizeof(unsigned_cell) * (data->n_args + data->n_returns), + processor, cia); +} + + +/* OpenBoot emulation functions */ + +/* client interface */ + +static int +chirp_emul_test(os_emul_data *data, + cpu *processor, + unsigned_word cia) +{ + struct test_args { + /*in*/ + unsigned_cell name; /*string*/ + /*out*/ + unsigned_cell missing; + } args; + char name[32]; + chirp_services *service = NULL; + /* read in the arguments */ + if (chirp_read_t2h_args(&args, sizeof(args), 1, 1, data, processor, cia)) + return -1; + emul_read_string(name, args.name, sizeof(name), + processor, cia); + TRACE(trace_os_emul, ("test - in - name=`%s'\n", name)); + /* see if we know about the service */ + service = data->services; + while (service->name != NULL && strcmp(service->name, name) != 0) { + service++; + } + if (service->name == NULL) + args.missing = -1; + else + args.missing = 0; + /* write the arguments back out */ + TRACE(trace_os_emul, ("test - out - missing=%ld\n", + (long)args.missing)); + chirp_write_h2t_args(&args, + sizeof(args), + data, + processor, cia); + return 0; +} + + +/* Device tree */ + +static int +chirp_emul_peer(os_emul_data *data, + cpu *processor, + unsigned_word cia) +{ + struct peer_args { + /*in*/ + unsigned_cell phandle; + /*out*/ + unsigned_cell sibling_phandle; + } args; + device *phandle; + device *sibling_phandle = NULL; + /* read in the arguments */ + if (chirp_read_t2h_args(&args, sizeof(args), 1, 1, data, processor, cia)) + return -1; + phandle = external_to_device(data->root, args.phandle); + TRACE(trace_os_emul, ("peer - in - phandle=0x%lx(0x%lx`%s')\n", + (unsigned long)args.phandle, + (unsigned long)phandle, + (phandle == NULL ? "" : device_name(phandle)))); + /* find the peer */ + if (args.phandle == 0) { + sibling_phandle = data->root; + args.sibling_phandle = device_to_external(sibling_phandle); + } + else if (phandle == NULL) { + sibling_phandle = NULL; + args.sibling_phandle = -1; + } + else { + sibling_phandle = device_sibling(phandle); + if (sibling_phandle == NULL) + args.sibling_phandle = 0; + else + args.sibling_phandle = device_to_external(sibling_phandle); + } + /* write the arguments back out */ + TRACE(trace_os_emul, ("peer - out - sibling_phandle=0x%lx(0x%lx`%s')\n", + (unsigned long)args.sibling_phandle, + (unsigned long)sibling_phandle, + (sibling_phandle == NULL ? "" : device_name(sibling_phandle)))); + chirp_write_h2t_args(&args, + sizeof(args), + data, + processor, cia); + return 0; +} + +static int +chirp_emul_child(os_emul_data *data, + cpu *processor, + unsigned_word cia) +{ + struct child_args { + /*in*/ + unsigned_cell phandle; + /*out*/ + unsigned_cell child_phandle; + } args; + device *phandle; + device *child_phandle; + /* read the arguments in */ + if (chirp_read_t2h_args(&args, sizeof(args), 1, 1, data, processor, cia)) + return -1; + phandle = external_to_device(data->root, args.phandle); + TRACE(trace_os_emul, ("child - in - phandle=0x%lx(0x%lx`%s')\n", + (unsigned long)args.phandle, + (unsigned long)phandle, + (phandle == NULL ? "" : device_name(phandle)))); + /* find a child */ + if (args.phandle == 0 + || phandle == NULL) { + child_phandle = NULL; + args.child_phandle = -1; + } + else { + child_phandle = device_child(phandle); + if (child_phandle == NULL) + args.child_phandle = 0; + else + args.child_phandle = device_to_external(child_phandle); + } + /* write the result out */ + TRACE(trace_os_emul, ("child - out - child_phandle=0x%lx(0x%lx`%s')\n", + (unsigned long)args.child_phandle, + (unsigned long)child_phandle, + (child_phandle == NULL ? "" : device_name(child_phandle)))); + chirp_write_h2t_args(&args, + sizeof(args), + data, + processor, cia); + return 0; +} + +static int +chirp_emul_parent(os_emul_data *data, + cpu *processor, + unsigned_word cia) +{ + struct parent_args { + /*in*/ + unsigned_cell phandle; + /*out*/ + unsigned_cell parent_phandle; + } args; + device *phandle; + device *parent_phandle; + /* read the args in */ + if (chirp_read_t2h_args(&args, sizeof(args), 1, 1, data, processor, cia)) + return -1; + phandle = external_to_device(data->root, args.phandle); + TRACE(trace_os_emul, ("parent - in - phandle=0x%lx(0x%lx`%s')\n", + (unsigned long)args.phandle, + (unsigned long)phandle, + (phandle == NULL ? "" : device_name(phandle)))); + /* find a parent */ + if (args.phandle == 0 + || phandle == NULL) { + parent_phandle = NULL; + args.parent_phandle = -1; + } + else { + parent_phandle = device_parent(phandle); + if (parent_phandle == NULL) + args.parent_phandle = 0; + else + args.parent_phandle = device_to_external(parent_phandle); + } + /* return the result */ + TRACE(trace_os_emul, ("parent - out - parent_phandle=0x%lx(0x%lx`%s')\n", + (unsigned long)args.parent_phandle, + (unsigned long)parent_phandle, + (parent_phandle == NULL ? "" : device_name(parent_phandle)))); + chirp_write_h2t_args(&args, + sizeof(args), + data, + processor, cia); + return 0; +} + +static int +chirp_emul_instance_to_package(os_emul_data *data, + cpu *processor, + unsigned_word cia) +{ + struct instance_to_package_args { + /*in*/ + unsigned_cell ihandle; + /*out*/ + unsigned_cell phandle; + } args; + device_instance *ihandle; + device *phandle = NULL; + /* read the args in */ + if (chirp_read_t2h_args(&args, sizeof(args), 1, 1, data, processor, cia)) + return -1; + ihandle = external_to_device_instance(data->root, args.ihandle); + TRACE(trace_os_emul, ("instance-to-package - in - ihandle=0x%lx(0x%lx`%s')\n", + (unsigned long)args.ihandle, + (unsigned long)ihandle, + ihandle_name(ihandle))); + /* find the corresponding phandle */ + if (ihandle == NULL) { + phandle = NULL; + args.phandle = -1; + } + else { + phandle = device_instance_device(ihandle); + args.phandle = device_to_external(phandle); + } + /* return the result */ + TRACE(trace_os_emul, ("instance-to-package - out - phandle=0x%lx(0x%lx`%s')\n", + (unsigned long)args.phandle, + (unsigned long)phandle, + (phandle == NULL ? "" : device_name(phandle)))); + chirp_write_h2t_args(&args, + sizeof(args), + data, + processor, cia); + return 0; +} + +static int +chirp_emul_getproplen(os_emul_data *data, + cpu *processor, + unsigned_word cia) +{ + struct getproplen_args { + /*in*/ + unsigned_cell phandle; + unsigned_cell name; + /*out*/ + unsigned_cell proplen; + } args; + char name[32]; + device *phandle; + /* read the args in */ + if (chirp_read_t2h_args(&args, sizeof(args), 2, 1, data, processor, cia)) + return -1; + phandle = external_to_device(data->root, args.phandle); + emul_read_string(name, + args.name, + sizeof(name), + processor, cia); + TRACE(trace_os_emul, ("getproplen - in - phandle=0x%lx(0x%lx`%s') name=`%s'\n", + (unsigned long)args.phandle, + (unsigned long)phandle, + (phandle == NULL ? "" : device_name(phandle)), + name)); + /* find our prop and get its length */ + if (args.phandle == 0 + || phandle == NULL) { + args.proplen = -1; + } + else { + const device_property *prop = device_find_property(phandle, name); + if (prop == (device_property*)0) { + args.proplen = -1; + } + else { + args.proplen = prop->sizeof_array; + } + } + /* return the result */ + TRACE(trace_os_emul, ("getproplen - out - proplen=%ld\n", + (unsigned long)args.proplen)); + chirp_write_h2t_args(&args, + sizeof(args), + data, + processor, cia); + return 0; +} + +static int +chirp_emul_getprop(os_emul_data *data, + cpu *processor, + unsigned_word cia) +{ + struct getprop_args { + /*in*/ + unsigned_cell phandle; + unsigned_cell name; + unsigned_cell buf; + unsigned_cell buflen; + /*out*/ + unsigned_cell size; + } args; + char name[32]; + device *phandle; + /* read in the args, the return is optional */ + if (chirp_read_t2h_args(&args, sizeof(args), 4, -1, data, processor, cia)) + return -1; + phandle = external_to_device(data->root, args.phandle); + emul_read_string(name, + args.name, + sizeof(name), + processor, cia); + TRACE(trace_os_emul, ("getprop - in - phandle=0x%lx(0x%lx`%s') name=`%s' buf=0x%lx buflen=%ld\n", + (unsigned long)args.phandle, + (unsigned long)phandle, + (phandle == NULL ? "" : device_name(phandle)), + name, + (unsigned long)args.buf, + (unsigned long)args.buflen)); + /* get the property */ + if (args.phandle == 0 + || phandle == NULL) { + args.size = -1; + } + else { + const device_property *prop = device_find_property(phandle, name); + if (prop == NULL) { + args.size = -1; + } + else { + int size = args.buflen; + if (size > prop->sizeof_array) + size = prop->sizeof_array; + emul_write_buffer(prop->array, args.buf, + size, + processor, cia); + args.size = size; + switch (prop->type) { + case string_property: + TRACE(trace_os_emul, ("getprop - string `%s'\n", + device_find_string_property(phandle, name))); + break; + case ihandle_property: + TRACE(trace_os_emul, ("getprop - ihandle=0x%lx(0x%lx`%s')\n", + BE2H_cell(*(unsigned_cell*)prop->array), + (unsigned long)device_find_ihandle_property(phandle, name), + ihandle_name(device_find_ihandle_property(phandle, name)))); + break; + default: + break; + } + } + } + /* write back the result */ + if (data->n_returns == 0) + TRACE(trace_os_emul, ("getprop - out - size=%ld (not returned)\n", + (unsigned long)args.size)); + else { + TRACE(trace_os_emul, ("getprop - out - size=%ld\n", + (unsigned long)args.size)); + chirp_write_h2t_args(&args, + sizeof(args), + data, + processor, cia); + } + return 0; +} + +static int +chirp_emul_nextprop(os_emul_data *data, + cpu *processor, + unsigned_word cia) +{ + struct nextprop_args { + /*in*/ + unsigned_cell phandle; + unsigned_cell previous; + unsigned_cell buf; + /*out*/ + unsigned_cell flag; + } args; + char previous[32]; + device *phandle; + /* read in the args */ + if (chirp_read_t2h_args(&args, sizeof(args), 3, 1, data, processor, cia)) + return -1; + phandle = external_to_device(data->root, args.phandle); + emul_read_string(previous, + args.previous, + sizeof(previous), + processor, cia); + TRACE(trace_os_emul, ("nextprop - in - phandle=0x%lx(0x%lx`%s') previous=`%s' buf=0x%lx\n", + (unsigned long)args.phandle, + (unsigned long)phandle, + (phandle == NULL ? "" : device_name(phandle)), + previous, + (unsigned long)args.buf)); + /* find the next property */ + if (args.phandle == 0 + || phandle == NULL) { + args.flag = -1; + } + else { + const device_property *prev_prop = device_find_property(phandle, previous); + if (prev_prop == NULL) { + args.flag = -1; /* name invalid */ + } + else { + const device_property *next_prop; + next_prop = device_next_property(prev_prop); + if (next_prop == NULL) { + args.flag = 0; /* last property */ + } + else { + emul_write_buffer(next_prop->name, args.buf, strlen(next_prop->name), + processor, cia); + TRACE(trace_os_emul, ("nextprop - name=`%s'\n", next_prop->name)); + args.flag = 1; /* worked ok */ + } + } + } + /* write back the result */ + TRACE(trace_os_emul, ("nextprop - out - flag=%ld\n", + (unsigned long)args.flag)); + chirp_write_h2t_args(&args, + sizeof(args), + data, + processor, cia); + return 0; +} + +#if 0 +static int +chirp_emul_setprop(os_emul_data *data, + cpu *processor, + unsigned_word cia) +{ + error("chirp: setprop method not implemented\n"); + return 0; +} +#endif + +static int +chirp_emul_canon(os_emul_data *data, + cpu *processor, + unsigned_word cia) +{ + struct canon_args { + /*in*/ + unsigned_cell device_specifier; + unsigned_cell buf; + unsigned_cell buflen; + /*out*/ + unsigned_cell length; + } args; + char device_specifier[1024]; + device *phandle; + const char *path; + int length; + /* read in the args */ + if (chirp_read_t2h_args(&args, sizeof(args), 3, 1, data, processor, cia)) + return -1; + emul_read_string(device_specifier, + args.device_specifier, + sizeof(device_specifier), + processor, cia); + TRACE(trace_os_emul, ("canon - in - device_specifier=`%s' buf=0x%lx buflen=%lx\n", + device_specifier, + (unsigned long)args.buf, + (unsigned long)args.buflen)); + /* canon the name */ + phandle = tree_find_device(data->root, device_specifier); + if (phandle == NULL) { + length = -1; + path = ""; + args.length = -1; + } + else { + path = device_path(phandle); + length = strlen(path); + if (length >= args.buflen) + length = args.buflen - 1; + emul_write_buffer(path, args.buf, length, + processor, cia); + args.length = length; + } + /* write back the result */ + TRACE(trace_os_emul, ("canon - out - length=%ld buf=`%s'\n", + (unsigned long)args.length, + path)); + chirp_write_h2t_args(&args, + sizeof(args), + data, + processor, cia); + return 0; +} + +static int +chirp_emul_finddevice(os_emul_data *data, + cpu *processor, + unsigned_word cia) +{ + struct finddevice_args { + /*in*/ + unsigned_cell device_specifier; + /*out*/ + unsigned_cell phandle; + } args; + char device_specifier[1024]; + device *phandle; + /* get the args */ + if (chirp_read_t2h_args(&args, sizeof(args), 1, 1, data, processor, cia)) + return -1; + emul_read_string(device_specifier, + args.device_specifier, + sizeof(device_specifier), + processor, cia); + TRACE(trace_os_emul, ("finddevice - in - device_specifier=`%s'\n", + device_specifier)); + /* find the device */ + phandle = tree_find_device(data->root, device_specifier); + if (phandle == NULL) + args.phandle = -1; + else + args.phandle = device_to_external(phandle); + /* return its phandle */ + TRACE(trace_os_emul, ("finddevice - out - phandle=0x%lx(0x%lx`%s')\n", + (unsigned long)args.phandle, + (unsigned long)phandle, + (phandle == NULL ? "" : device_name(phandle)))); + chirp_write_h2t_args(&args, + sizeof(args), + data, + processor, cia); + return 0; +} + +static int +chirp_emul_instance_to_path(os_emul_data *data, + cpu *processor, + unsigned_word cia) +{ + struct instance_to_path_args { + /*in*/ + unsigned_cell ihandle; + unsigned_cell buf; + unsigned_cell buflen; + /*out*/ + unsigned_cell length; + } args; + device_instance *ihandle; + const char *path; + int length; + /* get the args */ + if (chirp_read_t2h_args(&args, sizeof(args), 3, 1, data, processor, cia)) + return -1; + ihandle = external_to_device_instance(data->root, args.ihandle); + TRACE(trace_os_emul, ("instance-to-path - in - ihandle=0x%lx(0x%lx`%s') buf=0x%lx buflen=%ld\n", + (unsigned long)args.ihandle, + (unsigned long)ihandle, + ihandle_name(ihandle), + (unsigned long)args.buf, + (unsigned long)args.buflen)); + /* get the devices name */ + if (ihandle == NULL) { + args.length = -1; + path = "(null)"; + } + else { + path = device_instance_path(ihandle); + length = strlen(path); + if (length >= args.buflen) + length = args.buflen - 1; + emul_write_buffer(path, args.buf, length, + processor, cia); + args.length = length; + } + /* return its phandle */ + TRACE(trace_os_emul, ("instance-to-path - out - length=%ld buf=`%s')\n", + (unsigned long)args.length, + path)); + chirp_write_h2t_args(&args, + sizeof(args), + data, + processor, cia); + return 0; +} + +static int +chirp_emul_package_to_path(os_emul_data *data, + cpu *processor, + unsigned_word cia) +{ + struct package_to_path_args { + /*in*/ + unsigned_cell phandle; + unsigned_cell buf; + unsigned_cell buflen; + /*out*/ + unsigned_cell length; + } args; + device *phandle; + const char *path; + /* get the args */ + if (chirp_read_t2h_args(&args, sizeof(args), 3, 1, data, processor, cia)) + return -1; + phandle = external_to_device(data->root, args.phandle); + TRACE(trace_os_emul, ("package-to-path - in - phandle=0x%lx(0x%lx`%s') buf=0x%lx buflen=%ld\n", + (unsigned long)args.phandle, + (unsigned long)phandle, + (phandle == NULL ? "" : device_name(phandle)), + (unsigned long)args.buf, + (unsigned long)args.buflen)); + /* get the devices name */ + if (phandle == NULL) { + args.length = -1; + path = "(null)"; + } + else { + int length; + path = device_path(phandle); + length = strlen(path); + if (length >= args.buflen) + length = args.buflen - 1; + emul_write_buffer(path, args.buf, length, + processor, cia); + args.length = length; + } + /* return its phandle */ + TRACE(trace_os_emul, ("package-to-path - out - length=%ld buf=`%s')\n", + (unsigned long)args.length, + path)); + chirp_write_h2t_args(&args, + sizeof(args), + data, + processor, cia); + return 0; +} + +static int +chirp_emul_call_method(os_emul_data *data, + cpu *processor, + unsigned_word cia) +{ + struct call_method_args { + /*in*/ + unsigned_cell method; + unsigned_cell ihandle; + /*in/out*/ + unsigned_cell stack[13]; /*6in + 6out + catch */ + } args; + char method[32]; + device_instance *ihandle; + /* some useful info about our mini stack */ + int n_stack_args; + int n_stack_returns; + int stack_catch_result; + int stack_returns; + /* read the args */ + if (chirp_read_t2h_args(&args, sizeof(args), -1, -1, data, processor, cia)) + return -1; + emul_read_string(method, + args.method, + sizeof(method), + processor, cia); + ihandle = external_to_device_instance(data->root, args.ihandle); + n_stack_args = data->n_args - 2; + n_stack_returns = data->n_returns - 1; + stack_catch_result = n_stack_args; + stack_returns = stack_catch_result + 1; + TRACE(trace_os_emul, ("call-method - in - n_args=%ld n_returns=%ld method=`%s' ihandle=0x%lx(0x%lx`%s')\n", + (unsigned long)data->n_args, + (unsigned long)data->n_returns, + method, + (unsigned long)args.ihandle, + (unsigned long)ihandle, + ihandle_name(ihandle))); + /* see if we can emulate this method */ + if (ihandle == NULL) { + /* OpenFirmware doesn't define this error */ + error("chirp: invalid ihandle passed to call-method method"); + } + else { + args.stack[stack_catch_result] = + device_instance_call_method(ihandle, + method, + n_stack_args, + &args.stack[0], + n_stack_returns, + &args.stack[stack_returns]); + } + /* finished */ + TRACE(trace_os_emul, ("call-method - out - catch-result=%ld\n", + (unsigned long)args.stack[stack_catch_result])); + chirp_write_h2t_args(&args, + sizeof(args), + data, + processor, cia); + return 0; +} + + +/* Device I/O */ + +static int +chirp_emul_open(os_emul_data *data, + cpu *processor, + unsigned_word cia) +{ + struct open_args { + /*in*/ + unsigned_cell device_specifier; + /*out*/ + unsigned_cell ihandle; + } args; + char device_specifier[1024]; + device_instance *ihandle; + /* read the args */ + if (chirp_read_t2h_args(&args, sizeof(args), 1, 1, data, processor, cia)) + return -1; + emul_read_string(device_specifier, + args.device_specifier, + sizeof(device_specifier), + processor, cia); + TRACE(trace_os_emul, ("open - in - device_specifier=`%s'\n", + device_specifier)); + /* open the device */ + ihandle = tree_instance(data->root, device_specifier); + if (ihandle == NULL) + args.ihandle = -1; + else + args.ihandle = device_instance_to_external(ihandle); + /* return the ihandle result */ + TRACE(trace_os_emul, ("open - out - ihandle=0x%lx(0x%lx`%s')\n", + (unsigned long)args.ihandle, + (unsigned long)ihandle, + ihandle_name(ihandle))); + chirp_write_h2t_args(&args, + sizeof(args), + data, + processor, cia); + return 0; +} + +static int +chirp_emul_close(os_emul_data *data, + cpu *processor, + unsigned_word cia) +{ + struct close_args { + /*in*/ + unsigned_cell ihandle; + /*out*/ + } args; + device_instance *ihandle; + /* read the args */ + if (chirp_read_t2h_args(&args, sizeof(args), 1, 0, data, processor, cia)) + return -1; + ihandle = external_to_device_instance(data->root, args.ihandle); + TRACE(trace_os_emul, ("close - in - ihandle=0x%lx(0x%lx`%s')\n", + (unsigned long)args.ihandle, + (unsigned long)ihandle, + ihandle_name(ihandle))); + /* close the device */ + if (ihandle == NULL) { + /* OpenFirmware doesn't define this error */ + error("chirp: invalid ihandle passed to close method"); + } + else { + device_instance_delete(ihandle); + } + /* return the ihandle result */ + TRACE(trace_os_emul, ("close - out\n")); + chirp_write_h2t_args(&args, + sizeof(args), + data, + processor, cia); + return 0; +} + +static int +chirp_emul_read(os_emul_data *data, + cpu *processor, + unsigned_word cia) +{ + struct read_args { + /*in*/ + unsigned_cell ihandle; + unsigned_cell addr; + unsigned_cell len; + /*out*/ + unsigned_cell actual; + } args; + char buf[1024]; + device_instance *ihandle; + /* read the args */ + if (chirp_read_t2h_args(&args, sizeof(args), 3, 1, data, processor, cia)) + return -1; + ihandle = external_to_device_instance(data->root, args.ihandle); + TRACE(trace_os_emul, ("read - in - ihandle=0x%lx(0x%lx`%s') addr=0x%lx len=%ld\n", + (unsigned long)args.ihandle, + (unsigned long)ihandle, + ihandle_name(ihandle), + (unsigned long)args.addr, + (unsigned long)args.len)); + if (ihandle == NULL) { + /* OpenFirmware doesn't define this error */ + error("chirp: invalid ihandle passed to read method"); + } + else { + /* do the reads */ + int actual = 0; + while (actual < args.len) { + int remaining = args.len - actual; + int to_read = (remaining <= sizeof(buf) ? remaining : sizeof(buf)); + int nr_read = device_instance_read(ihandle, buf, to_read); + if (nr_read < 0) { + actual = nr_read; /* the error */ + break; + } + else if (nr_read == 0) { + break; + } + emul_write_buffer(buf, + args.addr + actual, + nr_read, + processor, cia); + actual += nr_read; + } + if (actual >= 0) { + args.actual = actual; + if (actual < sizeof(buf)) + buf[actual] = '\0'; + else + buf[sizeof(buf) - 1] = '\0'; + } + else { + switch (actual) { + case sim_io_eof: + args.actual = 0; + break; + case sim_io_not_ready: + ASSERT(sim_io_not_ready == -2); + args.actual = sim_io_not_ready; + break; + default: + error("Bad error value %ld", (long)actual); + break; + } + } + } + /* return the result */ + TRACE(trace_os_emul, ("read - out - actual=%ld `%s'\n", + (long)args.actual, + ((args.actual > 0 && args.actual < sizeof(buf)) ? buf : "") + )); + chirp_write_h2t_args(&args, + sizeof(args), + data, + processor, cia); + return 0; +} + +static int +chirp_emul_write(os_emul_data *data, + cpu *processor, + unsigned_word cia) +{ + struct write_args { + /*in*/ + unsigned_cell ihandle; + unsigned_cell addr; + unsigned_cell len; + /*out*/ + unsigned_cell actual; + } args; + char buf[1024]; + device_instance *ihandle; + int actual; + /* get the args */ + if (chirp_read_t2h_args(&args, sizeof(args), 3, 1, data, processor, cia)) + return -1; + actual = args.len; + if (actual >= sizeof(buf)) + actual = sizeof(buf) - 1; + emul_read_buffer(buf, + args.addr, + actual, + processor, cia); + buf[actual] = '\0'; + ihandle = external_to_device_instance(data->root, args.ihandle); + TRACE(trace_os_emul, ("write - in - ihandle=0x%lx(0x%lx`%s') `%s' (%ld)\n", + (unsigned long)args.ihandle, + (unsigned long)ihandle, + ihandle_name(ihandle), + buf, (long)actual)); + if (ihandle == NULL) { + /* OpenFirmware doesn't define this error */ + error("chirp: invalid ihandle passed to write method"); + } + else { + /* write it out */ + actual = device_instance_write(ihandle, buf, actual); + if (actual < 0) + args.actual = 0; + else + args.actual = actual; + } + /* return the result */ + TRACE(trace_os_emul, ("write - out - actual=%ld\n", + (long)args.actual)); + chirp_write_h2t_args(&args, + sizeof(args), + data, + processor, cia); + return 0; +} + +static int +chirp_emul_seek(os_emul_data *data, + cpu *processor, + unsigned_word cia) +{ + struct seek_args { + /*in*/ + unsigned_cell ihandle; + unsigned_cell pos_hi; + unsigned_cell pos_lo; + /*out*/ + unsigned_cell status; + } args; + int status; + device_instance *ihandle; + /* get the args */ + if (chirp_read_t2h_args(&args, sizeof(args), 3, 1, data, processor, cia)) + return -1; + ihandle = external_to_device_instance(data->root, args.ihandle); + TRACE(trace_os_emul, ("seek - in - ihandle=0x%lx(0x%lx`%s') pos.hi=0x%lx pos.lo=0x%lx\n", + (unsigned long)args.ihandle, + (unsigned long)ihandle, + ihandle_name(ihandle), + args.pos_hi, args.pos_lo)); + if (ihandle == NULL) { + /* OpenFirmware doesn't define this error */ + error("chirp: invalid ihandle passed to seek method"); + } + else { + /* seek it out */ + status = device_instance_seek(ihandle, args.pos_hi, args.pos_lo); + args.status = status; + } + /* return the result */ + TRACE(trace_os_emul, ("seek - out - status=%ld\n", + (long)args.status)); + chirp_write_h2t_args(&args, + sizeof(args), + data, + processor, cia); + return 0; +} + + +/* memory */ + +static int +chirp_emul_claim(os_emul_data *data, + cpu *processor, + unsigned_word cia) +{ + /* NOTE: the client interface claim routine is *very* different to + the "claim" method described in IEEE-1275 appendix A. The latter + uses real addresses while this uses virtual (effective) + addresses. */ + struct claim_args { + /* in */ + unsigned_cell virt; + unsigned_cell size; + unsigned_cell align; + /* out */ + unsigned_cell baseaddr; + } args; + /* read the args */ + if (chirp_read_t2h_args(&args, sizeof(args), + 3 /*n_args*/, 1 /*n_returns*/, + data, processor, cia)) + return -1; + TRACE(trace_os_emul, ("claim - in - virt=0x%lx size=%ld align=%d\n", + (unsigned long)args.virt, + (long int)args.size, + (int)args.align)); + /* use the memory device to allocate (real) memory at the requested + address */ + { + device_instance *memory = tree_find_ihandle_property(data->root, "/chosen/memory"); + unsigned_cell mem_in[3]; + unsigned_cell mem_out[1]; + mem_in[0] = args.align; /*top-of-stack*/ + mem_in[1] = args.size; + mem_in[2] = args.virt; + if (device_instance_call_method(memory, "claim", + 3, mem_in, 1, mem_out) < 0) + error("chirp: claim failed to allocate memory virt=0x%lx size=%ld align=%d", + (unsigned long)args.virt, + (long int)args.size, + (int)args.align); + args.baseaddr = mem_out[0]; + } + /* if using virtual addresses, create a 1-1 map of this address space */ + if (!data->real_mode) { + error("chirp: claim method does not support virtual mode"); + } + /* return the base address */ + TRACE(trace_os_emul, ("claim - out - baseaddr=0x%lx\n", + (unsigned long)args.baseaddr)); + chirp_write_h2t_args(&args, + sizeof(args), + data, + processor, cia); + return 0; +} + +static int +chirp_emul_release(os_emul_data *data, + cpu *processor, + unsigned_word cia) +{ + /* NOTE: the client interface release routine is *very* different to + the "claim" method described in IEEE-1275 appendix A. The latter + uses real addresses while this uses virtual (effective) + addresses. */ + struct claim_args { + /* in */ + unsigned_cell virt; + unsigned_cell size; + /* out */ + } args; + /* read the args */ + if (chirp_read_t2h_args(&args, sizeof(args), + 2 /*n_args*/, 0 /*n_returns*/, + data, processor, cia)) + return -1; + TRACE(trace_os_emul, ("release - in - virt=0x%lx size=%ld\n", + (unsigned long)args.virt, + (long int)args.size)); + /* use the memory device to release (real) memory at the requested + address */ + { + device_instance *memory = tree_find_ihandle_property(data->root, "/chosen/memory"); + unsigned_cell mem_in[2]; + mem_in[0] = args.size; + mem_in[1] = args.virt; + if (device_instance_call_method(memory, "release", + 2, mem_in, 0, NULL) < 0) + error("chirp: claim failed to release memory virt=0x%lx size=%ld", + (unsigned long)args.virt, + (long int)args.size); + } + /* if using virtual addresses, remove the 1-1 map of this address space */ + if (!data->real_mode) { + error("chirp: release method does not support virtual mode"); + } + /* return the base address */ + TRACE(trace_os_emul, ("release - out\n")); + chirp_write_h2t_args(&args, + sizeof(args), + data, + processor, cia); + return 0; +} + + +/* Control transfer */ + +static int +chirp_emul_boot(os_emul_data *data, + cpu *processor, + unsigned_word cia) +{ + /* unlike OpenFirmware this one can take an argument */ + struct boot_args { + /*in*/ + unsigned_cell bootspec; + /*out*/ + } args; + char bootspec[1024]; + /* read in the arguments */ + if (chirp_read_t2h_args(&args, sizeof(args), -1, 0, data, processor, cia)) + cpu_halt(processor, cia, was_exited, -1); + if (args.bootspec != 0) + emul_read_string(bootspec, args.bootspec, sizeof(bootspec), + processor, cia); + else + strcpy(bootspec, "(null)"); + TRACE(trace_os_emul, ("boot - in bootspec=`%s'\n", bootspec)); + /* just report this and exit */ + printf_filtered("chrp: boot %s called, exiting.\n", bootspec); + cpu_halt(processor, cia, was_exited, 0); + return 0; +} + +static int +chirp_emul_enter(os_emul_data *data, + cpu *processor, + unsigned_word cia) +{ + error("chirp: enter method not implemented\n"); + return 0; +} + +static int +chirp_emul_exit(os_emul_data *data, + cpu *processor, + unsigned_word cia) +{ + /* unlike OpenBoot this one can take an argument */ + struct exit_args { + /*in*/ + signed_cell status; + /*out*/ + } args; + if (chirp_read_t2h_args(&args, sizeof(args), -1, 0, data, processor, cia)) + cpu_halt(processor, cia, was_exited, -1); + cpu_halt(processor, cia, was_exited, args.status); + return 0; +} + +static int +chirp_emul_chain(os_emul_data *data, + cpu *processor, + unsigned_word cia) +{ + error("chirp: chain method not implemented\n"); + return 0; +} + + +/* user interface */ + +static int +chirp_emul_interpret(os_emul_data *data, + cpu *processor, + unsigned_word cia) +{ + error("chirp: interpret method not implemented\n"); + return 0; +} + +static int +chirp_emul_set_callback(os_emul_data *data, + cpu *processor, + unsigned_word cia) +{ + error("chirp: set_callback method not implemented\n"); + return 0; +} + +static int +chirp_emul_set_symbol_lookup(os_emul_data *data, + cpu *processor, + unsigned_word cia) +{ + error("chirp: set_symbol_lookup method not implemented\n"); + return 0; +} + + +/* Time */ + +static int +chirp_emul_milliseconds(os_emul_data *data, + cpu *processor, + unsigned_word cia) +{ + struct test_args { + /*in*/ + /*out*/ + unsigned_cell ms; + } args; + unsigned64 time; + /* read in the arguments */ + if (chirp_read_t2h_args(&args, sizeof(args), 1, 1, data, processor, cia)) + return -1; + /* make up a number */ + time = event_queue_time(psim_event_queue(cpu_system(processor))) / 1000000; + args.ms = time; + /* write the arguments back out */ + TRACE(trace_os_emul, ("milliseconds - out - ms=%ld\n", + (unsigned long)args.ms)); + chirp_write_h2t_args(&args, + sizeof(args), + data, + processor, cia); + return 0; +} + + + + +static chirp_services services[] = { + + /* client interface */ + { "test", chirp_emul_test }, + + /* device tree */ + { "peer", chirp_emul_peer }, + { "child", chirp_emul_child }, + { "parent", chirp_emul_parent }, + { "instance-to-package", chirp_emul_instance_to_package }, + { "getproplen", chirp_emul_getproplen }, + { "getprop", chirp_emul_getprop }, + { "nextprop", chirp_emul_nextprop }, + /* { "setprop", chirp_emul_setprop }, */ + { "canon", chirp_emul_canon }, + { "finddevice", chirp_emul_finddevice }, + { "instance-to-path", chirp_emul_instance_to_path }, + { "package-to-path", chirp_emul_package_to_path }, + { "call-method", chirp_emul_call_method }, + + /* device I/O */ + { "open", chirp_emul_open }, + { "close", chirp_emul_close }, + { "read", chirp_emul_read }, + { "write", chirp_emul_write }, + { "seek", chirp_emul_seek }, + { "write", chirp_emul_write }, + + /* memory */ + { "claim", chirp_emul_claim }, + { "release", chirp_emul_release }, + + /* control transfer */ + { "boot", chirp_emul_boot }, + { "enter", chirp_emul_enter }, + { "exit", chirp_emul_exit }, + { "chain", chirp_emul_chain }, + + /* user interface */ + { "interpret", chirp_emul_interpret }, + { "set_callback", chirp_emul_set_callback }, + { "set_symbol_lookup", chirp_emul_set_symbol_lookup }, + + /* time */ + { "milliseconds", chirp_emul_milliseconds }, + + { 0, /* sentinal */ }, +}; + + +/* main handlers */ + +/* Any starting address greater than this is assumed to be an Chirp + rather than VEA */ + +#ifndef CHIRP_START_ADDRESS +#define CHIRP_START_ADDRESS 0x80000000 +#endif +#ifndef CHIRP_LOAD_BASE +#define CHIRP_LOAD_BASE -1 +#endif + + +typedef struct _chirp_note_desc { + signed32 real_mode; + signed32 real_base; + signed32 real_size; + signed32 virt_base; + signed32 virt_size; + signed32 load_base; +} chirp_note_desc; + +typedef enum { + note_missing, + note_found, + note_correct, +} note_found_status; +typedef struct _chirp_note { + chirp_note_desc desc; + note_found_status found; +} chirp_note; + +typedef struct _chirp_note_head { + unsigned32 namesz; + unsigned32 descsz; + unsigned32 type; +} chirp_note_head; + +static void +map_over_chirp_note(bfd *image, + asection *sect, + PTR obj) +{ + chirp_note *note = (chirp_note*)obj; + if (strcmp(sect->name, ".note") == 0) { + chirp_note_head head; + char name[16]; + /* check the head */ + if (!bfd_get_section_contents(image, sect, + &head, 0, sizeof(head))) + return; + head.namesz = bfd_get_32(image, (void*)&head.namesz); + head.descsz = bfd_get_32(image, (void*)&head.descsz); + head.type = bfd_get_32(image, (void*)&head.type); + if (head.type != 0x1275) + return; + /* check the name field */ + if (head.namesz > sizeof(name)) { + error("chirp: note name too long (%d > %d)\n", (int)head.namesz, sizeof(name)); + } + if (!bfd_get_section_contents(image, sect, + name, sizeof(head), head.namesz)) { + error("chirp: note name unreadable\n"); + } + if (strcmp(name, "PowerPC") != 0) { + printf_filtered("chirp: note name (%s) not `PowerPC'\n", name); + } + /* check the size */ + if (head.descsz == sizeof(note->desc) - sizeof(signed32)) { + sim_io_printf_filtered("chirp: note descriptor missing load-base\n"); + } + else if (head.descsz != sizeof(note->desc)) { + sim_io_printf_filtered("chirp: note descriptor of wrong size\n"); + note->found = note_found; + return; + } + note->found = note_correct; + /* get the contents */ + if (!bfd_get_section_contents(image, sect, + ¬e->desc, /* page align start */ + ((sizeof(head) + head.namesz) + 3) & ~3, + head.descsz)) { + error("chirp: note descriptor unreadable\n"); + } + note->desc.real_mode = bfd_get_32(image, (void*)¬e->desc.real_mode); + note->desc.real_base = bfd_get_32(image, (void*)¬e->desc.real_base); + note->desc.real_size = bfd_get_32(image, (void*)¬e->desc.real_size); + note->desc.virt_base = bfd_get_32(image, (void*)¬e->desc.virt_base); + note->desc.virt_size = bfd_get_32(image, (void*)¬e->desc.virt_size); + if (head.descsz == sizeof(note->desc)) + note->desc.load_base = bfd_get_32(image, (void*)¬e->desc.load_base); + else + note->desc.load_base = CHIRP_LOAD_BASE; + } +} + + +static os_emul_data * +emul_chirp_create(device *root, + bfd *image, + const char *name) +{ + os_emul_data *chirp; + device *node; + chirp_note note; + int i; + + /* Sanity check that this really is the chosen emulation */ + if (name == NULL && image == NULL) + return NULL; + if (name != NULL + && strcmp(name, "ob") != 0 + && strcmp(name, "ieee1274") != 0 + && strcmp(name, "chrp") != 0 + && strcmp(name, "chirp") != 0 + && strcmp(name, "openboot") != 0) + return NULL; + + /* look for an elf note section, enter its values into the device tree */ + memset(¬e, 0, sizeof(note)); + if (image != NULL) + bfd_map_over_sections(image, map_over_chirp_note, ¬e); + if (name == NULL && image != NULL && note.found == note_missing) + return NULL; + + /* Assume that it is a chirp emulation */ + + chirp = ZALLOC(os_emul_data); + chirp->root = root; + chirp->services = services; + + /* the root node */ + tree_parse(root, "/name \"gpl,clayton"); + + /* default options */ + emul_add_tree_options(root, image, "chirp", "oea", + 0 /*oea-interrupt-prefix*/); + + /* hardware */ + emul_add_tree_hardware(root); + + /* basic information */ + chirp->memory_size + = tree_find_integer_property(root, "/openprom/options/oea-memory-size"); + chirp->little_endian + = tree_find_boolean_property(root, "/options/little-endian?"); + chirp->floating_point_available + = tree_find_boolean_property(root, "/openprom/options/floating-point?"); + chirp->interrupt_prefix = + tree_find_integer_property(root, "/openprom/options/oea-interrupt-prefix"); + + + /* Perform an interum layout of the openboot firmware in memory */ + + + /* a page for firmware calls */ + chirp->sizeof_code = 4096; + chirp->code_offset = 0x4000; /* possible space for interrupt table */ + + /* the stack */ + chirp->sizeof_stack = 32 * 1024; + chirp->stack_offset = chirp->code_offset + chirp->sizeof_code; + + /* the hash table */ + if (!note.desc.real_mode) { + chirp->nr_page_table_entry_groups = (chirp->memory_size < 0x800000 + ? 1024 /* min allowed */ + : (chirp->memory_size / 4096 / 2)); + chirp->sizeof_htab = chirp->nr_page_table_entry_groups * 64; + } + chirp->htab_offset = chirp->stack_offset + chirp->sizeof_stack; + + /* the actual amount of space needed */ + chirp->real_size = chirp->htab_offset + chirp->sizeof_htab; + + + /* now go through and see if it fits in what is available */ + + + /* resolve real-mode? */ + if (note.found == note_correct) + chirp->real_mode = note.desc.real_mode; + else if (tree_find_property(root, "/options/real-mode?") != NULL) + chirp->real_mode = tree_find_boolean_property(root, "/options/real-mode?"); + else + chirp->real_mode = 0; + if (tree_find_property(root, "/options/real-mode?") != NULL) { + if (!chirp->real_mode + != !tree_find_boolean_property(root, "/options/real-mode?")) + error("chirp: /options/real-mode? conflicts with note section\n"); + } + else + tree_parse(root, "/options/real-mode? %s", + chirp->real_mode ? "true" : "false"); + + /* resolve real-base */ + if (note.found == note_correct + && note.desc.real_base != (signed32)-1) + chirp->real_base = note.desc.real_base; + else if (tree_find_property(root, "/options/real-base") != NULL) + chirp->real_base = tree_find_integer_property(root, "/options/real-base"); + else + chirp->real_base = chirp->memory_size - chirp->real_size; + if (tree_find_property(root, "/options/real-base") != NULL) { + if (chirp->real_base != tree_find_integer_property(root, "/options/real-base")) + error("chirp: /options/real-base conflicts with note section\n"); + } + else + tree_parse(root, "/options/real-base 0x%lx", + (unsigned long)chirp->real_base); + + /* resolve real-size */ + if (note.found == note_correct + && note.desc.real_size != (signed32)-1 + && note.desc.real_size != 0 + && chirp->real_size > note.desc.real_size) + error("chirp: insufficient physical memory for firmware\n"); + if (tree_find_property(root, "/options/real-size") != NULL) { + if (chirp->real_size > tree_find_integer_property(root, "/options/real-size")) + error("chirp: /options/real-size conflicts with note section\n"); + } + else + tree_parse(root, "/options/real-size 0x%lx", + (unsigned long)chirp->real_size); + + /* resolve virt-base */ + if (chirp->real_mode) + chirp->virt_base = chirp->real_base; + else if (note.found == note_correct && note.desc.virt_base != -1) + chirp->virt_base = note.desc.virt_base; + else if (tree_find_property(root, "/options/virt-base") != NULL) + chirp->virt_base = tree_find_integer_property(root, "/options/virt-base"); + else + chirp->virt_base = CHIRP_START_ADDRESS; + if (tree_find_property(root, "/options/virt-base") != NULL) { + unsigned_word virt_base = tree_find_integer_property(root, "/options/virt-base"); + if (virt_base != -1 && chirp->virt_base != virt_base) + error("chirp: /options/virt-base conflicts with note section\n"); + } + else + tree_parse(root, "/options/virt-base 0x%lx", + chirp->real_mode ? -1 : (unsigned long)chirp->virt_base); + + /* resolve virt-size */ + chirp->virt_size = chirp->real_size; + if (note.found == note_correct + && note.desc.virt_size != (signed32)-1 + && note.desc.virt_size != 0 + && !chirp->real_mode + && chirp->virt_size > note.desc.virt_size) + error("chirp: insufficent virtual memory for firmware\n"); + if (tree_find_property(root, "/options/virt-size") != NULL) { + if (chirp->virt_size > tree_find_integer_property(root, "/options/virt-size")) + error("chirp: /options/virt-size conflicts with note section\n"); + } + else + tree_parse(root, "/options/virt-size 0x%lx", + chirp->real_mode ? -1 : (unsigned long)chirp->virt_size); + + /* resolve load-base */ + if (note.found == note_correct + && note.desc.load_base != (signed32)-1) + chirp->load_base = note.desc.load_base; + else if (tree_find_property(root, "/options/load-base") != NULL) + chirp->load_base = tree_find_integer_property(root, "/options/load-base"); + else + chirp->load_base = CHIRP_LOAD_BASE; + if (tree_find_property(root, "/options/load-base") != NULL) { + if (chirp->load_base != tree_find_integer_property(root, "/options/load-base")) + error("chirp: /options/load-base conflicts with note section\n"); + } + else + tree_parse(root, "/options/load-base 0x%lx", + (unsigned long)chirp->load_base); + + /* now adjust the preliminary firmware addresses to final values */ + chirp->code_ra = chirp->code_offset + chirp->real_base; + chirp->stack_ra = chirp->stack_offset + chirp->real_base; + chirp->htab_ra = chirp->htab_offset + chirp->real_base; + + /* the virtual addresses. In real mode these are real addresses. */ + + chirp->code_va = chirp->code_offset + chirp->virt_base; + chirp->stack_va = chirp->stack_offset + chirp->virt_base; + chirp->htab_va = chirp->htab_offset + chirp->virt_base; + + chirp->code_client_va = chirp->code_va; + chirp->code_client_ra = chirp->code_ra; + + chirp->code_callback_va = chirp->code_client_va + 16; + chirp->code_callback_ra = chirp->code_client_ra + 16; + + chirp->code_loop_va = chirp->code_callback_va + 16; + chirp->code_loop_ra = chirp->code_callback_ra + 16; + + /* initialization */ + + tree_parse(root, "/openprom/init"); + tree_parse(root, "/openprom/init/register"); + tree_parse(root, "/openprom/init/register/0.pc 0x%lx", + (unsigned long)bfd_get_start_address(image)); + tree_parse(root, "/openprom/init/register/pc 0x%lx", + (unsigned long)chirp->code_loop_va); + tree_parse(root, "/openprom/init/register/msr 0x%x", + (msr_machine_check_enable + | (chirp->real_mode + ? 0 + : (msr_instruction_relocate + | msr_data_relocate)) + | (chirp->little_endian + ? (msr_little_endian_mode + | msr_interrupt_little_endian_mode) + : 0) + | (chirp->floating_point_available + ? msr_floating_point_available + : 0) + | (chirp->interrupt_prefix + ? msr_interrupt_prefix + : 0) + )); + tree_parse(root, "/openprom/init/register/sdr1 0x%lx", + (unsigned long)(chirp->htab_ra + | MASK32(16, 22) + | ((chirp->sizeof_htab - 1) >> 16))); + /* make certain that the segment registers map straight through */ + for (i = 0; i < 16; i++) { + tree_parse(root, "/openprom/init/register/sr%d 0x%lx", + i, (unsigned long)i); + } + + /* establish an initial state for all processors */ + + + /* the client interface address */ + tree_parse(root, "/openprom/init/register/r5 0x%lx", + (unsigned long)chirp->code_client_va); + /* a stack */ + tree_parse(root, "/openprom/init/register/sp 0x%lx", + (unsigned long)(chirp->stack_va + chirp->sizeof_stack - 16)); + /* in chrp mode any arguments end up being concatinated */ + tree_parse(root, "/openprom/init/stack/stack-type chirp"); + + + /* client interface - emul-call followed by return instruction */ + + + node = tree_parse(root, "/openprom/init/data@0x%lx", + (unsigned long)chirp->code_client_ra); + tree_parse(node, "./psim,description \"client-interface instruction"); + tree_parse(node, "./real-address 0x%lx", + (unsigned long)chirp->code_client_ra); + tree_parse(node, "./data 0x%lx", + (unsigned long)emul_call_instruction); + + node = tree_parse(root, "/openprom/init/data@0x%lx", + (unsigned long)(chirp->code_client_ra + 4)); + tree_parse(node, "./psim,description \"client-interface return instruction"); + tree_parse(node, "./real-address 0x%lx", + (unsigned long)(chirp->code_client_ra + 4)); + tree_parse(node, "./data 0x%lx", + (unsigned long)emul_blr_instruction); + + + /* return address for client callbacks - an emul-call instruction + that is again followed by a return instruction */ + + + node = tree_parse(root, "/openprom/init/data@0x%lx", + (unsigned long)chirp->code_callback_ra); + tree_parse(node, "./psim,description \"client-callback instruction"); + tree_parse(node, "./real-address 0x%lx", + (unsigned long)chirp->code_callback_ra); + tree_parse(node, "./data 0x%lx", + (unsigned long)emul_call_instruction); + + node = tree_parse(root, "/openprom/init/data@0x%lx", + (unsigned long)(chirp->code_callback_ra + 4)); + tree_parse(node, "./psim,description \"client-callback return instruction"); + tree_parse(node, "./real-address 0x%lx", + (unsigned long)(chirp->code_callback_ra + 4)); + tree_parse(node, "./data 0x%lx", + (unsigned long)emul_blr_instruction); + + /* loop to keep other processors busy */ + + node = tree_parse(root, "/openprom/init/data@0x%lx", + (unsigned long)chirp->code_loop_ra); + tree_parse(node, "./psim,description \"processor busy loop"); + tree_parse(node, "./real-address 0x%lx", + (unsigned long)chirp->code_loop_ra); + tree_parse(node, "./data 0x%lx", + (unsigned long)emul_loop_instruction); + + /* hash table */ + + /* create a hash table */ + + if (!chirp->real_mode) { + node = tree_parse(root, "/openprom/init/htab@0x%lx", + (unsigned long)chirp->htab_ra); + tree_parse(node, "./claim 0"); + tree_parse(node, "./real-address 0x%lx", + (unsigned long)chirp->htab_ra); + tree_parse(node, "./nr-bytes 0x%lx", + (unsigned long)chirp->sizeof_htab); + } + + /* map in the stack */ + + if (!chirp->real_mode) { + node = tree_parse(root, "/openprom/init/htab/pte@0x%lx", + (unsigned long)chirp->stack_ra); + tree_parse(node, "./psim,description \"map in the stack"); + tree_parse(node, "./claim 1"); + tree_parse(node, "./virtual-address 0x%lx", + (unsigned long)chirp->stack_va); + tree_parse(node, "./real-address 0x%lx", + (unsigned long)chirp->stack_ra); + tree_parse(node, "./nr-bytes 0x%lx", + (unsigned long)chirp->sizeof_stack); + tree_parse(node, "./wimg %d", 0x7); + tree_parse(node, "./pp %d", 0x2); + } + + /* map in the chrp openboot callback code */ + + if (!chirp->real_mode) { + node = tree_parse(root, "/openprom/init/htab/pte@0x%lx", + (unsigned long)chirp->code_ra); + tree_parse(node, "./psim,description \"map in chrp openboot callback code"); + tree_parse(node, "./claim 1"); + tree_parse(node, "./virtual-address 0x%lx", + (unsigned long)chirp->code_va); + tree_parse(node, "./real-address 0x%lx", + (unsigned long)chirp->code_ra); + tree_parse(node, "./nr-bytes 0x%lx", + (unsigned long)chirp->sizeof_code); + tree_parse(node, "./wimg %d", 0x7); + tree_parse(node, "./pp %d", 0x2); + } + + /* map in the program to run */ + + if (chirp->real_mode) { + node = tree_parse(node, "/openprom/init/load-binary"); + tree_parse(node, "./psim,description \"load the binary"); + tree_parse(node, "./file-name %s", bfd_get_filename(image)); + tree_parse(node, "./claim 1"); + } + else { + node = tree_parse(root, "/openprom/init/htab/pte@0x%lx", + (unsigned long)chirp->load_base); + tree_parse(node, "./psim,description \"load & map the binary"); + tree_parse(node, "./claim 1"); + tree_parse(node, "./file-name \"%s", bfd_get_filename(image)); + tree_parse(node, "./wimg %d", 0x7); + tree_parse(node, "./pp %d", 0x2); + } + + return chirp; +} + +static void +emul_chirp_init(os_emul_data *emul_data, + int nr_cpus) +{ + emul_data->state = serving; +} + +static int +emul_chirp_instruction_call(cpu *processor, + unsigned_word cia, + unsigned_word ra, + os_emul_data *emul_data) +{ + unsigned_word service_name_addr; + unsigned_word result; + char service_buf[32]; + char *service_name; + chirp_services *service; + + switch (emul_data->state) { + + case serving: + /* we are waiting on an OpenBoot request from the client program + via the client interface */ + if (cia != emul_data->code_client_va) + return 0; + emul_data->return_address = LR; + emul_data->arguments = cpu_registers(processor)->gpr[3]; + /* try to determine what to do */ + service_name_addr = emul_read_word(cpu_registers(processor)->gpr[3], + processor, cia); + service_name = emul_read_string(service_buf, service_name_addr, + sizeof(service_buf), processor, cia); + emul_data->n_args = emul_read_word(emul_data->arguments + sizeof(unsigned_cell), + processor, cia); + emul_data->n_returns = emul_read_word(emul_data->arguments + 2 * sizeof(unsigned_cell), + processor, cia); + /* verify what was passed */ + if (service_name_addr == 0 + || service_name == NULL) { + error("OpenFirmware called with invalid (NULL) service name from 0x%lx with args 0x%lx\n", + (unsigned long)emul_data->return_address, + (unsigned long)emul_data->arguments); + } + if (emul_data->n_args > 6) { /* See iee1275 requirements on nr returns */ + error("OpenFirmware service %s called from 0x%lx with args 0x%lx, too many args (%d)\n", + (unsigned long)emul_data->return_address, + (unsigned long)emul_data->arguments, + emul_data->n_returns); + } + if (emul_data->n_returns > 6) { + error("OpenFirmware service %s called from 0x%lx with args 0x%lx, with too many returns (%d)\n", + (unsigned long)emul_data->return_address, + (unsigned long)emul_data->arguments, + emul_data->n_args); + } + /* look it up */ + TRACE(trace_os_emul, ("%s called from 0x%lx with args 0x%lx\n", + service_name, + (unsigned long)emul_data->return_address, + (unsigned long)emul_data->arguments)); + service = services; + while (service->name != NULL && strcmp(service->name, service_name) != 0) + service++; + /* found or not? */ + if (service->name == NULL) { + error("OpenBoot service `%s' not found\n", service_name); + TRACE(trace_os_emul, ("%s not found\n", service_name)); + cpu_registers(processor)->gpr[3] = -1; + } + else { + emul_data->service = service; + /* call upon it */ + result = service->handler(emul_data, processor, cia); + if (result != 0) + TRACE(trace_os_emul, ("%s aborted with %ld\n", service_name, (long)result)); + cpu_registers(processor)->gpr[3] = result; + } + break; + + default: + error("emul_chirp_instruction_call() unknown internal state\n"); + result = -1; + break; + + } + + /* return to caller - instruction following this is a function return */ + return 1; +} + +const os_emul emul_chirp = { + "chirp", + emul_chirp_create, + emul_chirp_init, + NULL, /*system_call*/ + emul_chirp_instruction_call, + 0 /*data*/ +}; + +#endif diff --git a/sim/ppc/emul_chirp.h b/sim/ppc/emul_chirp.h new file mode 100644 index 00000000000..2265f5c3ee4 --- /dev/null +++ b/sim/ppc/emul_chirp.h @@ -0,0 +1,75 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1997, 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 _EMUL_CHIRP_H_ +#define _EMUL_CHIRP_H_ + +/* EMUL_CHIRP: + + The emulation of the OpenBoot client interface (as defined in 1275) + illustrates how it is possible for PSIM to implement an interface + that is both running in virtual memory and is called using a + standard function call interface. + + The OpenBoot client interface is implemented by using two + instructions: + + client_interface: + <emul_call> + blr + + A client program makes a function call to `client_interface' using + the `bl' instruction. The simulator will then execute the + <emul_call> instruction (which calls emul_chirp) and then the `blr' + which will return to the caller. + + In addition to providing the `client_interface' entry point, while + a client request is being handled, emul_chirp patches (well it will + one day) the data access exception vector with a <emul_call> + instruction. By doing this, emul_chirp is able to catch and handle + any invalid data accesses it makes while emulating a client call. + + When such an exception occures, emul_chirp is able to recover by + restoring the processor and then calling the clients callback + interface so that the client can recover from the data exception. + + Handling this are the emul_chirp states: + + serving---. + / | + Emulation compleated ^ v Client makes call to + - restore int vectors | | emulated interface + ^ v - patch exception vectors + | / + `-emulating-. emulating the request + / | + | v Emulation encounters + Client callback recovers ^ | data access exception + from data exception and | v - re-enable vm + returns. ^ | - call client callback + - restart request | / + `--faulting + */ + + +extern const os_emul emul_chirp; + +#endif diff --git a/sim/ppc/emul_generic.c b/sim/ppc/emul_generic.c new file mode 100644 index 00000000000..1d431750dda --- /dev/null +++ b/sim/ppc/emul_generic.c @@ -0,0 +1,343 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1997, 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 _EMUL_GENERIC_C_ +#define _EMUL_GENERIC_C_ + +#include "emul_generic.h" + +#ifndef STATIC_INLINE_EMUL_GENERIC +#define STATIC_INLINE_EMUL_GENERIC STATIC_INLINE +#endif + + +STATIC_INLINE_EMUL_GENERIC void +emul_syscall_enter(emul_syscall *emul, + int call, + int arg0, + cpu *processor, + unsigned_word cia) +{ + printf_filtered("%d:0x%lx:%s(", + cpu_nr(processor) + 1, + (long)cia, + emul->syscall_descriptor[call].name); +} + + +STATIC_INLINE_EMUL_GENERIC void +emul_syscall_exit(emul_syscall *emul, + int call, + int arg0, + cpu *processor, + unsigned_word cia) +{ + int status = cpu_registers(processor)->gpr[3]; + int error = cpu_registers(processor)->gpr[0]; + printf_filtered(")=%d", status); + if (error > 0 && error < emul->nr_error_names) + printf_filtered("[%s]", emul->error_names[error]); + printf_filtered("\n"); +} + + +INLINE_EMUL_GENERIC unsigned64 +emul_read_gpr64(cpu *processor, + int g) +{ + unsigned32 hi; + unsigned32 lo; + if (CURRENT_TARGET_BYTE_ORDER == BIG_ENDIAN) { + hi = cpu_registers(processor)->gpr[g]; + lo = cpu_registers(processor)->gpr[g+1]; + } + else { + lo = cpu_registers(processor)->gpr[g]; + hi = cpu_registers(processor)->gpr[g+1]; + } + return (INSERTED64(hi, 0, 31) | INSERTED64(lo, 32, 63)); +} + + +INLINE_EMUL_GENERIC void +emul_write_gpr64(cpu *processor, + int g, + unsigned64 val) +{ + unsigned32 hi = EXTRACTED64(val, 0, 31); + unsigned32 lo = EXTRACTED64(val, 32, 63); + if (CURRENT_TARGET_BYTE_ORDER == BIG_ENDIAN) { + cpu_registers(processor)->gpr[g] = hi; + cpu_registers(processor)->gpr[g+1] = lo; + } + else { + cpu_registers(processor)->gpr[g] = lo; + cpu_registers(processor)->gpr[g+1] = hi; + } +} + + +INLINE_EMUL_GENERIC char * +emul_read_string(char *dest, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + unsigned nr_moved = 0; + if (addr == 0) + return NULL; + while (1) { + dest[nr_moved] = vm_data_map_read_1(cpu_data_map(processor), + addr + nr_moved, + processor, cia); + if (dest[nr_moved] == '\0' || nr_moved >= nr_bytes) + break; + nr_moved++; + } + dest[nr_moved] = '\0'; + return dest; +} + + +INLINE_EMUL_GENERIC void +emul_write_status(cpu *processor, + int status, + int errno) +{ + if (status == -1 && errno != 0) { + cpu_registers(processor)->gpr[3] = errno; + CR_SET(0, cr_i_summary_overflow); + } + else { + cpu_registers(processor)->gpr[3] = status; + CR_SET(0, 0); + } +} + + +INLINE_EMUL_GENERIC void +emul_write2_status(cpu *processor, + int status1, + int status2, + int errno) +{ + if (status1 == -1 && errno != 0) { + cpu_registers(processor)->gpr[3] = errno; + CR_SET(0, cr_i_summary_overflow); + } + else { + cpu_registers(processor)->gpr[3] = status1; + cpu_registers(processor)->gpr[4] = status2; + CR_SET(0, 0); + } +} + + +INLINE_EMUL_GENERIC unsigned_word +emul_read_word(unsigned_word addr, + cpu *processor, + unsigned_word cia) +{ + return vm_data_map_read_word(cpu_data_map(processor), + addr, + processor, cia); +} + + +INLINE_EMUL_GENERIC void +emul_write_word(unsigned_word addr, + unsigned_word buf, + cpu *processor, + unsigned_word cia) +{ + vm_data_map_write_word(cpu_data_map(processor), + addr, + buf, + processor, cia); +} + + +INLINE_EMUL_GENERIC void +emul_write_buffer(const void *source, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + int nr_moved; + for (nr_moved = 0; nr_moved < nr_bytes; nr_moved++) { + vm_data_map_write_1(cpu_data_map(processor), + addr + nr_moved, + ((const char*)source)[nr_moved], + processor, cia); + } +} + + +INLINE_EMUL_GENERIC void +emul_read_buffer(void *dest, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + int nr_moved; + for (nr_moved = 0; nr_moved < nr_bytes; nr_moved++) { + ((char*)dest)[nr_moved] = vm_data_map_read_1(cpu_data_map(processor), + addr + nr_moved, + processor, cia); + } +} + + +INLINE_EMUL_GENERIC void +emul_do_system_call(os_emul_data *emul_data, + emul_syscall *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + emul_syscall_handler *handler = NULL; + if (call >= emul->nr_system_calls) + error("do_call() os_emul call %d out-of-range\n", call); + + handler = emul->syscall_descriptor[call].handler; + if (handler == NULL) { + if (emul->syscall_descriptor[call].name) { + error("do_call() unimplemented call %s\n", emul->syscall_descriptor[call].name); + } else { + error("do_call() unimplemented call %d\n", call); + } + } + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + emul_syscall_enter(emul, call, arg0, processor, cia); + + cpu_registers(processor)->gpr[0] = 0; /* default success */ + handler(emul_data, call, arg0, processor, cia); + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + emul_syscall_exit(emul, call, arg0, processor, cia); +} + + +/* default size for the first bank of memory */ + +#ifndef OEA_MEMORY_SIZE +#define OEA_MEMORY_SIZE 0x100000 +#endif + + +/* Add options to the device tree */ + +INLINE_EMUL_GENERIC void +emul_add_tree_options(device *tree, + bfd *image, + const char *emul, + const char *env, + int oea_interrupt_prefix) +{ + int little_endian = 0; + + /* sort out little endian */ + if (tree_find_property(tree, "/options/little-endian?")) + little_endian = tree_find_boolean_property(tree, "/options/little-endian?"); + else { +#ifdef bfd_little_endian /* new bfd */ + little_endian = (image != NULL && bfd_little_endian(image)); +#else + little_endian = (image != NULL && + !image->xvec->byteorder_big_p); +#endif + tree_parse(tree, "/options/little-endian? %s", + little_endian ? "true" : "false"); + } + + /* misc other stuff */ + tree_parse(tree, "/openprom/options/oea-memory-size 0x%x", + OEA_MEMORY_SIZE); + tree_parse(tree, "/openprom/options/oea-interrupt-prefix %d", + oea_interrupt_prefix); + tree_parse(tree, "/openprom/options/smp 1"); + tree_parse(tree, "/openprom/options/env %s", env); + tree_parse(tree, "/openprom/options/os-emul %s", emul); + tree_parse(tree, "/openprom/options/strict-alignment? %s", + (WITH_ALIGNMENT == STRICT_ALIGNMENT) + ? "true" : "false"); + tree_parse(tree, "/openprom/options/floating-point? %s", + WITH_FLOATING_POINT ? "true" : "false"); + tree_parse(tree, "/openprom/options/use-stdio? %s", + ((WITH_STDIO == DO_USE_STDIO + || WITH_STDIO == 0) + ? "true" : "false")); + tree_parse(tree, "/openprom/options/model \"%s", + model_name[WITH_DEFAULT_MODEL]); + tree_parse(tree, "/openprom/options/model-issue %d", + MODEL_ISSUE_IGNORE); + + /* useful options */ +} + +INLINE_EMUL_GENERIC void +emul_add_tree_hardware(device *root) +{ + int i; + int nr_cpus = tree_find_integer_property(root, "/openprom/options/smp"); + + /* sanity check the number of processors */ + if (nr_cpus > MAX_NR_PROCESSORS) + error("Specified number of processors (%d) exceeds the number configured (%d).\n", + nr_cpus, MAX_NR_PROCESSORS); + + /* set the number of address cells (1 or 2) */ + tree_parse(root, "#address-cells %d", WITH_TARGET_WORD_BITSIZE / 32); + + /* add some memory */ + if (tree_find_device(root, "/memory") == NULL) { + unsigned_word memory_size = + tree_find_integer_property(root, "/openprom/options/oea-memory-size"); + const unsigned_word avail_start = 0x3000; + tree_parse(root, "/memory@0/reg 0x0 0x%lx", + (unsigned long)memory_size); + /* reserve the first 0x3000 for the PowerPC interrupt table */ + tree_parse(root, "/memory@0/available 0x%lx 0x%lx", + (unsigned long)avail_start, + (unsigned long)memory_size - avail_start); + } + + /* our processors */ + for (i = 0; i < nr_cpus; i++) { + tree_parse(root, "/cpus/cpu@%d/cpu-nr %d", i, i); + } + + /* the debugging pal - hide it in the openprom and don't attach it + to any bus */ + tree_parse(root, "/openprom/pal"); + + /* chosen etc */ + tree_parse(root, "/chosen/stdin */openprom/pal"); + tree_parse(root, "/chosen/stdout !/chosen/stdin"); + tree_parse(root, "/chosen/memory */memory"); +} + +#endif /* _EMUL_GENERIC_C_ */ diff --git a/sim/ppc/emul_generic.h b/sim/ppc/emul_generic.h new file mode 100644 index 00000000000..a86026e9b9d --- /dev/null +++ b/sim/ppc/emul_generic.h @@ -0,0 +1,178 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1996, 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 _EMUL_GENERIC_H_ +#define _EMUL_GENERIC_H_ + +#include "cpu.h" +#include "idecode.h" +#include "os_emul.h" + +#include "tree.h" + +#include "bfd.h" + +#ifndef INLINE_EMUL_GENERIC +#define INLINE_EMUL_GENERIC +#endif + +/* various PowerPC instructions for writing into memory */ +enum { + emul_call_instruction = 0x1, + emul_loop_instruction = 0x48000000, /* branch to . */ + emul_rfi_instruction = 0x4c000064, + emul_blr_instruction = 0x4e800020, +}; + + +/* emulation specific data */ + +typedef struct _os_emul_data os_emul_data; + +typedef os_emul_data *(os_emul_create_handler) + (device *tree, + bfd *image, + const char *emul_name); +typedef void (os_emul_init_handler) + (os_emul_data *emul_data, + int nr_cpus); +typedef void (os_emul_system_call_handler) + (cpu *processor, + unsigned_word cia, + os_emul_data *emul_data); +typedef int (os_emul_instruction_call_handler) + (cpu *processor, + unsigned_word cia, + unsigned_word ra, + os_emul_data *emul_data); + +struct _os_emul { + const char *name; + os_emul_create_handler *create; + os_emul_init_handler *init; + os_emul_system_call_handler *system_call; + os_emul_instruction_call_handler *instruction_call; + os_emul_data *data; +}; + + +/* One class of emulation - system call is pretty general, provide a + common template for implementing this */ + +typedef struct _emul_syscall emul_syscall; +typedef struct _emul_syscall_descriptor emul_syscall_descriptor; + +typedef void (emul_syscall_handler) + (os_emul_data *emul_data, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia); + +struct _emul_syscall_descriptor { + emul_syscall_handler *handler; + const char *name; +}; + +struct _emul_syscall { + emul_syscall_descriptor *syscall_descriptor; + int nr_system_calls; + char **error_names; + int nr_error_names; + char **signal_names; + int nr_signal_names; +}; + + +INLINE_EMUL_GENERIC void emul_do_system_call +(os_emul_data *emul_data, + emul_syscall *syscall, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia); + + +INLINE_EMUL_GENERIC unsigned64 emul_read_gpr64 +(cpu *processor, + int g); + +INLINE_EMUL_GENERIC void emul_write_gpr64 +(cpu *processor, + int g, + unsigned64 val); + +INLINE_EMUL_GENERIC void emul_write_status +(cpu *processor, + int status, + int errno); + +INLINE_EMUL_GENERIC void emul_write2_status +(cpu *processor, + int status1, + int status2, + int errno); + +INLINE_EMUL_GENERIC char *emul_read_string +(char *dest, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia); + +INLINE_EMUL_GENERIC unsigned_word emul_read_word +(unsigned_word addr, + cpu *processor, + unsigned_word cia); + +INLINE_EMUL_GENERIC void emul_write_word +(unsigned_word addr, + unsigned_word buf, + cpu *processor, + unsigned_word cia); + +INLINE_EMUL_GENERIC void emul_read_buffer +(void *dest, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia); + +INLINE_EMUL_GENERIC void emul_write_buffer +(const void *source, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia); + +/* Simplify the construction of device trees */ + +INLINE_EMUL_GENERIC void emul_add_tree_options +(device *tree, + bfd *image, + const char *emul, + const char *env, + int oea_interrupt_prefix); + +INLINE_EMUL_GENERIC void emul_add_tree_hardware +(device *tree); + +#endif /* _EMUL_GENERIC_H_ */ diff --git a/sim/ppc/emul_netbsd.c b/sim/ppc/emul_netbsd.c new file mode 100644 index 00000000000..e2064a45afb --- /dev/null +++ b/sim/ppc/emul_netbsd.c @@ -0,0 +1,1439 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1998, 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 _EMUL_NETBSD_C_ +#define _EMUL_NETBSD_C_ + + +/* Note: this module is called via a table. There is no benefit in + making it inline */ + +#include "emul_generic.h" +#include "emul_netbsd.h" + +#ifdef HAVE_STRING_H +#include <string.h> +#else +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <signal.h> +#include <fcntl.h> +#include <sys/errno.h> +#include <sys/param.h> +#include <sys/time.h> + +#ifdef HAVE_GETRUSAGE +#ifndef HAVE_SYS_RESOURCE_H +#undef HAVE_GETRUSAGE +#endif +#endif + +#ifdef HAVE_GETRUSAGE +#include <sys/resource.h> +int getrusage(); +#endif + +#if HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif + +#if HAVE_DIRENT_H +# include <dirent.h> +# define NAMLEN(dirent) strlen((dirent)->d_name) +#else +# define dirent direct +# define NAMLEN(dirent) (dirent)->d_namlen +# if HAVE_SYS_NDIR_H +# include <sys/ndir.h> +# endif +# if HAVE_SYS_DIR_H +# include <sys/dir.h> +# endif +# if HAVE_NDIR_H +# include <ndir.h> +# endif +#endif + +#ifdef HAVE_UNISTD_H +#undef MAXPATHLEN /* sys/param.h might define this also */ +#include <unistd.h> +#endif + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#define WITH_NetBSD_HOST (NetBSD >= 199306) +#if WITH_NetBSD_HOST /* here NetBSD as that is what we're emulating */ +#include <sys/syscall.h> /* FIXME - should not be including this one */ +#include <sys/sysctl.h> +extern int getdirentries(int fd, char *buf, int nbytes, long *basep); +#else + +/* If this is not netbsd, don't allow fstatfs or getdirentries at this time */ +#undef HAVE_FSTATFS +#undef HAVE_GETDIRENTRIES +#endif + +#if (BSD < 199306) /* here BSD as just a bug */ +extern int errno; +#endif + +#ifndef STATIC_INLINE_EMUL_NETBSD +#define STATIC_INLINE_EMUL_NETBSD STATIC_INLINE +#endif + + +#if WITH_NetBSD_HOST +#define SYS(X) ASSERT(call == (SYS_##X)) +#else +#define SYS(X) +#endif + +#if WITH_NetBSD_HOST && (PATH_MAX != 1024) +#error "PATH_MAX not 1024" +#elif !defined(PATH_MAX) +#define PATH_MAX 1024 +#endif + + +/* EMULATION + + NetBSD - Emulation of user programs for NetBSD/PPC + + DESCRIPTION + + */ + + +/* NetBSD's idea of what is needed to implement emulations */ + +struct _os_emul_data { + device *vm; + emul_syscall *syscalls; +}; + + + +STATIC_INLINE_EMUL_NETBSD void +write_stat(unsigned_word addr, + struct stat buf, + cpu *processor, + unsigned_word cia) +{ + 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_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); */ +#ifdef AC_STRUCT_ST_RDEV + H2T(buf.st_rdev); +#endif +#ifdef AC_STRUCT_ST_BLKSIZE + H2T(buf.st_blksize); +#endif +#ifdef AC_STRUCT_ST_BLOCKS + H2T(buf.st_blocks); +#endif +#if WITH_NetBSD_HOST + H2T(buf.st_flags); + H2T(buf.st_gen); +#endif + emul_write_buffer(&buf, addr, sizeof(buf), processor, cia); +} + + +#ifdef HAVE_FSTATFS +STATIC_INLINE_EMUL_NETBSD void +write_statfs(unsigned_word addr, + struct statfs buf, + cpu *processor, + unsigned_word cia) +{ + H2T(buf.f_type); + H2T(buf.f_flags); + H2T(buf.f_bsize); + H2T(buf.f_iosize); + H2T(buf.f_blocks); + H2T(buf.f_bfree); + H2T(buf.f_bavail); + H2T(buf.f_files); + H2T(buf.f_ffree); + H2T(buf.f_fsid.val[0]); + H2T(buf.f_fsid.val[1]); + H2T(buf.f_owner); + /* f_spare[4]; */ + /* f_fstypename[MFSNAMELEN]; */ + /* f_mntonname[MNAMELEN]; */ + /* f_mntfromname[MNAMELEN]; */ + emul_write_buffer(&buf, addr, sizeof(buf), processor, cia); +} +#endif + + +STATIC_INLINE_EMUL_NETBSD void +write_timeval(unsigned_word addr, + struct timeval t, + cpu *processor, + unsigned_word cia) +{ + H2T(t.tv_sec); + H2T(t.tv_usec); + emul_write_buffer(&t, addr, sizeof(t), processor, cia); +} + + +STATIC_INLINE_EMUL_NETBSD void +write_timezone(unsigned_word addr, + struct timezone tz, + cpu *processor, + unsigned_word cia) +{ + H2T(tz.tz_minuteswest); + H2T(tz.tz_dsttime); + emul_write_buffer(&tz, addr, sizeof(tz), processor, cia); +} + + +#ifdef HAVE_GETDIRENTRIES +STATIC_INLINE_EMUL_NETBSD void +write_direntries(unsigned_word addr, + char *buf, + int nbytes, + cpu *processor, + unsigned_word cia) +{ + while (nbytes > 0) { + struct dirent *out; + struct dirent *in = (struct dirent*)buf; + ASSERT(in->d_reclen <= nbytes); + out = (struct dirent*)zalloc(in->d_reclen); + memcpy(out/*dest*/, in/*src*/, in->d_reclen); + H2T(out->d_fileno); + H2T(out->d_reclen); + H2T(out->d_type); + H2T(out->d_namlen); + emul_write_buffer(out, addr, in->d_reclen, processor, cia); + nbytes -= in->d_reclen; + addr += in->d_reclen; + buf += in->d_reclen; + zfree(out); + } +} +#endif + + +#ifdef HAVE_GETRUSAGE +STATIC_INLINE_EMUL_NETBSD void +write_rusage(unsigned_word addr, + struct rusage rusage, + cpu *processor, + unsigned_word cia) +{ + H2T(rusage.ru_utime.tv_sec); /* user time used */ + H2T(rusage.ru_utime.tv_usec); + H2T(rusage.ru_stime.tv_sec); /* system time used */ + H2T(rusage.ru_stime.tv_usec); + H2T(rusage.ru_maxrss); /* integral max resident set size */ + H2T(rusage.ru_ixrss); /* integral shared text memory size */ + H2T(rusage.ru_idrss); /* integral unshared data size */ + H2T(rusage.ru_isrss); /* integral unshared stack size */ + H2T(rusage.ru_minflt); /* page reclaims */ + H2T(rusage.ru_majflt); /* page faults */ + H2T(rusage.ru_nswap); /* swaps */ + H2T(rusage.ru_inblock); /* block input operations */ + H2T(rusage.ru_oublock); /* block output operations */ + H2T(rusage.ru_msgsnd); /* messages sent */ + H2T(rusage.ru_msgrcv); /* messages received */ + H2T(rusage.ru_nsignals); /* signals received */ + H2T(rusage.ru_nvcsw); /* voluntary context switches */ + H2T(rusage.ru_nivcsw); /* involuntary context switches */ + emul_write_buffer(&rusage, addr, sizeof(rusage), processor, cia); +} +#endif + +static void +do_exit(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + int status = (int)cpu_registers(processor)->gpr[arg0]; + SYS(exit); + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("%d)\n", status); + + cpu_halt(processor, cia, was_exited, status); +} + + +static void +do_read(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + void *scratch_buffer; + int d = (int)cpu_registers(processor)->gpr[arg0]; + unsigned_word buf = cpu_registers(processor)->gpr[arg0+1]; + int nbytes = cpu_registers(processor)->gpr[arg0+2]; + int status; + SYS(read); + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("%d, 0x%lx, %d", d, (long)buf, nbytes); + + /* get a tempoary bufer */ + scratch_buffer = zalloc(nbytes); + + /* check if buffer exists by reading it */ + emul_read_buffer(scratch_buffer, buf, nbytes, processor, cia); + + /* read */ +#if 0 + if (d == 0) { + status = fread (scratch_buffer, 1, nbytes, stdin); + if (status == 0 && ferror (stdin)) + status = -1; + } +#endif + status = read (d, scratch_buffer, nbytes); + + emul_write_status(processor, status, errno); + if (status > 0) + emul_write_buffer(scratch_buffer, buf, status, processor, cia); + + zfree(scratch_buffer); +} + + +static void +do_write(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + void *scratch_buffer = NULL; + int d = (int)cpu_registers(processor)->gpr[arg0]; + unsigned_word buf = cpu_registers(processor)->gpr[arg0+1]; + int nbytes = cpu_registers(processor)->gpr[arg0+2]; + int status; + SYS(write); + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("%d, 0x%lx, %d", d, (long)buf, nbytes); + + /* get a tempoary bufer */ + scratch_buffer = zalloc(nbytes); /* FIXME - nbytes == 0 */ + + /* copy in */ + emul_read_buffer(scratch_buffer, buf, nbytes, + processor, cia); + + /* write */ + status = write(d, scratch_buffer, nbytes); + emul_write_status(processor, status, errno); + zfree(scratch_buffer); + + flush_stdoutput(); +} + + +static void +do_open(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + unsigned_word path_addr = cpu_registers(processor)->gpr[arg0]; + char path_buf[PATH_MAX]; + char *path = emul_read_string(path_buf, path_addr, PATH_MAX, processor, cia); + int flags = (int)cpu_registers(processor)->gpr[arg0+1]; + int mode = (int)cpu_registers(processor)->gpr[arg0+2]; + int status; + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("0x%lx [%s], 0x%x, 0x%x", (long)path_addr, path, flags, mode); + + SYS(open); + + /* Can't combine these statements, cuz open sets errno. */ + status = open(path, flags, mode); + emul_write_status(processor, status, errno); +} + + +static void +do_close(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + int d = (int)cpu_registers(processor)->gpr[arg0]; + int status; + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("%d", d); + + SYS(close); + + /* Can't combine these statements, cuz close sets errno. */ + status = close(d); + emul_write_status(processor, status, errno); +} + + +static void +do_break(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + /* just pass this onto the `vm' device */ + unsigned_word new_break = cpu_registers(processor)->gpr[arg0]; + int status; + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("0x%lx", (long)cpu_registers(processor)->gpr[arg0]); + + SYS(break); + status = device_ioctl(emul->vm, + processor, + cia, + device_ioctl_break, + new_break); /*ioctl-data*/ + emul_write_status(processor, 0, status); +} + + +#ifndef HAVE_GETPID +#define do_getpid 0 +#else +static void +do_getpid(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + SYS(getpid); + emul_write_status(processor, (int)getpid(), 0); +} +#endif + +#ifndef HAVE_GETUID +#define do_getuid 0 +#else +static void +do_getuid(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + SYS(getuid); + emul_write_status(processor, (int)getuid(), 0); +} +#endif + +#ifndef HAVE_GETEUID +#define do_geteuid 0 +#else +static void +do_geteuid(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + SYS(geteuid); + emul_write_status(processor, (int)geteuid(), 0); +} +#endif + +#ifndef HAVE_KILL +#define do_kill 0 +#else +static void +do_kill(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + pid_t pid = cpu_registers(processor)->gpr[arg0]; + int sig = cpu_registers(processor)->gpr[arg0+1]; + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("%d, %d", (int)pid, sig); + + SYS(kill); + printf_filtered("SYS_kill at 0x%lx - more to this than just being killed\n", + (long)cia); + cpu_halt(processor, cia, was_signalled, sig); +} +#endif + +#ifndef HAVE_DUP +#define do_dup 0 +#else +static void +do_dup(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + int oldd = cpu_registers(processor)->gpr[arg0]; + int status = dup(oldd); + int err = errno; + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("%d", oldd); + + SYS(dup); + emul_write_status(processor, status, err); +} +#endif + +#ifndef HAVE_GETEGID +#define do_getegid 0 +#else +static void +do_getegid(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + SYS(getegid); + emul_write_status(processor, (int)getegid(), 0); +} +#endif + +#ifndef HAVE_GETGID +#define do_getgid 0 +#else +static void +do_getgid(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + SYS(getgid); + emul_write_status(processor, (int)getgid(), 0); +} +#endif + +#ifndef HAVE_SIGPROCMASK +#define do_sigprocmask 0 +#else +static void +do_sigprocmask(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + natural_word how = cpu_registers(processor)->gpr[arg0]; + unsigned_word set = cpu_registers(processor)->gpr[arg0+1]; + unsigned_word oset = cpu_registers(processor)->gpr[arg0+2]; + SYS(sigprocmask); + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("%ld, 0x%ld, 0x%ld", (long)how, (long)set, (long)oset); + + emul_write_status(processor, 0, 0); + cpu_registers(processor)->gpr[4] = set; +} +#endif + +#ifndef HAVE_IOCTL +#define do_ioctl 0 +#else +static void +do_ioctl(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + int d = cpu_registers(processor)->gpr[arg0]; + unsigned request = cpu_registers(processor)->gpr[arg0+1]; + unsigned_word argp_addr = cpu_registers(processor)->gpr[arg0+2]; + +#if !WITH_NetBSD_HOST + cpu_registers(processor)->gpr[arg0] = 0; /* just succeed */ +#else + unsigned dir = request & IOC_DIRMASK; + int status; + SYS(ioctl); + /* what we haven't done */ + if (dir & IOC_IN /* write into the io device */ + || dir & IOC_OUT + || !(dir & IOC_VOID)) + error("do_ioctl() read or write of parameter not implemented\n"); + status = ioctl(d, request, NULL); + emul_write_status(processor, status, errno); +#endif + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("%d, 0x%x, 0x%lx", d, request, (long)argp_addr); +} +#endif + +#ifndef HAVE_UMASK +#define do_umask 0 +#else +static void +do_umask(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + int mask = cpu_registers(processor)->gpr[arg0]; + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("0%o", mask); + + SYS(umask); + emul_write_status(processor, umask(mask), 0); +} +#endif + +#ifndef HAVE_DUP2 +#define do_dup2 0 +#else +static void +do_dup2(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + int oldd = cpu_registers(processor)->gpr[arg0]; + int newd = cpu_registers(processor)->gpr[arg0+1]; + int status = dup2(oldd, newd); + int err = errno; + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("%d, %d", oldd, newd); + + SYS(dup2); + emul_write_status(processor, status, err); +} +#endif + +#ifndef HAVE_FCNTL +#define do_fcntl 0 +#else +static void +do_fcntl(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + int fd = cpu_registers(processor)->gpr[arg0]; + int cmd = cpu_registers(processor)->gpr[arg0+1]; + int arg = cpu_registers(processor)->gpr[arg0+2]; + int status; + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("%d, %d, %d", fd, cmd, arg); + + SYS(fcntl); + status = fcntl(fd, cmd, arg); + emul_write_status(processor, status, errno); +} +#endif + +#ifndef HAVE_GETTIMEOFDAY +#define do_gettimeofday 0 +#else +static void +do_gettimeofday(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + unsigned_word t_addr = cpu_registers(processor)->gpr[arg0]; + unsigned_word tz_addr = cpu_registers(processor)->gpr[arg0+1]; + struct timeval t; + struct timezone tz; + int status = gettimeofday((t_addr != 0 ? &t : NULL), + (tz_addr != 0 ? &tz : NULL)); + int err = errno; + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("0x%lx, 0x%lx", (long)t_addr, (long)tz_addr); + + SYS(gettimeofday); + emul_write_status(processor, status, err); + if (status == 0) { + if (t_addr != 0) + write_timeval(t_addr, t, processor, cia); + if (tz_addr != 0) + write_timezone(tz_addr, tz, processor, cia); + } +} +#endif + +#ifndef HAVE_GETRUSAGE +#define do_getrusage 0 +#else +static void +do_getrusage(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + int who = cpu_registers(processor)->gpr[arg0]; + unsigned_word rusage_addr = cpu_registers(processor)->gpr[arg0+1]; + struct rusage rusage; + int status = getrusage(who, (rusage_addr != 0 ? &rusage : NULL)); + int err = errno; + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("%d, 0x%lx", who, (long)rusage_addr); + + SYS(getrusage); + emul_write_status(processor, status, err); + if (status == 0) { + if (rusage_addr != 0) + write_rusage(rusage_addr, rusage, processor, cia); + } +} +#endif + + +#ifndef HAVE_FSTATFS +#define do_fstatfs 0 +#else +static void +do_fstatfs(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + int fd = cpu_registers(processor)->gpr[arg0]; + unsigned_word buf_addr = cpu_registers(processor)->gpr[arg0+1]; + struct statfs buf; + int status; + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("%d, 0x%lx", fd, (long)buf_addr); + + SYS(fstatfs); + status = fstatfs(fd, (buf_addr == 0 ? NULL : &buf)); + emul_write_status(processor, status, errno); + if (status == 0) { + if (buf_addr != 0) + write_statfs(buf_addr, buf, processor, cia); + } +} +#endif + +#ifndef HAVE_STAT +#define do_stat 0 +#else +static void +do_stat(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + char path_buf[PATH_MAX]; + unsigned_word path_addr = cpu_registers(processor)->gpr[arg0]; + unsigned_word stat_buf_addr = cpu_registers(processor)->gpr[arg0+1]; + char *path = emul_read_string(path_buf, path_addr, PATH_MAX, processor, cia); + struct stat buf; + int status; + SYS(stat); + status = stat(path, &buf); + emul_write_status(processor, status, errno); + if (status == 0) + write_stat(stat_buf_addr, buf, processor, cia); +} +#endif + +#ifndef HAVE_FSTAT +#define do_fstat 0 +#else +static void +do_fstat(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + int fd = cpu_registers(processor)->gpr[arg0]; + unsigned_word stat_buf_addr = cpu_registers(processor)->gpr[arg0+1]; + struct stat buf; + int status; + SYS(fstat); + /* Can't combine these statements, cuz fstat sets errno. */ + status = fstat(fd, &buf); + emul_write_status(processor, status, errno); + write_stat(stat_buf_addr, buf, processor, cia); +} +#endif + +#ifndef HAVE_LSTAT +#define do_lstat 0 +#else +static void +do_lstat(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + char path_buf[PATH_MAX]; + unsigned_word path_addr = cpu_registers(processor)->gpr[arg0]; + char *path = emul_read_string(path_buf, path_addr, PATH_MAX, processor, cia); + unsigned_word stat_buf_addr = cpu_registers(processor)->gpr[arg0+1]; + struct stat buf; + int status; + SYS(lstat); + /* Can't combine these statements, cuz lstat sets errno. */ + status = lstat(path, &buf); + emul_write_status(processor, status, errno); + write_stat(stat_buf_addr, buf, processor, cia); +} +#endif + +#ifndef HAVE_GETDIRENTRIES +#define do_getdirentries 0 +#else +static void +do_getdirentries(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + int fd = cpu_registers(processor)->gpr[arg0]; + unsigned_word buf_addr = cpu_registers(processor)->gpr[arg0+1]; + char *buf; + int nbytes = cpu_registers(processor)->gpr[arg0+2]; + unsigned_word basep_addr = cpu_registers(processor)->gpr[arg0+3]; + long basep; + int status; + SYS(getdirentries); + if (buf_addr != 0 && nbytes >= 0) + buf = zalloc(nbytes); + else + buf = NULL; + status = getdirentries(fd, + (buf_addr == 0 ? NULL : buf), + nbytes, + (basep_addr == 0 ? NULL : &basep)); + emul_write_status(processor, status, errno); + if (basep_addr != 0) + emul_write_word(basep_addr, basep, processor, cia); + if (status > 0) + write_direntries(buf_addr, buf, status, processor, cia); + if (buf != NULL) + zfree(buf); +} +#endif + + +static void +do___syscall(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + SYS(__syscall); + emul_do_system_call(emul, + emul->syscalls, + cpu_registers(processor)->gpr[arg0], + arg0 + 1, + processor, + cia); +} + +#ifndef HAVE_LSEEK +#define do_lseek 0 +#else +static void +do_lseek(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + int fildes = cpu_registers(processor)->gpr[arg0]; + off_t offset = emul_read_gpr64(processor, arg0+2); + int whence = cpu_registers(processor)->gpr[arg0+4]; + off_t status; + SYS(lseek); + status = lseek(fildes, offset, whence); + if (status == -1) + emul_write_status(processor, -1, errno); + else { + emul_write_status(processor, 0, 0); /* success */ + emul_write_gpr64(processor, 3, status); + } +} +#endif + +static void +do___sysctl(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + /* call the arguments by their real name */ + unsigned_word name = cpu_registers(processor)->gpr[arg0]; + natural_word namelen = cpu_registers(processor)->gpr[arg0+1]; + unsigned_word oldp = cpu_registers(processor)->gpr[arg0+2]; + unsigned_word oldlenp = cpu_registers(processor)->gpr[arg0+3]; + natural_word oldlen; + natural_word mib; + natural_word int_val; + SYS(__sysctl); + + /* pluck out the management information base id */ + if (namelen < 1) + error("system_call()SYS___sysctl bad name[0]\n"); + mib = vm_data_map_read_word(cpu_data_map(processor), + name, + processor, + cia); + name += sizeof(mib); + + /* see what to do with it ... */ + switch ((int)mib) { + case 6/*CTL_HW*/: +#if WITH_NetBSD_HOST && (CTL_HW != 6) +# error "CTL_HW" +#endif + if (namelen < 2) + error("system_call()SYS___sysctl - CTL_HW - bad name[1]\n"); + mib = vm_data_map_read_word(cpu_data_map(processor), + name, + processor, + cia); + name += sizeof(mib); + switch ((int)mib) { + case 7/*HW_PAGESIZE*/: +#if WITH_NetBSD_HOST && (HW_PAGESIZE != 7) +# error "HW_PAGESIZE" +#endif + oldlen = vm_data_map_read_word(cpu_data_map(processor), + oldlenp, + processor, + cia); + if (sizeof(natural_word) > oldlen) + error("system_call()sysctl - CTL_HW.HW_PAGESIZE - to small\n"); + int_val = 8192; + oldlen = sizeof(int_val); + emul_write_word(oldp, int_val, processor, cia); + emul_write_word(oldlenp, oldlen, processor, cia); + break; + default: + error("sysctl() CTL_HW.%d unknown\n", mib); + break; + } + break; + default: + error("sysctl() name[0]=%d unknown\n", (int)mib); + break; + } + emul_write_status(processor, 0, 0); /* always succeed */ +} + + + +static emul_syscall_descriptor netbsd_descriptors[] = { + /* 0 */ { 0, "syscall" }, + /* 1 */ { do_exit, "exit" }, + /* 2 */ { 0, "fork" }, + /* 3 */ { do_read, "read" }, + /* 4 */ { do_write, "write" }, + /* 5 */ { do_open, "open" }, + /* 6 */ { do_close, "close" }, + /* 7 */ { 0, "wait4" }, + { 0, }, /* 8 is old creat */ + /* 9 */ { 0, "link" }, + /* 10 */ { 0, "unlink" }, + { 0, }, /* 11 is obsolete execv */ + /* 12 */ { 0, "chdir" }, + /* 13 */ { 0, "fchdir" }, + /* 14 */ { 0, "mknod" }, + /* 15 */ { 0, "chmod" }, + /* 16 */ { 0, "chown" }, + /* 17 */ { do_break, "break" }, + /* 18 */ { 0, "getfsstat" }, + { 0, }, /* 19 is old lseek */ + /* 20 */ { do_getpid, "getpid" }, + /* 21 */ { 0, "mount" }, + /* 22 */ { 0, "unmount" }, + /* 23 */ { 0, "setuid" }, + /* 24 */ { do_getuid, "getuid" }, + /* 25 */ { do_geteuid, "geteuid" }, + /* 26 */ { 0, "ptrace" }, + /* 27 */ { 0, "recvmsg" }, + /* 28 */ { 0, "sendmsg" }, + /* 29 */ { 0, "recvfrom" }, + /* 30 */ { 0, "accept" }, + /* 31 */ { 0, "getpeername" }, + /* 32 */ { 0, "getsockname" }, + /* 33 */ { 0, "access" }, + /* 34 */ { 0, "chflags" }, + /* 35 */ { 0, "fchflags" }, + /* 36 */ { 0, "sync" }, + /* 37 */ { do_kill, "kill" }, + { 0, }, /* 38 is old stat */ + /* 39 */ { 0, "getppid" }, + { 0, }, /* 40 is old lstat */ + /* 41 */ { do_dup, "dup" }, + /* 42 */ { 0, "pipe" }, + /* 43 */ { do_getegid, "getegid" }, + /* 44 */ { 0, "profil" }, + /* 45 */ { 0, "ktrace" }, + /* 46 */ { 0, "sigaction" }, + /* 47 */ { do_getgid, "getgid" }, + /* 48 */ { do_sigprocmask, "sigprocmask" }, + /* 49 */ { 0, "getlogin" }, + /* 50 */ { 0, "setlogin" }, + /* 51 */ { 0, "acct" }, + /* 52 */ { 0, "sigpending" }, + /* 53 */ { 0, "sigaltstack" }, + /* 54 */ { do_ioctl, "ioctl" }, + /* 55 */ { 0, "reboot" }, + /* 56 */ { 0, "revoke" }, + /* 57 */ { 0, "symlink" }, + /* 58 */ { 0, "readlink" }, + /* 59 */ { 0, "execve" }, + /* 60 */ { do_umask, "umask" }, + /* 61 */ { 0, "chroot" }, + { 0, }, /* 62 is old fstat */ + { 0, }, /* 63 is old getkerninfo */ + { 0, }, /* 64 is old getpagesize */ + /* 65 */ { 0, "msync" }, + /* 66 */ { 0, "vfork" }, + { 0, }, /* 67 is obsolete vread */ + { 0, }, /* 68 is obsolete vwrite */ + /* 69 */ { 0, "sbrk" }, + /* 70 */ { 0, "sstk" }, + { 0, }, /* 71 is old mmap */ + /* 72 */ { 0, "vadvise" }, + /* 73 */ { 0, "munmap" }, + /* 74 */ { 0, "mprotect" }, + /* 75 */ { 0, "madvise" }, + { 0, }, /* 76 is obsolete vhangup */ + { 0, }, /* 77 is obsolete vlimit */ + /* 78 */ { 0, "mincore" }, + /* 79 */ { 0, "getgroups" }, + /* 80 */ { 0, "setgroups" }, + /* 81 */ { 0, "getpgrp" }, + /* 82 */ { 0, "setpgid" }, + /* 83 */ { 0, "setitimer" }, + { 0, }, /* 84 is old wait */ + /* 85 */ { 0, "swapon" }, + /* 86 */ { 0, "getitimer" }, + { 0, }, /* 87 is old gethostname */ + { 0, }, /* 88 is old sethostname */ + { 0, }, /* 89 is old getdtablesize */ + { do_dup2, "dup2" }, + { 0, }, /* 91 */ + /* 92 */ { do_fcntl, "fcntl" }, + /* 93 */ { 0, "select" }, + { 0, }, /* 94 */ + /* 95 */ { 0, "fsync" }, + /* 96 */ { 0, "setpriority" }, + /* 97 */ { 0, "socket" }, + /* 98 */ { 0, "connect" }, + { 0, }, /* 99 is old accept */ + /* 100 */ { 0, "getpriority" }, + { 0, }, /* 101 is old send */ + { 0, }, /* 102 is old recv */ + /* 103 */ { 0, "sigreturn" }, + /* 104 */ { 0, "bind" }, + /* 105 */ { 0, "setsockopt" }, + /* 106 */ { 0, "listen" }, + { 0, }, /* 107 is obsolete vtimes */ + { 0, }, /* 108 is old sigvec */ + { 0, }, /* 109 is old sigblock */ + { 0, }, /* 110 is old sigsetmask */ + /* 111 */ { 0, "sigsuspend" }, + { 0, }, /* 112 is old sigstack */ + { 0, }, /* 113 is old recvmsg */ + { 0, }, /* 114 is old sendmsg */ + /* - is obsolete vtrace */ { 0, "vtrace 115" }, + /* 116 */ { do_gettimeofday, "gettimeofday" }, + /* 117 */ { do_getrusage, "getrusage" }, + /* 118 */ { 0, "getsockopt" }, + /* 119 */ { 0, "resuba" }, + /* 120 */ { 0, "readv" }, + /* 121 */ { 0, "writev" }, + /* 122 */ { 0, "settimeofday" }, + /* 123 */ { 0, "fchown" }, + /* 124 */ { 0, "fchmod" }, + { 0, }, /* 125 is old recvfrom */ + { 0, }, /* 126 is old setreuid */ + { 0, }, /* 127 is old setregid */ + /* 128 */ { 0, "rename" }, + { 0, }, /* 129 is old truncate */ + { 0, }, /* 130 is old ftruncate */ + /* 131 */ { 0, "flock" }, + /* 132 */ { 0, "mkfifo" }, + /* 133 */ { 0, "sendto" }, + /* 134 */ { 0, "shutdown" }, + /* 135 */ { 0, "socketpair" }, + /* 136 */ { 0, "mkdir" }, + /* 137 */ { 0, "rmdir" }, + /* 138 */ { 0, "utimes" }, + { 0, }, /* 139 is obsolete 4.2 sigreturn */ + /* 140 */ { 0, "adjtime" }, + { 0, }, /* 141 is old getpeername */ + { 0, }, /* 142 is old gethostid */ + { 0, }, /* 143 is old sethostid */ + { 0, }, /* 144 is old getrlimit */ + { 0, }, /* 145 is old setrlimit */ + { 0, }, /* 146 is old killpg */ + /* 147 */ { 0, "setsid" }, + /* 148 */ { 0, "quotactl" }, + { 0, }, /* 149 is old quota */ + { 0, }, /* 150 is old getsockname */ + { 0, }, /* 151 */ + { 0, }, /* 152 */ + { 0, }, /* 153 */ + { 0, }, /* 154 */ + /* 155 */ { 0, "nfssvc" }, + { 0, }, /* 156 is old getdirentries */ + /* 157 */ { 0, "statfs" }, + /* 158 */ { do_fstatfs, "fstatfs" }, + { 0, }, /* 159 */ + { 0, }, /* 160 */ + /* 161 */ { 0, "getfh" }, + { 0, }, /* 162 is old getdomainname */ + { 0, }, /* 163 is old setdomainname */ + { 0, }, /* 164 is old uname */ + /* 165 */ { 0, "sysarch" }, + { 0, }, /* 166 */ + { 0, }, /* 167 */ + { 0, }, /* 168 */ + /* 169 */ { 0, "semsys" }, + /* 170 */ { 0, "msgsys" }, + /* 171 */ { 0, "shmsys" }, + { 0, }, /* 172 */ + { 0, }, /* 173 */ + { 0, }, /* 174 */ + { 0, }, /* 175 */ + { 0, }, /* 176 */ + { 0, }, /* 177 */ + { 0, }, /* 178 */ + { 0, }, /* 179 */ + { 0, }, /* 180 */ + /* 181 */ { 0, "setgid" }, + /* 182 */ { 0, "setegid" }, + /* 183 */ { 0, "seteuid" }, + /* 184 */ { 0, "lfs_bmapv" }, + /* 185 */ { 0, "lfs_markv" }, + /* 186 */ { 0, "lfs_segclean" }, + /* 187 */ { 0, "lfs_segwait" }, + /* 188 */ { do_stat, "stat" }, + /* 189 */ { do_fstat, "fstat" }, + /* 190 */ { do_lstat, "lstat" }, + /* 191 */ { 0, "pathconf" }, + /* 192 */ { 0, "fpathconf" }, + { 0, }, /* 193 */ + /* 194 */ { 0, "getrlimit" }, + /* 195 */ { 0, "setrlimit" }, + /* 196 */ { do_getdirentries, "getdirentries" }, + /* 197 */ { 0, "mmap" }, + /* 198 */ { do___syscall, "__syscall" }, + /* 199 */ { do_lseek, "lseek" }, + /* 200 */ { 0, "truncate" }, + /* 201 */ { 0, "ftruncate" }, + /* 202 */ { do___sysctl, "__sysctl" }, + /* 203 */ { 0, "mlock" }, + /* 204 */ { 0, "munlock" }, +}; + +static char *(netbsd_error_names[]) = { + /* 0 */ "ESUCCESS", + /* 1 */ "EPERM", + /* 2 */ "ENOENT", + /* 3 */ "ESRCH", + /* 4 */ "EINTR", + /* 5 */ "EIO", + /* 6 */ "ENXIO", + /* 7 */ "E2BIG", + /* 8 */ "ENOEXEC", + /* 9 */ "EBADF", + /* 10 */ "ECHILD", + /* 11 */ "EDEADLK", + /* 12 */ "ENOMEM", + /* 13 */ "EACCES", + /* 14 */ "EFAULT", + /* 15 */ "ENOTBLK", + /* 16 */ "EBUSY", + /* 17 */ "EEXIST", + /* 18 */ "EXDEV", + /* 19 */ "ENODEV", + /* 20 */ "ENOTDIR", + /* 21 */ "EISDIR", + /* 22 */ "EINVAL", + /* 23 */ "ENFILE", + /* 24 */ "EMFILE", + /* 25 */ "ENOTTY", + /* 26 */ "ETXTBSY", + /* 27 */ "EFBIG", + /* 28 */ "ENOSPC", + /* 29 */ "ESPIPE", + /* 30 */ "EROFS", + /* 31 */ "EMLINK", + /* 32 */ "EPIPE", + /* 33 */ "EDOM", + /* 34 */ "ERANGE", + /* 35 */ "EAGAIN", + /* 36 */ "EINPROGRESS", + /* 37 */ "EALREADY", + /* 38 */ "ENOTSOCK", + /* 39 */ "EDESTADDRREQ", + /* 40 */ "EMSGSIZE", + /* 41 */ "EPROTOTYPE", + /* 42 */ "ENOPROTOOPT", + /* 43 */ "EPROTONOSUPPORT", + /* 44 */ "ESOCKTNOSUPPORT", + /* 45 */ "EOPNOTSUPP", + /* 46 */ "EPFNOSUPPORT", + /* 47 */ "EAFNOSUPPORT", + /* 48 */ "EADDRINUSE", + /* 49 */ "EADDRNOTAVAIL", + /* 50 */ "ENETDOWN", + /* 51 */ "ENETUNREACH", + /* 52 */ "ENETRESET", + /* 53 */ "ECONNABORTED", + /* 54 */ "ECONNRESET", + /* 55 */ "ENOBUFS", + /* 56 */ "EISCONN", + /* 57 */ "ENOTCONN", + /* 58 */ "ESHUTDOWN", + /* 59 */ "ETOOMANYREFS", + /* 60 */ "ETIMEDOUT", + /* 61 */ "ECONNREFUSED", + /* 62 */ "ELOOP", + /* 63 */ "ENAMETOOLONG", + /* 64 */ "EHOSTDOWN", + /* 65 */ "EHOSTUNREACH", + /* 66 */ "ENOTEMPTY", + /* 67 */ "EPROCLIM", + /* 68 */ "EUSERS", + /* 69 */ "EDQUOT", + /* 70 */ "ESTALE", + /* 71 */ "EREMOTE", + /* 72 */ "EBADRPC", + /* 73 */ "ERPCMISMATCH", + /* 74 */ "EPROGUNAVAIL", + /* 75 */ "EPROGMISMATCH", + /* 76 */ "EPROCUNAVAIL", + /* 77 */ "ENOLCK", + /* 78 */ "ENOSYS", + /* 79 */ "EFTYPE", + /* 80 */ "EAUTH", + /* 81 */ "ENEEDAUTH", + /* 81 */ "ELAST", +}; + +static char *(netbsd_signal_names[]) = { + /* 0 */ 0, + /* 1 */ "SIGHUP", + /* 2 */ "SIGINT", + /* 3 */ "SIGQUIT", + /* 4 */ "SIGILL", + /* 5 */ "SIGTRAP", + /* 6 */ "SIGABRT", + /* 7 */ "SIGEMT", + /* 8 */ "SIGFPE", + /* 9 */ "SIGKILL", + /* 10 */ "SIGBUS", + /* 11 */ "SIGSEGV", + /* 12 */ "SIGSYS", + /* 13 */ "SIGPIPE", + /* 14 */ "SIGALRM", + /* 15 */ "SIGTERM", + /* 16 */ "SIGURG", + /* 17 */ "SIGSTOP", + /* 18 */ "SIGTSTP", + /* 19 */ "SIGCONT", + /* 20 */ "SIGCHLD", + /* 21 */ "SIGTTIN", + /* 22 */ "SIGTTOU", + /* 23 */ "SIGIO", + /* 24 */ "SIGXCPU", + /* 25 */ "SIGXFSZ", + /* 26 */ "SIGVTALRM", + /* 27 */ "SIGPROF", + /* 28 */ "SIGWINCH", + /* 29 */ "SIGINFO", + /* 30 */ "SIGUSR1", + /* 31 */ "SIGUSR2", +}; + +static emul_syscall emul_netbsd_syscalls = { + netbsd_descriptors, + sizeof(netbsd_descriptors) / sizeof(netbsd_descriptors[0]), + netbsd_error_names, + sizeof(netbsd_error_names) / sizeof(netbsd_error_names[0]), + netbsd_signal_names, + sizeof(netbsd_signal_names) / sizeof(netbsd_signal_names[0]), +}; + + +/* NetBSD's os_emul interface, most are just passed on to the generic + syscall stuff */ + +static os_emul_data * +emul_netbsd_create(device *root, + bfd *image, + const char *name) +{ + unsigned_word top_of_stack; + unsigned stack_size; + int elf_binary; + os_emul_data *bsd_data; + device *vm; + + /* check that this emulation is really for us */ + if (name != NULL && strcmp(name, "netbsd") != 0) + return NULL; + if (image == NULL) + return NULL; + + + /* merge any emulation specific entries into the device tree */ + + /* establish a few defaults */ + if (image->xvec->flavour == bfd_target_elf_flavour) { + elf_binary = 1; + top_of_stack = 0xe0000000; + stack_size = 0x00100000; + } + else { + elf_binary = 0; + top_of_stack = 0x20000000; + stack_size = 0x00100000; + } + + /* options */ + emul_add_tree_options(root, image, "netbsd", + (WITH_ENVIRONMENT == USER_ENVIRONMENT + ? "user" : "virtual"), + 0 /*oea-interrupt-prefix*/); + + /* virtual memory - handles growth of stack/heap */ + vm = tree_parse(root, "/openprom/vm"); + tree_parse(vm, "./stack-base 0x%lx", + (unsigned long)(top_of_stack - stack_size)); + tree_parse(vm, "./nr-bytes 0x%x", stack_size); + + tree_parse(root, "/openprom/vm/map-binary/file-name %s", + bfd_get_filename(image)); + + /* finish the init */ + tree_parse(root, "/openprom/init/register/pc 0x%lx", + (unsigned long)bfd_get_start_address(image)); + tree_parse(root, "/openprom/init/register/sp 0x%lx", + (unsigned long)top_of_stack); + tree_parse(root, "/openprom/init/register/msr 0x%x", + ((tree_find_boolean_property(root, "/options/little-endian?") + ? msr_little_endian_mode + : 0) + | (tree_find_boolean_property(root, "/openprom/options/floating-point?") + ? (msr_floating_point_available + | msr_floating_point_exception_mode_0 + | msr_floating_point_exception_mode_1) + : 0))); + tree_parse(root, "/openprom/init/stack/stack-type %s", + (elf_binary ? "ppc-elf" : "ppc-xcoff")); + + /* finally our emulation data */ + bsd_data = ZALLOC(os_emul_data); + bsd_data->vm = vm; + bsd_data->syscalls = &emul_netbsd_syscalls; + return bsd_data; +} + +static void +emul_netbsd_init(os_emul_data *emul_data, + int nr_cpus) +{ + /* nothing yet */ +} + +static void +emul_netbsd_system_call(cpu *processor, + unsigned_word cia, + os_emul_data *emul_data) +{ + emul_do_system_call(emul_data, + emul_data->syscalls, + cpu_registers(processor)->gpr[0], + 3, /*r3 contains arg0*/ + processor, + cia); +} + +const os_emul emul_netbsd = { + "netbsd", + emul_netbsd_create, + emul_netbsd_init, + emul_netbsd_system_call, + 0, /*instruction_call*/ + 0 /*data*/ +}; + +#endif _EMUL_NETBSD_C_ diff --git a/sim/ppc/emul_netbsd.h b/sim/ppc/emul_netbsd.h new file mode 100644 index 00000000000..f9a3e85e8be --- /dev/null +++ b/sim/ppc/emul_netbsd.h @@ -0,0 +1,27 @@ +/* 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 _EMUL_NETBSD_H_ +#define _EMUL_NETBSD_H_ + +extern const os_emul emul_netbsd; + +#endif diff --git a/sim/ppc/emul_unix.c b/sim/ppc/emul_unix.c new file mode 100644 index 00000000000..d5e56296105 --- /dev/null +++ b/sim/ppc/emul_unix.c @@ -0,0 +1,2812 @@ +/* This file is part of the program psim. + + Copyright (C) 1996-1998, 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 _EMUL_UNIX_C_ +#define _EMUL_UNIX_C_ + + +/* Note: this module is called via a table. There is no benefit in + making it inline */ + +#include "emul_generic.h" +#include "emul_unix.h" + +#ifdef HAVE_STRING_H +#include <string.h> +#else +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif +#endif + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif + +#ifdef HAVE_SYS_TYPES_H +#include <sys/stat.h> +#else +#undef HAVE_STAT +#undef HAVE_LSTAT +#undef HAVE_FSTAT +#endif + +#include <stdio.h> +#include <signal.h> +#include <errno.h> + +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif + +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif + +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif + +#ifndef HAVE_TERMIOS_STRUCTURE +#undef HAVE_SYS_TERMIOS_H +#undef HAVE_TCGETATTR +#else +#ifndef HAVE_SYS_TERMIOS_H +#undef HAVE_TERMIOS_STRUCTURE +#endif +#endif + +#ifdef HAVE_TERMIOS_STRUCTURE +#include <sys/termios.h> + +/* If we have TERMIOS, use that for the termio structure, since some systems + don't like including both sys/termios.h and sys/termio.h at the same + time. */ +#undef HAVE_TERMIO_STRUCTURE +#undef TCGETA +#undef termio +#define termio termios +#endif + +#ifndef HAVE_TERMIO_STRUCTURE +#undef HAVE_SYS_TERMIO_H +#else +#ifndef HAVE_SYS_TERMIO_H +#undef HAVE_TERMIO_STRUCTURE +#endif +#endif + +#ifdef HAVE_TERMIO_STRUCTURE +#include <sys/termio.h> +#endif + +#ifdef HAVE_GETRUSAGE +#ifndef HAVE_SYS_RESOURCE_H +#undef HAVE_GETRUSAGE +#endif +#endif + +#ifdef HAVE_GETRUSAGE +#include <sys/resource.h> +int getrusage(); +#endif + +#if HAVE_DIRENT_H +# include <dirent.h> +# define NAMLEN(dirent) strlen((dirent)->d_name) +#else +# define dirent direct +# define NAMLEN(dirent) (dirent)->d_namlen +# if HAVE_SYS_NDIR_H +# include <sys/ndir.h> +# endif +# if HAVE_SYS_DIR_H +# include <sys/dir.h> +# endif +# if HAVE_NDIR_H +# include <ndir.h> +# endif +#endif + +#ifdef HAVE_UNISTD_H +#undef MAXPATHLEN /* sys/param.h might define this also */ +#include <unistd.h> +#endif + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#if defined(BSD) && !defined(errno) && (BSD < 199306) /* here BSD as just a bug */ +extern int errno; +#endif + +#ifndef STATIC_INLINE_EMUL_UNIX +#define STATIC_INLINE_EMUL_UNIX STATIC_INLINE +#endif + +#ifndef PATH_MAX +#define PATH_MAX 1024 +#endif + +#ifndef EINVAL +#define EINVAL -1 +#endif + +/* UNIX's idea of what is needed to implement emulations */ + +struct _os_emul_data { + device *vm; + emul_syscall *syscalls; +}; + + +/* Emulation of simple UNIX system calls that are common on all systems. */ + +/* Structures that are common agmonst the UNIX varients */ +struct unix_timeval { + signed32 tv_sec; /* seconds */ + signed32 tv_usec; /* microseconds */ +}; + +struct unix_timezone { + signed32 tz_minuteswest; /* minutes west of Greenwich */ + signed32 tz_dsttime; /* type of dst correction */ +}; + +#define UNIX_RUSAGE_SELF 0 +#define UNIX_RUSAGE_CHILDREN (-1) +#define UNIX_RUSAGE_BOTH (-2) /* sys_wait4() uses this */ + +struct unix_rusage { + struct unix_timeval ru_utime; /* user time used */ + struct unix_timeval ru_stime; /* system time used */ + signed32 ru_maxrss; /* maximum resident set size */ + signed32 ru_ixrss; /* integral shared memory size */ + signed32 ru_idrss; /* integral unshared data size */ + signed32 ru_isrss; /* integral unshared stack size */ + signed32 ru_minflt; /* any page faults not requiring I/O */ + signed32 ru_majflt; /* any page faults requiring I/O */ + signed32 ru_nswap; /* swaps */ + signed32 ru_inblock; /* block input operations */ + signed32 ru_oublock; /* block output operations */ + signed32 ru_msgsnd; /* messages sent */ + signed32 ru_msgrcv; /* messages received */ + signed32 ru_nsignals; /* signals received */ + signed32 ru_nvcsw; /* voluntary context switches */ + signed32 ru_nivcsw; /* involuntary " */ +}; + + +static void +do_unix_exit(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + int status = (int)cpu_registers(processor)->gpr[arg0]; + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("%d)\n", status); + + cpu_halt(processor, cia, was_exited, status); +} + + +static void +do_unix_read(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + void *scratch_buffer; + int d = (int)cpu_registers(processor)->gpr[arg0]; + unsigned_word buf = cpu_registers(processor)->gpr[arg0+1]; + int nbytes = cpu_registers(processor)->gpr[arg0+2]; + int status; + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("%d, 0x%lx, %d", d, (long)buf, nbytes); + + /* get a tempoary bufer */ + scratch_buffer = zalloc(nbytes); + + /* check if buffer exists by reading it */ + emul_read_buffer(scratch_buffer, buf, nbytes, processor, cia); + + /* read */ + status = read (d, scratch_buffer, nbytes); + + emul_write_status(processor, status, errno); + if (status > 0) + emul_write_buffer(scratch_buffer, buf, status, processor, cia); + + zfree(scratch_buffer); +} + + +static void +do_unix_write(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + void *scratch_buffer = NULL; + int d = (int)cpu_registers(processor)->gpr[arg0]; + unsigned_word buf = cpu_registers(processor)->gpr[arg0+1]; + int nbytes = cpu_registers(processor)->gpr[arg0+2]; + int status; + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("%d, 0x%lx, %d", d, (long)buf, nbytes); + + /* get a tempoary bufer */ + scratch_buffer = zalloc(nbytes); /* FIXME - nbytes == 0 */ + + /* copy in */ + emul_read_buffer(scratch_buffer, buf, nbytes, + processor, cia); + + /* write */ + status = write(d, scratch_buffer, nbytes); + emul_write_status(processor, status, errno); + zfree(scratch_buffer); + + flush_stdoutput(); +} + + +static void +do_unix_open(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + unsigned_word path_addr = cpu_registers(processor)->gpr[arg0]; + char path_buf[PATH_MAX]; + char *path = emul_read_string(path_buf, path_addr, PATH_MAX, processor, cia); + int flags = (int)cpu_registers(processor)->gpr[arg0+1]; + int mode = (int)cpu_registers(processor)->gpr[arg0+2]; + int status; + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("0x%lx [%s], 0x%x, 0x%x", (long)path_addr, path, flags, mode); + + status = open(path, flags, mode); + emul_write_status(processor, status, errno); +} + + +static void +do_unix_close(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + int d = (int)cpu_registers(processor)->gpr[arg0]; + int status; + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("%d", d); + + status = close(d); + emul_write_status(processor, status, errno); +} + + +static void +do_unix_break(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + /* just pass this onto the `vm' device */ + unsigned_word new_break = cpu_registers(processor)->gpr[arg0]; + int status; + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("0x%lx", (long)cpu_registers(processor)->gpr[arg0]); + + status = device_ioctl(emul->vm, + processor, + cia, + device_ioctl_break, + new_break); /*ioctl-data*/ + + emul_write_status(processor, 0, status); +} + +#ifndef HAVE_ACCESS +#define do_unix_access 0 +#else +static void +do_unix_access(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + unsigned_word path_addr = cpu_registers(processor)->gpr[arg0]; + char path_buf[PATH_MAX]; + char *path = emul_read_string(path_buf, path_addr, PATH_MAX, processor, cia); + int mode = (int)cpu_registers(processor)->gpr[arg0+1]; + int status; + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("0x%lx [%s], 0x%x [0%o]", (long)path_addr, path, mode, mode); + + status = access(path, mode); + emul_write_status(processor, status, errno); +} +#endif + +#ifndef HAVE_GETPID +#define do_unix_getpid 0 +#else +static void +do_unix_getpid(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + pid_t status = getpid(); + emul_write_status(processor, (int)status, errno); +} +#endif + +#ifndef HAVE_GETPPID +#define do_unix_getppid 0 +#else +static void +do_unix_getppid(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + pid_t status = getppid(); + emul_write_status(processor, (int)status, errno); +} +#endif + +#if !defined(HAVE_GETPID) || !defined(HAVE_GETPPID) +#define do_unix_getpid2 0 +#else +static void +do_unix_getpid2(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + int pid = (int)getpid(); + int ppid = (int)getppid(); + emul_write2_status(processor, pid, ppid, errno); +} +#endif + +#if !defined(HAVE_GETUID) || !defined(HAVE_GETEUID) +#define do_unix_getuid2 0 +#else +static void +do_unix_getuid2(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + uid_t uid = getuid(); + uid_t euid = geteuid(); + emul_write2_status(processor, (int)uid, (int)euid, errno); +} +#endif + +#ifndef HAVE_GETUID +#define do_unix_getuid 0 +#else +static void +do_unix_getuid(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + uid_t status = getuid(); + emul_write_status(processor, (int)status, errno); +} +#endif + +#ifndef HAVE_GETEUID +#define do_unix_geteuid 0 +#else +static void +do_unix_geteuid(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + uid_t status = geteuid(); + emul_write_status(processor, (int)status, errno); +} +#endif + +#if 0 +#ifndef HAVE_KILL +#define do_unix_kill 0 +#else +static void +do_unix_kill(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + pid_t pid = cpu_registers(processor)->gpr[arg0]; + int sig = cpu_registers(processor)->gpr[arg0+1]; + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("%d, %d", (int)pid, sig); + + printf_filtered("SYS_kill at 0x%lx - more to this than just being killed\n", + (long)cia); + + cpu_halt(processor, cia, was_signalled, sig); +} +#endif +#endif + +#ifndef HAVE_DUP +#define do_unix_dup 0 +#else +static void +do_unix_dup(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + int oldd = cpu_registers(processor)->gpr[arg0]; + int status = dup(oldd); + int err = errno; + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("%d", oldd); + + emul_write_status(processor, status, err); +} +#endif + +#ifndef HAVE_DUP2 +#define do_unix_dup2 0 +#else +static void +do_unix_dup2(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + int oldd = cpu_registers(processor)->gpr[arg0]; + int newd = cpu_registers(processor)->gpr[arg0+1]; + int status = dup2(oldd, newd); + int err = errno; + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("%d, %d", oldd, newd); + + emul_write_status(processor, status, err); +} +#endif + +#ifndef HAVE_LSEEK +#define do_unix_lseek 0 +#else +static void +do_unix_lseek(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + int fildes = (int)cpu_registers(processor)->gpr[arg0]; + off_t offset = (off_t)cpu_registers(processor)->gpr[arg0+1]; + int whence = (int)cpu_registers(processor)->gpr[arg0+2]; + off_t status; + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("%d %ld %d", fildes, (long)offset, whence); + + status = lseek(fildes, offset, whence); + emul_write_status(processor, (int)status, errno); +} +#endif + + +#if !defined(HAVE_GETGID) || !defined(HAVE_GETEGID) +#define do_unix_getgid2 0 +#else +static void +do_unix_getgid2(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + gid_t gid = getgid(); + gid_t egid = getegid(); + emul_write2_status(processor, (int)gid, (int)egid, errno); +} +#endif + +#ifndef HAVE_GETGID +#define do_unix_getgid 0 +#else +static void +do_unix_getgid(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + gid_t status = getgid(); + emul_write_status(processor, (int)status, errno); +} +#endif + +#ifndef HAVE_GETEGID +#define do_unix_getegid 0 +#else +static void +do_unix_getegid(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + gid_t status = getegid(); + emul_write_status(processor, (int)status, errno); +} +#endif + +#ifndef HAVE_UMASK +#define do_unix_umask 0 +#else +static void +do_unix_umask(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + mode_t mask = (mode_t)cpu_registers(processor)->gpr[arg0]; + int status = umask(mask); + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("0%o", (unsigned int)mask); + + emul_write_status(processor, status, errno); +} +#endif + +#ifndef HAVE_CHDIR +#define do_unix_chdir 0 +#else +static void +do_unix_chdir(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + unsigned_word path_addr = cpu_registers(processor)->gpr[arg0]; + char path_buf[PATH_MAX]; + char *path = emul_read_string(path_buf, path_addr, PATH_MAX, processor, cia); + int status; + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("0x%lx [%s]", (long)path_addr, path); + + status = chdir(path); + emul_write_status(processor, status, errno); +} +#endif + +#ifndef HAVE_LINK +#define do_unix_link 0 +#else +static void +do_unix_link(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + unsigned_word path1_addr = cpu_registers(processor)->gpr[arg0]; + char path1_buf[PATH_MAX]; + char *path1 = emul_read_string(path1_buf, path1_addr, PATH_MAX, processor, cia); + unsigned_word path2_addr = cpu_registers(processor)->gpr[arg0+1]; + char path2_buf[PATH_MAX]; + char *path2 = emul_read_string(path2_buf, path2_addr, PATH_MAX, processor, cia); + int status; + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("0x%lx [%s], 0x%lx [%s]", (long)path1_addr, path1, (long)path2_addr, path2); + + status = link(path1, path2); + emul_write_status(processor, status, errno); +} +#endif + +#ifndef HAVE_SYMLINK +#define do_unix_symlink 0 +#else +static void +do_unix_symlink(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + unsigned_word path1_addr = cpu_registers(processor)->gpr[arg0]; + char path1_buf[PATH_MAX]; + char *path1 = emul_read_string(path1_buf, path1_addr, PATH_MAX, processor, cia); + unsigned_word path2_addr = cpu_registers(processor)->gpr[arg0+1]; + char path2_buf[PATH_MAX]; + char *path2 = emul_read_string(path2_buf, path2_addr, PATH_MAX, processor, cia); + int status; + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("0x%lx [%s], 0x%lx [%s]", (long)path1_addr, path1, (long)path2_addr, path2); + + status = symlink(path1, path2); + emul_write_status(processor, status, errno); +} +#endif + +#ifndef HAVE_UNLINK +#define do_unix_unlink 0 +#else +static void +do_unix_unlink(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + unsigned_word path_addr = cpu_registers(processor)->gpr[arg0]; + char path_buf[PATH_MAX]; + char *path = emul_read_string(path_buf, path_addr, PATH_MAX, processor, cia); + int status; + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("0x%lx [%s]", (long)path_addr, path); + + status = unlink(path); + emul_write_status(processor, status, errno); +} +#endif + +#ifndef HAVE_MKDIR +#define do_unix_mkdir 0 +#else +static void +do_unix_mkdir(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + unsigned_word path_addr = cpu_registers(processor)->gpr[arg0]; + char path_buf[PATH_MAX]; + char *path = emul_read_string(path_buf, path_addr, PATH_MAX, processor, cia); + int mode = (int)cpu_registers(processor)->gpr[arg0+1]; + int status; + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("0x%lx [%s], 0%3o", (long)path_addr, path, mode); + + status = mkdir(path, mode); + emul_write_status(processor, status, errno); +} +#endif + +#ifndef HAVE_RMDIR +#define do_unix_rmdir 0 +#else +static void +do_unix_rmdir(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + unsigned_word path_addr = cpu_registers(processor)->gpr[arg0]; + char path_buf[PATH_MAX]; + char *path = emul_read_string(path_buf, path_addr, PATH_MAX, processor, cia); + int status; + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("0x%lx [%s]", (long)path_addr, path); + + status = rmdir(path); + emul_write_status(processor, status, errno); +} +#endif + +#ifndef HAVE_TIME +#define do_unix_time 0 +#else +static void +do_unix_time(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + unsigned_word tp = cpu_registers(processor)->gpr[arg0]; + time_t now = time ((time_t *)0); + unsigned_word status = H2T_4(now); + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("0x%lx", (long)tp); + + emul_write_status(processor, (int)status, errno); + + if (tp) + emul_write_buffer(&status, tp, sizeof(status), processor, cia); +} +#endif + +#if !defined(HAVE_GETTIMEOFDAY) || !defined(HAVE_SYS_TIME_H) +#define do_unix_gettimeofday 0 +#else +static void +do_unix_gettimeofday(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + unsigned_word tv = cpu_registers(processor)->gpr[arg0]; + unsigned_word tz = cpu_registers(processor)->gpr[arg0+1]; + struct unix_timeval target_timeval; + struct timeval host_timeval; + struct unix_timezone target_timezone; + struct timezone host_timezone; + int status; + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("0x%lx, 0x%lx", (long)tv, (long)tz); + + /* Just in case the system doesn't set the timezone structure */ + host_timezone.tz_minuteswest = 0; + host_timezone.tz_dsttime = 0; + + status = gettimeofday(&host_timeval, &host_timezone); + if (status >= 0) { + if (tv) { + target_timeval.tv_sec = H2T_4(host_timeval.tv_sec); + target_timeval.tv_usec = H2T_4(host_timeval.tv_usec); + emul_write_buffer((void *) &target_timeval, tv, sizeof(target_timeval), processor, cia); + } + + if (tz) { + target_timezone.tz_minuteswest = H2T_4(host_timezone.tz_minuteswest); + target_timezone.tz_dsttime = H2T_4(host_timezone.tz_dsttime); + emul_write_buffer((void *) &target_timezone, tv, sizeof(target_timezone), processor, cia); + } + } + + emul_write_status(processor, (int)status, errno); +} +#endif + + +#ifndef HAVE_GETRUSAGE +#define do_unix_getrusage 0 +#else +static void +do_unix_getrusage(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + signed_word who = (signed_word)cpu_registers(processor)->gpr[arg0]; + unsigned_word usage = cpu_registers(processor)->gpr[arg0+1]; + struct rusage host_rusage, host_rusage2; + struct unix_rusage target_rusage; + int status; + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("%ld, 0x%lx", (long)who, (long)usage); + + switch (who) { + default: + status = -1; + errno = EINVAL; + break; + + case UNIX_RUSAGE_SELF: + status = getrusage(RUSAGE_SELF, &host_rusage); + break; + + case UNIX_RUSAGE_CHILDREN: + status = getrusage(RUSAGE_CHILDREN, &host_rusage); + break; + + case UNIX_RUSAGE_BOTH: + status = getrusage(RUSAGE_SELF, &host_rusage); + if (status >= 0) { + status = getrusage(RUSAGE_CHILDREN, &host_rusage2); + if (status >= 0) { + host_rusage.ru_utime.tv_sec += host_rusage2.ru_utime.tv_sec; + host_rusage.ru_utime.tv_usec += host_rusage2.ru_utime.tv_usec; + host_rusage.ru_stime.tv_sec += host_rusage2.ru_stime.tv_sec; + host_rusage.ru_stime.tv_usec += host_rusage2.ru_stime.tv_usec; + host_rusage.ru_maxrss += host_rusage2.ru_maxrss; + host_rusage.ru_ixrss += host_rusage2.ru_ixrss; + host_rusage.ru_idrss += host_rusage2.ru_idrss; + host_rusage.ru_isrss += host_rusage2.ru_isrss; + host_rusage.ru_minflt += host_rusage2.ru_minflt; + host_rusage.ru_majflt += host_rusage2.ru_majflt; + host_rusage.ru_nswap += host_rusage2.ru_nswap; + host_rusage.ru_inblock += host_rusage2.ru_inblock; + host_rusage.ru_oublock += host_rusage2.ru_oublock; + host_rusage.ru_msgsnd += host_rusage2.ru_msgsnd; + host_rusage.ru_msgrcv += host_rusage2.ru_msgrcv; + host_rusage.ru_nsignals += host_rusage2.ru_nsignals; + host_rusage.ru_nvcsw += host_rusage2.ru_nvcsw; + host_rusage.ru_nivcsw += host_rusage2.ru_nivcsw; + } + } + } + + if (status >= 0) { + target_rusage.ru_utime.tv_sec = H2T_4(host_rusage2.ru_utime.tv_sec); + target_rusage.ru_utime.tv_usec = H2T_4(host_rusage2.ru_utime.tv_usec); + target_rusage.ru_stime.tv_sec = H2T_4(host_rusage2.ru_stime.tv_sec); + target_rusage.ru_stime.tv_usec = H2T_4(host_rusage2.ru_stime.tv_usec); + target_rusage.ru_maxrss = H2T_4(host_rusage2.ru_maxrss); + target_rusage.ru_ixrss = H2T_4(host_rusage2.ru_ixrss); + target_rusage.ru_idrss = H2T_4(host_rusage2.ru_idrss); + target_rusage.ru_isrss = H2T_4(host_rusage2.ru_isrss); + target_rusage.ru_minflt = H2T_4(host_rusage2.ru_minflt); + target_rusage.ru_majflt = H2T_4(host_rusage2.ru_majflt); + target_rusage.ru_nswap = H2T_4(host_rusage2.ru_nswap); + target_rusage.ru_inblock = H2T_4(host_rusage2.ru_inblock); + target_rusage.ru_oublock = H2T_4(host_rusage2.ru_oublock); + target_rusage.ru_msgsnd = H2T_4(host_rusage2.ru_msgsnd); + target_rusage.ru_msgrcv = H2T_4(host_rusage2.ru_msgrcv); + target_rusage.ru_nsignals = H2T_4(host_rusage2.ru_nsignals); + target_rusage.ru_nvcsw = H2T_4(host_rusage2.ru_nvcsw); + target_rusage.ru_nivcsw = H2T_4(host_rusage2.ru_nivcsw); + emul_write_buffer((void *) &target_rusage, usage, sizeof(target_rusage), processor, cia); + } + + emul_write_status(processor, status, errno); +} +#endif + + +static void +do_unix_nop(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx", + (long)cpu_registers(processor)->gpr[arg0], + (long)cpu_registers(processor)->gpr[arg0+1], + (long)cpu_registers(processor)->gpr[arg0+2], + (long)cpu_registers(processor)->gpr[arg0+3], + (long)cpu_registers(processor)->gpr[arg0+4], + (long)cpu_registers(processor)->gpr[arg0+5]); + + emul_write_status(processor, 0, errno); +} + + +/* Common code for initializing the system call stuff */ + +static os_emul_data * +emul_unix_create(device *root, + bfd *image, + const char *name, + emul_syscall *syscall) +{ + unsigned_word top_of_stack; + unsigned stack_size; + int elf_binary; + os_emul_data *data; + device *vm; + + /* merge any emulation specific entries into the device tree */ + + /* establish a few defaults */ + if (image->xvec->flavour == bfd_target_elf_flavour) { + elf_binary = 1; + top_of_stack = 0xe0000000; + stack_size = 0x00100000; + } + else { + elf_binary = 0; + top_of_stack = 0x20000000; + stack_size = 0x00100000; + } + + /* options */ + emul_add_tree_options(root, image, name, + (WITH_ENVIRONMENT == USER_ENVIRONMENT + ? "user" : "virtual"), + 0 /*oea-interrupt-prefix*/); + + /* virtual memory - handles growth of stack/heap */ + vm = tree_parse(root, "/openprom/vm@0x%lx", + (unsigned long)(top_of_stack - stack_size)); + tree_parse(vm, "./stack-base 0x%lx", + (unsigned long)(top_of_stack - stack_size)); + tree_parse(vm, "./nr-bytes 0x%x", stack_size); + + tree_parse(root, "/openprom/vm/map-binary/file-name %s", + bfd_get_filename(image)); + + /* finish the init */ + tree_parse(root, "/openprom/init/register/pc 0x%lx", + (unsigned long)bfd_get_start_address(image)); + tree_parse(root, "/openprom/init/register/sp 0x%lx", + (unsigned long)top_of_stack); + tree_parse(root, "/openprom/init/register/msr 0x%x", + ((tree_find_boolean_property(root, "/options/little-endian?") + ? msr_little_endian_mode + : 0) + | (tree_find_boolean_property(root, "/openprom/options/floating-point?") + ? (msr_floating_point_available + | msr_floating_point_exception_mode_0 + | msr_floating_point_exception_mode_1) + : 0))); + tree_parse(root, "/openprom/init/stack/stack-type %s", + (elf_binary ? "ppc-elf" : "ppc-xcoff")); + + /* finally our emulation data */ + data = ZALLOC(os_emul_data); + data->vm = vm; + data->syscalls = syscall; + return data; +} + + +/* EMULATION + + Solaris - Emulation of user programs for Solaris/PPC + + DESCRIPTION + + */ + + +/* Solaris specific implementation */ + +typedef signed32 solaris_uid_t; +typedef signed32 solaris_gid_t; +typedef signed32 solaris_off_t; +typedef signed32 solaris_pid_t; +typedef signed32 solaris_time_t; +typedef unsigned32 solaris_dev_t; +typedef unsigned32 solaris_ino_t; +typedef unsigned32 solaris_mode_t; +typedef unsigned32 solaris_nlink_t; + +#ifdef HAVE_SYS_STAT_H +#define SOLARIS_ST_FSTYPSZ 16 /* array size for file system type name */ + +struct solaris_stat { + solaris_dev_t st_dev; + signed32 st_pad1[3]; /* reserved for network id */ + solaris_ino_t st_ino; + solaris_mode_t st_mode; + solaris_nlink_t st_nlink; + solaris_uid_t st_uid; + solaris_gid_t st_gid; + solaris_dev_t st_rdev; + signed32 st_pad2[2]; + solaris_off_t st_size; + signed32 st_pad3; /* future off_t expansion */ + struct unix_timeval st_atim; + struct unix_timeval st_mtim; + struct unix_timeval st_ctim; + signed32 st_blksize; + signed32 st_blocks; + char st_fstype[SOLARIS_ST_FSTYPSZ]; + signed32 st_pad4[8]; /* expansion area */ +}; + +/* Convert from host stat structure to solaris stat structure */ +STATIC_INLINE_EMUL_UNIX void +convert_to_solaris_stat(unsigned_word addr, + struct stat *host, + cpu *processor, + unsigned_word cia) +{ + struct solaris_stat target; + int i; + + target.st_dev = H2T_4(host->st_dev); + target.st_ino = H2T_4(host->st_ino); + target.st_mode = H2T_4(host->st_mode); + target.st_nlink = H2T_4(host->st_nlink); + target.st_uid = H2T_4(host->st_uid); + target.st_gid = H2T_4(host->st_gid); + target.st_size = H2T_4(host->st_size); + +#ifdef HAVE_ST_RDEV + target.st_rdev = H2T_4(host->st_rdev); +#else + target.st_rdev = 0; +#endif + +#ifdef HAVE_ST_BLKSIZE + target.st_blksize = H2T_4(host->st_blksize); +#else + target.st_blksize = 0; +#endif + +#ifdef HAVE_ST_BLOCKS + target.st_blocks = H2T_4(host->st_blocks); +#else + target.st_blocks = 0; +#endif + + target.st_atim.tv_sec = H2T_4(host->st_atime); + target.st_atim.tv_usec = 0; + + target.st_ctim.tv_sec = H2T_4(host->st_ctime); + target.st_ctim.tv_usec = 0; + + target.st_mtim.tv_sec = H2T_4(host->st_mtime); + target.st_mtim.tv_usec = 0; + + for (i = 0; i < sizeof (target.st_pad1) / sizeof (target.st_pad1[0]); i++) + target.st_pad1[i] = 0; + + for (i = 0; i < sizeof (target.st_pad2) / sizeof (target.st_pad2[0]); i++) + target.st_pad2[i] = 0; + + target.st_pad3 = 0; + + for (i = 0; i < sizeof (target.st_pad4) / sizeof (target.st_pad4[0]); i++) + target.st_pad4[i] = 0; + + /* For now, just punt and always say it is a ufs file */ + strcpy (target.st_fstype, "ufs"); + + emul_write_buffer(&target, addr, sizeof(target), processor, cia); +} +#endif /* HAVE_SYS_STAT_H */ + +#ifndef HAVE_STAT +#define do_solaris_stat 0 +#else +static void +do_solaris_stat(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + unsigned_word path_addr = cpu_registers(processor)->gpr[arg0]; + unsigned_word stat_pkt = cpu_registers(processor)->gpr[arg0+1]; + char path_buf[PATH_MAX]; + struct stat buf; + char *path = emul_read_string(path_buf, path_addr, PATH_MAX, processor, cia); + int status; + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("0x%lx [%s], 0x%lx", (long)path_addr, path, (long)stat_pkt); + + status = stat (path, &buf); + if (status == 0) + convert_to_solaris_stat (stat_pkt, &buf, processor, cia); + + emul_write_status(processor, status, errno); +} +#endif + +#ifndef HAVE_LSTAT +#define do_solaris_lstat 0 +#else +static void +do_solaris_lstat(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + unsigned_word path_addr = cpu_registers(processor)->gpr[arg0]; + unsigned_word stat_pkt = cpu_registers(processor)->gpr[arg0+1]; + char path_buf[PATH_MAX]; + struct stat buf; + char *path = emul_read_string(path_buf, path_addr, PATH_MAX, processor, cia); + int status; + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("0x%lx [%s], 0x%lx", (long)path_addr, path, (long)stat_pkt); + + status = lstat (path, &buf); + if (status == 0) + convert_to_solaris_stat (stat_pkt, &buf, processor, cia); + + emul_write_status(processor, status, errno); +} +#endif + +#ifndef HAVE_FSTAT +#define do_solaris_fstat 0 +#else +static void +do_solaris_fstat(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + int fildes = (int)cpu_registers(processor)->gpr[arg0]; + unsigned_word stat_pkt = cpu_registers(processor)->gpr[arg0+1]; + struct stat buf; + int status; + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("%d, 0x%lx", fildes, (long)stat_pkt); + + status = fstat (fildes, &buf); + if (status == 0) + convert_to_solaris_stat (stat_pkt, &buf, processor, cia); + + emul_write_status(processor, status, errno); +} +#endif + +#if defined(HAVE_TERMIO_STRUCTURE) || defined(HAVE_TERMIOS_STRUCTURE) +#define SOLARIS_TIOC ('T'<<8) +#define SOLARIS_NCC 8 +#define SOLARIS_NCCS 19 + +#define SOLARIS_VINTR 0 +#define SOLARIS_VQUIT 1 +#define SOLARIS_VERASE 2 +#define SOLARIS_VKILL 3 +#define SOLARIS_VEOF 4 +#define SOLARIS_VEOL 5 +#define SOLARIS_VEOL2 6 +#define SOLARIS_VSWTCH 7 +#define SOLARIS_VSTART 8 +#define SOLARIS_VSTOP 9 +#define SOLARIS_VSUSP 10 +#define SOLARIS_VDSUSP 11 +#define SOLARIS_VREPRINT 12 +#define SOLARIS_VDISCARD 13 +#define SOLARIS_VWERASE 14 +#define SOLARIS_VLNEXT 15 +#endif + +#if defined(HAVE_TERMIO_STRUCTURE) || defined(HAVE_TERMIOS_STRUCTURE) +/* Convert to/from host termio structure */ + +struct solaris_termio { + unsigned16 c_iflag; /* input modes */ + unsigned16 c_oflag; /* output modes */ + unsigned16 c_cflag; /* control modes */ + unsigned16 c_lflag; /* line discipline modes */ + unsigned8 c_line; /* line discipline */ + unsigned8 c_cc[SOLARIS_NCC]; /* control chars */ +}; + +STATIC_INLINE_EMUL_UNIX void +convert_to_solaris_termio(unsigned_word addr, + struct termio *host, + cpu *processor, + unsigned_word cia) +{ + struct solaris_termio target; + int i; + + target.c_iflag = H2T_2 (host->c_iflag); + target.c_oflag = H2T_2 (host->c_oflag); + target.c_cflag = H2T_2 (host->c_cflag); + target.c_lflag = H2T_2 (host->c_lflag); + +#if defined(HAVE_TERMIO_CLINE) || defined(HAVE_TERMIOS_CLINE) + target.c_line = host->c_line; +#else + target.c_line = 0; +#endif + + for (i = 0; i < SOLARIS_NCC; i++) + target.c_cc[i] = 0; + +#ifdef VINTR + target.c_cc[SOLARIS_VINTR] = host->c_cc[VINTR]; +#endif + +#ifdef VQUIT + target.c_cc[SOLARIS_VQUIT] = host->c_cc[VQUIT]; +#endif + +#ifdef VERASE + target.c_cc[SOLARIS_VERASE] = host->c_cc[VERASE]; +#endif + +#ifdef VKILL + target.c_cc[SOLARIS_VKILL] = host->c_cc[VKILL]; +#endif + +#ifdef VEOF + target.c_cc[SOLARIS_VEOF] = host->c_cc[VEOF]; +#endif + +#ifdef VEOL + target.c_cc[SOLARIS_VEOL] = host->c_cc[VEOL]; +#endif + +#ifdef VEOL2 + target.c_cc[SOLARIS_VEOL2] = host->c_cc[VEOL2]; +#endif + +#ifdef VSWTCH + target.c_cc[SOLARIS_VSWTCH] = host->c_cc[VSWTCH]; + +#else +#ifdef VSWTC + target.c_cc[SOLARIS_VSWTCH] = host->c_cc[VSWTC]; +#endif +#endif + + emul_write_buffer(&target, addr, sizeof(target), processor, cia); +} +#endif /* HAVE_TERMIO_STRUCTURE || HAVE_TERMIOS_STRUCTURE */ + +#ifdef HAVE_TERMIOS_STRUCTURE +/* Convert to/from host termios structure */ + +typedef unsigned32 solaris_tcflag_t; +typedef unsigned8 solaris_cc_t; +typedef unsigned32 solaris_speed_t; + +struct solaris_termios { + solaris_tcflag_t c_iflag; + solaris_tcflag_t c_oflag; + solaris_tcflag_t c_cflag; + solaris_tcflag_t c_lflag; + solaris_cc_t c_cc[SOLARIS_NCCS]; +}; + +STATIC_INLINE_EMUL_UNIX void +convert_to_solaris_termios(unsigned_word addr, + struct termios *host, + cpu *processor, + unsigned_word cia) +{ + struct solaris_termios target; + int i; + + target.c_iflag = H2T_4 (host->c_iflag); + target.c_oflag = H2T_4 (host->c_oflag); + target.c_cflag = H2T_4 (host->c_cflag); + target.c_lflag = H2T_4 (host->c_lflag); + + for (i = 0; i < SOLARIS_NCCS; i++) + target.c_cc[i] = 0; + +#ifdef VINTR + target.c_cc[SOLARIS_VINTR] = host->c_cc[VINTR]; +#endif + +#ifdef VQUIT + target.c_cc[SOLARIS_VQUIT] = host->c_cc[VQUIT]; +#endif + +#ifdef VERASE + target.c_cc[SOLARIS_VERASE] = host->c_cc[VERASE]; +#endif + +#ifdef VKILL + target.c_cc[SOLARIS_VKILL] = host->c_cc[VKILL]; +#endif + +#ifdef VEOF + target.c_cc[SOLARIS_VEOF] = host->c_cc[VEOF]; +#endif + +#ifdef VEOL + target.c_cc[SOLARIS_VEOL] = host->c_cc[VEOL]; +#endif + +#ifdef VEOL2 + target.c_cc[SOLARIS_VEOL2] = host->c_cc[VEOL2]; +#endif + +#ifdef VSWTCH + target.c_cc[SOLARIS_VSWTCH] = host->c_cc[VSWTCH]; + +#else +#ifdef VSWTC + target.c_cc[SOLARIS_VSWTCH] = host->c_cc[VSWTC]; +#endif +#endif + +#ifdef VSTART + target.c_cc[SOLARIS_VSTART] = host->c_cc[VSTART]; +#endif + +#ifdef VSTOP + target.c_cc[SOLARIS_VSTOP] = host->c_cc[VSTOP]; +#endif + +#ifdef VSUSP + target.c_cc[SOLARIS_VSUSP] = host->c_cc[VSUSP]; +#endif + +#ifdef VDSUSP + target.c_cc[SOLARIS_VDSUSP] = host->c_cc[VDSUSP]; +#endif + +#ifdef VREPRINT + target.c_cc[SOLARIS_VREPRINT] = host->c_cc[VREPRINT]; +#endif + +#ifdef VDISCARD + target.c_cc[SOLARIS_VDISCARD] = host->c_cc[VDISCARD]; +#endif + +#ifdef VWERASE + target.c_cc[SOLARIS_VWERASE] = host->c_cc[VWERASE]; +#endif + +#ifdef VLNEXT + target.c_cc[SOLARIS_VLNEXT] = host->c_cc[VLNEXT]; +#endif + + emul_write_buffer(&target, addr, sizeof(target), processor, cia); +} +#endif /* HAVE_TERMIOS_STRUCTURE */ + +#ifndef HAVE_IOCTL +#define do_solaris_ioctl 0 +#else +static void +do_solaris_ioctl(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + int fildes = cpu_registers(processor)->gpr[arg0]; + unsigned request = cpu_registers(processor)->gpr[arg0+1]; + unsigned_word argp_addr = cpu_registers(processor)->gpr[arg0+2]; + int status = 0; + const char *name = "<unknown>"; + +#ifdef HAVE_TERMIOS_STRUCTURE + struct termios host_termio; + +#else +#ifdef HAVE_TERMIO_STRUCTURE + struct termio host_termio; +#endif +#endif + + switch (request) + { + case 0: /* make sure we have at least one case */ + default: + status = -1; + errno = EINVAL; + break; + +#if defined(HAVE_TERMIO_STRUCTURE) || defined(HAVE_TERMIOS_STRUCTURE) +#if defined(TCGETA) || defined(TCGETS) || defined(HAVE_TCGETATTR) + case SOLARIS_TIOC | 1: /* TCGETA */ + name = "TCGETA"; +#ifdef HAVE_TCGETATTR + status = tcgetattr(fildes, &host_termio); +#elif defined(TCGETS) + status = ioctl (fildes, TCGETS, &host_termio); +#else + status = ioctl (fildes, TCGETA, &host_termio); +#endif + if (status == 0) + convert_to_solaris_termio (argp_addr, &host_termio, processor, cia); + break; +#endif /* TCGETA */ +#endif /* HAVE_TERMIO_STRUCTURE */ + +#ifdef HAVE_TERMIOS_STRUCTURE +#if defined(TCGETS) || defined(HAVE_TCGETATTR) + case SOLARIS_TIOC | 13: /* TCGETS */ + name = "TCGETS"; +#ifdef HAVE_TCGETATTR + status = tcgetattr(fildes, &host_termio); +#else + status = ioctl (fildes, TCGETS, &host_termio); +#endif + if (status == 0) + convert_to_solaris_termios (argp_addr, &host_termio, processor, cia); + break; +#endif /* TCGETS */ +#endif /* HAVE_TERMIOS_STRUCTURE */ + } + + emul_write_status(processor, status, errno); + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("%d, 0x%x [%s], 0x%lx", fildes, request, name, (long)argp_addr); +} +#endif /* HAVE_IOCTL */ + +static emul_syscall_descriptor solaris_descriptors[] = { + /* 0 */ { 0, "syscall" }, + /* 1 */ { do_unix_exit, "exit" }, + /* 2 */ { 0, "fork" }, + /* 3 */ { do_unix_read, "read" }, + /* 4 */ { do_unix_write, "write" }, + /* 5 */ { do_unix_open, "open" }, + /* 6 */ { do_unix_close, "close" }, + /* 7 */ { 0, "wait" }, + /* 8 */ { 0, "creat" }, + /* 9 */ { do_unix_link, "link" }, + /* 10 */ { do_unix_unlink, "unlink" }, + /* 11 */ { 0, "exec" }, + /* 12 */ { do_unix_chdir, "chdir" }, + /* 13 */ { do_unix_time, "time" }, + /* 14 */ { 0, "mknod" }, + /* 15 */ { 0, "chmod" }, + /* 16 */ { 0, "chown" }, + /* 17 */ { do_unix_break, "brk" }, + /* 18 */ { do_solaris_stat, "stat" }, + /* 19 */ { do_unix_lseek, "lseek" }, + /* 20 */ { do_unix_getpid2, "getpid" }, + /* 21 */ { 0, "mount" }, + /* 22 */ { 0, "umount" }, + /* 23 */ { 0, "setuid" }, + /* 24 */ { do_unix_getuid2, "getuid" }, + /* 25 */ { 0, "stime" }, + /* 26 */ { 0, "ptrace" }, + /* 27 */ { 0, "alarm" }, + /* 28 */ { do_solaris_fstat, "fstat" }, + /* 29 */ { 0, "pause" }, + /* 30 */ { 0, "utime" }, + /* 31 */ { 0, "stty" }, + /* 32 */ { 0, "gtty" }, + /* 33 */ { do_unix_access, "access" }, + /* 34 */ { 0, "nice" }, + /* 35 */ { 0, "statfs" }, + /* 36 */ { 0, "sync" }, + /* 37 */ { 0, "kill" }, + /* 38 */ { 0, "fstatfs" }, + /* 39 */ { 0, "pgrpsys" }, + /* 40 */ { 0, "xenix" }, + /* 41 */ { do_unix_dup, "dup" }, + /* 42 */ { 0, "pipe" }, + /* 43 */ { 0, "times" }, + /* 44 */ { 0, "profil" }, + /* 45 */ { 0, "plock" }, + /* 46 */ { 0, "setgid" }, + /* 47 */ { do_unix_getgid2, "getgid" }, + /* 48 */ { 0, "signal" }, + /* 49 */ { 0, "msgsys" }, + /* 50 */ { 0, "syssun" }, + /* 51 */ { 0, "acct" }, + /* 52 */ { 0, "shmsys" }, + /* 53 */ { 0, "semsys" }, + /* 54 */ { do_solaris_ioctl, "ioctl" }, + /* 55 */ { 0, "uadmin" }, + /* 56 */ { 0, 0 /* reserved for exch */ }, + /* 57 */ { 0, "utssys" }, + /* 58 */ { 0, "fdsync" }, + /* 59 */ { 0, "execve" }, + /* 60 */ { do_unix_umask, "umask" }, + /* 61 */ { 0, "chroot" }, + /* 62 */ { 0, "fcntl" }, + /* 63 */ { 0, "ulimit" }, + /* 64 */ { 0, 0 /* reserved for UNIX PC */ }, + /* 64 */ { 0, 0 /* reserved for UNIX PC */ }, + /* 65 */ { 0, 0 /* reserved for UNIX PC */ }, + /* 66 */ { 0, 0 /* reserved for UNIX PC */ }, + /* 67 */ { 0, 0 /* reserved for UNIX PC */ }, + /* 68 */ { 0, 0 /* reserved for UNIX PC */ }, + /* 69 */ { 0, 0 /* reserved for UNIX PC */ }, + /* 70 */ { 0, 0 /* was advfs */ }, + /* 71 */ { 0, 0 /* was unadvfs */ }, + /* 72 */ { 0, 0 /* was rmount */ }, + /* 73 */ { 0, 0 /* was rumount */ }, + /* 74 */ { 0, 0 /* was rfstart */ }, + /* 75 */ { 0, 0 /* was sigret */ }, + /* 76 */ { 0, 0 /* was rdebug */ }, + /* 77 */ { 0, 0 /* was rfstop */ }, + /* 78 */ { 0, 0 /* was rfsys */ }, + /* 79 */ { do_unix_rmdir, "rmdir" }, + /* 80 */ { do_unix_mkdir, "mkdir" }, + /* 81 */ { 0, "getdents" }, + /* 82 */ { 0, 0 /* was libattach */ }, + /* 83 */ { 0, 0 /* was libdetach */ }, + /* 84 */ { 0, "sysfs" }, + /* 85 */ { 0, "getmsg" }, + /* 86 */ { 0, "putmsg" }, + /* 87 */ { 0, "poll" }, + /* 88 */ { do_solaris_lstat, "lstat" }, + /* 89 */ { do_unix_symlink, "symlink" }, + /* 90 */ { 0, "readlink" }, + /* 91 */ { 0, "setgroups" }, + /* 92 */ { 0, "getgroups" }, + /* 93 */ { 0, "fchmod" }, + /* 94 */ { 0, "fchown" }, + /* 95 */ { 0, "sigprocmask" }, + /* 96 */ { 0, "sigsuspend" }, + /* 97 */ { do_unix_nop, "sigaltstack" }, + /* 98 */ { do_unix_nop, "sigaction" }, + /* 99 */ { 0, "sigpending" }, + /* 100 */ { 0, "context" }, + /* 101 */ { 0, "evsys" }, + /* 102 */ { 0, "evtrapret" }, + /* 103 */ { 0, "statvfs" }, + /* 104 */ { 0, "fstatvfs" }, + /* 105 */ { 0, 0 /* reserved */ }, + /* 106 */ { 0, "nfssys" }, + /* 107 */ { 0, "waitsys" }, + /* 108 */ { 0, "sigsendsys" }, + /* 109 */ { 0, "hrtsys" }, + /* 110 */ { 0, "acancel" }, + /* 111 */ { 0, "async" }, + /* 112 */ { 0, "priocntlsys" }, + /* 113 */ { 0, "pathconf" }, + /* 114 */ { 0, "mincore" }, + /* 115 */ { 0, "mmap" }, + /* 116 */ { 0, "mprotect" }, + /* 117 */ { 0, "munmap" }, + /* 118 */ { 0, "fpathconf" }, + /* 119 */ { 0, "vfork" }, + /* 120 */ { 0, "fchdir" }, + /* 121 */ { 0, "readv" }, + /* 122 */ { 0, "writev" }, + /* 123 */ { 0, "xstat" }, + /* 124 */ { 0, "lxstat" }, + /* 125 */ { 0, "fxstat" }, + /* 126 */ { 0, "xmknod" }, + /* 127 */ { 0, "clocal" }, + /* 128 */ { 0, "setrlimit" }, + /* 129 */ { 0, "getrlimit" }, + /* 130 */ { 0, "lchown" }, + /* 131 */ { 0, "memcntl" }, + /* 132 */ { 0, "getpmsg" }, + /* 133 */ { 0, "putpmsg" }, + /* 134 */ { 0, "rename" }, + /* 135 */ { 0, "uname" }, + /* 136 */ { 0, "setegid" }, + /* 137 */ { 0, "sysconfig" }, + /* 138 */ { 0, "adjtime" }, + /* 139 */ { 0, "systeminfo" }, + /* 140 */ { 0, 0 /* reserved */ }, + /* 141 */ { 0, "seteuid" }, + /* 142 */ { 0, "vtrace" }, + /* 143 */ { 0, "fork1" }, + /* 144 */ { 0, "sigtimedwait" }, + /* 145 */ { 0, "lwp_info" }, + /* 146 */ { 0, "yield" }, + /* 147 */ { 0, "lwp_sema_wait" }, + /* 148 */ { 0, "lwp_sema_post" }, + /* 149 */ { 0, 0 /* reserved */ }, + /* 150 */ { 0, 0 /* reserved */ }, + /* 151 */ { 0, 0 /* reserved */ }, + /* 152 */ { 0, "modctl" }, + /* 153 */ { 0, "fchroot" }, + /* 154 */ { 0, "utimes" }, + /* 155 */ { 0, "vhangup" }, + /* 156 */ { do_unix_gettimeofday, "gettimeofday" }, + /* 157 */ { 0, "getitimer" }, + /* 158 */ { 0, "setitimer" }, + /* 159 */ { 0, "lwp_create" }, + /* 160 */ { 0, "lwp_exit" }, + /* 161 */ { 0, "lwp_suspend" }, + /* 162 */ { 0, "lwp_continue" }, + /* 163 */ { 0, "lwp_kill" }, + /* 164 */ { 0, "lwp_self" }, + /* 165 */ { 0, "lwp_setprivate" }, + /* 166 */ { 0, "lwp_getprivate" }, + /* 167 */ { 0, "lwp_wait" }, + /* 168 */ { 0, "lwp_mutex_unlock" }, + /* 169 */ { 0, "lwp_mutex_lock" }, + /* 170 */ { 0, "lwp_cond_wait" }, + /* 171 */ { 0, "lwp_cond_signal" }, + /* 172 */ { 0, "lwp_cond_broadcast" }, + /* 173 */ { 0, "pread" }, + /* 174 */ { 0, "pwrite" }, + /* 175 */ { 0, "llseek" }, + /* 176 */ { 0, "inst_sync" }, + /* 177 */ { 0, 0 /* reserved */ }, + /* 178 */ { 0, "kaio" }, + /* 179 */ { 0, 0 /* reserved */ }, + /* 180 */ { 0, 0 /* reserved */ }, + /* 181 */ { 0, 0 /* reserved */ }, + /* 182 */ { 0, 0 /* reserved */ }, + /* 183 */ { 0, 0 /* reserved */ }, + /* 184 */ { 0, "tsolsys" }, + /* 185 */ { 0, "acl" }, + /* 186 */ { 0, "auditsys" }, + /* 187 */ { 0, "processor_bind" }, + /* 188 */ { 0, "processor_info" }, + /* 189 */ { 0, "p_online" }, + /* 190 */ { 0, "sigqueue" }, + /* 191 */ { 0, "clock_gettime" }, + /* 192 */ { 0, "clock_settime" }, + /* 193 */ { 0, "clock_getres" }, + /* 194 */ { 0, "timer_create" }, + /* 195 */ { 0, "timer_delete" }, + /* 196 */ { 0, "timer_settime" }, + /* 197 */ { 0, "timer_gettime" }, + /* 198 */ { 0, "timer_getoverrun" }, + /* 199 */ { 0, "nanosleep" }, + /* 200 */ { 0, "facl" }, + /* 201 */ { 0, "door" }, + /* 202 */ { 0, "setreuid" }, + /* 203 */ { 0, "setregid" }, + /* 204 */ { 0, "install_utrap" }, + /* 205 */ { 0, 0 /* reserved */ }, + /* 206 */ { 0, 0 /* reserved */ }, + /* 207 */ { 0, 0 /* reserved */ }, + /* 208 */ { 0, 0 /* reserved */ }, + /* 209 */ { 0, 0 /* reserved */ }, + /* 210 */ { 0, "signotifywait" }, + /* 211 */ { 0, "lwp_sigredirect" }, + /* 212 */ { 0, "lwp_alarm" }, +}; + +static char *(solaris_error_names[]) = { + /* 0 */ "ESUCCESS", + /* 1 */ "EPERM", + /* 2 */ "ENOENT", + /* 3 */ "ESRCH", + /* 4 */ "EINTR", + /* 5 */ "EIO", + /* 6 */ "ENXIO", + /* 7 */ "E2BIG", + /* 8 */ "ENOEXEC", + /* 9 */ "EBADF", + /* 10 */ "ECHILD", + /* 11 */ "EAGAIN", + /* 12 */ "ENOMEM", + /* 13 */ "EACCES", + /* 14 */ "EFAULT", + /* 15 */ "ENOTBLK", + /* 16 */ "EBUSY", + /* 17 */ "EEXIST", + /* 18 */ "EXDEV", + /* 19 */ "ENODEV", + /* 20 */ "ENOTDIR", + /* 21 */ "EISDIR", + /* 22 */ "EINVAL", + /* 23 */ "ENFILE", + /* 24 */ "EMFILE", + /* 25 */ "ENOTTY", + /* 26 */ "ETXTBSY", + /* 27 */ "EFBIG", + /* 28 */ "ENOSPC", + /* 29 */ "ESPIPE", + /* 30 */ "EROFS", + /* 31 */ "EMLINK", + /* 32 */ "EPIPE", + /* 33 */ "EDOM", + /* 34 */ "ERANGE", + /* 35 */ "ENOMSG", + /* 36 */ "EIDRM", + /* 37 */ "ECHRNG", + /* 38 */ "EL2NSYNC", + /* 39 */ "EL3HLT", + /* 40 */ "EL3RST", + /* 41 */ "ELNRNG", + /* 42 */ "EUNATCH", + /* 43 */ "ENOCSI", + /* 44 */ "EL2HLT", + /* 45 */ "EDEADLK", + /* 46 */ "ENOLCK", + /* 47 */ "ECANCELED", + /* 48 */ "ENOTSUP", + /* 49 */ "EDQUOT", + /* 50 */ "EBADE", + /* 51 */ "EBADR", + /* 52 */ "EXFULL", + /* 53 */ "ENOANO", + /* 54 */ "EBADRQC", + /* 55 */ "EBADSLT", + /* 56 */ "EDEADLOCK", + /* 57 */ "EBFONT", + /* 58 */ "Error code 58", + /* 59 */ "Error code 59", + /* 60 */ "ENOSTR", + /* 61 */ "ENODATA", + /* 62 */ "ETIME", + /* 63 */ "ENOSR", + /* 64 */ "ENONET", + /* 65 */ "ENOPKG", + /* 66 */ "EREMOTE", + /* 67 */ "ENOLINK", + /* 68 */ "EADV", + /* 69 */ "ESRMNT", + /* 70 */ "ECOMM", + /* 71 */ "EPROTO", + /* 72 */ "Error code 72", + /* 73 */ "Error code 73", + /* 74 */ "EMULTIHOP", + /* 75 */ "Error code 75", + /* 76 */ "Error code 76", + /* 77 */ "EBADMSG", + /* 78 */ "ENAMETOOLONG", + /* 79 */ "EOVERFLOW", + /* 80 */ "ENOTUNIQ", + /* 81 */ "EBADFD", + /* 82 */ "EREMCHG", + /* 83 */ "ELIBACC", + /* 84 */ "ELIBBAD", + /* 85 */ "ELIBSCN", + /* 86 */ "ELIBMAX", + /* 87 */ "ELIBEXEC", + /* 88 */ "EILSEQ", + /* 89 */ "ENOSYS", + /* 90 */ "ELOOP", + /* 91 */ "ERESTART", + /* 92 */ "ESTRPIPE", + /* 93 */ "ENOTEMPTY", + /* 94 */ "EUSERS", + /* 95 */ "ENOTSOCK", + /* 96 */ "EDESTADDRREQ", + /* 97 */ "EMSGSIZE", + /* 98 */ "EPROTOTYPE", + /* 99 */ "ENOPROTOOPT", + /* 100 */ "Error code 100", + /* 101 */ "Error code 101", + /* 102 */ "Error code 102", + /* 103 */ "Error code 103", + /* 104 */ "Error code 104", + /* 105 */ "Error code 105", + /* 106 */ "Error code 106", + /* 107 */ "Error code 107", + /* 108 */ "Error code 108", + /* 109 */ "Error code 109", + /* 110 */ "Error code 110", + /* 111 */ "Error code 111", + /* 112 */ "Error code 112", + /* 113 */ "Error code 113", + /* 114 */ "Error code 114", + /* 115 */ "Error code 115", + /* 116 */ "Error code 116", + /* 117 */ "Error code 117", + /* 118 */ "Error code 118", + /* 119 */ "Error code 119", + /* 120 */ "EPROTONOSUPPORT", + /* 121 */ "ESOCKTNOSUPPORT", + /* 122 */ "EOPNOTSUPP", + /* 123 */ "EPFNOSUPPORT", + /* 124 */ "EAFNOSUPPORT", + /* 125 */ "EADDRINUSE", + /* 126 */ "EADDRNOTAVAIL", + /* 127 */ "ENETDOWN", + /* 128 */ "ENETUNREACH", + /* 129 */ "ENETRESET", + /* 130 */ "ECONNABORTED", + /* 131 */ "ECONNRESET", + /* 132 */ "ENOBUFS", + /* 133 */ "EISCONN", + /* 134 */ "ENOTCONN", + /* 135 */ "Error code 135", /* XENIX has 135 - 142 */ + /* 136 */ "Error code 136", + /* 137 */ "Error code 137", + /* 138 */ "Error code 138", + /* 139 */ "Error code 139", + /* 140 */ "Error code 140", + /* 141 */ "Error code 141", + /* 142 */ "Error code 142", + /* 143 */ "ESHUTDOWN", + /* 144 */ "ETOOMANYREFS", + /* 145 */ "ETIMEDOUT", + /* 146 */ "ECONNREFUSED", + /* 147 */ "EHOSTDOWN", + /* 148 */ "EHOSTUNREACH", + /* 149 */ "EALREADY", + /* 150 */ "EINPROGRESS", + /* 151 */ "ESTALE", +}; + +static char *(solaris_signal_names[]) = { + /* 0 */ 0, + /* 1 */ "SIGHUP", + /* 2 */ "SIGINT", + /* 3 */ "SIGQUIT", + /* 4 */ "SIGILL", + /* 5 */ "SIGTRAP", + /* 6 */ "SIGABRT", + /* 7 */ "SIGEMT", + /* 8 */ "SIGFPE", + /* 9 */ "SIGKILL", + /* 10 */ "SIGBUS", + /* 11 */ "SIGSEGV", + /* 12 */ "SIGSYS", + /* 13 */ "SIGPIPE", + /* 14 */ "SIGALRM", + /* 15 */ "SIGTERM", + /* 16 */ "SIGUSR1", + /* 17 */ "SIGUSR2", + /* 18 */ "SIGCHLD", + /* 19 */ "SIGPWR", + /* 20 */ "SIGWINCH", + /* 21 */ "SIGURG", + /* 22 */ "SIGPOLL", + /* 23 */ "SIGSTOP", + /* 24 */ "SIGTSTP", + /* 25 */ "SIGCONT", + /* 26 */ "SIGTTIN", + /* 27 */ "SIGTTOU", + /* 28 */ "SIGVTALRM", + /* 29 */ "SIGPROF", + /* 30 */ "SIGXCPU", + /* 31 */ "SIGXFSZ", + /* 32 */ "SIGWAITING", + /* 33 */ "SIGLWP", + /* 34 */ "SIGFREEZE", + /* 35 */ "SIGTHAW", + /* 36 */ "SIGCANCEL", +}; + +static emul_syscall emul_solaris_syscalls = { + solaris_descriptors, + sizeof(solaris_descriptors) / sizeof(solaris_descriptors[0]), + solaris_error_names, + sizeof(solaris_error_names) / sizeof(solaris_error_names[0]), + solaris_signal_names, + sizeof(solaris_signal_names) / sizeof(solaris_signal_names[0]), +}; + + +/* Solaris's os_emul interface, most are just passed on to the generic + syscall stuff */ + +static os_emul_data * +emul_solaris_create(device *root, + bfd *image, + const char *name) +{ + /* check that this emulation is really for us */ + if (name != NULL && strcmp(name, "solaris") != 0) + return NULL; + + if (image == NULL) + return NULL; + + return emul_unix_create(root, image, "solaris", &emul_solaris_syscalls); +} + +static void +emul_solaris_init(os_emul_data *emul_data, + int nr_cpus) +{ + /* nothing yet */ +} + +static void +emul_solaris_system_call(cpu *processor, + unsigned_word cia, + os_emul_data *emul_data) +{ + emul_do_system_call(emul_data, + emul_data->syscalls, + cpu_registers(processor)->gpr[0], + 3, /*r3 contains arg0*/ + processor, + cia); +} + +const os_emul emul_solaris = { + "solaris", + emul_solaris_create, + emul_solaris_init, + emul_solaris_system_call, + 0, /*instruction_call*/ + 0 /*data*/ +}; + + +/* EMULATION + + Linux - Emulation of user programs for Linux/PPC + + DESCRIPTION + + */ + + +/* Linux specific implementation */ + +typedef unsigned32 linux_dev_t; +typedef unsigned32 linux_ino_t; +typedef unsigned32 linux_mode_t; +typedef unsigned16 linux_nlink_t; +typedef signed32 linux_off_t; +typedef signed32 linux_pid_t; +typedef unsigned32 linux_uid_t; +typedef unsigned32 linux_gid_t; +typedef unsigned32 linux_size_t; +typedef signed32 linux_ssize_t; +typedef signed32 linux_ptrdiff_t; +typedef signed32 linux_time_t; +typedef signed32 linux_clock_t; +typedef signed32 linux_daddr_t; + +#ifdef HAVE_SYS_STAT_H +/* For the PowerPC, don't both with the 'old' stat structure, since there + should be no extant binaries with that structure. */ + +struct linux_stat { + linux_dev_t st_dev; + linux_ino_t st_ino; + linux_mode_t st_mode; + linux_nlink_t st_nlink; + linux_uid_t st_uid; + linux_gid_t st_gid; + linux_dev_t st_rdev; + linux_off_t st_size; + unsigned32 st_blksize; + unsigned32 st_blocks; + unsigned32 st_atimx; /* don't use st_{a,c,m}time, that might a macro */ + unsigned32 __unused1; /* defined by the host's stat.h */ + unsigned32 st_mtimx; + unsigned32 __unused2; + unsigned32 st_ctimx; + unsigned32 __unused3; + unsigned32 __unused4; + unsigned32 __unused5; +}; + +/* Convert from host stat structure to solaris stat structure */ +STATIC_INLINE_EMUL_UNIX void +convert_to_linux_stat(unsigned_word addr, + struct stat *host, + cpu *processor, + unsigned_word cia) +{ + struct linux_stat target; + + target.st_dev = H2T_4(host->st_dev); + target.st_ino = H2T_4(host->st_ino); + target.st_mode = H2T_4(host->st_mode); + target.st_nlink = H2T_2(host->st_nlink); + target.st_uid = H2T_4(host->st_uid); + target.st_gid = H2T_4(host->st_gid); + target.st_size = H2T_4(host->st_size); + +#ifdef HAVE_ST_RDEV + target.st_rdev = H2T_4(host->st_rdev); +#else + target.st_rdev = 0; +#endif + +#ifdef HAVE_ST_BLKSIZE + target.st_blksize = H2T_4(host->st_blksize); +#else + target.st_blksize = 0; +#endif + +#ifdef HAVE_ST_BLOCKS + target.st_blocks = H2T_4(host->st_blocks); +#else + target.st_blocks = 0; +#endif + + target.st_atimx = H2T_4(host->st_atime); + target.st_ctimx = H2T_4(host->st_ctime); + target.st_mtimx = H2T_4(host->st_mtime); + target.__unused1 = 0; + target.__unused2 = 0; + target.__unused3 = 0; + target.__unused4 = 0; + target.__unused5 = 0; + + emul_write_buffer(&target, addr, sizeof(target), processor, cia); +} +#endif /* HAVE_SYS_STAT_H */ + +#ifndef HAVE_STAT +#define do_linux_stat 0 +#else +static void +do_linux_stat(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + unsigned_word path_addr = cpu_registers(processor)->gpr[arg0]; + unsigned_word stat_pkt = cpu_registers(processor)->gpr[arg0+1]; + char path_buf[PATH_MAX]; + struct stat buf; + char *path = emul_read_string(path_buf, path_addr, PATH_MAX, processor, cia); + int status; + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("0x%lx [%s], 0x%lx", (long)path_addr, path, (long)stat_pkt); + + status = stat (path, &buf); + if (status == 0) + convert_to_linux_stat (stat_pkt, &buf, processor, cia); + + emul_write_status(processor, status, errno); +} +#endif + +#ifndef HAVE_LSTAT +#define do_linux_lstat 0 +#else +static void +do_linux_lstat(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + unsigned_word path_addr = cpu_registers(processor)->gpr[arg0]; + unsigned_word stat_pkt = cpu_registers(processor)->gpr[arg0+1]; + char path_buf[PATH_MAX]; + struct stat buf; + char *path = emul_read_string(path_buf, path_addr, PATH_MAX, processor, cia); + int status; + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("0x%lx [%s], 0x%lx", (long)path_addr, path, (long)stat_pkt); + + status = lstat (path, &buf); + if (status == 0) + convert_to_linux_stat (stat_pkt, &buf, processor, cia); + + emul_write_status(processor, status, errno); +} +#endif + +#ifndef HAVE_FSTAT +#define do_linux_fstat 0 +#else +static void +do_linux_fstat(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + int fildes = (int)cpu_registers(processor)->gpr[arg0]; + unsigned_word stat_pkt = cpu_registers(processor)->gpr[arg0+1]; + struct stat buf; + int status; + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("%d, 0x%lx", fildes, (long)stat_pkt); + + status = fstat (fildes, &buf); + if (status == 0) + convert_to_linux_stat (stat_pkt, &buf, processor, cia); + + emul_write_status(processor, status, errno); +} +#endif + +#if defined(HAVE_TERMIO_STRUCTURE) || defined(HAVE_TERMIOS_STRUCTURE) +#define LINUX_NCC 10 +#define LINUX_NCCS 19 + +#define LINUX_VINTR 0 +#define LINUX_VQUIT 1 +#define LINUX_VERASE 2 +#define LINUX_VKILL 3 +#define LINUX_VEOF 4 +#define LINUX_VMIN 5 +#define LINUX_VEOL 6 +#define LINUX_VTIME 7 +#define LINUX_VEOL2 8 +#define LINUX_VSWTC 9 +#define LINUX_VWERASE 10 +#define LINUX_VREPRINT 11 +#define LINUX_VSUSP 12 +#define LINUX_VSTART 13 +#define LINUX_VSTOP 14 +#define LINUX_VLNEXT 15 +#define LINUX_VDISCARD 16 + +#define LINUX_IOC_NRBITS 8 +#define LINUX_IOC_TYPEBITS 8 +#define LINUX_IOC_SIZEBITS 13 +#define LINUX_IOC_DIRBITS 3 + +#define LINUX_IOC_NRMASK ((1 << LINUX_IOC_NRBITS)-1) +#define LINUX_IOC_TYPEMASK ((1 << LINUX_IOC_TYPEBITS)-1) +#define LINUX_IOC_SIZEMASK ((1 << LINUX_IOC_SIZEBITS)-1) +#define LINUX_IOC_DIRMASK ((1 << LINUX_IOC_DIRBITS)-1) + +#define LINUX_IOC_NRSHIFT 0 +#define LINUX_IOC_TYPESHIFT (LINUX_IOC_NRSHIFT+LINUX_IOC_NRBITS) +#define LINUX_IOC_SIZESHIFT (LINUX_IOC_TYPESHIFT+LINUX_IOC_TYPEBITS) +#define LINUX_IOC_DIRSHIFT (LINUX_IOC_SIZESHIFT+LINUX_IOC_SIZEBITS) + +/* + * Direction bits _IOC_NONE could be 0, but OSF/1 gives it a bit. + * And this turns out useful to catch old ioctl numbers in header + * files for us. + */ +#define LINUX_IOC_NONE 1U +#define LINUX_IOC_READ 2U +#define LINUX_IOC_WRITE 4U + +#define LINUX_IOC(dir,type,nr,size) \ + (((dir) << LINUX_IOC_DIRSHIFT) | \ + ((type) << LINUX_IOC_TYPESHIFT) | \ + ((nr) << LINUX_IOC_NRSHIFT) | \ + ((size) << LINUX_IOC_SIZESHIFT)) + +/* used to create numbers */ +#define LINUX_IO(type,nr) LINUX_IOC(LINUX_IOC_NONE,(type),(nr),0) +#define LINUX_IOR(type,nr,size) LINUX_IOC(LINUX_IOC_READ,(type),(nr),sizeof(size)) +#define LINUX_IOW(type,nr,size) LINUX_IOC(LINUX_IOC_WRITE,(type),(nr),sizeof(size)) +#define LINUX_IOWR(type,nr,size) LINUX_IOC(LINUX_IOC_READ|LINUX_IOC_WRITE,(type),(nr),sizeof(size)) +#endif + +#if defined(HAVE_TERMIO_STRUCTURE) || defined(HAVE_TERMIOS_STRUCTURE) +/* Convert to/from host termio structure */ + +struct linux_termio { + unsigned16 c_iflag; /* input modes */ + unsigned16 c_oflag; /* output modes */ + unsigned16 c_cflag; /* control modes */ + unsigned16 c_lflag; /* line discipline modes */ + unsigned8 c_line; /* line discipline */ + unsigned8 c_cc[LINUX_NCC]; /* control chars */ +}; + +STATIC_INLINE_EMUL_UNIX void +convert_to_linux_termio(unsigned_word addr, + struct termio *host, + cpu *processor, + unsigned_word cia) +{ + struct linux_termio target; + int i; + + target.c_iflag = H2T_2 (host->c_iflag); + target.c_oflag = H2T_2 (host->c_oflag); + target.c_cflag = H2T_2 (host->c_cflag); + target.c_lflag = H2T_2 (host->c_lflag); + +#if defined(HAVE_TERMIO_CLINE) || defined(HAVE_TERMIOS_CLINE) + target.c_line = host->c_line; +#else + target.c_line = 0; +#endif + + for (i = 0; i < LINUX_NCC; i++) + target.c_cc[i] = 0; + +#ifdef VINTR + target.c_cc[LINUX_VINTR] = host->c_cc[VINTR]; +#endif + +#ifdef VQUIT + target.c_cc[LINUX_VQUIT] = host->c_cc[VQUIT]; +#endif + +#ifdef VERASE + target.c_cc[LINUX_VERASE] = host->c_cc[VERASE]; +#endif + +#ifdef VKILL + target.c_cc[LINUX_VKILL] = host->c_cc[VKILL]; +#endif + +#ifdef VEOF + target.c_cc[LINUX_VEOF] = host->c_cc[VEOF]; +#endif + +#ifdef VMIN + target.c_cc[LINUX_VMIN] = host->c_cc[VMIN]; +#endif + +#ifdef VEOL + target.c_cc[LINUX_VEOL] = host->c_cc[VEOL]; +#endif + +#ifdef VTIME + target.c_cc[LINUX_VTIME] = host->c_cc[VTIME]; +#endif + +#ifdef VEOL2 + target.c_cc[LINUX_VEOL2] = host->c_cc[VEOL2]; +#endif + +#ifdef VSWTC + target.c_cc[LINUX_VSWTC] = host->c_cc[VSWTC]; +#endif + +#ifdef VSWTCH + target.c_cc[LINUX_VSWTC] = host->c_cc[VSWTCH]; +#endif + + emul_write_buffer(&target, addr, sizeof(target), processor, cia); +} +#endif /* HAVE_TERMIO_STRUCTURE */ + +#ifdef HAVE_TERMIOS_STRUCTURE +/* Convert to/from host termios structure */ + +typedef unsigned32 linux_tcflag_t; +typedef unsigned8 linux_cc_t; +typedef unsigned32 linux_speed_t; + +struct linux_termios { + linux_tcflag_t c_iflag; + linux_tcflag_t c_oflag; + linux_tcflag_t c_cflag; + linux_tcflag_t c_lflag; + linux_cc_t c_cc[LINUX_NCCS]; + linux_cc_t c_line; + signed32 c_ispeed; + signed32 c_ospeed; +}; + +STATIC_INLINE_EMUL_UNIX void +convert_to_linux_termios(unsigned_word addr, + struct termios *host, + cpu *processor, + unsigned_word cia) +{ + struct linux_termios target; + int i; + + target.c_iflag = H2T_4 (host->c_iflag); + target.c_oflag = H2T_4 (host->c_oflag); + target.c_cflag = H2T_4 (host->c_cflag); + target.c_lflag = H2T_4 (host->c_lflag); + + for (i = 0; i < LINUX_NCCS; i++) + target.c_cc[i] = 0; + +#ifdef VINTR + target.c_cc[LINUX_VINTR] = host->c_cc[VINTR]; +#endif + +#ifdef VQUIT + target.c_cc[LINUX_VQUIT] = host->c_cc[VQUIT]; +#endif + +#ifdef VERASE + target.c_cc[LINUX_VERASE] = host->c_cc[VERASE]; +#endif + +#ifdef VKILL + target.c_cc[LINUX_VKILL] = host->c_cc[VKILL]; +#endif + +#ifdef VEOF + target.c_cc[LINUX_VEOF] = host->c_cc[VEOF]; +#endif + +#ifdef VEOL + target.c_cc[LINUX_VEOL] = host->c_cc[VEOL]; +#endif + +#ifdef VEOL2 + target.c_cc[LINUX_VEOL2] = host->c_cc[VEOL2]; +#endif + +#ifdef VSWTCH + target.c_cc[LINUX_VSWTC] = host->c_cc[VSWTCH]; +#endif + +#ifdef HAVE_TERMIOS_CLINE + target.c_line = host->c_line; +#else + target.c_line = 0; +#endif + +#ifdef HAVE_CFGETISPEED + target.c_ispeed = cfgetispeed (host); +#else + target.c_ispeed = 0; +#endif + +#ifdef HAVE_CFGETOSPEED + target.c_ospeed = cfgetospeed (host); +#else + target.c_ospeed = 0; +#endif + + emul_write_buffer(&target, addr, sizeof(target), processor, cia); +} +#endif /* HAVE_TERMIOS_STRUCTURE */ + +#ifndef HAVE_IOCTL +#define do_linux_ioctl 0 +#else +static void +do_linux_ioctl(os_emul_data *emul, + unsigned call, + const int arg0, + cpu *processor, + unsigned_word cia) +{ + int fildes = cpu_registers(processor)->gpr[arg0]; + unsigned request = cpu_registers(processor)->gpr[arg0+1]; + unsigned_word argp_addr = cpu_registers(processor)->gpr[arg0+2]; + int status = 0; + const char *name = "<unknown>"; + +#ifdef HAVE_TERMIOS_STRUCTURE + struct termios host_termio; + +#else +#ifdef HAVE_TERMIO_STRUCTURE + struct termio host_termio; +#endif +#endif + + switch (request) + { + case 0: /* make sure we have at least one case */ + default: + status = -1; + errno = EINVAL; + break; + +#if defined(HAVE_TERMIO_STRUCTURE) || defined(HAVE_TERMIOS_STRUCTURE) +#if defined(TCGETA) || defined(TCGETS) || defined(HAVE_TCGETATTR) + case LINUX_IOR('t', 23, struct linux_termio): /* TCGETA */ + name = "TCGETA"; +#ifdef HAVE_TCGETATTR + status = tcgetattr(fildes, &host_termio); +#elif defined(TCGETS) + status = ioctl (fildes, TCGETS, &host_termio); +#else + status = ioctl (fildes, TCGETA, &host_termio); +#endif + if (status == 0) + convert_to_linux_termio (argp_addr, &host_termio, processor, cia); + break; +#endif /* TCGETA */ +#endif /* HAVE_TERMIO_STRUCTURE */ + +#ifdef HAVE_TERMIOS_STRUCTURE +#if defined(TCGETS) || defined(HAVE_TCGETATTR) + case LINUX_IOR('t', 19, struct linux_termios): /* TCGETS */ + name = "TCGETS"; +#ifdef HAVE_TCGETATTR + status = tcgetattr(fildes, &host_termio); +#else + status = ioctl (fildes, TCGETS, &host_termio); +#endif + if (status == 0) + convert_to_linux_termios (argp_addr, &host_termio, processor, cia); + break; +#endif /* TCGETS */ +#endif /* HAVE_TERMIOS_STRUCTURE */ + } + + emul_write_status(processor, status, errno); + + if (WITH_TRACE && ppc_trace[trace_os_emul]) + printf_filtered ("%d, 0x%x [%s], 0x%lx", fildes, request, name, (long)argp_addr); +} +#endif /* HAVE_IOCTL */ + +static emul_syscall_descriptor linux_descriptors[] = { + /* 0 */ { 0, "setup" }, + /* 1 */ { do_unix_exit, "exit" }, + /* 2 */ { 0, "fork" }, + /* 3 */ { do_unix_read, "read" }, + /* 4 */ { do_unix_write, "write" }, + /* 5 */ { do_unix_open, "open" }, + /* 6 */ { do_unix_close, "close" }, + /* 7 */ { 0, "waitpid" }, + /* 8 */ { 0, "creat" }, + /* 9 */ { do_unix_link, "link" }, + /* 10 */ { do_unix_unlink, "unlink" }, + /* 11 */ { 0, "execve" }, + /* 12 */ { do_unix_chdir, "chdir" }, + /* 13 */ { do_unix_time, "time" }, + /* 14 */ { 0, "mknod" }, + /* 15 */ { 0, "chmod" }, + /* 16 */ { 0, "chown" }, + /* 17 */ { 0, "break" }, + /* 18 */ { 0, "stat" }, + /* 19 */ { do_unix_lseek, "lseek" }, + /* 20 */ { do_unix_getpid, "getpid" }, + /* 21 */ { 0, "mount" }, + /* 22 */ { 0, "umount" }, + /* 23 */ { 0, "setuid" }, + /* 24 */ { do_unix_getuid, "getuid" }, + /* 25 */ { 0, "stime" }, + /* 26 */ { 0, "ptrace" }, + /* 27 */ { 0, "alarm" }, + /* 28 */ { 0, "fstat" }, + /* 29 */ { 0, "pause" }, + /* 30 */ { 0, "utime" }, + /* 31 */ { 0, "stty" }, + /* 32 */ { 0, "gtty" }, + /* 33 */ { do_unix_access, "access" }, + /* 34 */ { 0, "nice" }, + /* 35 */ { 0, "ftime" }, + /* 36 */ { 0, "sync" }, + /* 37 */ { 0, "kill" }, + /* 38 */ { 0, "rename" }, + /* 39 */ { do_unix_mkdir, "mkdir" }, + /* 40 */ { do_unix_rmdir, "rmdir" }, + /* 41 */ { do_unix_dup, "dup" }, + /* 42 */ { 0, "pipe" }, + /* 43 */ { 0, "times" }, + /* 44 */ { 0, "prof" }, + /* 45 */ { do_unix_break, "brk" }, + /* 46 */ { 0, "setgid" }, + /* 47 */ { do_unix_getgid, "getgid" }, + /* 48 */ { 0, "signal" }, + /* 49 */ { do_unix_geteuid, "geteuid" }, + /* 50 */ { do_unix_getegid, "getegid" }, + /* 51 */ { 0, "acct" }, + /* 52 */ { 0, "phys" }, + /* 53 */ { 0, "lock" }, + /* 54 */ { do_linux_ioctl, "ioctl" }, + /* 55 */ { 0, "fcntl" }, + /* 56 */ { 0, "mpx" }, + /* 57 */ { 0, "setpgid" }, + /* 58 */ { 0, "ulimit" }, + /* 59 */ { 0, "olduname" }, + /* 60 */ { do_unix_umask, "umask" }, + /* 61 */ { 0, "chroot" }, + /* 62 */ { 0, "ustat" }, + /* 63 */ { do_unix_dup2, "dup2" }, + /* 64 */ { do_unix_getppid, "getppid" }, + /* 65 */ { 0, "getpgrp" }, + /* 66 */ { 0, "setsid" }, + /* 67 */ { 0, "sigaction" }, + /* 68 */ { 0, "sgetmask" }, + /* 69 */ { 0, "ssetmask" }, + /* 70 */ { 0, "setreuid" }, + /* 71 */ { 0, "setregid" }, + /* 72 */ { 0, "sigsuspend" }, + /* 73 */ { 0, "sigpending" }, + /* 74 */ { 0, "sethostname" }, + /* 75 */ { 0, "setrlimit" }, + /* 76 */ { 0, "getrlimit" }, + /* 77 */ { do_unix_getrusage, "getrusage" }, + /* 78 */ { do_unix_gettimeofday, "gettimeofday" }, + /* 79 */ { 0, "settimeofday" }, + /* 80 */ { 0, "getgroups" }, + /* 81 */ { 0, "setgroups" }, + /* 82 */ { 0, "select" }, + /* 83 */ { do_unix_symlink, "symlink" }, + /* 84 */ { 0, "lstat" }, + /* 85 */ { 0, "readlink" }, + /* 86 */ { 0, "uselib" }, + /* 87 */ { 0, "swapon" }, + /* 88 */ { 0, "reboot" }, + /* 89 */ { 0, "readdir" }, + /* 90 */ { 0, "mmap" }, + /* 91 */ { 0, "munmap" }, + /* 92 */ { 0, "truncate" }, + /* 93 */ { 0, "ftruncate" }, + /* 94 */ { 0, "fchmod" }, + /* 95 */ { 0, "fchown" }, + /* 96 */ { 0, "getpriority" }, + /* 97 */ { 0, "setpriority" }, + /* 98 */ { 0, "profil" }, + /* 99 */ { 0, "statfs" }, + /* 100 */ { 0, "fstatfs" }, + /* 101 */ { 0, "ioperm" }, + /* 102 */ { 0, "socketcall" }, + /* 103 */ { 0, "syslog" }, + /* 104 */ { 0, "setitimer" }, + /* 105 */ { 0, "getitimer" }, + /* 106 */ { do_linux_stat, "newstat" }, + /* 107 */ { do_linux_lstat, "newlstat" }, + /* 108 */ { do_linux_fstat, "newfstat" }, + /* 109 */ { 0, "uname" }, + /* 110 */ { 0, "iopl" }, + /* 111 */ { 0, "vhangup" }, + /* 112 */ { 0, "idle" }, + /* 113 */ { 0, "vm86" }, + /* 114 */ { 0, "wait4" }, + /* 115 */ { 0, "swapoff" }, + /* 116 */ { 0, "sysinfo" }, + /* 117 */ { 0, "ipc" }, + /* 118 */ { 0, "fsync" }, + /* 119 */ { 0, "sigreturn" }, + /* 120 */ { 0, "clone" }, + /* 121 */ { 0, "setdomainname" }, + /* 122 */ { 0, "newuname" }, + /* 123 */ { 0, "modify_ldt" }, + /* 124 */ { 0, "adjtimex" }, + /* 125 */ { 0, "mprotect" }, + /* 126 */ { 0, "sigprocmask" }, + /* 127 */ { 0, "create_module" }, + /* 128 */ { 0, "init_module" }, + /* 129 */ { 0, "delete_module" }, + /* 130 */ { 0, "get_kernel_syms" }, + /* 131 */ { 0, "quotactl" }, + /* 132 */ { 0, "getpgid" }, + /* 133 */ { 0, "fchdir" }, + /* 134 */ { 0, "bdflush" }, + /* 135 */ { 0, "sysfs" }, + /* 136 */ { 0, "personality" }, + /* 137 */ { 0, "afs_syscall" }, + /* 138 */ { 0, "setfsuid" }, + /* 139 */ { 0, "setfsgid" }, + /* 140 */ { 0, "llseek" }, + /* 141 */ { 0, "getdents" }, + /* 142 */ { 0, "newselect" }, + /* 143 */ { 0, "flock" }, + /* 144 */ { 0, "msync" }, + /* 145 */ { 0, "readv" }, + /* 146 */ { 0, "writev" }, + /* 147 */ { 0, "getsid" }, + /* 148 */ { 0, "fdatasync" }, + /* 149 */ { 0, "sysctl" }, + /* 150 */ { 0, "mlock" }, + /* 151 */ { 0, "munlock" }, + /* 152 */ { 0, "mlockall" }, + /* 153 */ { 0, "munlockall" }, + /* 154 */ { 0, "sched_setparam" }, + /* 155 */ { 0, "sched_getparam" }, + /* 156 */ { 0, "sched_setscheduler" }, + /* 157 */ { 0, "sched_getscheduler" }, + /* 158 */ { 0, "sched_yield" }, + /* 159 */ { 0, "sched_get_priority_max" }, + /* 160 */ { 0, "sched_get_priority_min" }, + /* 161 */ { 0, "sched_rr_get_interval" }, +}; + +static char *(linux_error_names[]) = { + /* 0 */ "ESUCCESS", + /* 1 */ "EPERM", + /* 2 */ "ENOENT", + /* 3 */ "ESRCH", + /* 4 */ "EINTR", + /* 5 */ "EIO", + /* 6 */ "ENXIO", + /* 7 */ "E2BIG", + /* 8 */ "ENOEXEC", + /* 9 */ "EBADF", + /* 10 */ "ECHILD", + /* 11 */ "EAGAIN", + /* 12 */ "ENOMEM", + /* 13 */ "EACCES", + /* 14 */ "EFAULT", + /* 15 */ "ENOTBLK", + /* 16 */ "EBUSY", + /* 17 */ "EEXIST", + /* 18 */ "EXDEV", + /* 19 */ "ENODEV", + /* 20 */ "ENOTDIR", + /* 21 */ "EISDIR", + /* 22 */ "EINVAL", + /* 23 */ "ENFILE", + /* 24 */ "EMFILE", + /* 25 */ "ENOTTY", + /* 26 */ "ETXTBSY", + /* 27 */ "EFBIG", + /* 28 */ "ENOSPC", + /* 29 */ "ESPIPE", + /* 30 */ "EROFS", + /* 31 */ "EMLINK", + /* 32 */ "EPIPE", + /* 33 */ "EDOM", + /* 34 */ "ERANGE", + /* 35 */ "EDEADLK", + /* 36 */ "ENAMETOOLONG", + /* 37 */ "ENOLCK", + /* 38 */ "ENOSYS", + /* 39 */ "ENOTEMPTY", + /* 40 */ "ELOOP", + /* 41 */ 0, + /* 42 */ "ENOMSG", + /* 43 */ "EIDRM", + /* 44 */ "ECHRNG", + /* 45 */ "EL2NSYNC", + /* 46 */ "EL3HLT", + /* 47 */ "EL3RST", + /* 48 */ "ELNRNG", + /* 49 */ "EUNATCH", + /* 50 */ "ENOCSI", + /* 51 */ "EL2HLT", + /* 52 */ "EBADE", + /* 53 */ "EBADR", + /* 54 */ "EXFULL", + /* 55 */ "ENOANO", + /* 56 */ "EBADRQC", + /* 57 */ "EBADSLT", + /* 58 */ "EDEADLOCK", + /* 59 */ "EBFONT", + /* 60 */ "ENOSTR", + /* 61 */ "ENODATA", + /* 62 */ "ETIME", + /* 63 */ "ENOSR", + /* 64 */ "ENONET", + /* 65 */ "ENOPKG", + /* 66 */ "EREMOTE", + /* 67 */ "ENOLINK", + /* 68 */ "EADV", + /* 69 */ "ESRMNT", + /* 70 */ "ECOMM", + /* 71 */ "EPROTO", + /* 72 */ "EMULTIHOP", + /* 73 */ "EDOTDOT", + /* 74 */ "EBADMSG", + /* 75 */ "EOVERFLOW", + /* 76 */ "ENOTUNIQ", + /* 77 */ "EBADFD", + /* 78 */ "EREMCHG", + /* 79 */ "ELIBACC", + /* 80 */ "ELIBBAD", + /* 81 */ "ELIBSCN", + /* 82 */ "ELIBMAX", + /* 83 */ "ELIBEXEC", + /* 84 */ "EILSEQ", + /* 85 */ "ERESTART", + /* 86 */ "ESTRPIPE", + /* 87 */ "EUSERS", + /* 88 */ "ENOTSOCK", + /* 89 */ "EDESTADDRREQ", + /* 90 */ "EMSGSIZE", + /* 91 */ "EPROTOTYPE", + /* 92 */ "ENOPROTOOPT", + /* 93 */ "EPROTONOSUPPORT", + /* 94 */ "ESOCKTNOSUPPORT", + /* 95 */ "EOPNOTSUPP", + /* 96 */ "EPFNOSUPPORT", + /* 97 */ "EAFNOSUPPORT", + /* 98 */ "EADDRINUSE", + /* 99 */ "EADDRNOTAVAIL", + /* 100 */ "ENETDOWN", + /* 101 */ "ENETUNREACH", + /* 102 */ "ENETRESET", + /* 103 */ "ECONNABORTED", + /* 104 */ "ECONNRESET", + /* 105 */ "ENOBUFS", + /* 106 */ "EISCONN", + /* 107 */ "ENOTCONN", + /* 108 */ "ESHUTDOWN", + /* 109 */ "ETOOMANYREFS", + /* 110 */ "ETIMEDOUT", + /* 111 */ "ECONNREFUSED", + /* 112 */ "EHOSTDOWN", + /* 113 */ "EHOSTUNREACH", + /* 114 */ "EALREADY", + /* 115 */ "EINPROGRESS", + /* 116 */ "ESTALE", + /* 117 */ "EUCLEAN", + /* 118 */ "ENOTNAM", + /* 119 */ "ENAVAIL", + /* 120 */ "EISNAM", + /* 121 */ "EREMOTEIO", + /* 122 */ "EDQUOT", +}; + +static char *(linux_signal_names[]) = { + /* 0 */ 0, + /* 1 */ "SIGHUP", + /* 2 */ "SIGINT", + /* 3 */ "SIGQUIT", + /* 4 */ "SIGILL", + /* 5 */ "SIGTRAP", + /* 6 */ "SIGABRT", + /* 6 */ "SIGIOT", + /* 7 */ "SIGBUS", + /* 8 */ "SIGFPE", + /* 9 */ "SIGKILL", + /* 10 */ "SIGUSR1", + /* 11 */ "SIGSEGV", + /* 12 */ "SIGUSR2", + /* 13 */ "SIGPIPE", + /* 14 */ "SIGALRM", + /* 15 */ "SIGTERM", + /* 16 */ "SIGSTKFLT", + /* 17 */ "SIGCHLD", + /* 18 */ "SIGCONT", + /* 19 */ "SIGSTOP", + /* 20 */ "SIGTSTP", + /* 21 */ "SIGTTIN", + /* 22 */ "SIGTTOU", + /* 23 */ "SIGURG", + /* 24 */ "SIGXCPU", + /* 25 */ "SIGXFSZ", + /* 26 */ "SIGVTALRM", + /* 27 */ "SIGPROF", + /* 28 */ "SIGWINCH", + /* 29 */ "SIGIO", + /* 30 */ "SIGPWR", + /* 31 */ "SIGUNUSED", +}; + +static emul_syscall emul_linux_syscalls = { + linux_descriptors, + sizeof(linux_descriptors) / sizeof(linux_descriptors[0]), + linux_error_names, + sizeof(linux_error_names) / sizeof(linux_error_names[0]), + linux_signal_names, + sizeof(linux_signal_names) / sizeof(linux_signal_names[0]), +}; + + +/* Linux's os_emul interface, most are just passed on to the generic + syscall stuff */ + +static os_emul_data * +emul_linux_create(device *root, + bfd *image, + const char *name) +{ + /* check that this emulation is really for us */ + if (name != NULL && strcmp(name, "linux") != 0) + return NULL; + + if (image == NULL) + return NULL; + + return emul_unix_create(root, image, "linux", &emul_linux_syscalls); +} + +static void +emul_linux_init(os_emul_data *emul_data, + int nr_cpus) +{ + /* nothing yet */ +} + +static void +emul_linux_system_call(cpu *processor, + unsigned_word cia, + os_emul_data *emul_data) +{ + emul_do_system_call(emul_data, + emul_data->syscalls, + cpu_registers(processor)->gpr[0], + 3, /*r3 contains arg0*/ + processor, + cia); +} + +const os_emul emul_linux = { + "linux", + emul_linux_create, + emul_linux_init, + emul_linux_system_call, + 0, /*instruction_call*/ + 0 /*data*/ +}; + +#endif /* _EMUL_UNIX_C_ */ diff --git a/sim/ppc/emul_unix.h b/sim/ppc/emul_unix.h new file mode 100644 index 00000000000..6695cc7aa81 --- /dev/null +++ b/sim/ppc/emul_unix.h @@ -0,0 +1,28 @@ +/* This file is part of the program psim. + + Copyright (C) 1996, 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 _EMUL_UNIX_H_ +#define _EMUL_UNIX_H_ + +extern const os_emul emul_solaris; +extern const os_emul emul_linux; + +#endif diff --git a/sim/ppc/events.c b/sim/ppc/events.c new file mode 100644 index 00000000000..83846222cff --- /dev/null +++ b/sim/ppc/events.c @@ -0,0 +1,387 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1998, 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_ + +#include "basics.h" +#include "events.h" + +#include <signal.h> + +#if !defined (SIM_EVENTS_POLL_RATE) +#define SIM_EVENTS_POLL_RATE 0x1000 +#endif + + + +/* 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. + + 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. + + Initially, the clock is started at time one (1) with TIME_OF_EVENT + == 0 and TIME_FROM_EVENT == -1. + + 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 { + int processing; + event_entry *queue; + event_entry *volatile held; + event_entry *volatile *volatile held_end; + signed64 time_of_event; + signed64 time_from_event; +}; + + +STATIC_INLINE_EVENTS\ +(void) +sim_events_poll (void *data) +{ + event_queue *queue = data; + /* just re-schedule in 1000 million ticks time */ + event_queue_schedule (queue, SIM_EVENTS_POLL_RATE, sim_events_poll, queue); + sim_io_poll_quit (); +} + + +INLINE_EVENTS\ +(event_queue *) +event_queue_create(void) +{ + event_queue *new_event_queue = ZALLOC(event_queue); + + new_event_queue->processing = 0; + 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; +} + + +INLINE_EVENTS\ +(void) +event_queue_init(event_queue *queue) +{ + event_entry *event; + + /* drain the interrupt queue */ + { +#if defined(HAVE_SIGPROCMASK) && defined(SIG_SETMASK) + sigset_t old_mask; + sigset_t new_mask; + sigfillset(&new_mask); + /*-LOCK-*/ sigprocmask(SIG_SETMASK, &new_mask, &old_mask); +#endif + event = queue->held; + while (event != NULL) { + event_entry *dead = event; + event = event->next; + zfree(dead); + } + queue->held = NULL; + queue->held_end = &queue->held; +#if defined(HAVE_SIGPROCMASK) && defined(SIG_SETMASK) + /*-UNLOCK-*/ sigprocmask(SIG_SETMASK, &old_mask, NULL); +#endif + } + + /* drain the normal queue */ + event = queue->queue; + while (event != NULL) { + event_entry *dead = event; + event = event->next; + zfree(dead); + } + queue->queue = NULL; + + /* wind time back to one */ + queue->processing = 0; + queue->time_of_event = 0; + queue->time_from_event = -1; + + /* schedule our initial counter event */ + event_queue_schedule (queue, 0, sim_events_poll, queue); +} + +INLINE_EVENTS\ +(signed64) +event_queue_time(event_queue *queue) +{ + return queue->time_of_event - queue->time_from_event; +} + +STATIC_INLINE_EVENTS\ +(void) +update_time_from_event(event_queue *events) +{ + signed64 current_time = event_queue_time(events); + if (events->queue != NULL) { + events->time_from_event = (events->queue->time_of_event - current_time); + events->time_of_event = events->queue->time_of_event; + } + else { + events->time_of_event = current_time - 1; + events->time_from_event = -1; + } + ASSERT(current_time == event_queue_time(events)); + ASSERT((events->time_from_event >= 0) == (events->queue != NULL)); +} + +STATIC_INLINE_EVENTS\ +(void) +insert_event_entry(event_queue *events, + event_entry *new_event, + signed64 delta) +{ + event_entry *curr; + event_entry **prev; + signed64 time_of_event; + + if (delta < 0) + error("what is past is past!\n"); + + /* compute when the event should occure */ + time_of_event = event_queue_time(events) + delta; + + /* find the queue insertion point - things are time ordered */ + prev = &events->queue; + curr = events->queue; + while (curr != NULL && time_of_event >= curr->time_of_event) { + ASSERT(curr->next == NULL + || curr->time_of_event <= curr->next->time_of_event); + prev = &curr->next; + curr = curr->next; + } + ASSERT(curr == NULL || time_of_event < curr->time_of_event); + + /* insert it */ + new_event->next = curr; + *prev = new_event; + new_event->time_of_event = time_of_event; + + /* adjust the time until the first event */ + update_time_from_event(events); +} + +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); + TRACE(trace_events, ("event scheduled at %ld - tag 0x%lx - time %ld, handler 0x%lx, data 0x%lx\n", + (long)event_queue_time(events), + (long)new_event, + (long)new_event->time_of_event, + (long)new_event->handler, + (long)new_event->data)); + return (event_entry_tag)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; + + { +#if defined(HAVE_SIGPROCMASK) && defined(SIG_SETMASK) + sigset_t old_mask; + sigset_t new_mask; + sigfillset(&new_mask); + /*-LOCK-*/ sigprocmask(SIG_SETMASK, &new_mask, &old_mask); +#endif + if (events->held == NULL) { + events->held = new_event; + } + else { + *events->held_end = new_event; + } + events->held_end = &new_event->next; +#if defined(HAVE_SIGPROCMASK) && defined(SIG_SETMASK) + /*-UNLOCK-*/ sigprocmask(SIG_SETMASK, &old_mask, NULL); +#endif + } + + TRACE(trace_events, ("event scheduled at %ld - tag 0x%lx - time %ld, handler 0x%lx, data 0x%lx\n", + (long)event_queue_time(events), + (long)new_event, + (long)new_event->time_of_event, + (long)new_event->handler, + (long)new_event->data)); + + return (event_entry_tag)new_event; +} + + +INLINE_EVENTS\ +(void) +event_queue_deschedule(event_queue *events, + event_entry_tag event_to_remove) +{ + event_entry *to_remove = (event_entry*)event_to_remove; + ASSERT((events->time_from_event >= 0) == (events->queue != NULL)); + 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 != to_remove; + ptr_to_current = ¤t->next, current = *ptr_to_current); + if (current == to_remove) { + *ptr_to_current = current->next; + TRACE(trace_events, ("event descheduled at %ld - tag 0x%lx - time %ld, handler 0x%lx, data 0x%lx\n", + (long)event_queue_time(events), + (long)event_to_remove, + (long)current->time_of_event, + (long)current->handler, + (long)current->data)); + zfree(current); + update_time_from_event(events); + } + else { + TRACE(trace_events, ("event descheduled at %ld - tag 0x%lx - not found\n", + (long)event_queue_time(events), + (long)event_to_remove)); + } + } + ASSERT((events->time_from_event >= 0) == (events->queue != NULL)); +} + + + + +INLINE_EVENTS\ +(int) +event_queue_tick(event_queue *events) +{ + signed64 time_from_event; + + /* we should only be here when the previous tick has been fully processed */ + ASSERT(!events->processing); + + /* move any events that were queued by any signal handlers onto the + real event queue. BTW: When inlining, having this code here, + instead of in event_queue_process() causes GCC to put greater + weight on keeping the pointer EVENTS in a register. This, in + turn results in better code being output. */ + if (events->held != NULL) { + event_entry *held_events; + event_entry *curr_event; + + { +#if defined(HAVE_SIGPROCMASK) && defined(SIG_SETMASK) + sigset_t old_mask; + sigset_t new_mask; + sigfillset(&new_mask); + /*-LOCK-*/ sigprocmask(SIG_SETMASK, &new_mask, &old_mask); +#endif + held_events = events->held; + events->held = NULL; + events->held_end = &events->held; +#if defined(HAVE_SIGPROCMASK) && defined(SIG_SETMASK) + /*-UNLOCK-*/ sigprocmask(SIG_SETMASK, &old_mask, NULL); +#endif + } + + 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 */ + time_from_event = events->time_from_event; + events->time_from_event = time_from_event - 1; + return time_from_event == 0; +} + + + +INLINE_EVENTS\ +(void) +event_queue_process(event_queue *events) +{ + signed64 event_time = event_queue_time(events); + + ASSERT((events->time_from_event == -1 && events->queue != NULL) + || events->processing); /* something to do */ + + /* consume all events for this or earlier times. Be careful to + allow a new event to appear under our feet */ + events->processing = 1; + while (events->queue != NULL + && events->queue->time_of_event <= event_time) { + event_entry *to_do = events->queue; + event_handler *handler = to_do->handler; + void *data = to_do->data; + events->queue = to_do->next; + TRACE(trace_events, ("event issued at %ld - tag 0x%lx - handler 0x%lx, data 0x%lx\n", + (long)event_time, + (long)to_do, + (long)handler, + (long)data)); + zfree(to_do); + handler(data); + } + events->processing = 0; + + /* re-caculate time for new events */ + update_time_from_event(events); +} + + +#endif /* _EVENTS_C_ */ diff --git a/sim/ppc/events.h b/sim/ppc/events.h new file mode 100644 index 00000000000..be7be50cfcb --- /dev/null +++ b/sim/ppc/events.h @@ -0,0 +1,79 @@ +/* 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_ + +/* typedef struct _event_queue event_queue; */ +/* typedef struct _event_entry_tag *event_entry_tag; */ + +typedef void event_handler(void *data); + +INLINE_EVENTS\ +(event_queue *) event_queue_create +(void); + +INLINE_EVENTS\ +(void) event_queue_init +(event_queue *queue); + + +/* (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/filter.c b/sim/ppc/filter.c new file mode 100644 index 00000000000..c901a1777f1 --- /dev/null +++ b/sim/ppc/filter.c @@ -0,0 +1,150 @@ +/* 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. + + */ + + +#include <stdio.h> + +#include "config.h" + +#ifdef HAVE_STRING_H +#include <string.h> +#else +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif +#endif + +#include "misc.h" +#include "filter.h" + +struct _filter { + char *flag; + filter *next; +}; + + +filter * +new_filter(const char *filt, + filter *filters) +{ + while (strlen(filt) > 0) { + filter *new_filter; + /* break up the filt list */ + char *end = strchr(filt, ','); + char *next; + int len; + if (end == NULL) { + end = strchr(filt, '\0'); + next = end; + } + else { + next = end + 1; + } + len = end - filt; + /* add to filter list */ + new_filter = ZALLOC(filter); + new_filter->flag = (char*)zalloc(len + 1); + strncpy(new_filter->flag, filt, len); + new_filter->next = filters; + filters = new_filter; + filt = next; + } + return filters; +} + + +int +is_filtered_out(const char *flags, + filter *filters) +{ + while (strlen(flags) > 0) { + int present; + filter *filt = filters; + /* break the string up */ + char *end = strchr(flags, ','); + char *next; + int len; + if (end == NULL) { + end = strchr(flags, '\0'); + next = end; + } + else { + next = end + 1; + } + len = end - flags; + /* check that it is present */ + present = 0; + filt = filters; + while (filt != NULL) { + if (strncmp(flags, filt->flag, len) == 0 + && strlen(filt->flag) == len) { + present = 1; + break; + } + filt = filt->next; + } + if (!present) + return 1; + flags = next; + } + return 0; +} + + +int +it_is(const char *flag, + const 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; +} + + +#ifdef MAIN +int +main(int argc, char **argv) +{ + filter *filters = NULL; + int i; + if (argc < 2) { + printf("Usage: filter <flags> <filter> ...\n"); + exit (1); + } + /* load the filter up */ + for (i = 2; i < argc; i++) + filters = new_filter(argv[i], filters); + if (is_filtered_out(argv[1], filters)) + printf("fail\n"); + else + printf("pass\n"); + return 0; +} +#endif diff --git a/sim/ppc/filter.h b/sim/ppc/filter.h new file mode 100644 index 00000000000..814f704f8d2 --- /dev/null +++ b/sim/ppc/filter.h @@ -0,0 +1,43 @@ +/* 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. + + */ + + +typedef struct _filter filter; + + +/* append the filter onto the end of the list */ + +extern filter *new_filter +(const char *filt, + filter *filters); + + +/* returns true if the flags are non empty and some are missing from the filter list */ + +extern int is_filtered_out +(const char *flags, + filter *filters); + +/* true if the flag is in the list */ + +extern int it_is +(const char *flag, + const char *flags); + diff --git a/sim/ppc/filter_filename.c b/sim/ppc/filter_filename.c new file mode 100644 index 00000000000..6d62d7a35ae --- /dev/null +++ b/sim/ppc/filter_filename.c @@ -0,0 +1,38 @@ +/* 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. + + */ + +#include "config.h" +#include "ppc-config.h" +#include "filter_filename.h" + +/* Shorten traces by eliminating the directory component to filenames. */ +extern const char * +filter_filename (const char *filename) +{ + const char *p = filename; + const char *last = filename; + int ch; + + while ((ch = *p++) != '\0' && ch != ':') + if (ch == '/') + last = p; + + return last; +} diff --git a/sim/ppc/filter_filename.h b/sim/ppc/filter_filename.h new file mode 100644 index 00000000000..a60b4f2d7dc --- /dev/null +++ b/sim/ppc/filter_filename.h @@ -0,0 +1,27 @@ +/* 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 _FILTER_FILENAME_H +#define _FILTER_FILENAME_H + +/* Remove directory part from filename */ +extern const char * +filter_filename(const char *filename); +#endif diff --git a/sim/ppc/gen-icache.c b/sim/ppc/gen-icache.c new file mode 100644 index 00000000000..0d58ab1873a --- /dev/null +++ b/sim/ppc/gen-icache.c @@ -0,0 +1,675 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1997, 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. + + */ + + +#include "misc.h" +#include "lf.h" +#include "table.h" + +#include "filter.h" + +#include "ld-decode.h" +#include "ld-cache.h" +#include "ld-insn.h" + +#include "igen.h" + +#include "gen-semantics.h" +#include "gen-idecode.h" +#include "gen-icache.h" + + + +static void +print_icache_function_header(lf *file, + const char *basename, + insn_bits *expanded_bits, + int is_function_definition) +{ + lf_printf(file, "\n"); + lf_print_function_type(file, ICACHE_FUNCTION_TYPE, "EXTERN_ICACHE", " "); + print_function_name(file, + basename, + expanded_bits, + function_name_prefix_icache); + lf_printf(file, "\n(%s)", ICACHE_FUNCTION_FORMAL); + if (!is_function_definition) + lf_printf(file, ";"); + lf_printf(file, "\n"); +} + + +void +print_icache_declaration(insn_table *entry, + lf *file, + void *data, + insn *instruction, + int depth) +{ + if (generate_expanded_instructions) { + ASSERT(entry->nr_insn == 1); + print_icache_function_header(file, + entry->insns->file_entry->fields[insn_name], + entry->expanded_bits, + 0/* is not function definition */); + } + else { + print_icache_function_header(file, + instruction->file_entry->fields[insn_name], + NULL, + 0/* is not function definition */); + } +} + + + +static void +print_icache_extraction(lf *file, + insn *instruction, + const char *entry_name, + const char *entry_type, + const char *entry_expression, + const char *original_name, + const char *file_name, + int line_nr, + insn_field *cur_field, + insn_bits *bits, + icache_decl_type what_to_declare, + icache_body_type what_to_do, + const char *reason) +{ + const char *expression; + ASSERT(entry_name != NULL); + + /* Define a storage area for the cache element */ + if (what_to_declare == undef_variables) { + /* We've finished with the value - destory it */ + lf_indent_suppress(file); + lf_printf(file, "#undef %s\n", entry_name); + return; + } + else if (what_to_declare == define_variables) { + lf_indent_suppress(file); + lf_printf(file, "#define %s ", entry_name); + } + else { + if (file_name != NULL) + lf_print__external_reference(file, line_nr, file_name); + lf_printf(file, "%s const %s UNUSED = ", + entry_type == NULL ? "unsigned" : entry_type, + entry_name); + } + + /* define a value for that storage area as determined by what is in + the cache */ + if (bits != NULL + && strcmp(entry_name, cur_field->val_string) == 0 + && ((bits->opcode->is_boolean && bits->value == 0) + || (!bits->opcode->is_boolean))) { + /* The simple field has been made constant (as a result of + expanding instructions or similar). Remember that for a + boolean field, value is either 0 (implying the required + boolean_constant) or nonzero (implying some other value and + handled later below) - Define the variable accordingly */ + expression = "constant field"; + ASSERT(bits->field == cur_field); + ASSERT(entry_type == NULL); + if (bits->opcode->is_boolean) + lf_printf(file, "%d", bits->opcode->boolean_constant); + else if (bits->opcode->last < bits->field->last) + lf_printf(file, "%d", + bits->value << (bits->field->last - bits->opcode->last)); + else + lf_printf(file, "%d", bits->value); + } + else if (bits != NULL + && original_name != NULL + && strncmp(entry_name, + original_name, strlen(original_name)) == 0 + && strncmp(entry_name + strlen(original_name), + "_is_", strlen("_is_")) == 0 + && ((bits->opcode->is_boolean + && (atol(entry_name + strlen(original_name) + strlen("_is_")) + == bits->opcode->boolean_constant)) + || (!bits->opcode->is_boolean))) { + expression = "constant compare"; + /* An entry, derived from ORIGINAL_NAME, is testing to see of the + ORIGINAL_NAME has a specific constant value. That value + matching a boolean or constant field */ + if (bits->opcode->is_boolean) + lf_printf(file, "%d /* %s == %d */", + bits->value == 0, + original_name, + bits->opcode->boolean_constant); + else if (bits->opcode->last < bits->field->last) + lf_printf(file, "%d /* %s == %d */", + (atol(entry_name + strlen(original_name) + strlen("_is_")) + == (bits->value << (bits->field->last - bits->opcode->last))), + original_name, + (bits->value << (bits->field->last - bits->opcode->last))); + else + lf_printf(file, "%d /* %s == %d */", + (atol(entry_name + strlen(original_name) + strlen("_is_")) + == bits->value), + original_name, + bits->value); + } + else { + /* put the field in the local variable, possibly also enter it + into the cache */ + expression = "extraction"; + /* handle the cache */ + if ((what_to_do & get_values_from_icache) + || (what_to_do & put_values_in_icache)) { + lf_printf(file, "cache_entry->crack.%s.%s", + instruction->file_entry->fields[insn_form], + entry_name); + if (what_to_do & put_values_in_icache) /* also put it in the cache? */ + lf_printf(file, " = "); + } + if ((what_to_do & put_values_in_icache) + || what_to_do == do_not_use_icache) { + if (cur_field != NULL && strcmp(entry_name, cur_field->val_string) == 0) + lf_printf(file, "EXTRACTED32(instruction, %d, %d)", + i2target(hi_bit_nr, cur_field->first), + i2target(hi_bit_nr, cur_field->last)); + else if (entry_expression != NULL) + lf_printf(file, "%s", entry_expression); + else + lf_printf(file, "eval_%s", entry_name); + } + } + + if (!((what_to_declare == define_variables) + || (what_to_declare == undef_variables))) + lf_printf(file, ";"); + if (reason != NULL) + lf_printf(file, " /* %s - %s */", reason, expression); + lf_printf(file, "\n"); +} + + +void +print_icache_body(lf *file, + insn *instruction, + insn_bits *expanded_bits, + cache_table *cache_rules, + icache_decl_type what_to_declare, + icache_body_type what_to_do) +{ + insn_field *cur_field; + + /* extract instruction fields */ + lf_printf(file, "/* extraction: %s ", + instruction->file_entry->fields[insn_format]); + switch (what_to_declare) { + case define_variables: + lf_printf(file, "#define"); + break; + case declare_variables: + lf_printf(file, "declare"); + break; + case undef_variables: + lf_printf(file, "#undef"); + break; + } + lf_printf(file, " "); + switch (what_to_do) { + case get_values_from_icache: + lf_printf(file, "get-values-from-icache"); + break; + case put_values_in_icache: + lf_printf(file, "put-values-in-icache"); + break; + case both_values_and_icache: + lf_printf(file, "get-values-from-icache|put-values-in-icache"); + break; + case do_not_use_icache: + lf_printf(file, "do-not-use-icache"); + break; + } + lf_printf(file, " */\n"); + + for (cur_field = instruction->fields->first; + cur_field->first < insn_bit_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 */ + { + cache_table *cache_rule; + for (cache_rule = cache_rules; + cache_rule != NULL; + cache_rule = cache_rule->next) { + if (strcmp(cur_field->val_string, cache_rule->field_name) == 0) { + found_rule = 1; + if (cache_rule->type == scratch_value + && ((what_to_do & put_values_in_icache) + || what_to_do == do_not_use_icache)) + print_icache_extraction(file, + instruction, + cache_rule->derived_name, + cache_rule->type_def, + cache_rule->expression, + cache_rule->field_name, + cache_rule->file_entry->file_name, + cache_rule->file_entry->line_nr, + cur_field, + bits, + what_to_declare, + do_not_use_icache, + "icache scratch"); + else if (cache_rule->type == compute_value + && ((what_to_do & get_values_from_icache) + || what_to_do == do_not_use_icache)) + print_icache_extraction(file, + instruction, + cache_rule->derived_name, + cache_rule->type_def, + cache_rule->expression, + cache_rule->field_name, + cache_rule->file_entry->file_name, + cache_rule->file_entry->line_nr, + cur_field, + bits, + what_to_declare, + do_not_use_icache, + "semantic compute"); + else if (cache_rule->type == cache_value + && ((what_to_declare != undef_variables) + || !(what_to_do & put_values_in_icache))) + print_icache_extraction(file, + instruction, + cache_rule->derived_name, + cache_rule->type_def, + cache_rule->expression, + cache_rule->field_name, + cache_rule->file_entry->file_name, + cache_rule->file_entry->line_nr, + cur_field, + bits, + ((what_to_do & put_values_in_icache) + ? declare_variables + : what_to_declare), + what_to_do, + "in icache"); + } + } + } + /* No rule at all, assume that this is needed in the semantic + function (when values are extracted from the icache) and + hence must be put into the cache */ + if (found_rule == 0 + && ((what_to_declare != undef_variables) + || !(what_to_do & put_values_in_icache))) + print_icache_extraction(file, + instruction, + cur_field->val_string, + NULL, NULL, NULL, /* type, exp, orig */ + instruction->file_entry->file_name, + instruction->file_entry->line_nr, + cur_field, + bits, + ((what_to_do & put_values_in_icache) + ? declare_variables + : what_to_declare), + what_to_do, + "default in icache"); + /* any thing else ... */ + } + } + + lf_print__internal_reference(file); + + if ((code & generate_with_insn_in_icache)) { + lf_printf(file, "\n"); + print_icache_extraction(file, + instruction, + "insn", + "instruction_word", + "instruction", + NULL, /* origin */ + NULL, 0, /* file_name & line_nr */ + NULL, NULL, + what_to_declare, + what_to_do, + NULL); + } +} + + + +typedef struct _icache_tree icache_tree; +struct _icache_tree { + char *name; + icache_tree *next; + icache_tree *children; +}; + +static 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 = ZALLOC(icache_tree); + new_tree->name = name; + new_tree->next = cur_tree; + *ptr_to_cur_tree = new_tree; + return new_tree; +} + + +static icache_tree * +insn_table_cache_fields(insn_table *table) +{ + icache_tree *tree = ZALLOC(icache_tree); + 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; +} + + + +extern void +print_icache_struct(insn_table *instructions, + cache_table *cache_rules, + lf *file) +{ + icache_tree *tree = insn_table_cache_fields(instructions); + + lf_printf(file, "\n"); + lf_printf(file, "#define WITH_IDECODE_CACHE_SIZE %d\n", + (code & generate_with_icache) ? icache_size : 0); + lf_printf(file, "\n"); + + /* create an instruction cache if being used */ + if ((code & generate_with_icache)) { + icache_tree *form; + lf_printf(file, "typedef struct _idecode_cache {\n"); + lf_printf(file, " unsigned_word address;\n"); + lf_printf(file, " void *semantic;\n"); + lf_printf(file, " union {\n"); + for (form = tree->children; + form != NULL; + form = form->next) { + icache_tree *field; + lf_printf(file, " struct {\n"); + if (code & generate_with_insn_in_icache) + lf_printf(file, " instruction_word insn;\n"); + for (field = form->children; + field != NULL; + field = field->next) { + cache_table *cache_rule; + int found_rule = 0; + for (cache_rule = cache_rules; + cache_rule != NULL; + cache_rule = cache_rule->next) { + if (strcmp(field->name, cache_rule->field_name) == 0) { + found_rule = 1; + if (cache_rule->derived_name != NULL) + lf_printf(file, " %s %s; /* %s */\n", + (cache_rule->type_def == NULL + ? "unsigned" + : cache_rule->type_def), + cache_rule->derived_name, + cache_rule->field_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"); + } + else { + /* alernativly, since no cache, emit a dummy definition for + idecode_cache so that code refering to the type can still compile */ + lf_printf(file, "typedef void idecode_cache;\n"); + } + lf_printf(file, "\n"); +} + + + +static void +print_icache_function(lf *file, + insn *instruction, + insn_bits *expanded_bits, + opcode_field *opcodes, + cache_table *cache_rules) +{ + int indent; + + /* generate code to enter decoded instruction into the icache */ + lf_printf(file, "\n"); + lf_print_function_type(file, ICACHE_FUNCTION_TYPE, "EXTERN_ICACHE", "\n"); + indent = print_function_name(file, + instruction->file_entry->fields[insn_name], + expanded_bits, + function_name_prefix_icache); + lf_indent(file, +indent); + lf_printf(file, "(%s)\n", ICACHE_FUNCTION_FORMAL); + lf_indent(file, -indent); + + /* function header */ + lf_printf(file, "{\n"); + lf_indent(file, +2); + + print_my_defines(file, expanded_bits, instruction->file_entry); + print_itrace(file, instruction->file_entry, 1/*putting-value-in-cache*/); + + print_idecode_validate(file, instruction, opcodes); + + lf_printf(file, "\n"); + lf_printf(file, "{\n"); + lf_indent(file, +2); + if ((code & generate_with_semantic_icache)) + lf_printf(file, "unsigned_word nia;\n"); + print_icache_body(file, + instruction, + expanded_bits, + cache_rules, + ((code & generate_with_direct_access) + ? define_variables + : declare_variables), + ((code & generate_with_semantic_icache) + ? both_values_and_icache + : put_values_in_icache)); + + lf_printf(file, "\n"); + lf_printf(file, "cache_entry->address = cia;\n"); + lf_printf(file, "cache_entry->semantic = "); + print_function_name(file, + instruction->file_entry->fields[insn_name], + expanded_bits, + function_name_prefix_semantics); + lf_printf(file, ";\n"); + lf_printf(file, "\n"); + + if ((code & generate_with_semantic_icache)) { + lf_printf(file, "/* semantic routine */\n"); + print_semantic_body(file, + instruction, + expanded_bits, + opcodes); + lf_printf(file, "return nia;\n"); + } + + if (!(code & generate_with_semantic_icache)) { + lf_printf(file, "/* return the function proper */\n"); + lf_printf(file, "return "); + print_function_name(file, + instruction->file_entry->fields[insn_name], + expanded_bits, + function_name_prefix_semantics); + lf_printf(file, ";\n"); + } + + if ((code & generate_with_direct_access)) + print_icache_body(file, + instruction, + expanded_bits, + cache_rules, + undef_variables, + ((code & generate_with_semantic_icache) + ? both_values_and_icache + : put_values_in_icache)); + + lf_indent(file, -2); + lf_printf(file, "}\n"); + lf_indent(file, -2); + lf_printf(file, "}\n"); +} + + +void +print_icache_definition(insn_table *entry, + lf *file, + void *data, + insn *instruction, + int depth) +{ + cache_table *cache_rules = (cache_table*)data; + if (generate_expanded_instructions) { + ASSERT(entry->nr_insn == 1 + && entry->opcode == NULL + && entry->parent != NULL + && entry->parent->opcode != NULL); + ASSERT(entry->nr_insn == 1 + && entry->opcode == NULL + && entry->parent != NULL + && entry->parent->opcode != NULL + && entry->parent->opcode_rule != NULL); + print_icache_function(file, + entry->insns, + entry->expanded_bits, + entry->opcode, + cache_rules); + } + else { + print_icache_function(file, + instruction, + NULL, + NULL, + cache_rules); + } +} + + + +void +print_icache_internal_function_declaration(insn_table *table, + lf *file, + void *data, + table_entry *function) +{ + ASSERT((code & generate_with_icache) != 0); + if (it_is("internal", function->fields[insn_flags])) { + lf_printf(file, "\n"); + lf_print_function_type(file, ICACHE_FUNCTION_TYPE, "INLINE_ICACHE", + "\n"); + print_function_name(file, + function->fields[insn_name], + NULL, + function_name_prefix_icache); + lf_printf(file, "\n(%s);\n", ICACHE_FUNCTION_FORMAL); + } +} + + +void +print_icache_internal_function_definition(insn_table *table, + lf *file, + void *data, + table_entry *function) +{ + ASSERT((code & generate_with_icache) != 0); + if (it_is("internal", function->fields[insn_flags])) { + lf_printf(file, "\n"); + lf_print_function_type(file, ICACHE_FUNCTION_TYPE, "INLINE_ICACHE", + "\n"); + print_function_name(file, + function->fields[insn_name], + NULL, + function_name_prefix_icache); + lf_printf(file, "\n(%s)\n", ICACHE_FUNCTION_FORMAL); + lf_printf(file, "{\n"); + lf_indent(file, +2); + lf_printf(file, "/* semantic routine */\n"); + table_entry_print_cpp_line_nr(file, function); + if ((code & generate_with_semantic_icache)) { + lf_print__c_code(file, function->annex); + lf_printf(file, "error(\"Internal function must longjump\\n\");\n"); + lf_printf(file, "return 0;\n"); + } + else { + lf_printf(file, "return "); + print_function_name(file, + function->fields[insn_name], + NULL, + function_name_prefix_semantics); + lf_printf(file, ";\n"); + } + + lf_print__internal_reference(file); + lf_indent(file, -2); + lf_printf(file, "}\n"); + } +} diff --git a/sim/ppc/gen-icache.h b/sim/ppc/gen-icache.h new file mode 100644 index 00000000000..7d9e0e16c01 --- /dev/null +++ b/sim/ppc/gen-icache.h @@ -0,0 +1,68 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1997 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. + + */ + + + +/* Output code to manipulate the instruction cache: either create it + or reference it */ + +typedef enum { + declare_variables, + define_variables, + undef_variables, +} icache_decl_type; + +typedef enum { + do_not_use_icache = 0, + get_values_from_icache = 0x1, + put_values_in_icache = 0x2, + both_values_and_icache = 0x3, +} icache_body_type; + +extern void print_icache_body +(lf *file, + insn *instruction, + insn_bits *expanded_bits, + cache_table *cache_rules, + icache_decl_type what_to_declare, + icache_body_type what_to_do); + + +/* Output an instruction cache decode function */ + +extern insn_handler print_icache_declaration; +extern insn_handler print_icache_definition; + + +/* Output an instruction cache support function */ + +extern function_handler print_icache_internal_function_declaration; +extern function_handler print_icache_internal_function_definition; + + +/* Output the instruction cache table data structure */ + +extern void print_icache_struct +(insn_table *instructions, + cache_table *cache_rules, + lf *file); + + +/* Output a single instructions decoder */ diff --git a/sim/ppc/gen-idecode.c b/sim/ppc/gen-idecode.c new file mode 100644 index 00000000000..397dcde1816 --- /dev/null +++ b/sim/ppc/gen-idecode.c @@ -0,0 +1,1545 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1997, 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. + + */ + +#include "misc.h" +#include "lf.h" +#include "table.h" + +#include "filter.h" + +#include "ld-decode.h" +#include "ld-cache.h" +#include "ld-insn.h" + +#include "igen.h" + +#include "gen-idecode.h" +#include "gen-icache.h" +#include "gen-semantics.h" + + + +static void +lf_print_opcodes(lf *file, + insn_table *table) +{ + if (table != NULL) { + while (1) { + ASSERT(table->opcode != NULL); + 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; + } + } +} + +/****************************************************************/ + + +static void +lf_print_table_name(lf *file, + insn_table *table) +{ + lf_printf(file, "idecode_table"); + lf_print_opcodes(file, table); +} + + + +static void +print_idecode_table(lf *file, + insn_table *entry, + const char *result) +{ + lf_printf(file, "/* prime the search */\n"); + 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", + i2target(hi_bit_nr, entry->opcode->first), + i2target(hi_bit_nr, entry->opcode->last)); + lf_printf(file, "idecode_table_entry *table_entry = table + opcode;\n"); + + lf_printf(file, "\n"); + lf_printf(file, "/* iterate until a leaf */\n"); + lf_printf(file, "while (1) {\n"); + lf_printf(file, " signed shift = table_entry->shift;\n"); + lf_printf(file, "if (shift == function_entry) break;\n"); + lf_printf(file, " if (shift >= 0) {\n"); + 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, " >> shift);\n"); + lf_printf(file, " table_entry = table + opcode;\n"); + lf_printf(file, " }\n"); + lf_printf(file, " else {\n"); + lf_printf(file, " /* must be a boolean */\n"); + lf_printf(file, " ASSERT(table_entry->shift == boolean_entry);\n"); + lf_printf(file, " opcode = ((instruction & table_entry->mask)\n"); + lf_printf(file, " != table_entry->value);\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_printf(file, " }\n"); + lf_printf(file, "}\n"); + + lf_printf(file, "\n"); + lf_printf(file, "/* call the leaf code */\n"); + if ((code & generate_jumps)) { + lf_printf(file, "goto *table_entry->function_or_table;\n"); + } + else { + lf_printf(file, "%s ", result); + if ((code & generate_with_icache)) { + lf_printf(file, "(((idecode_icache*)table_entry->function_or_table)\n"); + lf_printf(file, " (%s));\n", ICACHE_FUNCTION_ACTUAL); + } + else { + lf_printf(file, "((idecode_semantic*)table_entry->function_or_table)\n"); + lf_printf(file, " (%s);\n", SEMANTIC_FUNCTION_ACTUAL); + } + } +} + + +static void +print_idecode_table_start(insn_table *table, + lf *file, + void *data, + int depth) +{ + ASSERT(depth == 0); + /* start of the table */ + if (table->opcode_rule->gen == array_gen) { + lf_printf(file, "\n"); + lf_printf(file, "static idecode_table_entry "); + lf_print_table_name(file, table); + lf_printf(file, "[] = {\n"); + } +} + +static void +print_idecode_table_leaf(insn_table *entry, + lf *file, + void *data, + insn *instruction, + int depth) +{ + ASSERT(entry->parent != NULL); + ASSERT(depth == 0); + + /* add an entry to the table */ + if (entry->parent->opcode_rule->gen == array_gen) { + lf_printf(file, " /*%d*/ { ", entry->opcode_nr); + if (entry->opcode == NULL) { + /* table leaf entry */ + lf_printf(file, "function_entry, 0, 0, "); + if ((code & generate_jumps)) + lf_printf(file, "&&"); + print_function_name(file, + entry->insns->file_entry->fields[insn_name], + entry->expanded_bits, + ((code & generate_with_icache) + ? function_name_prefix_icache + : function_name_prefix_semantics)); + } + else if (entry->opcode_rule->gen == switch_gen + || entry->opcode_rule->gen == goto_switch_gen + || entry->opcode_rule->gen == padded_switch_gen) { + /* table calling switch statement */ + lf_printf(file, "function_entry, 0, 0, "); + if ((code & generate_jumps)) + lf_printf(file, "&&"); + lf_print_table_name(file, entry); + } + else if (entry->opcode->is_boolean) { + /* table `calling' boolean table */ + lf_printf(file, "boolean_entry, "); + lf_printf(file, "MASK32(%d, %d), ", + i2target(hi_bit_nr, entry->opcode->first), + i2target(hi_bit_nr, entry->opcode->last)); + lf_printf(file, "INSERTED32(%d, %d, %d), ", + entry->opcode->boolean_constant, + i2target(hi_bit_nr, entry->opcode->first), + i2target(hi_bit_nr, entry->opcode->last)); + lf_print_table_name(file, entry); + } + else { + /* table `calling' another table */ + lf_printf(file, "%d, ", insn_bit_size - entry->opcode->last - 1); + lf_printf(file, "MASK32(%d,%d), ", + i2target(hi_bit_nr, entry->opcode->first), + i2target(hi_bit_nr, entry->opcode->last)); + lf_printf(file, "0, "); + lf_print_table_name(file, entry); + } + lf_printf(file, " },\n"); + } +} + +static void +print_idecode_table_end(insn_table *table, + lf *file, + void *data, + int depth) +{ + ASSERT(depth == 0); + if (table->opcode_rule->gen == array_gen) { + lf_printf(file, "};\n"); + } +} + +static void +print_idecode_table_padding(insn_table *table, + lf *file, + void *data, + int depth, + int opcode_nr) +{ + ASSERT(depth == 0); + if (table->opcode_rule->gen == array_gen) { + lf_printf(file, " /*%d*/ { function_entry, 0, 0, ", opcode_nr); + if ((code & generate_jumps)) + lf_printf(file, "&&"); + lf_printf(file, "%s_illegal },\n", + ((code & generate_with_icache) ? "icache" : "semantic")); + } +} + + +/****************************************************************/ + + +static void +print_goto_switch_name(lf *file, + insn_table *entry) +{ + lf_printf(file, "case_"); + if (entry->opcode == NULL) + print_function_name(file, + entry->insns->file_entry->fields[insn_name], + entry->expanded_bits, + ((code & generate_with_icache) + ? function_name_prefix_icache + : function_name_prefix_semantics)); + else + lf_print_table_name(file, entry); +} + +static void +print_goto_switch_table_leaf(insn_table *entry, + lf *file, + void *data, + insn *instruction, + int depth) +{ + ASSERT(entry->parent != NULL); + ASSERT(depth == 0); + ASSERT(entry->parent->opcode_rule->gen == goto_switch_gen); + ASSERT(entry->parent->opcode); + + lf_printf(file, "&&"); + print_goto_switch_name(file, entry); + lf_printf(file, ",\n"); +} + +static void +print_goto_switch_table_padding(insn_table *table, + lf *file, + void *data, + int depth, + int opcode_nr) +{ + ASSERT(depth == 0); + ASSERT(table->opcode_rule->gen == goto_switch_gen); + + lf_printf(file, "&&illegal_"); + lf_print_table_name(file, table); + lf_printf(file, ",\n"); +} + +static void +print_goto_switch_break(lf *file, + insn_table *entry) +{ + lf_printf(file, "goto break_"); + lf_print_table_name(file, entry->parent); + lf_printf(file, ";\n"); +} + + +static void +print_goto_switch_table(lf *file, + insn_table *table) +{ + lf_printf(file, "const static void *"); + lf_print_table_name(file, table); + lf_printf(file, "[] = {\n"); + lf_indent(file, +2); + insn_table_traverse_tree(table, + file, NULL/*data*/, + 0, + NULL/*start*/, + print_goto_switch_table_leaf, + NULL/*end*/, + print_goto_switch_table_padding); + lf_indent(file, -2); + lf_printf(file, "};\n"); +} + + +void print_idecode_switch +(lf *file, + insn_table *table, + const char *result); + +static void +idecode_switch_start(insn_table *table, + lf *file, + void *data, + int depth) +{ + /* const char *result = data; */ + ASSERT(depth == 0); + ASSERT(table->opcode_rule->gen == switch_gen + || table->opcode_rule->gen == goto_switch_gen + || table->opcode_rule->gen == padded_switch_gen); + + if (table->opcode->is_boolean + || table->opcode_rule->gen == switch_gen + || table->opcode_rule->gen == padded_switch_gen) { + lf_printf(file, "switch (EXTRACTED32(instruction, %d, %d)) {\n", + i2target(hi_bit_nr, table->opcode->first), + i2target(hi_bit_nr, table->opcode->last)); + } + else if (table->opcode_rule->gen == goto_switch_gen) { + if (table->parent != NULL + && (table->parent->opcode_rule->gen == switch_gen + || table->parent->opcode_rule->gen == goto_switch_gen + || table->parent->opcode_rule->gen == padded_switch_gen)) { + lf_printf(file, "{\n"); + lf_indent(file, +2); + } + print_goto_switch_table(file, table); + lf_printf(file, "ASSERT(EXTRACTED32(instruction, %d, %d)\n", + i2target(hi_bit_nr, table->opcode->first), + i2target(hi_bit_nr, table->opcode->last)); + lf_printf(file, " < (sizeof("); + lf_print_table_name(file, table); + lf_printf(file, ") / sizeof(void*)));\n"); + lf_printf(file, "goto *"); + lf_print_table_name(file, table); + lf_printf(file, "[EXTRACTED32(instruction, %d, %d)];\n", + i2target(hi_bit_nr, table->opcode->first), + i2target(hi_bit_nr, table->opcode->last)); + } + else { + ASSERT("bad switch" == NULL); + } +} + + +static void +idecode_switch_leaf(insn_table *entry, + lf *file, + void *data, + insn *instruction, + int depth) +{ + const char *result = data; + ASSERT(entry->parent != NULL); + ASSERT(depth == 0); + ASSERT(entry->parent->opcode_rule->gen == switch_gen + || entry->parent->opcode_rule->gen == goto_switch_gen + || entry->parent->opcode_rule->gen == padded_switch_gen); + ASSERT(entry->parent->opcode); + + if (entry->parent->opcode->is_boolean + && entry->opcode_nr == 0) { + /* boolean false target */ + lf_printf(file, "case %d:\n", entry->parent->opcode->boolean_constant); + } + else if (entry->parent->opcode->is_boolean + && entry->opcode_nr != 0) { + /* boolean true case */ + lf_printf(file, "default:\n"); + } + else if (entry->parent->opcode_rule->gen == switch_gen + || entry->parent->opcode_rule->gen == padded_switch_gen) { + /* normal goto */ + lf_printf(file, "case %d:\n", entry->opcode_nr); + } + else if (entry->parent->opcode_rule->gen == goto_switch_gen) { + /* lf_indent(file, -1); */ + print_goto_switch_name(file, entry); + lf_printf(file, ":\n"); + /* lf_indent(file, +1); */ + } + else { + ASSERT("bad switch" == NULL); + } + lf_indent(file, +2); + { + if (entry->opcode == NULL) { + /* switch calling leaf */ + if ((code & generate_jumps)) + lf_printf(file, "goto "); + if ((code & generate_calls)) + lf_printf(file, "%s ", result); + print_function_name(file, + entry->insns->file_entry->fields[insn_name], + entry->expanded_bits, + ((code & generate_with_icache) + ? function_name_prefix_icache + : function_name_prefix_semantics)); + if ((code & generate_calls)) + lf_printf(file, "(%s)", SEMANTIC_FUNCTION_ACTUAL); + lf_printf(file, ";\n"); + } + else if (entry->opcode_rule->gen == switch_gen + || entry->opcode_rule->gen == goto_switch_gen + || entry->opcode_rule->gen == padded_switch_gen) { + /* switch calling switch */ + print_idecode_switch(file, entry, result); + } + else { + /* switch looking up a table */ + lf_printf(file, "{\n"); + lf_indent(file, -2); + print_idecode_table(file, entry, result); + lf_indent(file, -2); + lf_printf(file, "}\n"); + } + if (entry->parent->opcode->is_boolean + || entry->parent->opcode_rule->gen == switch_gen + || entry->parent->opcode_rule->gen == padded_switch_gen) { + lf_printf(file, "break;\n"); + } + else if (entry->parent->opcode_rule->gen == goto_switch_gen) { + print_goto_switch_break(file, entry); + } + else { + ASSERT("bad switch" == NULL); + } + } + lf_indent(file, -2); +} + + +static void +print_idecode_switch_illegal(lf *file, + const char *result) +{ + lf_indent(file, +2); + print_idecode_illegal(file, result); + lf_printf(file, "break;\n"); + lf_indent(file, -2); +} + +static void +idecode_switch_end(insn_table *table, + lf *file, + void *data, + int depth) +{ + const char *result = data; + ASSERT(depth == 0); + ASSERT(table->opcode_rule->gen == switch_gen + || table->opcode_rule->gen == goto_switch_gen + || table->opcode_rule->gen == padded_switch_gen); + ASSERT(table->opcode); + + if (table->opcode->is_boolean) { + lf_printf(file, "}\n"); + } + else if (table->opcode_rule->gen == switch_gen + || table->opcode_rule->gen == padded_switch_gen) { + lf_printf(file, "default:\n"); + switch (table->opcode_rule->gen) { + case switch_gen: + print_idecode_switch_illegal(file, result); + break; + case padded_switch_gen: + lf_printf(file, " error(\"Internal error - bad switch generated\\n\");\n"); + lf_printf(file, " break;\n"); + break; + default: + ASSERT("bad switch" == NULL); + } + lf_printf(file, "}\n"); + } + else if (table->opcode_rule->gen == goto_switch_gen) { + lf_printf(file, "illegal_"); + lf_print_table_name(file, table); + lf_printf(file, ":\n"); + print_idecode_illegal(file, result); + lf_printf(file, "break_"); + lf_print_table_name(file, table); + lf_printf(file, ":;\n"); + if (table->parent != NULL + && (table->parent->opcode_rule->gen == switch_gen + || table->parent->opcode_rule->gen == goto_switch_gen + || table->parent->opcode_rule->gen == padded_switch_gen)) { + lf_indent(file, -2); + lf_printf(file, "}\n"); + } + } + else { + ASSERT("bad switch" == NULL); + } +} + +static void +idecode_switch_padding(insn_table *table, + lf *file, + void *data, + int depth, + int opcode_nr) +{ + const char *result = data; + ASSERT(depth == 0); + ASSERT(table->opcode_rule->gen == switch_gen + || table->opcode_rule->gen == goto_switch_gen + || table->opcode_rule->gen == padded_switch_gen); + + switch (table->opcode_rule->gen) { + case switch_gen: + break; + case padded_switch_gen: + lf_printf(file, "case %d:\n", opcode_nr); + print_idecode_switch_illegal(file, result); + break; + case goto_switch_gen: + /* no padding needed */ + break; + default: + ASSERT("bad switch" != NULL); + } +} + + +void +print_idecode_switch(lf *file, + insn_table *table, + const char *result) +{ + insn_table_traverse_tree(table, + file, (void*)result, + 0, + idecode_switch_start, + idecode_switch_leaf, + idecode_switch_end, + idecode_switch_padding); +} + + +static void +print_idecode_switch_function_header(lf *file, + insn_table *table, + int is_function_definition) +{ + lf_printf(file, "\n"); + if ((code & generate_calls)) { + lf_printf(file, "static "); + if ((code & generate_with_icache)) + lf_printf(file, "idecode_semantic *"); + else + lf_printf(file, "unsigned_word"); + if (is_function_definition) + lf_printf(file, "\n"); + else + lf_printf(file, " "); + lf_print_table_name(file, table); + lf_printf(file, "\n(%s)", ICACHE_FUNCTION_FORMAL); + if (!is_function_definition) + lf_printf(file, ";"); + lf_printf(file, "\n"); + } + if ((code & generate_jumps) && is_function_definition) { + lf_indent(file, -1); + lf_print_table_name(file, table); + lf_printf(file, ":\n"); + lf_indent(file, +1); + } +} + + +static void +idecode_declare_if_switch(insn_table *table, + lf *file, + void *data, + int depth) +{ + if ((table->opcode_rule->gen == switch_gen + || table->opcode_rule->gen == goto_switch_gen + || table->opcode_rule->gen == padded_switch_gen) + && table->parent != NULL /* don't declare the top one yet */ + && table->parent->opcode_rule->gen == array_gen) { + print_idecode_switch_function_header(file, + table, + 0/*isnt function definition*/); + } +} + + +static void +idecode_expand_if_switch(insn_table *table, + lf *file, + void *data, + int depth) +{ + if ((table->opcode_rule->gen == switch_gen + || table->opcode_rule->gen == goto_switch_gen + || table->opcode_rule->gen == padded_switch_gen) + && table->parent != NULL /* don't expand the top one yet */ + && table->parent->opcode_rule->gen == array_gen) { + print_idecode_switch_function_header(file, + table, + 1/*is function definition*/); + if ((code & generate_calls)) { + lf_printf(file, "{\n"); + lf_indent(file, +2); + } + print_idecode_switch(file, table, "return"); + if ((code & generate_calls)) { + lf_indent(file, -2); + lf_printf(file, "}\n"); + } + } +} + + +/****************************************************************/ + + +static void +print_idecode_lookups(lf *file, + insn_table *table, + cache_table *cache_rules) +{ + int depth; + + /* output switch function declarations where needed by tables */ + insn_table_traverse_tree(table, + file, NULL, + 1, + idecode_declare_if_switch, /* START */ + NULL, NULL, NULL); + + /* output tables where needed */ + for (depth = insn_table_depth(table); + depth > 0; + depth--) { + insn_table_traverse_tree(table, + file, NULL, + 1-depth, + print_idecode_table_start, + print_idecode_table_leaf, + print_idecode_table_end, + print_idecode_table_padding); + } + + /* output switch functions where needed */ + insn_table_traverse_tree(table, + file, NULL, + 1, + idecode_expand_if_switch, /* START */ + NULL, NULL, NULL); +} + + +static void +print_idecode_body(lf *file, + insn_table *table, + const char *result) +{ + if (table->opcode_rule->gen == switch_gen + || table->opcode_rule->gen == goto_switch_gen + || table->opcode_rule->gen == padded_switch_gen) + print_idecode_switch(file, table, result); + else + print_idecode_table(file, table, result); +} + + +/****************************************************************/ + + +static void +print_run_until_stop_body(lf *file, + insn_table *table, + int can_stop) +{ + /* Output the function to execute real code: + + Unfortunatly, there are multiple cases to consider vis: + + <icache> X <smp> X <events> X <keep-running-flag> X ... + + Consequently this function is written in multiple different ways */ + + lf_putstr(file, "{\n"); + lf_indent(file, +2); + lf_putstr(file, "jmp_buf halt;\n"); + lf_putstr(file, "jmp_buf restart;\n"); + if (!generate_smp) { + lf_putstr(file, "cpu *processor = NULL;\n"); + lf_putstr(file, "unsigned_word cia = -1;\n"); + } + lf_putstr(file, "int last_cpu;\n"); + if (generate_smp) { + lf_putstr(file, "int current_cpu;\n"); + } + + if ((code & generate_with_icache)) { + lf_putstr(file, "int cpu_nr;\n"); + lf_putstr(file, "\n"); + lf_putstr(file, "/* flush the icache of a possible break insn */\n"); + lf_putstr(file, "for (cpu_nr = 0; cpu_nr < nr_cpus; cpu_nr++)\n"); + lf_putstr(file, " cpu_flush_icache(processors[cpu_nr]);\n"); + } + + lf_putstr(file, "\n"); + lf_putstr(file, "/* set the halt target initially */\n"); + lf_putstr(file, "psim_set_halt_and_restart(system, &halt, NULL);\n"); + lf_putstr(file, "if (setjmp(halt))\n"); + lf_putstr(file, " return;\n"); + + lf_putstr(file, "\n"); + lf_putstr(file, "/* where were we before the halt? */\n"); + lf_putstr(file, "last_cpu = psim_last_cpu(system);\n"); + + lf_putstr(file, "\n"); + lf_putstr(file, "/* check for need to force event processing first */\n"); + lf_putstr(file, "if (WITH_EVENTS) {\n"); + lf_putstr(file, " if (last_cpu == nr_cpus) {\n"); + lf_putstr(file, " /* halted during event processing */\n"); + lf_putstr(file, " event_queue_process(events);\n"); + lf_putstr(file, " last_cpu = -1;\n"); + lf_putstr(file, " }\n"); + lf_putstr(file, " else if (last_cpu == nr_cpus - 1) {\n"); + lf_putstr(file, " /* last cpu did halt */\n"); + lf_putstr(file, " if (event_queue_tick(events)) {\n"); + lf_putstr(file, " event_queue_process(events);\n"); + lf_putstr(file, " }\n"); + lf_putstr(file, " last_cpu = -1;\n"); + lf_putstr(file, " }\n"); + lf_putstr(file, "}\n"); + lf_putstr(file, "else {\n"); + lf_putstr(file, " if (last_cpu == nr_cpus - 1)\n"); + lf_putstr(file, " /* cpu zero is next */\n"); + lf_putstr(file, " last_cpu = -1;\n"); + lf_putstr(file, "}\n"); + + lf_putstr(file, "\n"); + lf_putstr(file, "/* have ensured that the event queue can not be first */\n"); + lf_putstr(file, "ASSERT(last_cpu >= -1 && last_cpu < nr_cpus - 1);\n"); + + if (!generate_smp) { + + lf_putstr(file, " +/* CASE 1: NO SMP (with or with out instruction cache). + + In this case, we can take advantage of the fact that the current + instruction address does not need to be returned to the cpu object + after every execution of an instruction. Instead it only needs to + be saved when either A. the main loop exits or B. A cpu-halt or + cpu-restart call forces the loop to be re-enered. The later + functions always save the current cpu instruction address. + + Two subcases also exist that with and that without an instruction + cache. + + An additional complexity is the need to ensure that a 1:1 ratio + is maintained between the execution of an instruction and the + incrementing of the simulation clock */"); + + lf_putstr(file, "\n"); + + lf_putstr(file, "\n"); + lf_putstr(file, "/* now add restart target as ready to run */\n"); + lf_putstr(file, "psim_set_halt_and_restart(system, &halt, &restart);\n"); + lf_putstr(file, "if (setjmp(restart)) {\n"); + lf_putstr(file, " if (WITH_EVENTS) {\n"); + lf_putstr(file, " /* when restart, cpu must have been last, clock next */\n"); + lf_putstr(file, " if (event_queue_tick(events)) {\n"); + lf_putstr(file, " event_queue_process(events);\n"); + lf_putstr(file, " }\n"); + lf_putstr(file, " }\n"); + lf_putstr(file, "}\n"); + + lf_putstr(file, "\n"); + lf_putstr(file, "/* prime the main loop */\n"); + lf_putstr(file, "processor = processors[0];\n"); + lf_putstr(file, "cia = cpu_get_program_counter(processor);\n"); + + lf_putstr(file, "\n"); + lf_putstr(file, "while (1) {\n"); + lf_indent(file, +2); + + if (!(code & generate_with_icache)) { + lf_putstr(file, "instruction_word instruction =\n"); + lf_putstr(file, " vm_instruction_map_read(cpu_instruction_map(processor), processor, cia);\n"); + lf_putstr(file, "\n"); + print_idecode_body(file, table, "cia =");; + } + + if ((code & generate_with_icache)) { + lf_putstr(file, "idecode_cache *cache_entry =\n"); + lf_putstr(file, " cpu_icache_entry(processor, cia);\n"); + lf_putstr(file, "if (cache_entry->address == cia) {\n"); + lf_putstr(file, " /* cache hit */\n"); + lf_putstr(file, " idecode_semantic *const semantic = cache_entry->semantic;\n"); + lf_putstr(file, " cia = semantic(processor, cache_entry, cia);\n"); + /* tail */ + if (can_stop) { + lf_putstr(file, "if (keep_running != NULL && !*keep_running)\n"); + lf_putstr(file, " cpu_halt(processor, cia, was_continuing, 0/*ignore*/);\n"); + } + lf_putstr(file, "}\n"); + lf_putstr(file, "else {\n"); + lf_putstr(file, " /* cache miss */\n"); + if (!(code & generate_with_semantic_icache)) { + lf_indent(file, +2); + lf_putstr(file, "idecode_semantic *semantic;\n"); + lf_indent(file, -2); + } + lf_putstr(file, " instruction_word instruction =\n"); + lf_putstr(file, " vm_instruction_map_read(cpu_instruction_map(processor), processor, cia);\n"); + lf_putstr(file, " if (WITH_MON != 0)\n"); + lf_putstr(file, " mon_event(mon_event_icache_miss, processor, cia);\n"); + if ((code & generate_with_semantic_icache)) { + lf_putstr(file, "{\n"); + lf_indent(file, +2); + print_idecode_body(file, table, "cia ="); + lf_indent(file, -2); + lf_putstr(file, "}\n"); + } + else { + print_idecode_body(file, table, "semantic ="); + lf_putstr(file, " cia = semantic(processor, cache_entry, cia);\n"); + } + lf_putstr(file, "}\n"); + } + + /* events */ + lf_putstr(file, "\n"); + lf_putstr(file, "/* process any events */\n"); + lf_putstr(file, "if (WITH_EVENTS) {\n"); + lf_putstr(file, " if (event_queue_tick(events)) {\n"); + lf_putstr(file, " cpu_set_program_counter(processor, cia);\n"); + lf_putstr(file, " event_queue_process(events);\n"); + lf_putstr(file, " cia = cpu_get_program_counter(processor);\n"); + lf_putstr(file, " }\n"); + lf_putstr(file, "}\n"); + + /* tail */ + if (can_stop) { + lf_putstr(file, "\n"); + lf_putstr(file, "/* abort if necessary */\n"); + lf_putstr(file, "if (keep_running != NULL && !*keep_running)\n"); + lf_putstr(file, " cpu_halt(processor, cia, was_continuing, 0/*not important*/);\n"); + } + + lf_indent(file, -2); + lf_putstr(file, "}\n"); + } + + if (generate_smp) { + + lf_putstr(file, " +/* CASE 2: SMP (With or without ICACHE) + + The complexity here comes from needing to correctly restart the + system when it is aborted. In particular if cpu0 requests a + restart, the next cpu is still cpu1. Cpu0 being restarted after + all the other CPU's and the event queue have been processed */"); + + lf_putstr(file, "\n"); + + lf_putstr(file, "\n"); + lf_putstr(file, "/* now establish the restart target */\n"); + lf_putstr(file, "psim_set_halt_and_restart(system, &halt, &restart);\n"); + lf_putstr(file, "if (setjmp(restart)) {\n"); + lf_putstr(file, " current_cpu = psim_last_cpu(system);\n"); + lf_putstr(file, " ASSERT(current_cpu >= 0 && current_cpu < nr_cpus);\n"); + lf_putstr(file, "}\n"); + lf_putstr(file, "else {\n"); + lf_putstr(file, " current_cpu = last_cpu;\n"); + lf_putstr(file, " ASSERT(current_cpu >= -1 && current_cpu < nr_cpus);\n"); + lf_putstr(file, "}\n"); + + + lf_putstr(file, "\n"); + lf_putstr(file, "while (1) {\n"); + lf_indent(file, +2); + + lf_putstr(file, "\n"); + lf_putstr(file, "if (WITH_EVENTS) {\n"); + lf_putstr(file, " current_cpu += 1;\n"); + lf_putstr(file, " if (current_cpu == nr_cpus) {\n"); + lf_putstr(file, " if (event_queue_tick(events)) {\n"); + lf_putstr(file, " event_queue_process(events);\n"); + lf_putstr(file, " }\n"); + lf_putstr(file, " current_cpu = 0;\n"); + lf_putstr(file, " }\n"); + lf_putstr(file, "}\n"); + lf_putstr(file, "else {\n"); + lf_putstr(file, " current_cpu = (current_cpu + 1) % nr_cpus;\n"); + lf_putstr(file, "}\n"); + + lf_putstr(file, "\n"); + lf_putstr(file, "{\n"); + lf_indent(file, +2); + lf_putstr(file, "cpu *processor = processors[current_cpu];\n"); + lf_putstr(file, "unsigned_word cia =\n"); + lf_putstr(file, " cpu_get_program_counter(processor);\n"); + + if (!(code & generate_with_icache)) { + lf_putstr(file, "instruction_word instruction =\n"); + lf_putstr(file, " vm_instruction_map_read(cpu_instruction_map(processor), processor, cia);\n"); + print_idecode_body(file, table, "cia ="); + if (can_stop) { + lf_putstr(file, "if (keep_running != NULL && !*keep_running)\n"); + lf_putstr(file, " cpu_halt(processor, cia, was_continuing, 0/*ignore*/);\n"); + } + lf_putstr(file, "cpu_set_program_counter(processor, cia);\n"); + } + + if ((code & generate_with_icache)) { + lf_putstr(file, "idecode_cache *cache_entry =\n"); + lf_putstr(file, " cpu_icache_entry(processor, cia);\n"); + lf_putstr(file, "\n"); + lf_putstr(file, "if (cache_entry->address == cia) {\n"); + { + lf_indent(file, +2); + lf_putstr(file, "\n"); + lf_putstr(file, "/* cache hit */\n"); + lf_putstr(file, "idecode_semantic *semantic = cache_entry->semantic;\n"); + lf_putstr(file, "cia = semantic(processor, cache_entry, cia);\n"); + /* tail */ + if (can_stop) { + lf_putstr(file, "if (keep_running != NULL && !*keep_running)\n"); + lf_putstr(file, " cpu_halt(processor, cia, was_continuing, 0/*ignore-signal*/);\n"); + } + lf_putstr(file, "cpu_set_program_counter(processor, cia);\n"); + lf_putstr(file, "\n"); + lf_indent(file, -2); + } + lf_putstr(file, "}\n"); + lf_putstr(file, "else {\n"); + { + lf_indent(file, +2); + lf_putstr(file, "\n"); + lf_putstr(file, "/* cache miss */\n"); + if (!(code & generate_with_semantic_icache)) { + lf_putstr(file, "idecode_semantic *semantic;\n"); + } + lf_putstr(file, "instruction_word instruction =\n"); + lf_putstr(file, " vm_instruction_map_read(cpu_instruction_map(processor), processor, cia);\n"); + lf_putstr(file, "if (WITH_MON != 0)\n"); + lf_putstr(file, " mon_event(mon_event_icache_miss, processors[current_cpu], cia);\n"); + if ((code & generate_with_semantic_icache)) { + lf_putstr(file, "{\n"); + lf_indent(file, +2); + print_idecode_body(file, table, "cia ="); + lf_indent(file, -2); + lf_putstr(file, "}\n"); + } + else { + print_idecode_body(file, table, "semantic = "); + lf_putstr(file, "cia = semantic(processor, cache_entry, cia);\n"); + } + /* tail */ + if (can_stop) { + lf_putstr(file, "if (keep_running != NULL && !*keep_running)\n"); + lf_putstr(file, " cpu_halt(processor, cia, was_continuing, 0/*ignore-signal*/);\n"); + } + lf_putstr(file, "cpu_set_program_counter(processor, cia);\n"); + lf_putstr(file, "\n"); + lf_indent(file, -2); + } + lf_putstr(file, "}\n"); + } + + /* close */ + lf_indent(file, -2); + lf_putstr(file, "}\n"); + + /* tail */ + lf_indent(file, -2); + lf_putstr(file, "}\n"); + } + + + lf_indent(file, -2); + lf_putstr(file, "}\n"); +} + + +/****************************************************************/ + +static void +print_jump(lf *file, + int is_tail) +{ + if (is_tail) { + lf_putstr(file, "if (keep_running != NULL && !*keep_running)\n"); + lf_putstr(file, " cpu_halt(processor, nia, was_continuing, 0/*na*/);\n"); + } + + if (!generate_smp) { + lf_putstr(file, "if (WITH_EVENTS) {\n"); + lf_putstr(file, " if (event_queue_tick(events)) {\n"); + lf_putstr(file, " cpu_set_program_counter(processor, nia);\n"); + lf_putstr(file, " event_queue_process(events);\n"); + lf_putstr(file, " nia = cpu_get_program_counter(processor);\n"); + lf_putstr(file, " }\n"); + lf_putstr(file, "}\n"); + } + + if (generate_smp) { + if (is_tail) + lf_putstr(file, "cpu_set_program_counter(processor, nia);\n"); + lf_putstr(file, "if (WITH_EVENTS) {\n"); + lf_putstr(file, " current_cpu += 1;\n"); + lf_putstr(file, " if (current_cpu >= nr_cpus) {\n"); + lf_putstr(file, " if (event_queue_tick(events)) {\n"); + lf_putstr(file, " event_queue_process(events);\n"); + lf_putstr(file, " }\n"); + lf_putstr(file, " current_cpu = 0;\n"); + lf_putstr(file, " }\n"); + lf_putstr(file, "}\n"); + lf_putstr(file, "else {\n"); + lf_putstr(file, " current_cpu = (current_cpu + 1) % nr_cpus;\n"); + lf_putstr(file, "}\n"); + lf_putstr(file, "processor = processors[current_cpu];\n"); + lf_putstr(file, "nia = cpu_get_program_counter(processor);\n"); + } + + if ((code & generate_with_icache)) { + lf_putstr(file, "cache_entry = cpu_icache_entry(processor, nia);\n"); + lf_putstr(file, "if (cache_entry->address == nia) {\n"); + lf_putstr(file, " /* cache hit */\n"); + lf_putstr(file, " goto *cache_entry->semantic;\n"); + lf_putstr(file, "}\n"); + if (is_tail) { + lf_putstr(file, "goto cache_miss;\n"); + } + } + + if (!(code & generate_with_icache) && is_tail) { + lf_printf(file, "goto idecode;\n"); + } + +} + + + + + +static void +print_jump_insn(lf *file, + insn *instruction, + insn_bits *expanded_bits, + opcode_field *opcodes, + cache_table *cache_rules) +{ + + /* what we are for the moment */ + lf_printf(file, "\n"); + print_my_defines(file, expanded_bits, instruction->file_entry); + + /* output the icache entry */ + if ((code & generate_with_icache)) { + lf_printf(file, "\n"); + lf_indent(file, -1); + print_function_name(file, + instruction->file_entry->fields[insn_name], + expanded_bits, + function_name_prefix_icache); + lf_printf(file, ":\n"); + lf_indent(file, +1); + lf_printf(file, "{\n"); + lf_indent(file, +2); + lf_putstr(file, "const unsigned_word cia = nia;\n"); + print_itrace(file, instruction->file_entry, 1/*putting-value-in-cache*/); + print_idecode_validate(file, instruction, opcodes); + lf_printf(file, "\n"); + lf_printf(file, "{\n"); + lf_indent(file, +2); + print_icache_body(file, + instruction, + expanded_bits, + cache_rules, + 0, /*use_defines*/ + put_values_in_icache); + lf_printf(file, "cache_entry->address = nia;\n"); + lf_printf(file, "cache_entry->semantic = &&"); + print_function_name(file, + instruction->file_entry->fields[insn_name], + expanded_bits, + function_name_prefix_semantics); + lf_printf(file, ";\n"); + if ((code & generate_with_semantic_icache)) { + print_semantic_body(file, + instruction, + expanded_bits, + opcodes); + print_jump(file, 1/*is-tail*/); + } + else { + lf_printf(file, "/* goto "); + print_function_name(file, + instruction->file_entry->fields[insn_name], + expanded_bits, + function_name_prefix_semantics); + lf_printf(file, "; */\n"); + } + lf_indent(file, -2); + lf_putstr(file, "}\n"); + lf_indent(file, -2); + lf_printf(file, "}\n"); + } + + /* print the semantics */ + lf_printf(file, "\n"); + lf_indent(file, -1); + print_function_name(file, + instruction->file_entry->fields[insn_name], + expanded_bits, + function_name_prefix_semantics); + lf_printf(file, ":\n"); + lf_indent(file, +1); + lf_printf(file, "{\n"); + lf_indent(file, +2); + lf_putstr(file, "const unsigned_word cia = nia;\n"); + print_icache_body(file, + instruction, + expanded_bits, + cache_rules, + ((code & generate_with_direct_access) + ? define_variables + : declare_variables), + ((code & generate_with_icache) + ? get_values_from_icache + : do_not_use_icache)); + print_semantic_body(file, + instruction, + expanded_bits, + opcodes); + if (code & generate_with_direct_access) + print_icache_body(file, + instruction, + expanded_bits, + cache_rules, + undef_variables, + ((code & generate_with_icache) + ? get_values_from_icache + : do_not_use_icache)); + print_jump(file, 1/*is tail*/); + lf_indent(file, -2); + lf_printf(file, "}\n"); +} + +static void +print_jump_definition(insn_table *entry, + lf *file, + void *data, + insn *instruction, + int depth) +{ + cache_table *cache_rules = (cache_table*)data; + if (generate_expanded_instructions) { + ASSERT(entry->nr_insn == 1 + && entry->opcode == NULL + && entry->parent != NULL + && entry->parent->opcode != NULL); + ASSERT(entry->nr_insn == 1 + && entry->opcode == NULL + && entry->parent != NULL + && entry->parent->opcode != NULL + && entry->parent->opcode_rule != NULL); + print_jump_insn(file, + entry->insns, + entry->expanded_bits, + entry->opcode, + cache_rules); + } + else { + print_jump_insn(file, + instruction, + NULL, + NULL, + cache_rules); + } +} + + +static void +print_jump_internal_function(insn_table *table, + lf *file, + void *data, + table_entry *function) +{ + if (it_is("internal", function->fields[insn_flags])) { + lf_printf(file, "\n"); + table_entry_print_cpp_line_nr(file, function); + lf_indent(file, -1); + print_function_name(file, + function->fields[insn_name], + NULL, + ((code & generate_with_icache) + ? function_name_prefix_icache + : function_name_prefix_semantics)); + lf_printf(file, ":\n"); + lf_indent(file, +1); + lf_printf(file, "{\n"); + lf_indent(file, +2); + lf_printf(file, "const unsigned_word cia = nia;\n"); + lf_print__c_code(file, function->annex); + lf_print__internal_reference(file); + lf_printf(file, "error(\"Internal function must longjump\\n\");\n"); + lf_indent(file, -2); + lf_printf(file, "}\n"); + } +} + +static void +print_jump_until_stop_body(lf *file, + insn_table *table, + cache_table *cache_rules, + int can_stop) +{ + lf_printf(file, "{\n"); + lf_indent(file, +2); + if (!can_stop) + lf_printf(file, "int *keep_running = NULL;\n"); + lf_putstr(file, "jmp_buf halt;\n"); + lf_putstr(file, "jmp_buf restart;\n"); + lf_putstr(file, "cpu *processor = NULL;\n"); + lf_putstr(file, "unsigned_word nia = -1;\n"); + lf_putstr(file, "instruction_word instruction = 0;\n"); + if ((code & generate_with_icache)) { + lf_putstr(file, "idecode_cache *cache_entry = NULL;\n"); + } + if (generate_smp) { + lf_putstr(file, "int current_cpu = -1;\n"); + } + + /* all the switches and tables - they know about jumping */ + print_idecode_lookups(file, table, cache_rules); + + /* start the simulation up */ + if ((code & generate_with_icache)) { + lf_putstr(file, "\n"); + lf_putstr(file, "{\n"); + lf_putstr(file, " int cpu_nr;\n"); + lf_putstr(file, " for (cpu_nr = 0; cpu_nr < nr_cpus; cpu_nr++)\n"); + lf_putstr(file, " cpu_flush_icache(processors[cpu_nr]);\n"); + lf_putstr(file, "}\n"); + } + + lf_putstr(file, "\n"); + lf_putstr(file, "psim_set_halt_and_restart(system, &halt, &restart);\n"); + + lf_putstr(file, "\n"); + lf_putstr(file, "if (setjmp(halt))\n"); + lf_putstr(file, " return;\n"); + + lf_putstr(file, "\n"); + lf_putstr(file, "setjmp(restart);\n"); + + lf_putstr(file, "\n"); + if (!generate_smp) { + lf_putstr(file, "processor = processors[0];\n"); + lf_putstr(file, "nia = cpu_get_program_counter(processor);\n"); + } + else { + lf_putstr(file, "current_cpu = psim_last_cpu(system);\n"); + } + + if (!(code & generate_with_icache)) { + lf_printf(file, "\n"); + lf_indent(file, -1); + lf_printf(file, "idecode:\n"); + lf_indent(file, +1); + } + + print_jump(file, 0/*is_tail*/); + + if ((code & generate_with_icache)) { + lf_indent(file, -1); + lf_printf(file, "cache_miss:\n"); + lf_indent(file, +1); + } + + lf_putstr(file, "instruction\n"); + lf_putstr(file, " = vm_instruction_map_read(cpu_instruction_map(processor),\n"); + lf_putstr(file, " processor, nia);\n"); + print_idecode_body(file, table, "/*IGORE*/"); + + /* print out a table of all the internals functions */ + insn_table_traverse_function(table, + file, NULL, + print_jump_internal_function); + + /* print out a table of all the instructions */ + if (generate_expanded_instructions) + insn_table_traverse_tree(table, + file, cache_rules, + 1, + NULL, /* start */ + print_jump_definition, /* leaf */ + NULL, /* end */ + NULL); /* padding */ + else + insn_table_traverse_insn(table, + file, cache_rules, + print_jump_definition); + lf_indent(file, -2); + lf_printf(file, "}\n"); +} + + +/****************************************************************/ + + + +static void +print_idecode_floating_point_unavailable(lf *file) +{ + if ((code & generate_jumps)) + lf_printf(file, "goto %s_floating_point_unavailable;\n", (code & generate_with_icache) ? "icache" : "semantic"); + else if ((code & generate_with_icache)) + lf_printf(file, "return icache_floating_point_unavailable(%s);\n", + ICACHE_FUNCTION_ACTUAL); + else + lf_printf(file, "return semantic_floating_point_unavailable(%s);\n", + SEMANTIC_FUNCTION_ACTUAL); +} + + +/* Output code to do any final checks on the decoded instruction. + This includes things like verifying any on decoded fields have the + correct value and checking that (for floating point) floating point + hardware isn't disabled */ + +void +print_idecode_validate(lf *file, + insn *instruction, + opcode_field *opcodes) +{ + /* Validate: unchecked instruction fields + + If any constant fields in the instruction were not checked by the + idecode tables, output code to check that they have the correct + value here */ + { + unsigned check_mask = 0; + unsigned check_val = 0; + insn_field *field; + opcode_field *opcode; + + /* form check_mask/check_val containing what needs to be checked + in the instruction */ + for (field = instruction->fields->first; + field->first < insn_bit_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 (check_mask) { + lf_printf(file, "\n"); + lf_printf(file, "/* validate: %s */\n", + instruction->file_entry->fields[insn_format]); + lf_printf(file, "if (WITH_RESERVED_BITS && (instruction & 0x%x) != 0x%x)\n", + check_mask, check_val); + lf_indent(file, +2); + print_idecode_illegal(file, "return"); + lf_indent(file, -2); + } + } + + /* Validate floating point hardware + + If the simulator is being built with out floating point hardware + (different to it being disabled in the MSR) then floating point + instructions are invalid */ + { + if (it_is("f", instruction->file_entry->fields[insn_flags])) { + lf_printf(file, "\n"); + lf_printf(file, "/* Validate: FP hardware exists */\n"); + lf_printf(file, "if (CURRENT_FLOATING_POINT != HARD_FLOATING_POINT)\n"); + lf_indent(file, +2); + print_idecode_illegal(file, "return"); + lf_indent(file, -2); + } + } + + /* Validate: Floating Point available + + If floating point is not available, we enter a floating point + unavailable interrupt into the cache instead of the instruction + proper. + + The PowerPC spec requires a CSI after MSR[FP] is changed and when + ever a CSI occures we flush the instruction cache. */ + + { + if (it_is("f", instruction->file_entry->fields[insn_flags])) { + lf_printf(file, "\n"); + lf_printf(file, "/* Validate: FP available according to MSR[FP] */\n"); + lf_printf(file, "if (!IS_FP_AVAILABLE(processor))\n"); + lf_indent(file, +2); + print_idecode_floating_point_unavailable(file); + lf_indent(file, -2); + } + } +} + + +/****************************************************************/ + + +static void +print_idecode_run_function_header(lf *file, + int can_stop, + int is_definition) +{ + int indent; + lf_printf(file, "\n"); + lf_print_function_type(file, "void", "INLINE_IDECODE", (is_definition ? " " : "\n")); + indent = lf_putstr(file, (can_stop ? "idecode_run_until_stop" : "idecode_run")); + if (is_definition) + lf_putstr(file, "\n"); + else + lf_indent(file, +indent); + lf_putstr(file, "(psim *system,\n"); + if (can_stop) + lf_putstr(file, " volatile int *keep_running,\n"); + lf_printf(file, " event_queue *events,\n"); + lf_putstr(file, " cpu *const processors[],\n"); + lf_putstr(file, " const int nr_cpus)"); + if (is_definition) + lf_putstr(file, ";"); + else + lf_indent(file, -indent); + lf_putstr(file, "\n"); +} + + +void +gen_idecode_h(lf *file, + insn_table *table, + cache_table *cache_rules) +{ + lf_printf(file, "/* The idecode_*.h functions shall move to support */\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"); + print_icache_struct(table, cache_rules, file); + lf_printf(file, "\n"); + lf_printf(file, "#define WITH_IDECODE_SMP %d\n", generate_smp); + lf_printf(file, "\n"); + print_idecode_run_function_header(file, 0/*can stop*/, 1/*is definition*/); + print_idecode_run_function_header(file, 1/*can stop*/, 1/*is definition*/); +} + + +void +gen_idecode_c(lf *file, + insn_table *table, + cache_table *cache_rules) +{ + /* the intro */ + lf_printf(file, "#include \"inline.c\"\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, "#include \"icache.h\"\n"); + lf_printf(file, "#include \"support.h\"\n"); + lf_printf(file, "\n"); + lf_printf(file, "#include <setjmp.h>\n"); + lf_printf(file, "\n"); + lf_printf(file, "enum {\n"); + lf_printf(file, " /* greater or equal to zero => table */\n"); + lf_printf(file, " function_entry = -1,\n"); + lf_printf(file, " boolean_entry = -2,\n"); + lf_printf(file, "};\n"); + lf_printf(file, "\n"); + lf_printf(file, "typedef struct _idecode_table_entry {\n"); + lf_printf(file, " int shift;\n"); + lf_printf(file, " instruction_word mask;\n"); + lf_printf(file, " instruction_word value;"); + lf_printf(file, " void *function_or_table;\n"); + lf_printf(file, "} idecode_table_entry;\n"); + lf_printf(file, "\n"); + lf_printf(file, "\n"); + + if ((code & generate_calls)) { + + print_idecode_lookups(file, table, cache_rules); + + /* output the main idecode routine */ + print_idecode_run_function_header(file, 0/*can stop*/, 0/*is definition*/); + print_run_until_stop_body(file, table, 0/* have stop argument */); + + print_idecode_run_function_header(file, 1/*can stop*/, 0/*is definition*/); + print_run_until_stop_body(file, table, 1/* no stop argument */); + + } + else if ((code & generate_jumps)) { + + print_idecode_run_function_header(file, 0/*can stop*/, 0/*is definition*/); + print_jump_until_stop_body(file, table, cache_rules, 0 /* have stop argument */); + + print_idecode_run_function_header(file, 1/*can stop*/, 0/*is definition*/); + print_jump_until_stop_body(file, table, cache_rules, 1/* have stop argument */); + + } + else { + error("Something is wrong!\n"); + } +} diff --git a/sim/ppc/gen-idecode.h b/sim/ppc/gen-idecode.h new file mode 100644 index 00000000000..f46376bf9ed --- /dev/null +++ b/sim/ppc/gen-idecode.h @@ -0,0 +1,40 @@ +/* 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. + + */ + +extern void gen_idecode_h +(lf *file, + insn_table *table, + cache_table *cache_rules); + +extern void gen_idecode_c +(lf *file, + insn_table *table, + cache_table *cache_rules); + + +/* Output code to do any final checks on the decoded instruction. + This includes things like verifying any on decoded fields have the + correct value and checking that (for floating point) floating point + hardware isn't disabled */ + +extern void print_idecode_validate +(lf *file, + insn *instruction, + opcode_field *opcodes); diff --git a/sim/ppc/gen-itable.c b/sim/ppc/gen-itable.c new file mode 100644 index 00000000000..132aa291e08 --- /dev/null +++ b/sim/ppc/gen-itable.c @@ -0,0 +1,122 @@ +/* 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. + + */ + + + +#include "misc.h" +#include "lf.h" +#include "table.h" + +#include "filter.h" + +#include "ld-decode.h" +#include "ld-insn.h" + +#include "igen.h" +#include "gen-itable.h" + +#ifndef NULL +#define NULL 0 +#endif + + + +static void +itable_h_insn(insn_table *entry, + lf *file, + void *data, + insn *instruction, + int depth) +{ + lf_printf(file, " "); + print_function_name(file, + instruction->file_entry->fields[insn_name], + NULL, + function_name_prefix_itable); + lf_printf(file, ",\n"); +} + + +extern void +gen_itable_h(insn_table *table, lf *file) +{ + /* output an enumerated type for each instruction */ + lf_printf(file, "typedef enum {\n"); + insn_table_traverse_insn(table, + file, NULL, + itable_h_insn); + lf_printf(file, " nr_itable_entries,\n"); + lf_printf(file, "} itable_index;\n"); + lf_printf(file, "\n"); + + /* output the table that contains the actual instruction info */ + lf_printf(file, "typedef struct _itable_instruction_info {\n"); + lf_printf(file, " itable_index nr;\n"); + lf_printf(file, " char *format;\n"); + lf_printf(file, " char *form;\n"); + lf_printf(file, " char *flags;\n"); + lf_printf(file, " char *mnemonic;\n"); + lf_printf(file, " char *name;\n"); + lf_printf(file, " char *file;\n"); + lf_printf(file, " int line_nr;\n"); + lf_printf(file, "} itable_info;\n"); + lf_printf(file, "\n"); + lf_printf(file, "extern itable_info itable[nr_itable_entries];\n"); +} + +/****************************************************************/ + +static void +itable_c_insn(insn_table *entry, + lf *file, + void *data, + insn *instruction, + int depth) +{ + char **fields = instruction->file_entry->fields; + lf_printf(file, " { "); + print_function_name(file, + instruction->file_entry->fields[insn_name], + NULL, + function_name_prefix_itable); + lf_printf(file, ",\n"); + lf_printf(file, " \"%s\",\n", fields[insn_format]); + lf_printf(file, " \"%s\",\n", fields[insn_form]); + lf_printf(file, " \"%s\",\n", fields[insn_flags]); + lf_printf(file, " \"%s\",\n", fields[insn_mnemonic]); + lf_printf(file, " \"%s\",\n", fields[insn_name]); + lf_printf(file, " \"%s\",\n", filter_filename (instruction->file_entry->file_name)); + lf_printf(file, " %d,\n", instruction->file_entry->line_nr); + lf_printf(file, " },\n"); +} + + +extern void +gen_itable_c(insn_table *table, lf *file) +{ + /* output the table that contains the actual instruction info */ + lf_printf(file, "#include \"itable.h\"\n"); + lf_printf(file, "\n"); + lf_printf(file, "itable_info itable[nr_itable_entries] = {\n"); + insn_table_traverse_insn(table, + file, NULL, + itable_c_insn); + lf_printf(file, "};\n"); +} diff --git a/sim/ppc/gen-itable.h b/sim/ppc/gen-itable.h new file mode 100644 index 00000000000..341dc678547 --- /dev/null +++ b/sim/ppc/gen-itable.h @@ -0,0 +1,28 @@ +/* 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. + + */ + + +extern void gen_itable_h +(insn_table *table, + lf *file); + +extern void gen_itable_c +(insn_table *table, + lf *file); diff --git a/sim/ppc/gen-model.c b/sim/ppc/gen-model.c new file mode 100644 index 00000000000..4ec167717c0 --- /dev/null +++ b/sim/ppc/gen-model.c @@ -0,0 +1,393 @@ +/* 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. + + */ + + +#include "misc.h" +#include "lf.h" +#include "table.h" + +#include "filter.h" + +#include "ld-decode.h" +#include "ld-insn.h" + +#include "gen-model.h" + +#ifndef NULL +#define NULL 0 +#endif + + +static void +model_c_or_h_data(insn_table *table, + lf *file, + table_entry *data) +{ + if (data->annex) { + table_entry_print_cpp_line_nr(file, data); + lf_print__c_code(file, data->annex); + lf_print__internal_reference(file); + lf_printf(file, "\n"); + } +} + +static void +model_c_or_h_function(insn_table *entry, + lf *file, + table_entry *function, + char *prefix) +{ + if (function->fields[function_type] == NULL + || function->fields[function_type][0] == '\0') { + error("Model function type not specified for %s", function->fields[function_name]); + } + lf_printf(file, "\n"); + lf_print_function_type(file, function->fields[function_type], prefix, " "); + lf_printf(file, "%s\n(%s);\n", + function->fields[function_name], + function->fields[function_param]); + lf_printf(file, "\n"); +} + +void +gen_model_h(insn_table *table, lf *file) +{ + insn *insn_ptr; + model *model_ptr; + insn *macro; + char *name; + int model_create_p = 0; + int model_init_p = 0; + int model_halt_p = 0; + int model_mon_info_p = 0; + int model_mon_info_free_p = 0; + + for(macro = model_macros; macro; macro = macro->next) { + model_c_or_h_data(table, file, macro->file_entry); + } + + lf_printf(file, "typedef enum _model_enum {\n"); + lf_printf(file, " MODEL_NONE,\n"); + for (model_ptr = models; model_ptr; model_ptr = model_ptr->next) { + lf_printf(file, " MODEL_%s,\n", model_ptr->name); + } + lf_printf(file, " nr_models\n"); + lf_printf(file, "} model_enum;\n"); + lf_printf(file, "\n"); + + lf_printf(file, "#define DEFAULT_MODEL MODEL_%s\n", (models) ? models->name : "NONE"); + lf_printf(file, "\n"); + + lf_printf(file, "typedef struct _model_data model_data;\n"); + lf_printf(file, "typedef struct _model_time model_time;\n"); + lf_printf(file, "\n"); + + lf_printf(file, "extern model_enum current_model;\n"); + lf_printf(file, "extern const char *model_name[ (int)nr_models ];\n"); + lf_printf(file, "extern const char *const *const model_func_unit_name[ (int)nr_models ];\n"); + lf_printf(file, "extern const model_time *const model_time_mapping[ (int)nr_models ];\n"); + lf_printf(file, "\n"); + + for(insn_ptr = model_functions; insn_ptr; insn_ptr = insn_ptr->next) { + model_c_or_h_function(table, file, insn_ptr->file_entry, "INLINE_MODEL"); + name = insn_ptr->file_entry->fields[function_name]; + if (strcmp (name, "model_create") == 0) + model_create_p = 1; + else if (strcmp (name, "model_init") == 0) + model_init_p = 1; + else if (strcmp (name, "model_halt") == 0) + model_halt_p = 1; + else if (strcmp (name, "model_mon_info") == 0) + model_mon_info_p = 1; + else if (strcmp (name, "model_mon_info_free") == 0) + model_mon_info_free_p = 1; + } + + if (!model_create_p) { + lf_print_function_type(file, "model_data *", "INLINE_MODEL", " "); + lf_printf(file, "model_create\n"); + lf_printf(file, "(cpu *processor);\n"); + lf_printf(file, "\n"); + } + + if (!model_init_p) { + lf_print_function_type(file, "void", "INLINE_MODEL", " "); + lf_printf(file, "model_init\n"); + lf_printf(file, "(model_data *model_ptr);\n"); + lf_printf(file, "\n"); + } + + if (!model_halt_p) { + lf_print_function_type(file, "void", "INLINE_MODEL", " "); + lf_printf(file, "model_halt\n"); + lf_printf(file, "(model_data *model_ptr);\n"); + lf_printf(file, "\n"); + } + + if (!model_mon_info_p) { + lf_print_function_type(file, "model_print *", "INLINE_MODEL", " "); + lf_printf(file, "model_mon_info\n"); + lf_printf(file, "(model_data *model_ptr);\n"); + lf_printf(file, "\n"); + } + + if (!model_mon_info_free_p) { + lf_print_function_type(file, "void", "INLINE_MODEL", " "); + lf_printf(file, "model_mon_info_free\n"); + lf_printf(file, "(model_data *model_ptr,\n"); + lf_printf(file, " model_print *info_ptr);\n"); + lf_printf(file, "\n"); + } + + lf_print_function_type(file, "void", "INLINE_MODEL", " "); + lf_printf(file, "model_set\n"); + lf_printf(file, "(const char *name);\n"); +} + +/****************************************************************/ + +typedef struct _model_c_passed_data model_c_passed_data; +struct _model_c_passed_data { + lf *file; + model *model_ptr; +}; + +static void +model_c_insn(insn_table *entry, + lf *phony_file, + void *data, + insn *instruction, + int depth) +{ + model_c_passed_data *data_ptr = (model_c_passed_data *)data; + lf *file = data_ptr->file; + char *current_name = data_ptr->model_ptr->printable_name; + table_model_entry *model_ptr = instruction->file_entry->model_first; + + while (model_ptr) { + if (model_ptr->fields[insn_model_name] == current_name) { + lf_printf(file, " { %-*s }, /* %s */\n", + max_model_fields_len, + model_ptr->fields[insn_model_fields], + instruction->file_entry->fields[insn_name]); + return; + } + + model_ptr = model_ptr->next; + } + + lf_printf(file, " { %-*s }, /* %s */\n", + max_model_fields_len, + data_ptr->model_ptr->insn_default, + instruction->file_entry->fields[insn_name]); +} + +static void +model_c_function(insn_table *table, + lf *file, + table_entry *function, + const char *prefix) +{ + if (function->fields[function_type] == NULL + || function->fields[function_type][0] == '\0') { + error("Model function return type not specified for %s", function->fields[function_name]); + } + else { + lf_printf(file, "\n"); + lf_print_function_type(file, function->fields[function_type], prefix, "\n"); + lf_printf(file, "%s(%s)\n", + function->fields[function_name], + function->fields[function_param]); + } + table_entry_print_cpp_line_nr(file, function); + lf_printf(file, "{\n"); + if (function->annex) { + lf_indent(file, +2); + lf_print__c_code(file, function->annex); + lf_indent(file, -2); + } + lf_printf(file, "}\n"); + lf_print__internal_reference(file); + lf_printf(file, "\n"); +} + +void +gen_model_c(insn_table *table, lf *file) +{ + insn *insn_ptr; + model *model_ptr; + char *name; + int model_create_p = 0; + int model_init_p = 0; + int model_halt_p = 0; + int model_mon_info_p = 0; + int model_mon_info_free_p = 0; + + lf_printf(file, "\n"); + lf_printf(file, "#include \"cpu.h\"\n"); + lf_printf(file, "#include \"mon.h\"\n"); + lf_printf(file, "\n"); + lf_printf(file, "#ifdef HAVE_STDLIB_H\n"); + lf_printf(file, "#include <stdlib.h>\n"); + lf_printf(file, "#endif\n"); + lf_printf(file, "\n"); + + for(insn_ptr = model_data; insn_ptr; insn_ptr = insn_ptr->next) { + model_c_or_h_data(table, file, insn_ptr->file_entry); + } + + for(insn_ptr = model_static; insn_ptr; insn_ptr = insn_ptr->next) { + model_c_or_h_function(table, file, insn_ptr->file_entry, "/*h*/STATIC"); + } + + for(insn_ptr = model_internal; insn_ptr; insn_ptr = insn_ptr->next) { + model_c_or_h_function(table, file, insn_ptr->file_entry, "STATIC_INLINE_MODEL"); + } + + for(insn_ptr = model_static; insn_ptr; insn_ptr = insn_ptr->next) { + model_c_function(table, file, insn_ptr->file_entry, "/*c*/STATIC"); + } + + for(insn_ptr = model_internal; insn_ptr; insn_ptr = insn_ptr->next) { + model_c_function(table, file, insn_ptr->file_entry, "STATIC_INLINE_MODEL"); + } + + for(insn_ptr = model_functions; insn_ptr; insn_ptr = insn_ptr->next) { + model_c_function(table, file, insn_ptr->file_entry, "INLINE_MODEL"); + name = insn_ptr->file_entry->fields[function_name]; + if (strcmp (name, "model_create") == 0) + model_create_p = 1; + else if (strcmp (name, "model_init") == 0) + model_init_p = 1; + else if (strcmp (name, "model_halt") == 0) + model_halt_p = 1; + else if (strcmp (name, "model_mon_info") == 0) + model_mon_info_p = 1; + else if (strcmp (name, "model_mon_info_free") == 0) + model_mon_info_free_p = 1; + } + + if (!model_create_p) { + lf_print_function_type(file, "model_data *", "INLINE_MODEL", "\n"); + lf_printf(file, "model_create(cpu *processor)\n"); + lf_printf(file, "{\n"); + lf_printf(file, " return (model_data *)0;\n"); + lf_printf(file, "}\n"); + lf_printf(file, "\n"); + } + + if (!model_init_p) { + lf_print_function_type(file, "void", "INLINE_MODEL", "\n"); + lf_printf(file, "model_init(model_data *model_ptr)\n"); + lf_printf(file, "{\n"); + lf_printf(file, "}\n"); + lf_printf(file, "\n"); + } + + if (!model_halt_p) { + lf_print_function_type(file, "void", "INLINE_MODEL", "\n"); + lf_printf(file, "model_halt(model_data *model_ptr)\n"); + lf_printf(file, "{\n"); + lf_printf(file, "}\n"); + lf_printf(file, "\n"); + } + + if (!model_mon_info_p) { + lf_print_function_type(file, "model_print *", "INLINE_MODEL", "\n"); + lf_printf(file, "model_mon_info(model_data *model_ptr)\n"); + lf_printf(file, "{\n"); + lf_printf(file, " return (model_print *)0;\n"); + lf_printf(file, "}\n"); + lf_printf(file, "\n"); + } + + if (!model_mon_info_free_p) { + lf_print_function_type(file, "void", "INLINE_MODEL", "\n"); + lf_printf(file, "model_mon_info_free(model_data *model_ptr,\n"); + lf_printf(file, " model_print *info_ptr)\n"); + lf_printf(file, "{\n"); + lf_printf(file, "}\n"); + lf_printf(file, "\n"); + } + + lf_printf(file, "/* Insn functional unit info */\n"); + for(model_ptr = models; model_ptr; model_ptr = model_ptr->next) { + model_c_passed_data data; + + lf_printf(file, "static const model_time model_time_%s[] = {\n", model_ptr->name); + data.file = file; + data.model_ptr = model_ptr; + insn_table_traverse_insn(table, + NULL, (void *)&data, + model_c_insn); + + lf_printf(file, "};\n"); + lf_printf(file, "\n"); + lf_printf(file, "\f\n"); + } + + lf_printf(file, "#ifndef _INLINE_C_\n"); + lf_printf(file, "const model_time *const model_time_mapping[ (int)nr_models ] = {\n"); + lf_printf(file, " (const model_time *const)0,\n"); + for(model_ptr = models; model_ptr; model_ptr = model_ptr->next) { + lf_printf(file, " model_time_%s,\n", model_ptr->name); + } + lf_printf(file, "};\n"); + lf_printf(file, "#endif\n"); + lf_printf(file, "\n"); + + lf_printf(file, "\f\n"); + lf_printf(file, "/* map model enumeration into printable string */\n"); + lf_printf(file, "#ifndef _INLINE_C_\n"); + lf_printf(file, "const char *model_name[ (int)nr_models ] = {\n"); + lf_printf(file, " \"NONE\",\n"); + for (model_ptr = models; model_ptr; model_ptr = model_ptr->next) { + lf_printf(file, " \"%s\",\n", model_ptr->printable_name); + } + lf_printf(file, "};\n"); + lf_printf(file, "#endif\n"); + lf_printf(file, "\n"); + + lf_print_function_type(file, "void", "INLINE_MODEL", "\n"); + lf_printf(file, "model_set(const char *name)\n"); + lf_printf(file, "{\n"); + if (models) { + lf_printf(file, " model_enum model;\n"); + lf_printf(file, " for(model = MODEL_%s; model < nr_models; model++) {\n", models->name); + lf_printf(file, " if(strcmp(name, model_name[model]) == 0) {\n"); + lf_printf(file, " current_model = model;\n"); + lf_printf(file, " return;\n"); + lf_printf(file, " }\n"); + lf_printf(file, " }\n"); + lf_printf(file, "\n"); + lf_printf(file, " error(\"Unknown model '%%s', Models which are known are:%%s\n\",\n"); + lf_printf(file, " name,\n"); + lf_printf(file, " \""); + for(model_ptr = models; model_ptr; model_ptr = model_ptr->next) { + lf_printf(file, "\\n\\t%s", model_ptr->printable_name); + } + lf_printf(file, "\");\n"); + } else { + lf_printf(file, " error(\"No models are currently known about\");\n"); + } + + lf_printf(file, "}\n"); +} + diff --git a/sim/ppc/gen-model.h b/sim/ppc/gen-model.h new file mode 100644 index 00000000000..b465a75abf8 --- /dev/null +++ b/sim/ppc/gen-model.h @@ -0,0 +1,30 @@ +/* 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. + + */ + + + +extern void gen_model_h +(insn_table *table, + lf *file); + + +extern void gen_model_c +(insn_table *table, + lf *file); diff --git a/sim/ppc/gen-semantics.c b/sim/ppc/gen-semantics.c new file mode 100644 index 00000000000..b4c0613fe50 --- /dev/null +++ b/sim/ppc/gen-semantics.c @@ -0,0 +1,250 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1997, 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. + + */ + + + +#include "misc.h" +#include "lf.h" +#include "table.h" +#include "filter.h" + +#include "ld-decode.h" +#include "ld-cache.h" +#include "ld-insn.h" + +#include "igen.h" + +#include "gen-semantics.h" +#include "gen-icache.h" +#include "gen-idecode.h" + + +static void +print_semantic_function_header(lf *file, + const char *basename, + insn_bits *expanded_bits, + int is_function_definition) +{ + int indent; + lf_printf(file, "\n"); + lf_print_function_type(file, SEMANTIC_FUNCTION_TYPE, "EXTERN_SEMANTICS", + (is_function_definition ? "\n" : " ")); + indent = print_function_name(file, + basename, + expanded_bits, + function_name_prefix_semantics); + if (is_function_definition) + lf_indent(file, +indent); + else + lf_printf(file, "\n"); + lf_printf(file, "(%s)", SEMANTIC_FUNCTION_FORMAL); + if (is_function_definition) + lf_indent(file, -indent); + else + lf_printf(file, ";"); + lf_printf(file, "\n"); +} + +void +print_semantic_declaration(insn_table *entry, + lf *file, + void *data, + insn *instruction, + int depth) +{ + if (generate_expanded_instructions) { + ASSERT(entry->nr_insn == 1); + print_semantic_function_header(file, + instruction->file_entry->fields[insn_name], + entry->expanded_bits, + 0/* is not function definition*/); + } + else { + print_semantic_function_header(file, + instruction->file_entry->fields[insn_name], + NULL, + 0/* is not function definition*/); + } +} + + + +/* generate the semantics.c file */ + + +void +print_idecode_illegal(lf *file, + const char *result) +{ + if ((code & generate_jumps)) + lf_printf(file, "goto %s_illegal;\n", (code & generate_with_icache) ? "icache" : "semantic"); + else if ((code & generate_with_icache)) + lf_printf(file, "%s icache_illegal(%s);\n", result, ICACHE_FUNCTION_ACTUAL); + else + lf_printf(file, "%s semantic_illegal(%s);\n", result, SEMANTIC_FUNCTION_ACTUAL); +} + + +void +print_semantic_body(lf *file, + insn *instruction, + insn_bits *expanded_bits, + opcode_field *opcodes) +{ + print_itrace(file, instruction->file_entry, 0/*put_value_in_cache*/); + + /* validate the instruction, if a cache this has already been done */ + if (!(code & generate_with_icache)) + print_idecode_validate(file, instruction, opcodes); + + /* generate the profiling call - this is delayed until after the + instruction has been verified */ + lf_printf(file, "\n"); + lf_printf(file, "/* monitoring: */\n"); + lf_printf(file, "if (WITH_MON & MONITOR_INSTRUCTION_ISSUE) {\n"); + lf_printf(file, " mon_issue("); + print_function_name(file, + instruction->file_entry->fields[insn_name], + NULL, + function_name_prefix_itable); + lf_printf(file, ", processor, cia);\n"); + lf_printf(file, "}\n"); + + /* generate the code (or at least something */ + lf_printf(file, "\n"); + lf_printf(file, "/* semantics: */\n"); + lf_printf(file, "nia = cia + %d;\n", insn_bit_size / 8); + if (instruction->file_entry->annex != NULL) { + /* true code */ + table_entry_print_cpp_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__internal_reference(file); + } + else if (it_is("nop", instruction->file_entry->fields[insn_flags])) { + lf_print__internal_reference(file); + } + else { + /* abort so it is implemented now */ + table_entry_print_cpp_line_nr(file, instruction->file_entry); + lf_putstr(file, "error(\"%s:%d:0x%08lx:%s unimplemented\\n\",\n"); + lf_printf(file, " itable[MY_INDEX].file, itable[MY_INDEX].line_nr, (long)cia, itable[MY_INDEX].name);\n"); + lf_print__internal_reference(file); + } +} + +static void +print_c_semantic(lf *file, + insn *instruction, + insn_bits *expanded_bits, + opcode_field *opcodes, + cache_table *cache_rules) +{ + + lf_printf(file, "{\n"); + lf_indent(file, +2); + + print_my_defines(file, expanded_bits, instruction->file_entry); + lf_printf(file, "\n"); + print_icache_body(file, + instruction, + expanded_bits, + cache_rules, + ((code & generate_with_direct_access) + ? define_variables + : declare_variables), + ((code & generate_with_icache) + ? get_values_from_icache + : do_not_use_icache)); + + lf_printf(file, "unsigned_word nia;\n"); + print_semantic_body(file, + instruction, + expanded_bits, + opcodes); + lf_printf(file, "return nia;\n"); + + /* generate something to clean up any #defines created for the cache */ + if (code & generate_with_direct_access) + print_icache_body(file, + instruction, + expanded_bits, + cache_rules, + undef_variables, + ((code & generate_with_icache) + ? get_values_from_icache + : do_not_use_icache)); + + lf_indent(file, -2); + lf_printf(file, "}\n"); +} + +static void +print_c_semantic_function(lf *file, + insn *instruction, + insn_bits *expanded_bits, + opcode_field *opcodes, + cache_table *cache_rules) +{ + /* build the semantic routine to execute the instruction */ + print_semantic_function_header(file, + instruction->file_entry->fields[insn_name], + expanded_bits, + 1/*is-function-definition*/); + print_c_semantic(file, + instruction, + expanded_bits, + opcodes, + cache_rules); +} + +void +print_semantic_definition(insn_table *entry, + lf *file, + void *data, + insn *instruction, + int depth) +{ + cache_table *cache_rules = (cache_table*)data; + if (generate_expanded_instructions) { + ASSERT(entry->nr_insn == 1 + && entry->opcode == NULL + && entry->parent != NULL + && entry->parent->opcode != NULL); + ASSERT(entry->nr_insn == 1 + && entry->opcode == NULL + && entry->parent != NULL + && entry->parent->opcode != NULL + && entry->parent->opcode_rule != NULL); + print_c_semantic_function(file, + entry->insns, + entry->expanded_bits, + entry->parent->opcode, + cache_rules); + } + else { + print_c_semantic_function(file, instruction, + NULL, NULL, + cache_rules); + } +} diff --git a/sim/ppc/gen-semantics.h b/sim/ppc/gen-semantics.h new file mode 100644 index 00000000000..2c25715f7c7 --- /dev/null +++ b/sim/ppc/gen-semantics.h @@ -0,0 +1,81 @@ +/* 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. + + */ + + +/* Creates the files semantics.[hc]. + + The generated file semantics contains functions that implement the + operations required to model a single target processor instruction. + + Several different variations on the semantics file can be created: + + o uncached + + No instruction cache exists. The semantic function + needs to generate any required values locally. + + o cached - separate cracker and semantic + + Two independant functions are created. Firstly the + function that cracks an instruction entering it into a + cache and secondly the semantic function propper that + uses the cache. + + o cached - semantic + cracking semantic + + The function that cracks the instruction and enters + all values into the cache also contains a copy of the + semantic code (avoiding the need to call both the + cracker and the semantic function when there is a + cache miss). + + For each of these general forms, several refinements can occure: + + o do/don't duplicate/expand semantic functions + + As a consequence of decoding an instruction, the + decoder, as part of its table may have effectivly made + certain of the variable fields in an instruction + constant. Separate functions for each of the + alternative values for what would have been treated as + a variable part can be created. + + o use cache struct directly. + + When a cracking cache is present, the semantic + functions can be generated to either hold intermediate + cache values in local variables or always refer to the + contents of the cache directly. */ + + + +extern insn_handler print_semantic_declaration; +extern insn_handler print_semantic_definition; + +extern void print_idecode_illegal +(lf *file, + const char *result); + +extern void print_semantic_body +(lf *file, + insn *instruction, + insn_bits *expanded_bits, + opcode_field *opcodes); + diff --git a/sim/ppc/gen-support.c b/sim/ppc/gen-support.c new file mode 100644 index 00000000000..56edec38e5f --- /dev/null +++ b/sim/ppc/gen-support.c @@ -0,0 +1,132 @@ +/* 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. + + */ + +#include "misc.h" +#include "lf.h" +#include "table.h" +#include "filter.h" + +#include "ld-decode.h" +#include "ld-cache.h" +#include "ld-insn.h" + +#include "igen.h" + +#include "gen-semantics.h" +#include "gen-support.h" + +static void +print_support_function_name(lf *file, + table_entry *function, + int is_function_definition) +{ + if (it_is("internal", function->fields[insn_flags])) { + lf_print_function_type(file, SEMANTIC_FUNCTION_TYPE, "INLINE_SUPPORT", + (is_function_definition ? "\n" : " ")); + print_function_name(file, + function->fields[function_name], + NULL, + function_name_prefix_semantics); + lf_printf(file, "\n(%s)", SEMANTIC_FUNCTION_FORMAL); + if (!is_function_definition) + lf_printf(file, ";"); + lf_printf(file, "\n"); + } + else { + lf_print_function_type(file, + function->fields[function_type], + "INLINE_SUPPORT", + (is_function_definition ? "\n" : " ")); + lf_printf(file, "%s\n(%s)%s", + function->fields[function_name], + function->fields[function_param], + (is_function_definition ? "\n" : ";\n")); + } +} + + +static void +support_h_function(insn_table *entry, + lf *file, + void *data, + table_entry *function) +{ + ASSERT(function->fields[function_type] != NULL); + ASSERT(function->fields[function_param] != NULL); + print_support_function_name(file, + function, + 0/*!is_definition*/); + lf_printf(file, "\n"); +} + + +extern void +gen_support_h(insn_table *table, + lf *file) +{ + /* output a declaration for all functions */ + insn_table_traverse_function(table, + file, NULL, + support_h_function); + lf_printf(file, "\n"); + lf_printf(file, "#if (SUPPORT_INLINE & INCLUDE_MODULE)\n"); + lf_printf(file, "# include \"support.c\"\n"); + lf_printf(file, "#endif\n"); +} + +static void +support_c_function(insn_table *table, + lf *file, + void *data, + table_entry *function) +{ + ASSERT(function->fields[function_type] != NULL); + print_support_function_name(file, + function, + 1/*!is_definition*/); + table_entry_print_cpp_line_nr(file, function); + lf_printf(file, "{\n"); + lf_indent(file, +2); + lf_print__c_code(file, function->annex); + if (it_is("internal", function->fields[insn_flags])) { + lf_printf(file, "error(\"Internal function must longjump\\n\");\n"); + lf_printf(file, "return 0;\n"); + } + lf_indent(file, -2); + lf_printf(file, "}\n"); + lf_print__internal_reference(file); + lf_printf(file, "\n"); +} + + +void +gen_support_c(insn_table *table, + lf *file) +{ + lf_printf(file, "#include \"cpu.h\"\n"); + lf_printf(file, "#include \"idecode.h\"\n"); + lf_printf(file, "#include \"support.h\"\n"); + lf_printf(file, "\n"); + + /* output a definition (c-code) for all functions */ + insn_table_traverse_function(table, + file, NULL, + support_c_function); +} diff --git a/sim/ppc/gen-support.h b/sim/ppc/gen-support.h new file mode 100644 index 00000000000..70862d76881 --- /dev/null +++ b/sim/ppc/gen-support.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. + + */ + + +extern void gen_support_h +(insn_table *table, + lf *file); + +extern void gen_support_c +(insn_table *table, + lf *file); + diff --git a/sim/ppc/hw_com.c b/sim/ppc/hw_com.c new file mode 100644 index 00000000000..436232289d2 --- /dev/null +++ b/sim/ppc/hw_com.c @@ -0,0 +1,559 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1996, 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 _HW_COM_C_ +#define _HW_COM_C_ + +#ifndef STATIC_INLINE_HW_COM +#define STATIC_INLINE_HW_COM STATIC_INLINE +#endif + +#include "device_table.h" + +#ifdef HAVE_STRING_H +#include <string.h> +#else +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif +#endif + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +/* DEVICE + + + com - '550 compatible serial device + + + DESCRIPTION + + + Models the basics of the 8 register '550 serial device. The model + includes an interrupt line, input and output fifos, and status + information. + + Independent configuration of the devices input and output streams is + allowed: use either the console or a file (buffered or unbuffered) as + the data source/sink; specify the real-time delay between each character + transfer. + + When the devices input stream is being taken from a file, the end of + file is signaled by a loss of carrier (the loss of carrier may be + incorrectly proceeded by a single null character). + + + PROPERTIES + + + reg = <address> <size> ... (optional - note 1) + + List of <address> <size> pairs. Each pair specifies an address for + the devices 8 registers. The address should be 8 byte aligned. + + + alternate-reg = <address> <size> ... (optional - note 1) + + Alternative addreses for the registers. + + + assigned-addresses = <address> <size> ... (optional - note 1) + + On a PCI bus, this property specifies the addresses assigned to the + device. The values reflect the devices configuration base registers. + + Note 1: At least one of "assigned-addresses", "reg" or "alternative-reg" + must be specified. If "assigned-addresses" is specified the other + address specifications are ignored. + + + input-file = <file-name> (optional) + + File to take all serial port input from (instead of the simulation + console). + + + output-file = <file-name> (optional) + + File to send all output to (instead of the simulation console). + + + input-buffering = "unbuffered" (optional) + + Specifying "unbuffered" buffering disables buffering on the serial + devices input stream (all data is immediatly read). In the future, + this option may be used to provide input buffering alternatives. + + + output-buffering = "unbuffered" (optional) + + Specifying "unbuffered" buffering disables buffering on the serial + devices output stream (all data is immediatly written). In the future, + this option may be extended to include other buffering alternatives. + + + input-delay = <integer-delay> (optional) + + Specify the number of ticks after the current character has been + read from the serial port that the next character becomes + available. + + + output-delay = <integer-delay> (optional) + + Specify the number of ticks after a character has been written to + the empty output fifo that the fifo finishes draining. Any + characters written to the output fifo before it has drained will + not be lost and will still be displayed. + + + EXAMPLES + + + | /iobus@0xf0000000/com@0x3000/reg 0x3000 8 + + Create a simple console device at address <<0x3000>> within + <<iobus>>. Since iobus starts at address <<0xf0000000>> the + absolute address of the serial port will be <<0xf0003000>>. + + The device will always be ready for I/O (no delay properties specified) + and both the input and output streams will use the simulation console + (no file properties). + + + | $ psim \ + | -o '/cpus/cpu@0' \ + | -o '/iobus@0xf0000000/com@0x4000/reg 0x4000 8' \ + | -o '/iobus@0xf0000000/com@0x4000/input-file /etc/passwd' \ + | -o '/iobus@0xf0000000/com@0x4000/input-delay 1000' \ + | -o '/iobus@0xf0000000/com@0x4000 > 0 int /cpus/cpu@0x0' \ + | psim-test/hw-com/cat.be 0xf0004000 + + The serial port (at address <<0xf0004000>> is configured so that it + takes its input from the file <</etc/passwd>> while its output is + allowed to appear on the simulation console. + + The node <</cpus/cpu@0>> was explicitly specified to ensure that it had + been created before any interrupts were attached to it. + + The program <<psim-test/hw-com/cat>> copies any characters on the serial + port's input (<</etc/passwd>>) to its output (the console). + Consequently, the aove program will display the contents of the file + <</etc/passwd>> on the screen. + + + BUGS + + + IEEE 1275 requires that a device on a PCI bus have, as its first reg + entry, the address of its configuration space registers. Currently, + this device does not even implement configuration registers. + + This model does not attempt to model the '550's input and output fifos. + Instead, the input fifo is limited to a single character at a time, + while the output fifo is effectivly infinite. Consequently, unlike the + '550, this device will not discard output characters once a stream of 16 + have been written to the data output register. + + The input and output can only be taken from a file (or the current + terminal device). In the future, the <<com>> device should allow the + specification of other data streams (such as an xterm or TK window). + + The input blocks if no data is available. + + Interrupts have not been tested. + + */ + +enum { + max_hw_com_registers = 8, +}; + +typedef struct _com_port { + int ready; + int delay; + int interrupting; + FILE *file; +} com_port; + +typedef struct _com_modem { + int carrier; + int carrier_changed; + int interrupting; +} com_modem; + +typedef struct _hw_com_device { + com_port input; + com_port output; + com_modem modem; + char dlab[2]; + char reg[max_hw_com_registers]; + int interrupting; +} hw_com_device; + + +static void +hw_com_device_init_data(device *me) +{ + hw_com_device *com = (hw_com_device*)device_data(me); + /* clean up */ + if (com->output.file != NULL) + fclose(com->output.file); + if (com->input.file != NULL) + fclose(com->input.file); + memset(com, 0, sizeof(hw_com_device)); + + /* the fifo speed */ + com->output.delay = (device_find_property(me, "output-delay") != NULL + ? device_find_integer_property(me, "output-delay") + : 0); + com->input.delay = (device_find_property(me, "input-delay") != NULL + ? device_find_integer_property(me, "input-delay") + : 0); + + /* the data source/sink */ + if (device_find_property(me, "input-file") != NULL) { + const char *input_file = device_find_string_property(me, "input-file"); + com->input.file = fopen(input_file, "r"); + if (com->input.file == NULL) + device_error(me, "Problem opening input file %s\n", input_file); + if (device_find_property(me, "input-buffering") != NULL) { + const char *buffering = device_find_string_property(me, "input-buffering"); + if (strcmp(buffering, "unbuffered") == 0) + setbuf(com->input.file, NULL); + } + } + if (device_find_property(me, "output-file") != NULL) { + const char *output_file = device_find_string_property(me, "output-file"); + com->output.file = fopen(output_file, "w"); + if (com->input.file == NULL) + device_error(me, "Problem opening output file %s\n", output_file); + if (device_find_property(me, "output-buffering") != NULL) { + const char *buffering = device_find_string_property(me, "output-buffering"); + if (strcmp(buffering, "unbuffered") == 0) + setbuf(com->output.file, NULL); + } + } + + /* ready from the start */ + com->input.ready = 1; + com->modem.carrier = 1; + com->output.ready = 1; +} + + +static void +update_com_interrupts(device *me, + hw_com_device *com) +{ + int interrupting; + com->modem.interrupting = (com->modem.carrier_changed && (com->reg[1] & 0x80)); + com->input.interrupting = (com->input.ready && (com->reg[1] & 0x1)); + com->output.interrupting = (com->output.ready && (com->reg[1] & 0x2)); + interrupting = (com->input.interrupting + || com->output.interrupting + || com->modem.interrupting); + + if (interrupting) { + if (!com->interrupting) { + device_interrupt_event(me, 0 /*port*/, 1 /*value*/, NULL, 0); + } + } + else /*!interrupting*/ { + if (com->interrupting) + device_interrupt_event(me, 0 /*port*/, 0 /*value*/, NULL, 0); + } + com->interrupting = interrupting; +} + + +static void +make_read_ready(void *data) +{ + device *me = (device*)data; + hw_com_device *com = (hw_com_device*)device_data(me); + com->input.ready = 1; + update_com_interrupts(me, com); +} + +static void +read_com(device *me, + hw_com_device *com, + unsigned_word a, + char val[1]) +{ + unsigned_word addr = a % 8; + + /* the divisor latch is special */ + if (com->reg[3] & 0x8 && addr < 2) { + *val = com->dlab[addr]; + return; + } + + switch (addr) { + + case 0: + /* fifo */ + if (!com->modem.carrier) + *val = '\0'; + if (com->input.ready) { + /* read the char in */ + if (com->input.file == NULL) { + if (sim_io_read_stdin(val, 1) < 0) + com->modem.carrier_changed = 1; + } + else { + if (fread(val, 1, 1, com->input.file) == 0) + com->modem.carrier_changed = 1; + } + /* setup for next read */ + if (com->modem.carrier_changed) { + /* once lost carrier, never ready */ + com->modem.carrier = 0; + com->input.ready = 0; + *val = '\0'; + } + else if (com->input.delay > 0) { + com->input.ready = 0; + device_event_queue_schedule(me, com->input.delay, make_read_ready, me); + } + } + else { + /* discard it? */ + /* overflow input fifo? */ + *val = '\0'; + } + break; + + case 2: + /* interrupt ident */ + if (com->interrupting) { + if (com->input.interrupting) + *val = 0x4; + else if (com->output.interrupting) + *val = 0x2; + else if (com->modem.interrupting == 0) + *val = 0; + else + device_error(me, "bad elif for interrupts\n"); + } + else + *val = 0x1; + break; + + case 5: + /* line status */ + *val = ((com->input.ready ? 0x1 : 0) + | (com->output.ready ? 0x60 : 0) + ); + break; + + case 6: + /* modem status */ + *val = ((com->modem.carrier_changed ? 0x08 : 0) + | (com->modem.carrier ? 0x80 : 0) + ); + com->modem.carrier_changed = 0; + break; + + default: + *val = com->reg[addr]; + break; + + } + update_com_interrupts(me, com); +} + +static unsigned +hw_com_io_read_buffer_callback(device *me, + void *dest, + int space, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + hw_com_device *com = device_data(me); + int i; + for (i = 0; i < nr_bytes; i++) { + read_com(me, com, addr + i, &((char*)dest)[i]); + } + return nr_bytes; +} + + +static void +make_write_ready(void *data) +{ + device *me = (device*)data; + hw_com_device *com = (hw_com_device*)device_data(me); + com->output.ready = 1; + update_com_interrupts(me, com); +} + +static void +write_com(device *me, + hw_com_device *com, + unsigned_word a, + char val) +{ + unsigned_word addr = a % 8; + + /* the divisor latch is special */ + if (com->reg[3] & 0x8 && addr < 2) { + com->dlab[addr] = val; + return; + } + + switch (addr) { + + case 0: + /* fifo */ + if (com->output.file == NULL) { + sim_io_write_stdout(&val, 1); + } + else { + fwrite(&val, 1, 1, com->output.file); + } + /* setup for next write */ + if (com->output.ready && com->output.delay > 0) { + com->output.ready = 0; + device_event_queue_schedule(me, com->output.delay, make_write_ready, me); + } + break; + + default: + com->reg[addr] = val; + break; + + } + update_com_interrupts(me, com); +} + +static unsigned +hw_com_io_write_buffer_callback(device *me, + const void *source, + int space, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + hw_com_device *com = device_data(me); + int i; + for (i = 0; i < nr_bytes; i++) { + write_com(me, com, addr + i, ((char*)source)[i]); + } + return nr_bytes; +} + + +/* instances of the hw_com device */ + +static void +hw_com_instance_delete(device_instance *instance) +{ + /* nothing to delete, the hw_com is attached to the device */ + return; +} + +static int +hw_com_instance_read(device_instance *instance, + void *buf, + unsigned_word len) +{ + device *me = device_instance_device(instance); + hw_com_device *com = device_data(me); + if (com->input.file == NULL) + return sim_io_read_stdin(buf, len); + else { + return fread(buf, 1, len, com->input.file); + } +} + +static int +hw_com_instance_write(device_instance *instance, + const void *buf, + unsigned_word len) +{ + device *me = device_instance_device(instance); + hw_com_device *com = device_data(me); + if (com->output.file == NULL) + return sim_io_write_stdout(buf, len); + else { + return fwrite(buf, 1, len, com->output.file); + } +} + +static const device_instance_callbacks hw_com_instance_callbacks = { + hw_com_instance_delete, + hw_com_instance_read, + hw_com_instance_write, +}; + +static device_instance * +hw_com_create_instance(device *me, + const char *path, + const char *args) +{ + /* point an instance directly at the device */ + return device_create_instance_from(me, NULL, + device_data(me), + path, args, + &hw_com_instance_callbacks); +} + + +static device_callbacks const hw_com_callbacks = { + { generic_device_init_address, + hw_com_device_init_data }, + { NULL, }, /* address */ + { hw_com_io_read_buffer_callback, + hw_com_io_write_buffer_callback, }, + { NULL, }, /* DMA */ + { NULL, }, /* interrupt */ + { NULL, }, /* unit */ + hw_com_create_instance, +}; + + +static void * +hw_com_create(const char *name, + const device_unit *unit_address, + const char *args) +{ + /* create the descriptor */ + hw_com_device *hw_com = ZALLOC(hw_com_device); + return hw_com; +} + + +const device_descriptor hw_com_device_descriptor[] = { + { "com", hw_com_create, &hw_com_callbacks }, + { NULL }, +}; + +#endif /* _HW_COM_C_ */ diff --git a/sim/ppc/hw_core.c b/sim/ppc/hw_core.c new file mode 100644 index 00000000000..1a439576bd9 --- /dev/null +++ b/sim/ppc/hw_core.c @@ -0,0 +1,143 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1996, 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 _HW_CORE_C_ +#define _HW_CORE_C_ + +#include "device_table.h" + +#include "corefile.h" + + +/* DEVICE + + core - root of the device tree + + DESCRIPTION + + The core device positioned at the root of the device tree appears + to its child devices as a normal device just like every other + device in the tree. + + Internally it is implemented using a core object. Requests to + attach (or detach) address spaces are passed to that core object. + Requests to transfer (DMA) data are reflected back down the device + tree using the core_map data transfer methods. + + PROPERTIES + + None. + + */ + + +static void +hw_core_init_address_callback(device *me) +{ + core *memory = (core*)device_data(me); + core_init(memory); +} + + +static void +hw_core_attach_address_callback(device *me, + attach_type attach, + int space, + unsigned_word addr, + unsigned nr_bytes, + access_type access, + device *client) /*callback/default*/ +{ + core *memory = (core*)device_data(me); + if (space != 0) + error("core_attach_address_callback() invalid address space\n"); + core_attach(memory, + attach, + space, + access, + addr, + nr_bytes, + client); +} + + +static unsigned +hw_core_dma_read_buffer_callback(device *me, + void *dest, + int space, + unsigned_word addr, + unsigned nr_bytes) +{ + core *memory = (core*)device_data(me); + return core_map_read_buffer(core_readable(memory), + dest, + addr, + nr_bytes); +} + + +static unsigned +hw_core_dma_write_buffer_callback(device *me, + const void *source, + int space, + unsigned_word addr, + unsigned nr_bytes, + int violate_read_only_section) +{ + core *memory = (core*)device_data(me); + core_map *map = (violate_read_only_section + ? core_readable(memory) + : core_writeable(memory)); + return core_map_write_buffer(map, + source, + addr, + nr_bytes); +} + +static device_callbacks const hw_core_callbacks = { + { hw_core_init_address_callback, }, + { hw_core_attach_address_callback, }, + { NULL, }, /* IO */ + { hw_core_dma_read_buffer_callback, + hw_core_dma_write_buffer_callback, }, + { NULL, }, /* interrupt */ + { generic_device_unit_decode, + generic_device_unit_encode, + generic_device_address_to_attach_address, + generic_device_size_to_attach_size, }, +}; + + +static void * +hw_core_create(const char *name, + const device_unit *unit_address, + const char *args) +{ + core *memory = core_create(); + return memory; +} + +const device_descriptor hw_core_device_descriptor[] = { + { "core", hw_core_create, &hw_core_callbacks }, + { NULL }, +}; + +#endif /* _HW_CORE_C_ */ diff --git a/sim/ppc/hw_cpu.c b/sim/ppc/hw_cpu.c new file mode 100644 index 00000000000..117a4c256f2 --- /dev/null +++ b/sim/ppc/hw_cpu.c @@ -0,0 +1,167 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1996, 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 _HW_CPU_C_ +#define _HW_CPU_C_ + +#ifndef STATIC_INLINE_HW_CPU +#define STATIC_INLINE_HW_CPU STATIC_INLINE +#endif + +#include "device_table.h" +#include "hw_cpu.h" + +#include "interrupts.h" +#include "cpu.h" + + +/* DEVICE + + + cpu - Interface to a Processor + + + DESCRIPTION + + + The CPU device provides the connection between the interrupt net + (linking the devices and the interrupt controller) and the + simulated model of each processor. This device contains interrupt + ports that correspond directly to the external interrupt stimulus + that can be sent to a given processor. Sending an interrupt to one + of the ports results in an interrupt being delivered to the + corresponding processor. + + Typically, an interrupt controller would have its inputs connected + to device interrupt sources and its outputs (sreset, int, et.al.) + connected to this device. + + + PROPERTIES + + + cpu-nr = <integer> (required) + + + Specify the processor (1..N) that this cpu device node should + control. + + + EXAMPLES + + + Connect an OpenPIC interrupt controller interrupt ports to + processor zero. + + | -o '/phb/opic@0 > irq0 int /cpus/cpu@0' \ + | -o '/phb/opic@0 > init hreset /cpus/cpu@0' \ + + + */ + +typedef struct _hw_cpu_device { + int cpu_nr; + cpu *processor; +} hw_cpu_device; + +static const device_interrupt_port_descriptor hw_cpu_interrupt_ports[] = { + { "hreset", hw_cpu_hard_reset }, + { "sreset", hw_cpu_soft_reset }, + { "int", hw_cpu_external_interrupt }, + { "mci", hw_cpu_machine_check_interrupt }, + { "smi", hw_cpu_system_management_interrupt }, + { NULL } +}; + + +static void * +hw_cpu_create(const char *name, + const device_unit *unit_address, + const char *args) +{ + hw_cpu_device *hw_cpu = ZALLOC(hw_cpu_device); + return hw_cpu; +} + + +/* during address initialization ensure that any missing cpu + properties are added to this devices node */ + +static void +hw_cpu_init_address(device *me) +{ + hw_cpu_device *hw_cpu = (hw_cpu_device*)device_data(me); + /* populate the node with properties */ + /* clear our data */ + memset(hw_cpu, 0x0, sizeof(hw_cpu_device)); + hw_cpu->cpu_nr = device_find_integer_property(me, "cpu-nr"); + hw_cpu->processor = psim_cpu(device_system(me), hw_cpu->cpu_nr); +} + + +/* Take the interrupt and synchronize its delivery with the clock. If + we've not yet scheduled an interrupt for the next clock tick, take + the oportunity to do it now */ + +static void +hw_cpu_interrupt_event(device *me, + int my_port, + device *source, + int source_port, + int level, + cpu *processor, + unsigned_word cia) +{ + hw_cpu_device *hw_cpu = (hw_cpu_device*)device_data(me); + if (my_port < 0 || my_port >= hw_cpu_nr_interrupt_ports) + error("hw_cpu_interrupt_event_callback: interrupt port out of range %d\n", + my_port); + switch (my_port) { + /*case hw_cpu_hard_reset:*/ + /*case hw_cpu_soft_reset:*/ + case hw_cpu_external_interrupt: + external_interrupt(hw_cpu->processor, level); + break; + /*case hw_cpu_machine_check_interrupt:*/ + default: + error("hw_cpu_deliver_interrupt: unimplemented interrupt port %d\n", + my_port); + break; + } +} + + +static device_callbacks const hw_cpu_callbacks = { + { hw_cpu_init_address, }, /* init */ + { NULL, }, /* address */ + { NULL, }, /* io */ + { NULL, }, /* DMA */ + { hw_cpu_interrupt_event, NULL, hw_cpu_interrupt_ports }, /* interrupts */ + { NULL, NULL, }, +}; + +const device_descriptor hw_cpu_device_descriptor[] = { + { "hw-cpu", hw_cpu_create, &hw_cpu_callbacks }, + { "cpu", hw_cpu_create, &hw_cpu_callbacks }, + { NULL, }, +}; + +#endif /* _HW_CPU_C_ */ diff --git a/sim/ppc/hw_cpu.h b/sim/ppc/hw_cpu.h new file mode 100644 index 00000000000..11d0298ee12 --- /dev/null +++ b/sim/ppc/hw_cpu.h @@ -0,0 +1,34 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1996, 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 _HW_CPU_H_ +#define _HW_CPU_H_ + +enum { + hw_cpu_hard_reset, + hw_cpu_soft_reset, + hw_cpu_external_interrupt, + hw_cpu_machine_check_interrupt, + hw_cpu_system_management_interrupt, + hw_cpu_nr_interrupt_ports +}; + +#endif /* _HW_CPU_H_ */ diff --git a/sim/ppc/hw_disk.c b/sim/ppc/hw_disk.c new file mode 100644 index 00000000000..5934fead1a1 --- /dev/null +++ b/sim/ppc/hw_disk.c @@ -0,0 +1,557 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1997, 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 _HW_DISK_C_ +#define _HW_DISK_C_ + +#include "device_table.h" + +#include "pk.h" + +#include <stdio.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +/* DEVICE + + + cdrom - read-only removable mass storage device + + disk - mass storage device + + floppy - removable mass storage device + + + DESCRIPTION + + + Mass storage devices such as a hard-disk or cdrom-drive are not + normally directly connected to the processor. Instead, these + devices are attached to a logical bus, such as SCSI or IDE, and + then a controller of that bus is made accessible to the processor. + + Reflecting this, within a device tree, mass storage devices such as + a <<cdrom>>, <<disk>> or <<floppy>> are created as children of of a + logical bus controller node (such as a SCSI or IDE interface). + That controller, in turn, would be made the child of a physical bus + node that is directly accessible to the processor. + + The above mass storage devices provide two interfaces - a logical + and a physical. + + At the physical level the <<device_io_...>> functions can be used + perform reads and writes of the raw media. The address being + interpreted as an offset from the start of the disk. + + At the logical level, it is possible to create an instance of the + disk that provides access to any of the physical media, a disk + partition, or even a file within a partition. The <<disk-label>> + package, which implements this functionality, is described + elsewhere. Both the Open Firmware and Moto BUG rom emulations + support this interface. + + Block devices such as the <<floppy>> and <<cdrom>> have removable + media. At the programmer level, the media can be changed using the + <<change_media>> ioctl. From within GDB, a <<change-media>> + operation can be initated by using the command. + + | (gdb) sim + + + PROPERTIES + + + file = <file-name> (required) + + The name of the file that contains an image of the disk. For + <<disk>> and <<floppy>> devices, the image will be opened for both + reading and writing. Multiple image files may be specified, the + second and later files being opened when <<change-media>> (with a + NULL file name) being specified. + + + block-size = <nr-bytes> (optional) + + The value is returned by the block-size method. The default value + is 512 bytes. + + + max-transfer = <nr-bytes> (optional) + + The value is returned by the max-transfer method. The default value + is 512 bytes. + + + #blocks = <nr-blocks> (optional) + + The value is returned by the #blocks method. If no value is + present then -1 is returned. + + + read-only = <anything> (optional) + + If this property is present, the disk file image is always opened + read-only. + + EXAMPLES + + + Enable tracing + + | $ psim -t 'disk-device' \ + + + Add a CDROM and disk to an IDE bus. Specify the host operating + system's cd drive as the CD-ROM image. + + | -o '/pci/ide/disk@0/file "disk-image' \ + | -o '/pci/ide/cdrom@1/file "/dev/cd0a' \ + + + As part of the code implementing a logical bus device (for instance + the IDE controller), locate the CDROM device and then read block + 47. + + | device *cdrom = device_tree_find_device(me, "cdrom"); + | char block[512]; + | device_io_read_buffer(cdrom, buf, 0, + 0, 47 * sizeof(block), // space, address + sizeof(block), NULL, 0); + + + Use the device instance interface to read block 47 of the file + called <<netbsd.elf>> on the disks default partition. Similar code + would be used in an operating systems pre-boot loader. + + | device_instance *netbsd = + | device_create_instance(root, "/pci/ide/disk:,\netbsd.elf"); + | char block[512]; + | device_instance_seek(netbsd, 0, 47 * sizeof(block)); + | device_instance_read(netbsd, block, sizeof(block)); + + + BUGS + + + The block device specification includes mechanisms for determining + the physical device characteristics - such as the disks size. + Currently this mechanism is not implemented. + + The functionality of this device (in particular the device instance + interface) depends on the implementation of <<disk-label>> package. + That package may not be fully implemented. + + The disk does not know its size. Hence it relies on the failure of + fread(), fwrite() and fseek() calls to detect errors. + + The disk size is limited by the addressable range covered by + unsigned_word (addr). An extension would be to instead use the + concatenated value space:addr. + + The method #blocks should `stat' the disk to determine the number + of blocks if there is no #blocks property. + + It would appear that OpenFirmware does not define a client call for + changing (ejecting) the media of a device. + + */ + +typedef struct _hw_disk_device { + int name_index; + int nr_names; + char *name; + int read_only; + /* unsigned_word size; */ + FILE *image; +} hw_disk_device; + +typedef struct _hw_disk_instance { + unsigned_word pos; + hw_disk_device *disk; +} hw_disk_instance; + + +static void +open_disk_image(device *me, + hw_disk_device *disk, + const char *name) +{ + if (disk->image != NULL) + fclose(disk->image); + if (disk->name != NULL) + zfree(disk->name); + disk->name = strdup(name); + disk->image = fopen(disk->name, disk->read_only ? "r" : "r+"); + if (disk->image == NULL) { + perror(device_name(me)); + device_error(me, "open %s failed\n", disk->name); + } + + DTRACE(disk, ("image %s (%s)\n", + disk->name, + (disk->read_only ? "read-only" : "read-write"))); +} + +static void +hw_disk_init_address(device *me) +{ + hw_disk_device *disk = device_data(me); + unsigned_word address; + int space; + const char *name; + + /* attach to the parent. Since the bus is logical, attach using just + the unit-address (size must be zero) */ + device_address_to_attach_address(device_parent(me), device_unit_address(me), + &space, &address, me); + device_attach_address(device_parent(me), attach_callback, + space, address, 0/*size*/, access_read_write_exec, + me); + + /* get the name of the file specifying the disk image */ + disk->name_index = 0; + disk->nr_names = device_find_string_array_property(me, "file", + disk->name_index, &name); + if (!disk->nr_names) + device_error(me, "invalid file property"); + + /* is it a RO device? */ + disk->read_only = + (strcmp(device_name(me), "disk") != 0 + && strcmp(device_name(me), "floppy") != 0 + && device_find_property(me, "read-only") == NULL); + + /* now open it */ + open_disk_image(me, disk, name); +} + +static int +hw_disk_ioctl(device *me, + cpu *processor, + unsigned_word cia, + device_ioctl_request request, + va_list ap) +{ + switch (request) { + case device_ioctl_change_media: + { + hw_disk_device *disk = device_data(me); + const char *name = va_arg(ap, const char *); + if (name != NULL) { + disk->name_index = -1; + } + else { + disk->name_index = (disk->name_index + 1) % disk->nr_names; + if (!device_find_string_array_property(me, "file", + disk->name_index, &name)) + device_error(me, "invalid file property"); + } + open_disk_image(me, disk, name); + } + break; + default: + device_error(me, "insupported ioctl request"); + break; + } + return 0; +} + + + + + +static unsigned +hw_disk_io_read_buffer(device *me, + void *dest, + int space, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + hw_disk_device *disk = device_data(me); + unsigned nr_bytes_read; + if (space != 0) + device_error(me, "read - extended disk addressing unimplemented"); + if (nr_bytes == 0) + nr_bytes_read = 0; + else if (fseek(disk->image, addr, SEEK_SET) < 0) + nr_bytes_read = 0; + else if (fread(dest, nr_bytes, 1, disk->image) != 1) + nr_bytes_read = 0; + else + nr_bytes_read = nr_bytes; + DTRACE(disk, ("io-read - address 0x%lx, nr-bytes-read %d, requested %d\n", + (unsigned long) addr, (int)nr_bytes_read, (int)nr_bytes)); + return nr_bytes_read; +} + + +static unsigned +hw_disk_io_write_buffer(device *me, + const void *source, + int space, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + hw_disk_device *disk = device_data(me); + unsigned nr_bytes_written; + if (space != 0) + device_error(me, "write - extended disk addressing unimplemented"); + if (disk->read_only) + nr_bytes_written = 0; + else if (nr_bytes == 0) + nr_bytes_written = 0; + else if (fseek(disk->image, addr, SEEK_SET) < 0) + nr_bytes_written = 0; + else if (fwrite(source, nr_bytes, 1, disk->image) != 1) + nr_bytes_written = 0; + else + nr_bytes_written = nr_bytes; + DTRACE(disk, ("io-write - address 0x%lx, nr-bytes-written %d, requested %d\n", + (unsigned long) addr, (int)nr_bytes_written, (int)nr_bytes)); + return nr_bytes_written; +} + + +/* instances of the hw_disk device */ + +static void +hw_disk_instance_delete(device_instance *instance) +{ + hw_disk_instance *data = device_instance_data(instance); + DITRACE(disk, ("delete - instance=%ld\n", + (unsigned long)device_instance_to_external(instance))); + zfree(data); +} + +static int +hw_disk_instance_read(device_instance *instance, + void *buf, + unsigned_word len) +{ + hw_disk_instance *data = device_instance_data(instance); + DITRACE(disk, ("read - instance=%ld len=%ld\n", + (unsigned long)device_instance_to_external(instance), + (long)len)); + if ((data->pos + len) < data->pos) + return -1; /* overflow */ + if (fseek(data->disk->image, data->pos, SEEK_SET) < 0) + return -1; + if (fread(buf, len, 1, data->disk->image) != 1) + return -1; + data->pos = ftell(data->disk->image); + return len; +} + +static int +hw_disk_instance_write(device_instance *instance, + const void *buf, + unsigned_word len) +{ + hw_disk_instance *data = device_instance_data(instance); + DITRACE(disk, ("write - instance=%ld len=%ld\n", + (unsigned long)device_instance_to_external(instance), + (long)len)); + if ((data->pos + len) < data->pos) + return -1; /* overflow */ + if (data->disk->read_only) + return -1; + if (fseek(data->disk->image, data->pos, SEEK_SET) < 0) + return -1; + if (fwrite(buf, len, 1, data->disk->image) != 1) + return -1; + data->pos = ftell(data->disk->image); + return len; +} + +static int +hw_disk_instance_seek(device_instance *instance, + unsigned_word pos_hi, + unsigned_word pos_lo) +{ + hw_disk_instance *data = device_instance_data(instance); + if (pos_hi != 0) + device_error(device_instance_device(instance), + "seek - extended addressing unimplemented"); + DITRACE(disk, ("seek - instance=%ld pos_hi=%ld pos_lo=%ld\n", + (unsigned long)device_instance_to_external(instance), + (long)pos_hi, (long)pos_lo)); + data->pos = pos_lo; + return 0; +} + +static int +hw_disk_max_transfer(device_instance *instance, + int n_stack_args, + unsigned32 stack_args[/*n_stack_args*/], + int n_stack_returns, + unsigned32 stack_returns[/*n_stack_returns*/]) +{ + device *me = device_instance_device(instance); + if ((n_stack_args != 0) + || (n_stack_returns != 1)) { + device_error(me, "Incorrect number of arguments for max-transfer method\n"); + return -1; + } + else { + unsigned_cell max_transfer; + if (device_find_property(me, "max-transfer")) + max_transfer = device_find_integer_property(me, "max-transfer"); + else + max_transfer = 512; + DITRACE(disk, ("max-transfer - instance=%ld max-transfer=%ld\n", + (unsigned long)device_instance_to_external(instance), + (long int)max_transfer)); + stack_returns[0] = max_transfer; + return 0; + } +} + +static int +hw_disk_block_size(device_instance *instance, + int n_stack_args, + unsigned32 stack_args[/*n_stack_args*/], + int n_stack_returns, + unsigned32 stack_returns[/*n_stack_returns*/]) +{ + device *me = device_instance_device(instance); + if ((n_stack_args != 0) + || (n_stack_returns != 1)) { + device_error(me, "Incorrect number of arguments for block-size method\n"); + return -1; + } + else { + unsigned_cell block_size; + if (device_find_property(me, "block-size")) + block_size = device_find_integer_property(me, "block-size"); + else + block_size = 512; + DITRACE(disk, ("block-size - instance=%ld block-size=%ld\n", + (unsigned long)device_instance_to_external(instance), + (long int)block_size)); + stack_returns[0] = block_size; + return 0; + } +} + +static int +hw_disk_nr_blocks(device_instance *instance, + int n_stack_args, + unsigned32 stack_args[/*n_stack_args*/], + int n_stack_returns, + unsigned32 stack_returns[/*n_stack_returns*/]) +{ + device *me = device_instance_device(instance); + if ((n_stack_args != 0) + || (n_stack_returns != 1)) { + device_error(me, "Incorrect number of arguments for block-size method\n"); + return -1; + } + else { + unsigned_word nr_blocks; + if (device_find_property(me, "#blocks")) + nr_blocks = device_find_integer_property(me, "#blocks"); + else + nr_blocks = -1; + DITRACE(disk, ("#blocks - instance=%ld #blocks=%ld\n", + (unsigned long)device_instance_to_external(instance), + (long int)nr_blocks)); + stack_returns[0] = nr_blocks; + return 0; + } +} + +static device_instance_methods hw_disk_instance_methods[] = { + { "max-transfer", hw_disk_max_transfer }, + { "block-size", hw_disk_block_size }, + { "#blocks", hw_disk_nr_blocks }, + { NULL, }, +}; + +static const device_instance_callbacks hw_disk_instance_callbacks = { + hw_disk_instance_delete, + hw_disk_instance_read, + hw_disk_instance_write, + hw_disk_instance_seek, + hw_disk_instance_methods, +}; + +static device_instance * +hw_disk_create_instance(device *me, + const char *path, + const char *args) +{ + device_instance *instance; + hw_disk_device *disk = device_data(me); + hw_disk_instance *data = ZALLOC(hw_disk_instance); + data->disk = disk; + data->pos = 0; + instance = device_create_instance_from(me, NULL, + data, + path, args, + &hw_disk_instance_callbacks); + DITRACE(disk, ("create - path=%s(%s) instance=%ld\n", + path, args, + (unsigned long)device_instance_to_external(instance))); + return pk_disklabel_create_instance(instance, args); +} + +static device_callbacks const hw_disk_callbacks = { + { hw_disk_init_address, NULL }, + { NULL, }, /* address */ + { hw_disk_io_read_buffer, + hw_disk_io_write_buffer, }, + { NULL, }, /* DMA */ + { NULL, }, /* interrupt */ + { NULL, }, /* unit */ + hw_disk_create_instance, + hw_disk_ioctl, +}; + + +static void * +hw_disk_create(const char *name, + const device_unit *unit_address, + const char *args) +{ + /* create the descriptor */ + hw_disk_device *hw_disk = ZALLOC(hw_disk_device); + return hw_disk; +} + + +const device_descriptor hw_disk_device_descriptor[] = { + { "disk", hw_disk_create, &hw_disk_callbacks }, + { "cdrom", hw_disk_create, &hw_disk_callbacks }, + { "floppy", hw_disk_create, &hw_disk_callbacks }, + { NULL }, +}; + +#endif /* _HW_DISK_C_ */ diff --git a/sim/ppc/hw_eeprom.c b/sim/ppc/hw_eeprom.c new file mode 100644 index 00000000000..5092984369e --- /dev/null +++ b/sim/ppc/hw_eeprom.c @@ -0,0 +1,839 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1996, 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 _HW_EEPROM_C_ +#define _HW_EEPROM_C_ + +#include "device_table.h" + +#ifdef HAVE_STRING_H +#include <string.h> +#else +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif +#endif + + +/* DEVICE + + + eeprom - JEDEC? compatible electricaly erasable programable device + + + DESCRIPTION + + + This device implements a small byte addressable EEPROM. + Programming is performed using the same write sequences as used by + standard modern EEPROM components. Writes occure in real time, the + device returning a progress value until the programing has been + completed. + + It is based on the AMD 29F040 component. + + + PROPERTIES + + + reg = <address> <size> (required) + + Determine where the device lives in the parents address space. + + + nr-sectors = <integer> (required) + + When erasing an entire sector is cleared at a time. This specifies + the number of sectors in the EEPROM component. + + + sector-size = <integer> (required) + + The number of bytes in a sector. When erasing, memory chunks of + this size are cleared. + + NOTE: The product nr-sectors * sector-size does not need to map the + size specified in the reg property. If the specified size is + smaller part of the eeprom will not be accessible while if it is + larger the addresses will wrap. + + + byte-write-delay = <integer> (required) + + Number of clock ticks before the programming of a single byte + completes. + + + sector-start-delay = <integer> (required) + + When erasing sectors, the number of clock ticks after the sector + has been specified that the actual erase process commences. + + + erase-delay = <intger> (required) + + Number of clock ticks before an erase program completes + + + manufacture-code = <integer> (required) + + The one byte value returned when the auto-select manufacturer code + is read. + + + device-code = <integer> (required) + + The one byte value returned when the auto-select device code is + read. + + + input-file = <file-name> (optional) + + Initialize the eeprom using the specified binary file. + + + output-file = <file-name> (optional) + + When ever the eeprom is updated, save the modified image into the + specified file. + + + EXAMPLES + + + Enable tracing of the eeprom: + + | bash$ psim -t eeprom-device \ + + + Configure something very like the Amd Am29F040 - 512byte EEPROM + (but a bit faster): + + | -o '/eeprom@0xfff00000/reg 0xfff00000 0x80000' \ + | -o '/eeprom@0xfff00000/nr-sectors 8' \ + | -o '/eeprom@0xfff00000/sector-size 0x10000' \ + | -o '/eeprom@0xfff00000/byte-write-delay 1000' \ + | -o '/eeprom@0xfff00000/sector-start-delay 100' \ + | -o '/eeprom@0xfff00000/erase-delay 1000' \ + | -o '/eeprom@0xfff00000/manufacture-code 0x01' \ + | -o '/eeprom@0xfff00000/device-code 0xa4' \ + + + Initialize the eeprom from the file <</dev/zero>>: + + | -o '/eeprom@0xfff00000/input-file /dev/zero' + + + BUGS + + + */ + +typedef enum { + read_reset, + write_nr_2, + write_nr_3, + write_nr_4, + write_nr_5, + write_nr_6, + byte_program, + byte_programming, + chip_erase, + sector_erase, + sector_erase_suspend, + autoselect, +} hw_eeprom_states; + +static const char * +state2a(hw_eeprom_states state) +{ + switch (state) { + case read_reset: return "read_reset"; + case write_nr_2: return "write_nr_2"; + case write_nr_3: return "write_nr_3"; + case write_nr_4: return "write_nr_4"; + case write_nr_5: return "write_nr_5"; + case write_nr_6: return "write_nr_6"; + case byte_program: return "byte_program"; + case byte_programming: return "byte_programming"; + case chip_erase: return "chip_erase"; + case sector_erase: return "sector_erase"; + case sector_erase_suspend: return "sector_erase_suspend"; + case autoselect: return "autoselect"; + } + return NULL; +} + +typedef struct _hw_eeprom_device { + /* general */ + hw_eeprom_states state; + unsigned8 *memory; + unsigned sizeof_memory; + unsigned erase_delay; + signed64 program_start_time; + signed64 program_finish_time; + unsigned8 manufacture_code; + unsigned8 device_code; + unsigned8 toggle_bit; + /* initialization */ + const char *input_file_name; + const char *output_file_name; + /* for sector and sector programming */ + hw_eeprom_states sector_state; + unsigned8 *sectors; + unsigned nr_sectors; + unsigned sizeof_sector; + unsigned sector_start_delay; + unsigned sector_start_time; + /* byte and byte programming */ + unsigned byte_write_delay; + unsigned_word byte_program_address; + unsigned8 byte_program_byte; +} hw_eeprom_device; + +typedef struct _hw_eeprom_reg_spec { + unsigned32 base; + unsigned32 size; +} hw_eeprom_reg_spec; + +static void +hw_eeprom_init_data(device *me) +{ + hw_eeprom_device *eeprom = (hw_eeprom_device*)device_data(me); + + /* have we any input or output files */ + if (device_find_property(me, "input-file") != NULL) + eeprom->input_file_name = device_find_string_property(me, "input-file"); + if (device_find_property(me, "output-file") != NULL) + eeprom->input_file_name = device_find_string_property(me, "output-file"); + + /* figure out the sectors in the eeprom */ + if (eeprom->sectors == NULL) { + eeprom->nr_sectors = device_find_integer_property(me, "nr-sectors"); + eeprom->sizeof_sector = device_find_integer_property(me, "sector-size"); + eeprom->sectors = zalloc(eeprom->nr_sectors); + } + else + memset(eeprom->sectors, 0, eeprom->nr_sectors); + + /* initialize the eeprom */ + if (eeprom->memory == NULL) { + eeprom->sizeof_memory = eeprom->sizeof_sector * eeprom->nr_sectors; + eeprom->memory = zalloc(eeprom->sizeof_memory); + } + else + memset(eeprom->memory, 0, eeprom->sizeof_memory); + if (eeprom->input_file_name != NULL) { + int i; + FILE *input_file = fopen(eeprom->input_file_name, "r"); + if (input_file == NULL) { + perror("eeprom"); + device_error(me, "Failed to open input file %s\n", eeprom->input_file_name); + } + for (i = 0; i < eeprom->sizeof_memory; i++) { + if (fread(&eeprom->memory[i], 1, 1, input_file) != 1) + break; + } + fclose(input_file); + } + + /* timing */ + eeprom->byte_write_delay = device_find_integer_property(me, "byte-write-delay"); + eeprom->sector_start_delay = device_find_integer_property(me, "sector-start-delay"); + eeprom->erase_delay = device_find_integer_property(me, "erase-delay"); + + /* misc */ + eeprom->manufacture_code = device_find_integer_property(me, "manufacture-code"); + eeprom->device_code = device_find_integer_property(me, "device-code"); +} + + +static void +invalid_read(device *me, + hw_eeprom_states state, + unsigned_word address, + const char *reason) +{ + DTRACE(eeprom, ("Invalid read to 0x%lx while in state %s (%s)\n", + (unsigned long)address, + state2a(state), + reason)); +} + +static void +invalid_write(device *me, + hw_eeprom_states state, + unsigned_word address, + unsigned8 data, + const char *reason) +{ + DTRACE(eeprom, ("Invalid write of 0x%lx to 0x%lx while in state %s (%s)\n", + (unsigned long)data, + (unsigned long)address, + state2a(state), + reason)); +} + +static void +dump_eeprom(device *me, + hw_eeprom_device *eeprom) +{ + if (eeprom->output_file_name != NULL) { + int i; + FILE *output_file = fopen(eeprom->output_file_name, "w"); + if (output_file == NULL) { + perror("eeprom"); + device_error(me, "Failed to open output file %s\n", + eeprom->output_file_name); + } + for (i = 0; i < eeprom->sizeof_memory; i++) { + if (fwrite(&eeprom->memory[i], 1, 1, output_file) != 1) + break; + } + fclose(output_file); + } +} + + +/* program a single byte of eeprom */ + +static void +start_programming_byte(device *me, + hw_eeprom_device *eeprom, + unsigned_word address, + unsigned8 new_byte) +{ + unsigned8 old_byte = eeprom->memory[address]; + DTRACE(eeprom, ("start-programing-byte - address 0x%lx, new 0x%lx, old 0x%lx\n", + (unsigned long)address, + (unsigned long)new_byte, + (unsigned long)old_byte)); + eeprom->byte_program_address = address; + /* : old new : ~old : new&~old + : 0 0 : 1 : 0 + : 0 1 : 1 : 1 -- can not set a bit + : 1 0 : 0 : 0 + : 1 1 : 0 : 0 */ + if (~old_byte & new_byte) + invalid_write(me, eeprom->state, address, new_byte, "setting cleared bit"); + /* : old new : old&new + : 0 0 : 0 + : 0 1 : 0 + : 1 0 : 0 + : 1 1 : 1 */ + eeprom->byte_program_byte = new_byte & old_byte; + eeprom->memory[address] = ~new_byte & ~0x24; /* LE-bits 5:3 zero */ + eeprom->program_start_time = device_event_queue_time(me); + eeprom->program_finish_time = (eeprom->program_start_time + + eeprom->byte_write_delay); +} + +static void +finish_programming_byte(device *me, + hw_eeprom_device *eeprom) +{ + DTRACE(eeprom, ("finish-programming-byte - address 0x%lx, byte 0x%lx\n", + (unsigned long)eeprom->byte_program_address, + (unsigned long)eeprom->byte_program_byte)); + eeprom->memory[eeprom->byte_program_address] = eeprom->byte_program_byte; + dump_eeprom(me, eeprom); +} + + +/* erase the eeprom completly */ + +static void +start_erasing_chip(device *me, + hw_eeprom_device *eeprom) +{ + DTRACE(eeprom, ("start-erasing-chip\n")); + memset(eeprom->memory, 0, eeprom->sizeof_memory); + eeprom->program_start_time = device_event_queue_time(me); + eeprom->program_finish_time = (eeprom->program_start_time + + eeprom->erase_delay); +} + +static void +finish_erasing_chip(device *me, + hw_eeprom_device *eeprom) +{ + DTRACE(eeprom, ("finish-erasing-chip\n")); + memset(eeprom->memory, 0xff, eeprom->sizeof_memory); + dump_eeprom(me, eeprom); +} + + +/* erase a single sector of the eeprom */ + +static void +start_erasing_sector(device *me, + hw_eeprom_device *eeprom, + unsigned_word address) +{ + int sector = address / eeprom->sizeof_sector; + DTRACE(eeprom, ("start-erasing-sector - address 0x%lx, sector %d\n", + (unsigned long)address, sector)); + ASSERT(sector < eeprom->nr_sectors); + eeprom->sectors[sector] = 1; + memset(eeprom->memory + sector * eeprom->sizeof_sector, + 0x4, eeprom->sizeof_sector); + eeprom->program_start_time = device_event_queue_time(me); + eeprom->sector_start_time = (eeprom->program_start_time + + eeprom->sector_start_delay); + eeprom->program_finish_time = (eeprom->sector_start_time + + eeprom->erase_delay); + +} + +static void +finish_erasing_sector(device *me, + hw_eeprom_device *eeprom) +{ + int sector; + DTRACE(eeprom, ("finish-erasing-sector\n")); + for (sector = 0; sector < eeprom->nr_sectors; sector++) { + if (eeprom->sectors[sector]) { + eeprom->sectors[sector] = 0; + memset(eeprom->memory + sector * eeprom->sizeof_sector, + 0xff, eeprom->sizeof_sector); + } + } + dump_eeprom(me, eeprom); +} + + +/* eeprom reads */ + +static unsigned8 +toggle(hw_eeprom_device *eeprom, + unsigned8 byte) +{ + eeprom->toggle_bit = eeprom->toggle_bit ^ 0x40; /* le-bit 6 */ + return eeprom->toggle_bit ^ byte; +} + +static unsigned8 +read_byte(device *me, + hw_eeprom_device *eeprom, + unsigned_word address) +{ + /* may need multiple iterations of this */ + while (1) { + switch (eeprom->state) { + + case read_reset: + return eeprom->memory[address]; + + case autoselect: + if ((address & 0xff) == 0x00) + return eeprom->manufacture_code; + else if ((address & 0xff) == 0x01) + return eeprom->device_code; + else + return 0; /* not certain about this */ + + case byte_programming: + if (device_event_queue_time(me) > eeprom->program_finish_time) { + finish_programming_byte(me, eeprom); + eeprom->state = read_reset; + continue; + } + else if (address == eeprom->byte_program_address) { + return toggle(eeprom, eeprom->memory[address]); + } + else { + /* trash that memory location */ + invalid_read(me, eeprom->state, address, "not byte program address"); + eeprom->memory[address] = (eeprom->memory[address] + & eeprom->byte_program_byte); + return toggle(eeprom, eeprom->memory[eeprom->byte_program_address]); + } + + case chip_erase: + if (device_event_queue_time(me) > eeprom->program_finish_time) { + finish_erasing_chip(me, eeprom); + eeprom->state = read_reset; + continue; + } + else { + return toggle(eeprom, eeprom->memory[address]); + } + + case sector_erase: + if (device_event_queue_time(me) > eeprom->program_finish_time) { + finish_erasing_sector(me, eeprom); + eeprom->state = read_reset; + continue; + } + else if (!eeprom->sectors[address / eeprom->sizeof_sector]) { + /* read to wrong sector */ + invalid_read(me, eeprom->state, address, "sector not being erased"); + return toggle(eeprom, eeprom->memory[address]) & ~0x8; + } + else if (device_event_queue_time(me) > eeprom->sector_start_time) { + return toggle(eeprom, eeprom->memory[address]) | 0x8; + } + else { + return toggle(eeprom, eeprom->memory[address]) & ~0x8; + } + + case sector_erase_suspend: + if (!eeprom->sectors[address / eeprom->sizeof_sector]) { + return eeprom->memory[address]; + } + else { + invalid_read(me, eeprom->state, address, "sector being erased"); + return eeprom->memory[address]; + } + + default: + invalid_read(me, eeprom->state, address, "invalid state"); + return eeprom->memory[address]; + + } + } + return 0; +} + +static unsigned +hw_eeprom_io_read_buffer(device *me, + void *dest, + int space, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + hw_eeprom_device *eeprom = (hw_eeprom_device*)device_data(me); + int i; + for (i = 0; i < nr_bytes; i++) { + unsigned_word address = (addr + i) % eeprom->sizeof_memory; + unsigned8 byte = read_byte(me, eeprom, address); + ((unsigned8*)dest)[i] = byte; + } + return nr_bytes; +} + + +/* eeprom writes */ + +static void +write_byte(device *me, + hw_eeprom_device *eeprom, + unsigned_word address, + unsigned8 data) +{ + /* may need multiple transitions to process a write */ + while (1) { + switch (eeprom->state) { + + case read_reset: + if (address == 0x5555 && data == 0xaa) + eeprom->state = write_nr_2; + else if (data == 0xf0) + eeprom->state = read_reset; + else { + invalid_write(me, eeprom->state, address, data, "unexpected"); + eeprom->state = read_reset; + } + return; + + case write_nr_2: + if (address == 0x2aaa && data == 0x55) + eeprom->state = write_nr_3; + else { + invalid_write(me, eeprom->state, address, data, "unexpected"); + eeprom->state = read_reset; + } + return; + + case write_nr_3: + if (address == 0x5555 && data == 0xf0) + eeprom->state = read_reset; + else if (address == 0x5555 && data == 0x90) + eeprom->state = autoselect; + else if (address == 0x5555 && data == 0xa0) { + eeprom->state = byte_program; + } + else if (address == 0x5555 && data == 0x80) + eeprom->state = write_nr_4; + else { + invalid_write(me, eeprom->state, address, data, "unexpected"); + eeprom->state = read_reset; + } + return; + + case write_nr_4: + if (address == 0x5555 && data == 0xaa) + eeprom->state = write_nr_5; + else { + invalid_write(me, eeprom->state, address, data, "unexpected"); + eeprom->state = read_reset; + } + return; + + case write_nr_5: + if (address == 0x2aaa && data == 0x55) + eeprom->state = write_nr_6; + else { + invalid_write(me, eeprom->state, address, data, "unexpected"); + eeprom->state = read_reset; + } + return; + + case write_nr_6: + if (address == 0x5555 && data == 0x10) { + start_erasing_chip(me, eeprom); + eeprom->state = chip_erase; + } + else { + start_erasing_sector(me, eeprom, address); + eeprom->sector_state = read_reset; + eeprom->state = sector_erase; + } + return; + + case autoselect: + if (data == 0xf0) + eeprom->state = read_reset; + else if (address == 0x5555 && data == 0xaa) + eeprom->state = write_nr_2; + else { + invalid_write(me, eeprom->state, address, data, "unsupported address"); + eeprom->state = read_reset; + } + return; + + case byte_program: + start_programming_byte(me, eeprom, address, data); + eeprom->state = byte_programming; + return; + + case byte_programming: + if (device_event_queue_time(me) > eeprom->program_finish_time) { + finish_programming_byte(me, eeprom); + eeprom->state = read_reset; + continue; + } + /* ignore it */ + return; + + case chip_erase: + if (device_event_queue_time(me) > eeprom->program_finish_time) { + finish_erasing_chip(me, eeprom); + eeprom->state = read_reset; + continue; + } + /* ignore it */ + return; + + case sector_erase: + if (device_event_queue_time(me) > eeprom->program_finish_time) { + finish_erasing_sector(me, eeprom); + eeprom->state = eeprom->sector_state; + continue; + } + else if (device_event_queue_time(me) > eeprom->sector_start_time + && data == 0xb0) { + eeprom->sector_state = read_reset; + eeprom->state = sector_erase_suspend; + } + else { + if (eeprom->sector_state == read_reset + && address == 0x5555 && data == 0xaa) + eeprom->sector_state = write_nr_2; + else if (eeprom->sector_state == write_nr_2 + && address == 0x2aaa && data == 0x55) + eeprom->sector_state = write_nr_3; + else if (eeprom->sector_state == write_nr_3 + && address == 0x5555 && data == 0x80) + eeprom->sector_state = write_nr_4; + else if (eeprom->sector_state == write_nr_4 + && address == 0x5555 && data == 0xaa) + eeprom->sector_state = write_nr_5; + else if (eeprom->sector_state == write_nr_5 + && address == 0x2aaa && data == 0x55) + eeprom->sector_state = write_nr_6; + else if (eeprom->sector_state == write_nr_6 + && address != 0x5555 && data == 0x30) { + if (device_event_queue_time(me) > eeprom->sector_start_time) { + DTRACE(eeprom, ("sector erase command after window closed\n")); + eeprom->sector_state = read_reset; + } + else { + start_erasing_sector(me, eeprom, address); + eeprom->sector_state = read_reset; + } + } + else { + invalid_write(me, eeprom->state, address, data, state2a(eeprom->sector_state)); + eeprom->state = read_reset; + } + } + return; + + case sector_erase_suspend: + if (data == 0x30) + eeprom->state = sector_erase; + else { + invalid_write(me, eeprom->state, address, data, "not resume command"); + eeprom->state = read_reset; + } + return; + + } + } +} + +static unsigned +hw_eeprom_io_write_buffer(device *me, + const void *source, + int space, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + hw_eeprom_device *eeprom = (hw_eeprom_device*)device_data(me); + int i; + for (i = 0; i < nr_bytes; i++) { + unsigned_word address = (addr + i) % eeprom->sizeof_memory; + unsigned8 byte = ((unsigned8*)source)[i]; + write_byte(me, eeprom, address, byte); + } + return nr_bytes; +} + + +/* An instance of the eeprom */ + +typedef struct _hw_eeprom_instance { + unsigned_word pos; + hw_eeprom_device *eeprom; + device *me; +} hw_eeprom_instance; + +static void +hw_eeprom_instance_delete(device_instance *instance) +{ + hw_eeprom_instance *data = device_instance_data(instance); + zfree(data); +} + +static int +hw_eeprom_instance_read(device_instance *instance, + void *buf, + unsigned_word len) +{ + hw_eeprom_instance *data = device_instance_data(instance); + int i; + if (data->eeprom->state != read_reset) + DITRACE(eeprom, ("eeprom not idle during instance read\n")); + for (i = 0; i < len; i++) { + ((unsigned8*)buf)[i] = data->eeprom->memory[data->pos]; + data->pos = (data->pos + 1) % data->eeprom->sizeof_memory; + } + return len; +} + +static int +hw_eeprom_instance_write(device_instance *instance, + const void *buf, + unsigned_word len) +{ + hw_eeprom_instance *data = device_instance_data(instance); + int i; + if (data->eeprom->state != read_reset) + DITRACE(eeprom, ("eeprom not idle during instance write\n")); + for (i = 0; i < len; i++) { + data->eeprom->memory[data->pos] = ((unsigned8*)buf)[i]; + data->pos = (data->pos + 1) % data->eeprom->sizeof_memory; + } + dump_eeprom(data->me, data->eeprom); + return len; +} + +static int +hw_eeprom_instance_seek(device_instance *instance, + unsigned_word pos_hi, + unsigned_word pos_lo) +{ + hw_eeprom_instance *data = device_instance_data(instance); + if (pos_lo >= data->eeprom->sizeof_memory) + device_error(data->me, "seek value 0x%lx out of range\n", + (unsigned long)pos_lo); + data->pos = pos_lo; + return 0; +} + +static const device_instance_callbacks hw_eeprom_instance_callbacks = { + hw_eeprom_instance_delete, + hw_eeprom_instance_read, + hw_eeprom_instance_write, + hw_eeprom_instance_seek, +}; + +static device_instance * +hw_eeprom_create_instance(device *me, + const char *path, + const char *args) +{ + hw_eeprom_device *eeprom = device_data(me); + hw_eeprom_instance *data = ZALLOC(hw_eeprom_instance); + data->eeprom = eeprom; + data->me = me; + return device_create_instance_from(me, NULL, + data, + path, args, + &hw_eeprom_instance_callbacks); +} + + + +static device_callbacks const hw_eeprom_callbacks = { + { generic_device_init_address, + hw_eeprom_init_data }, + { NULL, }, /* address */ + { hw_eeprom_io_read_buffer, + hw_eeprom_io_write_buffer }, /* IO */ + { NULL, }, /* DMA */ + { NULL, }, /* interrupt */ + { NULL, }, /* unit */ + hw_eeprom_create_instance, +}; + +static void * +hw_eeprom_create(const char *name, + const device_unit *unit_address, + const char *args) +{ + hw_eeprom_device *eeprom = ZALLOC(hw_eeprom_device); + return eeprom; +} + + + +const device_descriptor hw_eeprom_device_descriptor[] = { + { "eeprom", hw_eeprom_create, &hw_eeprom_callbacks }, + { NULL }, +}; + +#endif /* _HW_EEPROM_C_ */ diff --git a/sim/ppc/hw_glue.c b/sim/ppc/hw_glue.c new file mode 100644 index 00000000000..d7ad9290361 --- /dev/null +++ b/sim/ppc/hw_glue.c @@ -0,0 +1,371 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1996, 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 _HW_GLUE_C_ +#define _HW_GLUE_C_ + +#include "device_table.h" + + +/* DEVICE + + + glue - glue to interconnect and test interrupts + + + DESCRIPTION + + + The glue device provides two functions. Firstly, it provides a + mechanism for inspecting and driving the interrupt net. Secondly, + it provides a set of boolean primitives that can be used add + combinatorial operations to the interrupt network. + + Glue devices have a variable number of big endian <<output>> + registers. Each host-word size. The registers can be both read + and written. + + Writing a value to an output register causes an interrupt (of the + specified level) to be driven on the devices corresponding output + interrupt port. + + Reading an <<output>> register returns either the last value + written or the most recently computed value (for that register) as + a result of an interrupt ariving (which ever was computed last). + + At present the following sub device types are available: + + <<glue>>: In addition to driving its output interrupt port with any + value written to an interrupt input port is stored in the + corresponding <<output>> register. Such input interrupts, however, + are not propogated to an output interrupt port. + + <<glue-and>>: The bit-wise AND of the interrupt inputs is computed + and then both stored in <<output>> register zero and propogated to + output interrupt output port zero. + + + PROPERTIES + + + reg = <address> <size> (required) + + Specify the address (within the parent bus) that this device is to + live. The address must be 2048 * sizeof(word) (8k in a 32bit + simulation) aligned. + + + interrupt-ranges = <int-number> <range> (optional) + + If present, this specifies the number of valid interrupt inputs (up + to the maximum of 2048). By default, <<int-number>> is zero and + range is determined by the <<reg>> size. + + + EXAMPLES + + + Enable tracing of the device: + + | -t glue-device \ + + + Create source, bitwize-and, and sink glue devices. Since the + device at address <<0x10000>> is of size <<8>> it will have two + output interrupt ports. + + | -o '/iobus@0xf0000000/glue@0x10000/reg 0x10000 8' \ + | -o '/iobus@0xf0000000/glue-and@0x20000/reg 0x20000 4' \ + | -o '/iobus@0xf0000000/glue-and/interrupt-ranges 0 2' \ + | -o '/iobus@0xf0000000/glue@0x30000/reg 0x30000 4' \ + + + Wire the two source interrupts to the AND device: + + | -o '/iobus@0xf0000000/glue@0x10000 > 0 0 /iobus/glue-and' \ + | -o '/iobus@0xf0000000/glue@0x10000 > 1 1 /iobus/glue-and' \ + + + Wire the AND device up to the sink so that the and's output is not + left open. + + | -o '/iobus@0xf0000000/glue-and > 0 0 /iobus/glue@0x30000' \ + + + With the above configuration. The client program is able to + compute a two bit AND. For instance the <<C>> stub below prints 1 + AND 0. + + | unsigned *input = (void*)0xf0010000; + | unsigned *output = (void*)0xf0030000; + | unsigned ans; + | input[0] = htonl(1); + | input[1] = htonl(0); + | ans = ntohl(*output); + | write_string("AND is "); + | write_int(ans); + | write_line(); + + + BUGS + + + A future implementation of this device may support multiple + interrupt ranges. + + Some of the devices listed may not yet be fully implemented. + + Additional devices such as a dff, an inverter or a latch may be + useful. + + */ + + +enum { + max_nr_interrupts = 2048, +}; + +typedef enum _hw_glue_type { + glue_undefined = 0, + glue_io, + glue_and, + glue_nand, + glue_or, + glue_xor, + glue_nor, + glue_not, +} hw_glue_type; + +typedef struct _hw_glue_device { + hw_glue_type type; + int int_number; + int *input; + int nr_inputs; + unsigned sizeof_input; + /* our output registers */ + int space; + unsigned_word address; + unsigned sizeof_output; + int *output; + int nr_outputs; +} hw_glue_device; + + +static void +hw_glue_init_address(device *me) +{ + hw_glue_device *glue = (hw_glue_device*)device_data(me); + + /* attach to my parent */ + generic_device_init_address(me); + + /* establish the output registers */ + if (glue->output != NULL) { + memset(glue->output, 0, glue->sizeof_output); + } + else { + reg_property_spec unit; + int reg_nr; + /* find a relevant reg entry */ + reg_nr = 0; + while (device_find_reg_array_property(me, "reg", reg_nr, &unit) + && !device_size_to_attach_size(device_parent(me), &unit.size, + &glue->sizeof_output, me)) + reg_nr++; + /* check out the size */ + if (glue->sizeof_output == 0) + device_error(me, "at least one reg property size must be nonzero"); + if (glue->sizeof_output % sizeof(unsigned_word) != 0) + device_error(me, "reg property size must be %d aligned", sizeof(unsigned_word)); + /* and the address */ + device_address_to_attach_address(device_parent(me), + &unit.address, &glue->space, &glue->address, + me); + if (glue->address % (sizeof(unsigned_word) * max_nr_interrupts) != 0) + device_error(me, "reg property address must be %d aligned", + sizeof(unsigned_word) * max_nr_interrupts); + glue->nr_outputs = glue->sizeof_output / sizeof(unsigned_word); + glue->output = zalloc(glue->sizeof_output); + } + + /* establish the input interrupt ports */ + if (glue->input != NULL) { + memset(glue->input, 0, glue->sizeof_input); + } + else { + const device_property *ranges = device_find_property(me, "interrupt-ranges"); + if (ranges == NULL) { + glue->int_number = 0; + glue->nr_inputs = glue->nr_outputs; + } + else if (ranges->sizeof_array != sizeof(unsigned_cell) * 2) { + device_error(me, "invalid interrupt-ranges property (incorrect size)"); + } + else { + const unsigned_cell *int_range = ranges->array; + glue->int_number = BE2H_cell(int_range[0]); + glue->nr_inputs = BE2H_cell(int_range[1]); + } + glue->sizeof_input = glue->nr_inputs * sizeof(unsigned); + glue->input = zalloc(glue->sizeof_input); + } + + /* determine our type */ + if (glue->type == glue_undefined) { + const char *name = device_name(me); + if (strcmp(name, "glue") == 0) + glue->type = glue_io; + else if (strcmp(name, "glue-and") == 0) + glue->type = glue_and; + else + device_error(me, "unimplemented glue type"); + } + + DTRACE(glue, ("int-number %d, nr_inputs %d, nr_outputs %d\n", + glue->int_number, glue->nr_inputs, glue->nr_outputs)); +} + +static unsigned +hw_glue_io_read_buffer_callback(device *me, + void *dest, + int space, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + hw_glue_device *glue = (hw_glue_device*)device_data(me); + int reg = ((addr - glue->address) / sizeof(unsigned_word)) % glue->nr_outputs; + if (nr_bytes != sizeof(unsigned_word) + || (addr % sizeof(unsigned_word)) != 0) + device_error(me, "missaligned read access (%d:0x%lx:%d) not supported", + space, (unsigned long)addr, nr_bytes); + *(unsigned_word*)dest = H2BE_4(glue->output[reg]); + DTRACE(glue, ("read - interrupt %d (0x%lx), level %d\n", + reg, (unsigned long) addr, glue->output[reg])); + return nr_bytes; +} + + +static unsigned +hw_glue_io_write_buffer_callback(device *me, + const void *source, + int space, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + hw_glue_device *glue = (hw_glue_device*)device_data(me); + int reg = ((addr - glue->address) / sizeof(unsigned_word)) % max_nr_interrupts; + if (nr_bytes != sizeof(unsigned_word) + || (addr % sizeof(unsigned_word)) != 0) + device_error(me, "missaligned write access (%d:0x%lx:%d) not supported", + space, (unsigned long)addr, nr_bytes); + glue->output[reg] = H2BE_4(*(unsigned_word*)source); + DTRACE(glue, ("write - interrupt %d (0x%lx), level %d\n", + reg, (unsigned long) addr, glue->output[reg])); + device_interrupt_event(me, reg, glue->output[reg], processor, cia); + return nr_bytes; +} + +static void +hw_glue_interrupt_event(device *me, + int my_port, + device *source, + int source_port, + int level, + cpu *processor, + unsigned_word cia) +{ + hw_glue_device *glue = (hw_glue_device*)device_data(me); + int i; + if (my_port < glue->int_number + || my_port >= glue->int_number + glue->nr_inputs) + device_error(me, "interrupt %d outside of valid range", my_port); + glue->input[my_port - glue->int_number] = level; + switch (glue->type) { + case glue_io: + { + int port = my_port % glue->nr_outputs; + glue->output[port] = level; + DTRACE(glue, ("input - interrupt %d (0x%lx), level %d\n", + my_port, + (unsigned long)glue->address + port * sizeof(unsigned_word), + level)); + break; + } + case glue_and: + glue->output[0] = glue->input[0]; + for (i = 1; i < glue->nr_inputs; i++) + glue->output[0] &= glue->input[i]; + DTRACE(glue, ("and - interrupt %d, level %d arrived - output %d\n", + my_port, level, glue->output[0])); + device_interrupt_event(me, 0, glue->output[0], processor, cia); + break; + default: + device_error(me, "operator not implemented"); + break; + } +} + + +static const device_interrupt_port_descriptor hw_glue_interrupt_ports[] = { + { "int", 0, max_nr_interrupts }, + { NULL } +}; + + +static device_callbacks const hw_glue_callbacks = { + { hw_glue_init_address, NULL }, + { NULL, }, /* address */ + { hw_glue_io_read_buffer_callback, + hw_glue_io_write_buffer_callback, }, + { NULL, }, /* DMA */ + { hw_glue_interrupt_event, NULL, hw_glue_interrupt_ports }, /* interrupt */ + { NULL, }, /* unit */ + NULL, /* instance */ +}; + + +static void * +hw_glue_create(const char *name, + const device_unit *unit_address, + const char *args) +{ + /* create the descriptor */ + hw_glue_device *glue = ZALLOC(hw_glue_device); + return glue; +} + + +const device_descriptor hw_glue_device_descriptor[] = { + { "glue", hw_glue_create, &hw_glue_callbacks }, + { "glue-and", hw_glue_create, &hw_glue_callbacks }, + { "glue-nand", hw_glue_create, &hw_glue_callbacks }, + { "glue-or", hw_glue_create, &hw_glue_callbacks }, + { "glue-xor", hw_glue_create, &hw_glue_callbacks }, + { "glue-nor", hw_glue_create, &hw_glue_callbacks }, + { "glue-not", hw_glue_create, &hw_glue_callbacks }, + { NULL }, +}; + +#endif /* _HW_GLUE_C_ */ diff --git a/sim/ppc/hw_htab.c b/sim/ppc/hw_htab.c new file mode 100644 index 00000000000..35aa5711371 --- /dev/null +++ b/sim/ppc/hw_htab.c @@ -0,0 +1,683 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1996, 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 _HW_HTAB_C_ +#define _HW_HTAB_C_ + +#include "device_table.h" + +#include "bfd.h" + + +/* DEVICE + + + htab - pseudo-device describing a PowerPC hash table + + + DESCRIPTION + + + During the initialization of the device tree, the pseudo-device + <<htab>>, in conjunction with any child <<pte>> pseudo-devices, + will create a PowerPC hash table in memory. The hash table values + are written using dma transfers. + + The size and address of the hash table are determined by properties + of the htab node. + + By convention, the htab device is made a child of the + <</openprom/init>> node. + + By convention, the real address of the htab is used as the htab + nodes unit address. + + + PROPERTIES + + + real-address = <address> (required) + + The physical address of the hash table. The PowerPC architecture + places limitations on what is a valid hash table real-address. + + + nr-bytes = <size> (required) + + The size of the hash table (in bytes) that is to be created at + <<real-address>>. The PowerPC architecture places limitations on + what is a valid hash table size. + + + claim = <anything> (optional) + + If this property is present, the memory used to construct the hash + table will be claimed from the memory device. The memory device + being specified by the <</chosen/memory>> ihandle property. + + + EXAMPLES + + Enable tracing. + + | $ psim -t htab-device \ + + + Create a htab specifying the base address and minimum size. + + | -o '/openprom/init/htab@0x10000/real-address 0x10000' \ + | -o '/openprom/init/htab@0x10000/claim 0' \ + | -o '/openprom/init/htab@0x10000/nr-bytes 65536' \ + + + BUGS + + + See the <<pte>> device. + + + */ + + +/* DEVICE + + + pte - pseudo-device describing a htab entry + + + DESCRIPTION + + + The <<pte>> pseudo-device, which must be a child of a <<htabl>> + node, describes a virtual to physical mapping that is to be entered + into the parents hash table. + + Two alternative specifications of the mapping are allowed. Either + a section of physical memory can be mapped to a virtual address, or + the header of an executible image can be used to define the + mapping. + + By convention, the real address of the map is specified as the pte + devices unit address. + + + PROPERTIES + + + real-address = <address> (required) + + The starting physical address that is to be mapped by the hash + table. + + + wimg = <int> (required) + pp = <int> (required) + + The value of hash table protection bits that are to be used when + creating the virtual to physical address map. + + + claim = <anything> (optional) + + If this property is present, the real memory that is being mapped by the + hash table will be claimed from the memory node (specified by the + ihandle <</chosen/memory>>). + + + virtual-address = <integer> [ <integer> ] (option A) + nr-bytes = <size> (option A) + + Option A - Virtual virtual address (and size) at which the physical + address is to be mapped. If multiple values are specified for the + virtual address then they are concatenated to gether to form a + longer virtual address. + + + file-name = <string> (option B) + + Option B - An executable image that is to be loaded (starting at + the physical address specified above) and then mapped in using + informatioin taken from the executables header. information found + in the files header. + + + EXAMPLES + + + Enable tracing (note that both the <<htab>> and <<pte>> device use the + same trace option). + + | -t htab-device \ + + + Map a block of physical memory into a specified virtual address: + + | -o '/openprom/init/htab/pte@0x0/real-address 0' \ + | -o '/openprom/init/htab/pte@0x0/nr-bytes 4096' \ + | -o '/openprom/init/htab/pte@0x0/virtual-address 0x1000000' \ + | -o '/openprom/init/htab/pte@0x0/claim 0' \ + | -o '/openprom/init/htab/pte@0x0/wimg 0x7' \ + | -o '/openprom/init/htab/pte@0x0/pp 0x2' \ + + + Map a file into memory. + + | -o '/openprom/init/htab/pte@0x10000/real-address 0x10000' \ + | -o '/openprom/init/htab/pte@0x10000/file-name "netbsd.elf' \ + | -o '/openprom/init/htab/pte@0x10000/wimg 0x7' \ + | -o '/openprom/init/htab/pte@0x10000/pp 0x2' \ + + + BUGS + + + For an ELF executable, the header defines both the virtual and real + address at which each file section should be loaded. At present, the + real addresses that are specified in the header are ignored, the file + instead being loaded in to physical memory in a linear fashion. + + When claiming memory, this device assumes that the #address-cells + and #size-cells is one. For future implementations, this may not + be the case. + + */ + + + +static void +htab_decode_hash_table(device *me, + unsigned32 *htaborg, + unsigned32 *htabmask) +{ + unsigned_word htab_ra; + unsigned htab_nr_bytes; + unsigned n; + device *parent = device_parent(me); + /* determine the location/size of the hash table */ + if (parent == NULL + || strcmp(device_name(parent), "htab") != 0) + device_error(parent, "must be a htab device"); + htab_ra = device_find_integer_property(parent, "real-address"); + htab_nr_bytes = device_find_integer_property(parent, "nr-bytes"); + for (n = htab_nr_bytes; n > 1; n = n / 2) { + if (n % 2 != 0) + device_error(parent, "htab size 0x%x not a power of two", + htab_nr_bytes); + } + *htaborg = htab_ra; + *htabmask = MASKED32(htab_nr_bytes - 1, 7, 31-6); + if ((htab_ra & INSERTED32(*htabmask, 7, 15)) != 0) { + device_error(parent, "htaborg 0x%lx not aligned to htabmask 0x%lx", + (unsigned long)*htaborg, (unsigned long)*htabmask); + } + DTRACE(htab, ("htab - htaborg=0x%lx htabmask=0x%lx\n", + (unsigned long)*htaborg, (unsigned long)*htabmask)); +} + +static void +htab_map_page(device *me, + unsigned_word ra, + unsigned64 va, + unsigned wimg, + unsigned pp, + unsigned32 htaborg, + unsigned32 htabmask) +{ + /* keep everything left shifted so that the numbering is easier */ + unsigned64 vpn = va << 12; + unsigned32 vsid = INSERTED32(EXTRACTED64(vpn, 0, 23), 0, 23); + unsigned32 vpage = INSERTED32(EXTRACTED64(vpn, 24, 39), 0, 15); + unsigned32 hash = INSERTED32(EXTRACTED32(vsid, 5, 23) + ^ EXTRACTED32(vpage, 0, 15), + 7, 31-6); + int h; + for (h = 0; h < 2; h++) { + unsigned32 pteg = (htaborg | (hash & htabmask)); + int pti; + for (pti = 0; pti < 8; pti++) { + unsigned32 pte = pteg + 8 * pti; + unsigned32 current_target_pte0; + unsigned32 current_pte0; + if (device_dma_read_buffer(device_parent(me), + ¤t_target_pte0, + 0, /*space*/ + pte, + sizeof(current_target_pte0)) != 4) + device_error(me, "failed to read a pte at 0x%lx", (unsigned long)pte); + current_pte0 = T2H_4(current_target_pte0); + if (MASKED32(current_pte0, 0, 0)) { + /* full pte, check it isn't already mapping the same virtual + address */ + unsigned32 curr_vsid = INSERTED32(EXTRACTED32(current_pte0, 1, 24), 0, 23); + unsigned32 curr_api = INSERTED32(EXTRACTED32(current_pte0, 26, 31), 0, 5); + unsigned32 curr_h = EXTRACTED32(current_pte0, 25, 25); + if (curr_h == h + && curr_vsid == vsid + && curr_api == MASKED32(vpage, 0, 5)) + device_error(me, "duplicate map - va=0x%08lx ra=0x%lx vsid=0x%lx h=%d vpage=0x%lx hash=0x%lx pteg=0x%lx+%2d pte0=0x%lx", + (unsigned long)va, + (unsigned long)ra, + (unsigned long)vsid, + h, + (unsigned long)vpage, + (unsigned long)hash, + (unsigned long)pteg, + pti * 8, + (unsigned long)current_pte0); + } + else { + /* empty pte fill it */ + unsigned32 pte0 = (MASK32(0, 0) + | INSERTED32(EXTRACTED32(vsid, 0, 23), 1, 24) + | INSERTED32(h, 25, 25) + | INSERTED32(EXTRACTED32(vpage, 0, 5), 26, 31)); + unsigned32 target_pte0 = H2T_4(pte0); + unsigned32 pte1 = (INSERTED32(EXTRACTED32(ra, 0, 19), 0, 19) + | INSERTED32(wimg, 25, 28) + | INSERTED32(pp, 30, 31)); + unsigned32 target_pte1 = H2T_4(pte1); + if (device_dma_write_buffer(device_parent(me), + &target_pte0, + 0, /*space*/ + pte, + sizeof(target_pte0), + 1/*ro?*/) != 4 + || device_dma_write_buffer(device_parent(me), + &target_pte1, + 0, /*space*/ + pte + 4, + sizeof(target_pte1), + 1/*ro?*/) != 4) + device_error(me, "failed to write a pte a 0x%lx", (unsigned long)pte); + DTRACE(htab, ("map - va=0x%08lx ra=0x%lx vsid=0x%lx h=%d vpage=0x%lx hash=0x%lx pteg=0x%lx+%2d pte0=0x%lx pte1=0x%lx\n", + (unsigned long)va, + (unsigned long)ra, + (unsigned long)vsid, + h, + (unsigned long)vpage, + (unsigned long)hash, + (unsigned long)pteg, + pti * 8, + (unsigned long)pte0, + (unsigned long)pte1)); + return; + } + } + /* re-hash */ + hash = MASKED32(~hash, 0, 18); + } +} + +static unsigned_word +claim_memory(device *me, + device_instance *memory, + unsigned_word ra, + unsigned_word size) +{ + unsigned32 args[3]; + unsigned32 results[1]; + int status; + args[0] = 0; /* alignment */ + args[1] = size; + args[2] = ra; + status = device_instance_call_method(memory, "claim", 3, args, 1, results); + if (status != 0) + device_error(me, "failed to claim memory"); + return results[0]; +} + +static void +htab_map_region(device *me, + device_instance *memory, + unsigned_word pte_ra, + unsigned64 pte_va, + unsigned nr_bytes, + unsigned wimg, + unsigned pp, + unsigned32 htaborg, + unsigned32 htabmask) +{ + unsigned_word ra; + unsigned64 va; + /* claim the memory */ + if (memory != NULL) + claim_memory(me, memory, pte_ra, nr_bytes); + /* go through all pages and create a pte for each */ + for (ra = pte_ra, va = pte_va; + ra < pte_ra + nr_bytes; + ra += 0x1000, va += 0x1000) { + htab_map_page(me, ra, va, wimg, pp, htaborg, htabmask); + } +} + +typedef struct _htab_binary_sizes { + unsigned_word text_ra; + unsigned_word text_base; + unsigned_word text_bound; + unsigned_word data_ra; + unsigned_word data_base; + unsigned data_bound; + device *me; +} htab_binary_sizes; + +static void +htab_sum_binary(bfd *abfd, + sec_ptr sec, + PTR data) +{ + htab_binary_sizes *sizes = (htab_binary_sizes*)data; + unsigned_word size = bfd_get_section_size_before_reloc (sec); + unsigned_word vma = bfd_get_section_vma (abfd, sec); +#define bfd_get_section_lma(abfd, sec) ((sec)->lma + 0) + unsigned_word ra = bfd_get_section_lma (abfd, sec); + + /* skip the section if no memory to allocate */ + if (! (bfd_get_section_flags(abfd, sec) & SEC_ALLOC)) + return; + + if ((bfd_get_section_flags (abfd, sec) & SEC_CODE) + || (bfd_get_section_flags (abfd, sec) & SEC_READONLY)) { + if (sizes->text_bound < vma + size) + sizes->text_bound = ALIGN_PAGE(vma + size); + if (sizes->text_base > vma) + sizes->text_base = FLOOR_PAGE(vma); + if (sizes->text_ra > ra) + sizes->text_ra = FLOOR_PAGE(ra); + } + else if ((bfd_get_section_flags (abfd, sec) & SEC_DATA) + || (bfd_get_section_flags (abfd, sec) & SEC_ALLOC)) { + if (sizes->data_bound < vma + size) + sizes->data_bound = ALIGN_PAGE(vma + size); + if (sizes->data_base > vma) + sizes->data_base = FLOOR_PAGE(vma); + if (sizes->data_ra > ra) + sizes->data_ra = FLOOR_PAGE(ra); + } +} + +static void +htab_dma_binary(bfd *abfd, + sec_ptr sec, + PTR data) +{ + htab_binary_sizes *sizes = (htab_binary_sizes*)data; + void *section_init; + unsigned_word section_vma; + unsigned_word section_size; + unsigned_word section_ra; + device *me = sizes->me; + + /* skip the section if no memory to allocate */ + if (! (bfd_get_section_flags(abfd, sec) & SEC_ALLOC)) + return; + + /* check/ignore any sections of size zero */ + section_size = bfd_get_section_size_before_reloc(sec); + if (section_size == 0) + return; + + /* if nothing to load, ignore this one */ + if (! (bfd_get_section_flags(abfd, sec) & SEC_LOAD)) + return; + + /* find where it is to go */ + section_vma = bfd_get_section_vma(abfd, sec); + section_ra = 0; + if ((bfd_get_section_flags (abfd, sec) & SEC_CODE) + || (bfd_get_section_flags (abfd, sec) & SEC_READONLY)) + section_ra = (section_vma - sizes->text_base + sizes->text_ra); + else if ((bfd_get_section_flags (abfd, sec) & SEC_DATA)) + section_ra = (section_vma - sizes->data_base + sizes->data_ra); + else + return; /* just ignore it */ + + DTRACE(htab, + ("load - name=%-7s vma=0x%.8lx size=%6ld ra=0x%.8lx flags=%3lx(%s%s%s%s%s )\n", + bfd_get_section_name(abfd, sec), + (long)section_vma, + (long)section_size, + (long)section_ra, + (long)bfd_get_section_flags(abfd, sec), + bfd_get_section_flags(abfd, sec) & SEC_LOAD ? " LOAD" : "", + bfd_get_section_flags(abfd, sec) & SEC_CODE ? " CODE" : "", + bfd_get_section_flags(abfd, sec) & SEC_DATA ? " DATA" : "", + bfd_get_section_flags(abfd, sec) & SEC_ALLOC ? " ALLOC" : "", + bfd_get_section_flags(abfd, sec) & SEC_READONLY ? " READONLY" : "" + )); + + /* dma in the sections data */ + section_init = zalloc(section_size); + if (!bfd_get_section_contents(abfd, + sec, + section_init, 0, + section_size)) { + bfd_perror("devices/pte"); + device_error(me, "no data loaded"); + } + if (device_dma_write_buffer(device_parent(me), + section_init, + 0 /*space*/, + section_ra, + section_size, + 1 /*violate_read_only*/) + != section_size) + device_error(me, "broken dma transfer"); + zfree(section_init); /* only free if load */ +} + +/* create a memory map from a binaries virtual addresses to a copy of + the binary laid out linearly in memory */ + +static void +htab_map_binary(device *me, + device_instance *memory, + unsigned_word ra, + unsigned wimg, + unsigned pp, + const char *file_name, + unsigned32 htaborg, + unsigned32 htabmask) +{ + htab_binary_sizes sizes; + bfd *image; + sizes.text_ra = -1; + sizes.data_ra = -1; + sizes.text_base = -1; + sizes.data_base = -1; + sizes.text_bound = 0; + sizes.data_bound = 0; + sizes.me = me; + + /* open the file */ + image = bfd_openr(file_name, NULL); + if (image == NULL) { + bfd_perror("devices/pte"); + device_error(me, "the file %s not loaded", file_name); + } + + /* check it is valid */ + if (!bfd_check_format(image, bfd_object)) { + bfd_close(image); + device_error(me, "the file %s has an invalid binary format", file_name); + } + + /* determine the size of each of the files regions */ + bfd_map_over_sections (image, htab_sum_binary, (PTR) &sizes); + + /* if needed, determine the real addresses of the sections */ + if (ra != -1) { + sizes.text_ra = ra; + sizes.data_ra = ALIGN_PAGE(sizes.text_ra + + (sizes.text_bound - sizes.text_base)); + } + + DTRACE(htab, ("text map - base=0x%lx bound=0x%lx-1 ra=0x%lx\n", + (unsigned long)sizes.text_base, + (unsigned long)sizes.text_bound, + (unsigned long)sizes.text_ra)); + DTRACE(htab, ("data map - base=0x%lx bound=0x%lx-1 ra=0x%lx\n", + (unsigned long)sizes.data_base, + (unsigned long)sizes.data_bound, + (unsigned long)sizes.data_ra)); + + /* check for and fix a botched image (text and data segments + overlap) */ + if ((sizes.text_base <= sizes.data_base + && sizes.text_bound >= sizes.data_bound) + || (sizes.data_base <= sizes.text_base + && sizes.data_bound >= sizes.data_bound) + || (sizes.text_bound > sizes.data_base + && sizes.text_bound <= sizes.data_bound) + || (sizes.text_base >= sizes.data_base + && sizes.text_base < sizes.data_bound)) { + DTRACE(htab, ("text and data segment overlaped - using just data segment\n")); + /* check va->ra linear */ + if ((sizes.text_base - sizes.text_ra) + != (sizes.data_base - sizes.data_ra)) + device_error(me, "overlapping but missaligned text and data segments"); + /* enlarge the data segment */ + if (sizes.text_base < sizes.data_base) + sizes.data_base = sizes.text_base; + if (sizes.text_bound > sizes.data_bound) + sizes.data_bound = sizes.text_bound; + if (sizes.text_ra < sizes.data_ra) + sizes.data_ra = sizes.text_ra; + /* zap the text segment */ + sizes.text_base = 0; + sizes.text_bound = 0; + sizes.text_ra = 0; + DTRACE(htab, ("common map - base=0x%lx bound=0x%lx-1 ra=0x%lx\n", + (unsigned long)sizes.data_base, + (unsigned long)sizes.data_bound, + (unsigned long)sizes.data_ra)); + } + + /* set up virtual memory maps for each of the regions */ + htab_map_region(me, memory, sizes.text_ra, sizes.text_base, + sizes.text_bound - sizes.text_base, + wimg, pp, + htaborg, htabmask); + + htab_map_region(me, memory, sizes.data_ra, sizes.data_base, + sizes.data_bound - sizes.data_base, + wimg, pp, + htaborg, htabmask); + + /* dma the sections into physical memory */ + bfd_map_over_sections (image, htab_dma_binary, (PTR) &sizes); +} + +static void +htab_init_data_callback(device *me) +{ + device_instance *memory = NULL; + if (WITH_TARGET_WORD_BITSIZE != 32) + device_error(me, "only 32bit targets currently suported"); + + /* find memory device */ + if (device_find_property(me, "claim") != NULL) + memory = tree_find_ihandle_property(me, "/chosen/memory"); + + /* for the htab, just allocate space for it */ + if (strcmp(device_name(me), "htab") == 0) { + unsigned_word address = device_find_integer_property(me, "real-address"); + unsigned_word length = device_find_integer_property(me, "nr-bytes"); + unsigned_word base = claim_memory(me, memory, address, length); + if (base == -1 || base != address) + device_error(me, "cannot allocate hash table"); + } + + /* for the pte, do all the real work */ + if (strcmp(device_name(me), "pte") == 0) { + unsigned32 htaborg; + unsigned32 htabmask; + + htab_decode_hash_table(me, &htaborg, &htabmask); + + if (device_find_property(me, "file-name") != NULL) { + /* map in a binary */ + unsigned pte_wimg = device_find_integer_property(me, "wimg"); + unsigned pte_pp = device_find_integer_property(me, "pp"); + const char *file_name = device_find_string_property(me, "file-name"); + if (device_find_property(me, "real-address") != NULL) { + unsigned32 pte_ra = device_find_integer_property(me, "real-address"); + DTRACE(htab, ("pte - ra=0x%lx, wimg=%ld, pp=%ld, file-name=%s\n", + (unsigned long)pte_ra, + (unsigned long)pte_wimg, + (long)pte_pp, + file_name)); + htab_map_binary(me, memory, pte_ra, pte_wimg, pte_pp, file_name, + htaborg, htabmask); + } + else { + DTRACE(htab, ("pte - wimg=%ld, pp=%ld, file-name=%s\n", + (unsigned long)pte_wimg, + (long)pte_pp, + file_name)); + htab_map_binary(me, memory, -1, pte_wimg, pte_pp, file_name, + htaborg, htabmask); + } + } + else { + /* handle a normal mapping definition */ + unsigned64 pte_va = 0; + unsigned32 pte_ra = device_find_integer_property(me, "real-address"); + unsigned pte_nr_bytes = device_find_integer_property(me, "nr-bytes"); + unsigned pte_wimg = device_find_integer_property(me, "wimg"); + unsigned pte_pp = device_find_integer_property(me, "pp"); + signed_cell partial_va; + int i; + for (i = 0; + device_find_integer_array_property(me, "virtual-address", i, &partial_va); + i++) { + pte_va = (pte_va << WITH_TARGET_WORD_BITSIZE) | (unsigned_cell)partial_va; + } + DTRACE(htab, ("pte - ra=0x%lx, wimg=%ld, pp=%ld, va=0x%lx, nr_bytes=%ld\n", + (unsigned long)pte_ra, + (long)pte_wimg, + (long)pte_pp, + (unsigned long)pte_va, + (long)pte_nr_bytes)); + htab_map_region(me, memory, pte_ra, pte_va, pte_nr_bytes, pte_wimg, pte_pp, + htaborg, htabmask); + } + } +} + + +static device_callbacks const htab_callbacks = { + { NULL, htab_init_data_callback, }, + { NULL, }, /* address */ + { NULL, }, /* IO */ + { passthrough_device_dma_read_buffer, + passthrough_device_dma_write_buffer, }, + { NULL, }, /* interrupt */ + { generic_device_unit_decode, + generic_device_unit_encode, }, +}; + +const device_descriptor hw_htab_device_descriptor[] = { + { "htab", NULL, &htab_callbacks }, + { "pte", NULL, &htab_callbacks }, /* yep - uses htab's table */ + { NULL }, +}; + +#endif /* _HW_HTAB_C_ */ diff --git a/sim/ppc/hw_ide.c b/sim/ppc/hw_ide.c new file mode 100644 index 00000000000..00d54b32e1e --- /dev/null +++ b/sim/ppc/hw_ide.c @@ -0,0 +1,869 @@ +/* This file is part of the program psim. + + Copyright (C) 1996, 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 _HW_IDE_C_ +#define _HW_IDE_C_ + +#include "device_table.h" + + + +/* DEVICE + + + ide - Integrated Disk Electronics + + + DESCRIPTION + + + This device models the primary/secondary <<ide>> controller + described in the [CHRPIO] document. + + The controller has separate independant interrupt outputs for each + <<ide>> bus. + + + PROPERTIES + + + reg = ... (required) + + The <<reg>> property is described in the document [CHRPIO]. + + + ready-delay = <integer> (optional) + + If present, this specifies the time that the <<ide>> device takes + to complete an I/O operation. + + + disk@?/ide-byte-count = <integer> (optional) + + disk@?/ide-sector-count = <integer> (optional) + + disk@?/ide-head-count = <integer> (optional) + + The <<ide>> device checks each child (disk device) node to see if + it has the above properties. If present, these values will be used + to compute the <<LBA>> address in <<CHS>> addressing mode. + + + EXAMPLES + + + Enable tracing: + + | -t ide-device \ + + + Attach the <<ide>> device to the <<pci>> bus at slot one. Specify + legacy I/O addresses: + + | -o '/phb/ide@1/assigned-addresses \ + | ni0,0,10,1f0 8 \ + | ni0,0,14,3f8 8 \ + | ni0,0,18,170 8 \ + | ni0,0,1c,378 8 \ + | ni0,0,20,200 8' \ + | -o '/phb@0x80000000/ide@1/reg \ + | 1 0 \ + | i0,0,10,0 8 \ + | i0,0,18,0 8 \ + | i0,0,14,6 1 \ + | i0,0,1c,6 1 \ + | i0,0,20,0 8' \ + + Note: the fouth and fifth reg entries specify that the register is + at an offset into the address specified by the base register + (<<assigned-addresses>>); Apart from restrictions placed by the + <<pci>> specification, no restrictions are placed on the number of + base registers specified by the <<assigned-addresses>> property. + + Attach a <<disk>> to the primary and a <<cdrom>> to the secondary + <<ide>> controller. + + | -o '/phb@0x80000000/ide@1/disk@0/file "zero' \ + | -o '/phb@0x80000000/ide@1/cdrom@2/file "/dev/cdrom"' \ + + Connect the two interrupt outputs (a and b) to a <<glue>> device to + allow testing of the interrupt port. In a real simulation they + would be wired to the interrupt controller. + + | -o '/phb@0x80000000/glue@2/reg 2 0 ni0,0,0,0 8' \ + | -o '/phb@0x80000000/ide@1 > a 0 /phb@0x80000000/glue@2' \ + | -o '/phb@0x80000000/ide@1 > b 1 /phb@0x80000000/glue@2' + + + BUGS + + + While the DMA registers are present, DMA support has not yet been + implemented. + + The number of supported commands is very limited. + + The standards documents appear to be vague on how to specify the + <<unit-address>> of disk devices devices being attached to the + <<ide>> controller. I've chosen to use integers with devices zero + and one going to the primary controller while two and three are + connected to the secondary controller. + + + REFERENCES + + + [CHRPIO] PowerPC(tm) Microprocessor Common Hardware Reference + Platform: I/O Device Reference. http://chrp.apple.com/???. + + [SCHMIDT] The SCSI Bus and IDE Interface - Protocols, Applications + and Programming. Friedhelm Schmidt (translated by Michael + Schultz). ISBN 0-201-42284-0. Addison-Wesley Publishing Company. + + + */ + + + +typedef enum _io_direction { + is_read, + is_write, +} io_direction; + + +enum { + nr_ide_controllers = 2, + nr_ide_drives_per_controller = 2, + nr_fifo_entries = 8192, +}; + +enum { + /* command register block - read */ + ide_data_reg, + ide_error_reg, /*ide_feature_reg*/ + ide_sector_count_reg, + ide_sector_number_reg, + ide_cylinder_reg0, + ide_cylinder_reg1, + ide_drive_head_reg, + ide_status_reg, /*ide_command_reg*/ + /* command register block - write */ + ide_feature_reg, /*ide_error_reg*/ + ide_command_reg, /*ide_status_reg*/ + /* control register block - read */ + ide_alternate_status_reg, /*ide_control_reg*/ + ide_control_reg, /*ide_alternate_status_reg*/ + /* dma register block */ + ide_dma_command_reg, + ide_dma_unused_1_reg, + ide_dma_status_reg, + ide_dma_unused_3_reg, + ide_dma_prd_table_address_reg0, + ide_dma_prd_table_address_reg1, + ide_dma_prd_table_address_reg2, + ide_dma_prd_table_address_reg3, + nr_ide_registers, +}; + + +typedef enum _ide_states { + idle_state, + busy_loaded_state, + busy_drained_state, + busy_dma_state, + busy_command_state, + loading_state, + draining_state, +} ide_states; + +static const char * +ide_state_name(ide_states state) +{ + switch (state) { + case idle_state: return "idle"; + case busy_loaded_state: return "busy_loaded_state"; + case busy_drained_state: return "busy_drained_state"; + case busy_dma_state: return "busy_dma_state"; + case busy_command_state: return "busy_command_state"; + case loading_state: return "loading_state"; + case draining_state: return "draining_state"; + default: return "illegal-state"; + } +} + +typedef struct _ide_geometry { + int head; + int sector; + int byte; +} ide_geometry; + +typedef struct _ide_drive { + int nr; + device *device; + ide_geometry geometry; + ide_geometry default_geometry; +} ide_drive; + +typedef struct _ide_controller { + int nr; + ide_states state; + unsigned8 reg[nr_ide_registers]; + unsigned8 fifo[nr_fifo_entries]; + int fifo_pos; + int fifo_size; + ide_drive *current_drive; + int current_byte; + int current_transfer; + ide_drive drive[nr_ide_drives_per_controller]; + device *me; + event_entry_tag event_tag; + int is_interrupting; + signed64 ready_delay; +} ide_controller; + + + +static void +set_interrupt(device *me, + ide_controller *controller) +{ + if ((controller->reg[ide_control_reg] & 0x2) == 0) { + DTRACE(ide, ("controller %d - interrupt set\n", controller->nr)); + device_interrupt_event(me, controller->nr, 1, NULL, 0); + controller->is_interrupting = 1; + } +} + + +static void +clear_interrupt(device *me, + ide_controller *controller) +{ + if (controller->is_interrupting) { + DTRACE(ide, ("controller %d - interrupt clear\n", controller->nr)); + device_interrupt_event(me, controller->nr, 0, NULL, 0); + controller->is_interrupting = 0; + } +} + + +static void +do_event(void *data) +{ + ide_controller *controller = data; + device *me = controller->me; + controller->event_tag = 0; + switch (controller->state) { + case busy_loaded_state: + case busy_drained_state: + if (controller->current_transfer > 0) { + controller->state = (controller->state == busy_loaded_state + ? loading_state : draining_state); + } + else { + controller->state = idle_state; + } + set_interrupt(me, controller); + break; + default: + device_error(me, "controller %d - unexpected event", controller->nr); + break; + } +} + + +static void +schedule_ready_event(device *me, + ide_controller *controller) +{ + if (controller->event_tag != 0) + device_error(me, "controller %d - attempting to schedule multiple events", + controller->nr); + controller->event_tag = + device_event_queue_schedule(me, controller->ready_delay, + do_event, controller); +} + + +static void +do_fifo_read(device *me, + ide_controller *controller, + void *dest, + int nr_bytes) +{ + if (controller->state != draining_state) + device_error(me, "controller %d - reading fifo when not ready (%s)", + controller->nr, + ide_state_name(controller->state)); + if (controller->fifo_pos + nr_bytes > controller->fifo_size) + device_error(me, "controller %d - fifo underflow", controller->nr); + if (nr_bytes > 0) { + memcpy(dest, &controller->fifo[controller->fifo_pos], nr_bytes); + controller->fifo_pos += nr_bytes; + } + if (controller->fifo_pos == controller->fifo_size) { + controller->current_transfer -= 1; + if (controller->current_transfer > 0 + && controller->current_drive != NULL) { + DTRACE(ide, ("controller %d:%d - reading %d byte block at 0x%x\n", + controller->nr, + controller->current_drive->nr, + controller->fifo_size, + controller->current_byte)); + if (device_io_read_buffer(controller->current_drive->device, + controller->fifo, + 0, controller->current_byte, + controller->fifo_size, + NULL, 0) + != controller->fifo_size) + device_error(me, "controller %d - disk %s io read error", + controller->nr, + device_path(controller->current_drive->device)); + } + controller->state = busy_drained_state; + controller->fifo_pos = 0; + controller->current_byte += controller->fifo_size; + schedule_ready_event(me, controller); + } +} + + +static void +do_fifo_write(device *me, + ide_controller *controller, + const void *source, + int nr_bytes) +{ + if (controller->state != loading_state) + device_error(me, "controller %d - writing fifo when not ready (%s)", + controller->nr, + ide_state_name(controller->state)); + if (controller->fifo_pos + nr_bytes > controller->fifo_size) + device_error(me, "controller %d - fifo overflow", controller->nr); + if (nr_bytes > 0) { + memcpy(&controller->fifo[controller->fifo_pos], source, nr_bytes); + controller->fifo_pos += nr_bytes; + } + if (controller->fifo_pos == controller->fifo_size) { + if (controller->current_transfer > 0 + && controller->current_drive != NULL) { + DTRACE(ide, ("controller %d:%d - writing %d byte block at 0x%x\n", + controller->nr, + controller->current_drive->nr, + controller->fifo_size, + controller->current_byte)); + if (device_io_write_buffer(controller->current_drive->device, + controller->fifo, + 0, controller->current_byte, + controller->fifo_size, + NULL, 0) + != controller->fifo_size) + device_error(me, "controller %d - disk %s io write error", + controller->nr, + device_path(controller->current_drive->device)); + } + controller->current_transfer -= 1; + controller->fifo_pos = 0; + controller->current_byte += controller->fifo_size; + controller->state = busy_loaded_state; + schedule_ready_event(me, controller); + } +} + + +static void +setup_fifo(device *me, + ide_controller *controller, + int is_simple, + int is_with_disk, + io_direction direction) +{ + /* find the disk */ + if (is_with_disk) { + int drive_nr = (controller->reg[ide_drive_head_reg] & 0x10) != 0; + controller->current_drive = &controller->drive[drive_nr]; + } + else { + controller->current_drive = NULL; + } + + /* number of transfers */ + if (is_simple) + controller->current_transfer = 1; + else { + int sector_count = controller->reg[ide_sector_count_reg]; + if (sector_count == 0) + controller->current_transfer = 256; + else + controller->current_transfer = sector_count; + } + + /* the transfer size */ + if (controller->current_drive == NULL) + controller->fifo_size = 512; + else + controller->fifo_size = controller->current_drive->geometry.byte; + + /* empty the fifo */ + controller->fifo_pos = 0; + + /* the starting address */ + if (controller->current_drive == NULL) + controller->current_byte = 0; + else if (controller->reg[ide_drive_head_reg] & 0x40) { + /* LBA addressing mode */ + controller->current_byte = controller->fifo_size + * (((controller->reg[ide_drive_head_reg] & 0xf) << 24) + | (controller->reg[ide_cylinder_reg1] << 16) + | (controller->reg[ide_cylinder_reg0] << 8) + | (controller->reg[ide_sector_number_reg])); + } + else if (controller->current_drive->geometry.head != 0 + && controller->current_drive->geometry.sector != 0) { + /* CHS addressing mode */ + int head_nr = controller->reg[ide_drive_head_reg] & 0xf; + int cylinder_nr = ((controller->reg[ide_cylinder_reg1] << 8) + | controller->reg[ide_cylinder_reg0]); + int sector_nr = controller->reg[ide_sector_number_reg]; + controller->current_byte = controller->fifo_size + * ((cylinder_nr * controller->current_drive->geometry.head + head_nr) + * controller->current_drive->geometry.sector + sector_nr - 1); + } + else + device_error(me, "controller %d:%d - CHS addressing disabled", + controller->nr, controller->current_drive->nr); + DTRACE(ide, ("controller %ld:%ld - transfer (%s) %ld blocks of %ld bytes from 0x%lx\n", + (long)controller->nr, + controller->current_drive == NULL ? -1L : (long)controller->current_drive->nr, + direction == is_read ? "read" : "write", + (long)controller->current_transfer, + (long)controller->fifo_size, + (unsigned long)controller->current_byte)); + switch (direction) { + case is_read: + /* force a primeing read */ + controller->current_transfer += 1; + controller->state = draining_state; + controller->fifo_pos = controller->fifo_size; + do_fifo_read(me, controller, NULL, 0); + break; + case is_write: + controller->state = loading_state; + break; + } +} + + +static void +do_command(device *me, + ide_controller *controller, + int command) +{ + if (controller->state != idle_state) + device_error(me, "controller %d - command when not idle", controller->nr); + switch (command) { + case 0x20: case 0x21: /* read-sectors */ + setup_fifo(me, controller, 0/*is_simple*/, 1/*is_with_disk*/, is_read); + break; + case 0x30: case 0x31: /* write */ + setup_fifo(me, controller, 0/*is_simple*/, 1/*is_with_disk*/, is_write); + break; + } +} + +static unsigned8 +get_status(device *me, + ide_controller *controller) +{ + switch (controller->state) { + case loading_state: + case draining_state: + return 0x08; /* data req */ + case busy_loaded_state: + case busy_drained_state: + return 0x80; /* busy */ + case idle_state: + return 0x40; /* drive ready */ + default: + device_error(me, "internal error"); + return 0; + } +} + + +/* The address presented to the IDE controler is decoded and then + mapped onto a controller:reg pair */ + +enum { + nr_address_blocks = 6, +}; + +typedef struct _address_block { + int space; + unsigned_word base_addr; + unsigned_word bound_addr; + int controller; + int base_reg; +} address_block; + +typedef struct _address_decoder { + address_block block[nr_address_blocks]; +} address_decoder; + +static void +decode_address(device *me, + address_decoder *decoder, + int space, + unsigned_word address, + int *controller, + int *reg, + io_direction direction) +{ + int i; + for (i = 0; i < nr_address_blocks; i++) { + if (space == decoder->block[i].space + && address >= decoder->block[i].base_addr + && address <= decoder->block[i].bound_addr) { + *controller = decoder->block[i].controller; + *reg = (address + - decoder->block[i].base_addr + + decoder->block[i].base_reg); + if (direction == is_write) { + switch (*reg) { + case ide_error_reg: *reg = ide_feature_reg; break; + case ide_status_reg: *reg = ide_command_reg; break; + case ide_alternate_status_reg: *reg = ide_control_reg; break; + default: break; + } + } + return; + } + } + device_error(me, "address %d:0x%lx invalid", + space, (unsigned long)address); +} + + +static void +build_address_decoder(device *me, + address_decoder *decoder) +{ + int reg; + for (reg = 1; reg < 6; reg++) { + reg_property_spec unit; + int space; + unsigned_word address; + unsigned size; + /* find and decode the reg property */ + if (!device_find_reg_array_property(me, "reg", reg, &unit)) + device_error(me, "missing or invalid reg entry %d", reg); + device_address_to_attach_address(device_parent(me), &unit.address, + &space, &address, me); + device_size_to_attach_size(device_parent(me), &unit.size, &size, me); + /* insert it into the address decoder */ + switch (reg) { + case 1: + case 2: + /* command register block */ + if (size != 8) + device_error(me, "reg entry %d must have a size of 8", reg); + decoder->block[reg-1].space = space; + decoder->block[reg-1].base_addr = address; + decoder->block[reg-1].bound_addr = address + size - 1; + decoder->block[reg-1].controller = (reg + 1) % nr_ide_controllers; + decoder->block[reg-1].base_reg = ide_data_reg; + DTRACE(ide, ("controller %d command register block at %d:0x%lx..0x%lx\n", + decoder->block[reg-1].controller, + decoder->block[reg-1].space, + (unsigned long)decoder->block[reg-1].base_addr, + (unsigned long)decoder->block[reg-1].bound_addr)); + break; + case 3: + case 4: + /* control register block */ + if (size != 1) + device_error(me, "reg entry %d must have a size of 1", reg); + decoder->block[reg-1].space = space; + decoder->block[reg-1].base_addr = address; + decoder->block[reg-1].bound_addr = address + size - 1; + decoder->block[reg-1].controller = (reg + 1) % nr_ide_controllers; + decoder->block[reg-1].base_reg = ide_alternate_status_reg; + DTRACE(ide, ("controller %d control register block at %d:0x%lx..0x%lx\n", + decoder->block[reg-1].controller, + decoder->block[reg-1].space, + (unsigned long)decoder->block[reg-1].base_addr, + (unsigned long)decoder->block[reg-1].bound_addr)); + break; + case 5: + /* dma register block */ + if (size != 8) + device_error(me, "reg entry %d must have a size of 8", reg); + decoder->block[reg-1].space = space; + decoder->block[reg-1].base_addr = address; + decoder->block[reg-1].bound_addr = address + 4 - 1; + decoder->block[reg-1].base_reg = ide_dma_command_reg; + decoder->block[reg-1].controller = 0; + DTRACE(ide, ("controller %d dma register block at %d:0x%lx..0x%lx\n", + decoder->block[reg-1].controller, + decoder->block[reg-1].space, + (unsigned long)decoder->block[reg-1].base_addr, + (unsigned long)decoder->block[reg-1].bound_addr)); + decoder->block[reg].space = space; + decoder->block[reg].base_addr = address + 4; + decoder->block[reg].bound_addr = address + 8 - 1; + decoder->block[reg].controller = 1; + decoder->block[reg].base_reg = ide_dma_command_reg; + DTRACE(ide, ("controller %d dma register block at %d:0x%lx..0x%lx\n", + decoder->block[reg].controller, + decoder->block[reg-1].space, + (unsigned long)decoder->block[reg].base_addr, + (unsigned long)decoder->block[reg].bound_addr)); + break; + default: + device_error(me, "internal error - bad switch"); + break; + } + } +} + + + +typedef struct _hw_ide_device { + ide_controller controller[nr_ide_controllers]; + address_decoder decoder; +} hw_ide_device; + + +static void +hw_ide_init_address(device *me) +{ + hw_ide_device *ide = device_data(me); + int controller; + int drive; + + /* zero some things */ + for (controller = 0; controller < nr_ide_controllers; controller++) { + memset(&ide->controller[controller], 0, sizeof(ide_controller)); + for (drive = 0; drive < nr_ide_drives_per_controller; drive++) { + ide->controller[controller].drive[drive].nr = drive; + } + ide->controller[controller].me = me; + if (device_find_property(me, "ready-delay") != NULL) + ide->controller[controller].ready_delay = + device_find_integer_property(me, "ready-delay"); + } + + /* attach this device to its parent */ + generic_device_init_address(me); + + /* determine our own address map */ + build_address_decoder(me, &ide->decoder); + +} + + +static void +hw_ide_attach_address(device *me, + attach_type type, + int space, + unsigned_word addr, + unsigned nr_bytes, + access_type access, + device *client) /*callback/default*/ +{ + hw_ide_device *ide = (hw_ide_device*)device_data(me); + int controller_nr = addr / nr_ide_drives_per_controller; + int drive_nr = addr % nr_ide_drives_per_controller; + ide_controller *controller; + ide_drive *drive; + if (controller_nr >= nr_ide_controllers) + device_error(me, "no controller for disk %s", + device_path(client)); + + controller = &ide->controller[controller_nr]; + drive = &controller->drive[drive_nr]; + drive->device = client; + if (device_find_property(client, "ide-byte-count") != NULL) + drive->geometry.byte = device_find_integer_property(client, "ide-byte-count"); + else + drive->geometry.byte = 512; + if (device_find_property(client, "ide-sector-count") != NULL) + drive->geometry.sector = device_find_integer_property(client, "ide-sector-count"); + if (device_find_property(client, "ide-head-count") != NULL) + drive->geometry.head = device_find_integer_property(client, "ide-head-count"); + drive->default_geometry = drive->geometry; + DTRACE(ide, ("controller %d:%d %s byte-count %d, sector-count %d, head-count %d\n", + controller_nr, + drive->nr, + device_path(client), + drive->geometry.byte, + drive->geometry.sector, + drive->geometry.head)); +} + + +static unsigned +hw_ide_io_read_buffer(device *me, + void *dest, + int space, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + hw_ide_device *ide = (hw_ide_device *)device_data(me); + int control_nr; + int reg; + ide_controller *controller; + + /* find the interface */ + decode_address(me, &ide->decoder, space, addr, &control_nr, ®, is_read); + controller = & ide->controller[control_nr]; + + /* process the transfer */ + memset(dest, 0, nr_bytes); + switch (reg) { + case ide_data_reg: + do_fifo_read(me, controller, dest, nr_bytes); + break; + case ide_status_reg: + *(unsigned8*)dest = get_status(me, controller); + clear_interrupt(me, controller); + break; + case ide_alternate_status_reg: + *(unsigned8*)dest = get_status(me, controller); + break; + case ide_error_reg: + case ide_sector_count_reg: + case ide_sector_number_reg: + case ide_cylinder_reg0: + case ide_cylinder_reg1: + case ide_drive_head_reg: + case ide_control_reg: + case ide_dma_command_reg: + case ide_dma_status_reg: + case ide_dma_prd_table_address_reg0: + case ide_dma_prd_table_address_reg1: + case ide_dma_prd_table_address_reg2: + case ide_dma_prd_table_address_reg3: + *(unsigned8*)dest = controller->reg[reg]; + break; + default: + device_error(me, "bus-error at address 0x%lx", addr); + break; + } + return nr_bytes; +} + + +static unsigned +hw_ide_io_write_buffer(device *me, + const void *source, + int space, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + hw_ide_device *ide = (hw_ide_device *)device_data(me); + int control_nr; + int reg; + ide_controller *controller; + + /* find the interface */ + decode_address(me, &ide->decoder, space, addr, &control_nr, ®, is_write); + controller = &ide->controller[control_nr]; + + /* process the access */ + switch (reg) { + case ide_data_reg: + do_fifo_write(me, controller, source, nr_bytes); + break; + case ide_command_reg: + do_command(me, controller, *(unsigned8*)source); + break; + case ide_control_reg: + controller->reg[reg] = *(unsigned8*)source; + /* possibly cancel interrupts */ + if ((controller->reg[reg] & 0x02) == 0x02) + clear_interrupt(me, controller); + break; + case ide_feature_reg: + case ide_sector_count_reg: + case ide_sector_number_reg: + case ide_cylinder_reg0: + case ide_cylinder_reg1: + case ide_drive_head_reg: + case ide_dma_command_reg: + case ide_dma_status_reg: + case ide_dma_prd_table_address_reg0: + case ide_dma_prd_table_address_reg1: + case ide_dma_prd_table_address_reg2: + case ide_dma_prd_table_address_reg3: + controller->reg[reg] = *(unsigned8*)source; + break; + default: + device_error(me, "bus-error at 0x%lx", addr); + break; + } + return nr_bytes; +} + + +static const device_interrupt_port_descriptor hw_ide_interrupt_ports[] = { + { "a", 0, 0 }, + { "b", 1, 0 }, + { "c", 2, 0 }, + { "d", 3, 0 }, + { NULL } +}; + + + +static device_callbacks const hw_ide_callbacks = { + { hw_ide_init_address, }, + { hw_ide_attach_address, }, /* attach */ + { hw_ide_io_read_buffer, hw_ide_io_write_buffer, }, + { NULL, }, /* DMA */ + { NULL, NULL, hw_ide_interrupt_ports }, /* interrupt */ + { generic_device_unit_decode, + generic_device_unit_encode, + generic_device_address_to_attach_address, + generic_device_size_to_attach_size }, +}; + + +static void * +hw_ide_create(const char *name, + const device_unit *unit_address, + const char *args) +{ + hw_ide_device *ide = ZALLOC(hw_ide_device); + return ide; +} + + +const device_descriptor hw_ide_device_descriptor[] = { + { "ide", hw_ide_create, &hw_ide_callbacks }, + { NULL, }, +}; + +#endif /* _HW_IDE_ */ diff --git a/sim/ppc/hw_init.c b/sim/ppc/hw_init.c new file mode 100644 index 00000000000..b486f6f7860 --- /dev/null +++ b/sim/ppc/hw_init.c @@ -0,0 +1,721 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1997, 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 _HW_INIT_C_ +#define _HW_INIT_C_ + +#include "device_table.h" +#include "bfd.h" +#include "psim.h" + + +/* DMA a file into memory */ +static int +dma_file(device *me, + const char *file_name, + unsigned_word addr) +{ + int count; + int inc; + FILE *image; + char buf[1024]; + + /* get it open */ + image = fopen(file_name, "r"); + if (image == NULL) + return -1; + + /* read it in slowly */ + count = 0; + while (1) { + inc = fread(buf, 1, sizeof(buf), image); + if (feof(image) || ferror(image)) + break; + if (device_dma_write_buffer(device_parent(me), + buf, + 0 /*address-space*/, + addr+count, + inc /*nr-bytes*/, + 1 /*violate ro*/) != inc) { + fclose(image); + return -1; + } + count += inc; + } + + /* close down again */ + fclose(image); + + return count; +} + + +/* DEVICE + + file - load a file into memory + + DESCRIPTION + + Loads the entire contents of <file-name> into memory at starting at + <<real-address>>. Assumes that memory exists for the load. + + PROPERTIES + + file-name = <string> + + Name of the file to be loaded into memory + + real-address = <integer> + + Real address at which the file is to be loaded */ + +static void +hw_file_init_data_callback(device *me) +{ + int count; + const char *file_name = device_find_string_property(me, "file-name"); + unsigned_word addr = device_find_integer_property(me, "real-address"); + /* load the file */ + count = dma_file(me, file_name, addr); + if (count < 0) + device_error(me, "Problem loading file %s\n", file_name); +} + + +static device_callbacks const hw_file_callbacks = { + { NULL, hw_file_init_data_callback, }, + { NULL, }, /* address */ + { NULL, }, /* IO */ + { NULL, }, /* DMA */ + { NULL, }, /* interrupt */ + { NULL, }, /* unit */ +}; + + +/* DEVICE + + + data - initialize a memory location with specified data + + + DESCRIPTION + + + The pseudo device <<data>> provides a mechanism specifying the + initialization of a small section of memory. + + Normally, the data would be written using a dma operation. + However, for some addresses this will not result in the desired + result. For instance, to initialize an address in an eeprom, + instead of a simple dma of the data, a sequence of writes (and then + real delays) that program the eeprom would be required. + + For dma write initialization, the data device will write the + specified <<data>> to <<real-address>> using a normal dma. + + For instance write initialization, the specified <<instance>> is + opened. Then a seek to the <<real-address>> is performed followed + by a write of the data. + + + Integer properties are stored using the target's endian mode. + + + PROPERTIES + + + data = <any-valid-property> (required) + + Data to be loaded into memory. The property type determines how it + is loaded. + + + real-address = <integer> (required) + + Start address at which the data is to be stored. + + + instance = <string> (optional) + + Instance specification of the device that is to be opened so that + the specified data can be written to it. + + + EXAMPLES + + + The examples below illustrate the two alternative mechanisms that + can be used to store the value 0x12345678 at address 0xfff00c00, + which is normally part of the 512k system eeprom. + + + If the eeprom is being modeled by ram (<<memory>> device) then the + standard dma initialization can be used. By convention: the data + devices are uniquely identified by argumenting them with the + destinations real address; and all data devices are put under the + node <</openprom/init>>. + + | /openprom/memory@0xfff00000/reg 0xfff00000 0x80000 + | /openprom/init/data@0x1000/data 0x12345678 + | /openprom/init/data@0x1000/real-address 0x1000 + + + If instead a real eeprom was being used the instance write method + would instead need to be used (storing just a single byte in an + eeprom requires a complex sequence of accesses). The + <<real-address>> is specified as <<0x0c00>> which is the offset + into the eeprom. For brevity, most of the eeprom properties have + been omited. + + | /iobus/eeprom@0xfff00000/reg 0xfff00000 0x80000 + | /openprom/init/data@0xfff00c00/real-address 0x0c00 + | /openprom/init/data@0xfff00c00/data 0x12345667 + | /openprom/init/data@0xfff00c00/instance /iobus/eeprom@0xfff00000/reg + + + BUGS + + + At present, only <<integer>> properties can be specified for an + initial data value. + + */ + + +static void +hw_data_init_data_callback(device *me) +{ + unsigned_word addr = device_find_integer_property(me, "real-address"); + const device_property *data = device_find_property(me, "data"); + const char *instance_spec = (device_find_property(me, "instance") != NULL + ? device_find_string_property(me, "instance") + : NULL); + device_instance *instance = NULL; + if (data == NULL) + device_error(me, "missing property <data>\n"); + if (instance_spec != NULL) + instance = tree_instance(me, instance_spec); + switch (data->type) { + case integer_property: + { + unsigned_cell buf = device_find_integer_property(me, "data"); + H2T(buf); + if (instance == NULL) { + if (device_dma_write_buffer(device_parent(me), + &buf, + 0 /*address-space*/, + addr, + sizeof(buf), /*nr-bytes*/ + 1 /*violate ro*/) != sizeof(buf)) + device_error(me, "Problem storing integer 0x%x at 0x%lx\n", + (unsigned)buf, (unsigned long)addr); + } + else { + if (device_instance_seek(instance, 0, addr) < 0 + || device_instance_write(instance, &buf, sizeof(buf)) != sizeof(buf)) + device_error(me, "Problem storing integer 0x%x at 0x%lx of instance %s\n", + (unsigned)buf, (unsigned long)addr, instance_spec); + } + } + break; + default: + device_error(me, "Write of this data is not yet implemented\n"); + break; + } + if (instance != NULL) + device_instance_delete(instance); +} + + +static device_callbacks const hw_data_callbacks = { + { NULL, hw_data_init_data_callback, }, + { NULL, }, /* address */ + { NULL, }, /* IO */ + { NULL, }, /* DMA */ + { NULL, }, /* interrupt */ + { NULL, }, /* unit */ +}; + + +/* DEVICE + + + load-binary - load binary segments into memory + + + DESCRIPTION + + Each loadable segment of the specified binary is loaded into memory + at its required address. It is assumed that the memory at those + addresses already exists. + + This device is normally used to load an executable into memory as + part of real mode simulation. + + + PROPERTIES + + + file-name = <string> + + Name of the binary to be loaded. + + + claim = <anything> (optional) + + If this property is present, the real memory that is to be used by + the image being loaded will be claimed from the memory node + (specified by the ihandle <</chosen/memory>>). + + + BUGS + + + When loading the binary the bfd virtual-address is used. It should + be using the bfd load-address. + + */ + +/* DEVICE + + map-binary - map the binary into the users address space + + DESCRIPTION + + Similar to load-binary except that memory for each segment is + created before the corresponding data for the segment is loaded. + + This device is normally used to load an executable into a user mode + simulation. + + PROPERTIES + + file-name = <string> + + Name of the binary to be loaded. + + */ + +static void +update_for_binary_section(bfd *abfd, + asection *the_section, + PTR obj) +{ + unsigned_word section_vma; + unsigned_word section_size; + access_type access; + device *me = (device*)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); + + DTRACE(binary, + ("name=%-7s, vma=0x%.8lx, size=%6ld, flags=%3lx(%s%s%s%s%s )\n", + bfd_get_section_name(abfd, the_section), + (long)section_vma, + (long)section_size, + (long)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 there is an .interp section, it means it needs a shared library interpreter. */ + if (strcmp(".interp", bfd_get_section_name(abfd, the_section)) == 0) + error("Shared libraries are not yet supported.\n"); + + /* determine the devices access */ + access = access_read; + if (bfd_get_section_flags(abfd, the_section) & SEC_CODE) + access |= access_exec; + if (!(bfd_get_section_flags(abfd, the_section) & SEC_READONLY)) + access |= access_write; + + /* if claim specified, allocate region from the memory device */ + if (device_find_property(me, "claim") != NULL) { + device_instance *memory = tree_find_ihandle_property(me, "/chosen/memory"); + unsigned_cell mem_in[3]; + unsigned_cell mem_out[1]; + mem_in[0] = 0; /*alignment - top-of-stack*/ + mem_in[1] = section_size; + mem_in[2] = section_vma; + if (device_instance_call_method(memory, "claim", 3, mem_in, 1, mem_out) < 0) + device_error(me, "failed to claim memory for section at 0x%lx (0x%lx", + section_vma, + section_size); + if (mem_out[0] != section_vma) + device_error(me, "section address not as requested"); + } + + /* if a map, pass up a request to create the memory in core */ + if (strncmp(device_name(me), "map-binary", strlen("map-binary")) == 0) + device_attach_address(device_parent(me), + attach_raw_memory, + 0 /*address space*/, + section_vma, + section_size, + access, + me); + + /* if a load dma in the required data */ + if (bfd_get_section_flags(abfd, the_section) & SEC_LOAD) { + void *section_init = zalloc(section_size); + if (!bfd_get_section_contents(abfd, + the_section, + section_init, 0, + section_size)) { + bfd_perror("binary"); + device_error(me, "load of data failed"); + return; + } + if (device_dma_write_buffer(device_parent(me), + section_init, + 0 /*space*/, + section_vma, + section_size, + 1 /*violate_read_only*/) + != section_size) + device_error(me, "broken transfer\n"); + zfree(section_init); /* only free if load */ + } +} + +static void +hw_binary_init_data_callback(device *me) +{ + /* get the file name */ + const char *file_name = device_find_string_property(me, "file-name"); + bfd *image; + + /* open the file */ + image = bfd_openr(file_name, NULL); + if (image == NULL) { + bfd_perror("binary"); + device_error(me, "Failed to open file %s\n", file_name); + } + + /* check it is valid */ + if (!bfd_check_format(image, bfd_object)) { + bfd_close(image); + device_error(me, "The file %s has an invalid binary format\n", file_name); + } + + /* and the data sections */ + bfd_map_over_sections(image, + update_for_binary_section, + (PTR)me); + + bfd_close(image); +} + + +static device_callbacks const hw_binary_callbacks = { + { NULL, hw_binary_init_data_callback, }, + { NULL, }, /* address */ + { NULL, }, /* IO */ + { NULL, }, /* DMA */ + { NULL, }, /* interrupt */ + { NULL, }, /* unit */ +}; + + +/* DEVICE + + stack - create an initial stack frame in memory + + DESCRIPTION + + Creates a stack frame of the specified type in memory. + + Due to the startup sequence gdb uses when commencing a simulation, + it is not possible for the data to be placed on the stack to be + specified as part of the device tree. Instead the arguments to be + pushed onto the stack are specified using an IOCTL call. + + The IOCTL takes the additional arguments: + + | unsigned_word stack_end -- where the stack should come down from + | char **argv -- ... + | char **envp -- ... + + PROPERTIES + + stack-type = <string> + + The form of the stack frame that is to be created. + + */ + +static int +sizeof_argument_strings(char **arg) +{ + int sizeof_strings = 0; + + /* robust */ + if (arg == NULL) + return 0; + + /* add up all the string sizes (padding as we go) */ + for (; *arg != NULL; arg++) { + int len = strlen(*arg) + 1; + sizeof_strings += ALIGN_8(len); + } + + return sizeof_strings; +} + +static int +number_of_arguments(char **arg) +{ + int nr; + if (arg == NULL) + return 0; + for (nr = 0; *arg != NULL; arg++, nr++); + return nr; +} + +static int +sizeof_arguments(char **arg) +{ + return ALIGN_8((number_of_arguments(arg) + 1) * sizeof(unsigned_word)); +} + +static void +write_stack_arguments(device *me, + char **arg, + unsigned_word start_block, + unsigned_word end_block, + unsigned_word start_arg, + unsigned_word end_arg) +{ + DTRACE(stack, + ("write_stack_arguments(device=%s, arg=0x%lx, start_block=0x%lx, end_block=0x%lx, start_arg=0x%lx, end_arg=0x%lx)\n", + device_name(me), (long)arg, (long)start_block, (long)end_block, (long)start_arg, (long)end_arg)); + if (arg == NULL) + device_error(me, "Attempt to write a null array onto the stack\n"); + /* only copy in arguments, memory is already zero */ + for (; *arg != NULL; arg++) { + int len = strlen(*arg)+1; + unsigned_word target_start_block; + DTRACE(stack, + ("write_stack_arguments() write %s=%s at %s=0x%lx %s=0x%lx %s=0x%lx\n", + "**arg", *arg, "start_block", (long)start_block, + "len", (long)len, "start_arg", (long)start_arg)); + if (psim_write_memory(device_system(me), 0, *arg, + start_block, len, + 0/*violate_readonly*/) != len) + device_error(me, "Write of **arg (%s) at 0x%lx of stack failed\n", + *arg, (unsigned long)start_block); + target_start_block = H2T_word(start_block); + if (psim_write_memory(device_system(me), 0, &target_start_block, + start_arg, sizeof(target_start_block), + 0) != sizeof(target_start_block)) + device_error(me, "Write of *arg onto stack failed\n"); + start_block += ALIGN_8(len); + start_arg += sizeof(start_block); + } + start_arg += sizeof(start_block); /*the null at the end*/ + if (start_block != end_block + || ALIGN_8(start_arg) != end_arg) + device_error(me, "Probable corrpution of stack arguments\n"); + DTRACE(stack, ("write_stack_arguments() = void\n")); +} + +static void +create_ppc_elf_stack_frame(device *me, + unsigned_word bottom_of_stack, + char **argv, + char **envp) +{ + /* fixme - this is over aligned */ + + /* information block */ + const unsigned sizeof_envp_block = sizeof_argument_strings(envp); + const unsigned_word start_envp_block = bottom_of_stack - sizeof_envp_block; + const unsigned sizeof_argv_block = sizeof_argument_strings(argv); + const unsigned_word start_argv_block = start_envp_block - sizeof_argv_block; + + /* auxiliary vector - contains only one entry */ + const unsigned sizeof_aux_entry = 2*sizeof(unsigned_word); /* magic */ + const unsigned_word start_aux = start_argv_block - ALIGN_8(sizeof_aux_entry); + + /* environment points (including null sentinal) */ + const unsigned sizeof_envp = sizeof_arguments(envp); + const unsigned_word start_envp = start_aux - sizeof_envp; + + /* argument pointers (including null sentinal) */ + const int argc = number_of_arguments(argv); + const unsigned sizeof_argv = sizeof_arguments(argv); + const unsigned_word start_argv = start_envp - sizeof_argv; + + /* link register save address - alligned to a 16byte boundary */ + const unsigned_word top_of_stack = ((start_argv + - 2 * sizeof(unsigned_word)) + & ~0xf); + + /* install arguments on stack */ + write_stack_arguments(me, envp, + start_envp_block, bottom_of_stack, + start_envp, start_aux); + write_stack_arguments(me, argv, + start_argv_block, start_envp_block, + start_argv, start_envp); + + /* set up the registers */ + psim_write_register(device_system(me), -1, + &top_of_stack, "sp", cooked_transfer); + psim_write_register(device_system(me), -1, + &argc, "r3", cooked_transfer); + psim_write_register(device_system(me), -1, + &start_argv, "r4", cooked_transfer); + psim_write_register(device_system(me), -1, + &start_envp, "r5", cooked_transfer); + psim_write_register(device_system(me), -1, + &start_aux, "r6", cooked_transfer); +} + +static void +create_ppc_aix_stack_frame(device *me, + unsigned_word bottom_of_stack, + char **argv, + char **envp) +{ + unsigned_word core_envp; + unsigned_word core_argv; + unsigned_word core_argc; + unsigned_word core_aux; + unsigned_word top_of_stack; + + /* cheat - create an elf stack frame */ + create_ppc_elf_stack_frame(me, bottom_of_stack, argv, envp); + + /* extract argument addresses from registers */ + psim_read_register(device_system(me), 0, + &top_of_stack, "r1", cooked_transfer); + psim_read_register(device_system(me), 0, + &core_argc, "r3", cooked_transfer); + psim_read_register(device_system(me), 0, + &core_argv, "r4", cooked_transfer); + psim_read_register(device_system(me), 0, + &core_envp, "r5", cooked_transfer); + psim_read_register(device_system(me), 0, + &core_aux, "r6", cooked_transfer); + + /* extract arguments from registers */ + device_error(me, "Unfinished procedure create_ppc_aix_stack_frame\n"); +} + + +static void +create_ppc_chirp_bootargs(device *me, + char **argv) +{ + /* concat the arguments */ + char args[1024]; + char **chp = argv + 1; + args[0] = '\0'; + while (*chp != NULL) { + if (strlen(args) > 0) + strcat(args, " "); + if (strlen(args) + strlen(*chp) >= sizeof(args)) + device_error(me, "buffer overflow"); + strcat(args, *chp); + chp++; + } + + /* set the arguments property */ + tree_parse(me, "/chosen/bootargs \"%s", args); +} + + +static int +hw_stack_ioctl(device *me, + cpu *processor, + unsigned_word cia, + device_ioctl_request request, + va_list ap) +{ + switch (request) { + case device_ioctl_create_stack: + { + unsigned_word stack_pointer = va_arg(ap, unsigned_word); + char **argv = va_arg(ap, char **); + char **envp = va_arg(ap, char **); + const char *stack_type; + DTRACE(stack, + ("stack_ioctl_callback(me=0x%lx:%s processor=0x%lx cia=0x%lx argv=0x%lx envp=0x%lx)\n", + (long)me, device_name(me), + (long)processor, + (long)cia, + (long)argv, + (long)envp)); + stack_type = device_find_string_property(me, "stack-type"); + if (strcmp(stack_type, "ppc-elf") == 0) + create_ppc_elf_stack_frame(me, stack_pointer, argv, envp); + else if (strcmp(stack_type, "ppc-xcoff") == 0) + create_ppc_aix_stack_frame(me, stack_pointer, argv, envp); + else if (strcmp(stack_type, "chirp") == 0) + create_ppc_chirp_bootargs(me, argv); + else if (strcmp(stack_type, "none") != 0) + device_error(me, "Unknown initial stack frame type %s", stack_type); + DTRACE(stack, + ("stack_ioctl_callback() = void\n")); + break; + } + default: + device_error(me, "Unsupported ioctl requested"); + break; + } + return 0; +} + +static device_callbacks const hw_stack_callbacks = { + { NULL, }, + { NULL, }, /* address */ + { NULL, }, /* IO */ + { NULL, }, /* DMA */ + { NULL, }, /* interrupt */ + { NULL, }, /* unit */ + NULL, /* instance */ + hw_stack_ioctl, +}; + +const device_descriptor hw_init_device_descriptor[] = { + { "file", NULL, &hw_file_callbacks }, + { "data", NULL, &hw_data_callbacks }, + { "load-binary", NULL, &hw_binary_callbacks }, + { "map-binary", NULL, &hw_binary_callbacks }, + { "stack", NULL, &hw_stack_callbacks }, + { NULL }, +}; + +#endif _HW_INIT_C_ diff --git a/sim/ppc/hw_iobus.c b/sim/ppc/hw_iobus.c new file mode 100644 index 00000000000..3cd138c1c80 --- /dev/null +++ b/sim/ppc/hw_iobus.c @@ -0,0 +1,100 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1996, 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 _HW_IOBUS_C_ +#define _HW_IOBUS_C_ + +#ifndef STATIC_INLINE_HW_IOBUS +#define STATIC_INLINE_HW_IOBUS STATIC_INLINE +#endif + +#include "device_table.h" + + +/* DEVICE + + iobus - simple bus for attaching devices + + DESCRIPTION + + IOBUS provides a simple `local' bus for attaching (hanging) + programmed IO devices from. All child devices are directly mapped + into this devices parent address space (after checking that the + attach address lies within the <<iobus>> address range. address). + + PROPERTIES + + None. + + */ + +static void +hw_iobus_attach_address_callback(device *me, + attach_type type, + int space, + unsigned_word addr, + unsigned nr_bytes, + access_type access, + device *client) /*callback/default*/ +{ + int attach_space; + unsigned_word attach_address; + /* sanity check */ + if (space != 0) + device_error(me, "invalid space (%d) specified by %s", + space, device_path(client)); + /* get the bus address */ + device_address_to_attach_address(device_parent(me), + device_unit_address(me), + &attach_space, + &attach_address, + me); + if (addr < attach_address) + device_error(me, "Invalid attach address 0x%lx", (unsigned long)addr); + device_attach_address(device_parent(me), + type, + attach_space, + addr, + nr_bytes, + access, + client); +} + + +static device_callbacks const hw_iobus_callbacks = { + { NULL, }, + { hw_iobus_attach_address_callback, }, + { NULL, }, /* IO */ + { NULL, }, /* DMA */ + { NULL, }, /* interrupt */ + { generic_device_unit_decode, + generic_device_unit_encode, + generic_device_address_to_attach_address, + generic_device_size_to_attach_size } +}; + + +const device_descriptor hw_iobus_device_descriptor[] = { + { "iobus", NULL, &hw_iobus_callbacks }, + { NULL, }, +}; + +#endif /* _HW_IOBUS_ */ diff --git a/sim/ppc/hw_memory.c b/sim/ppc/hw_memory.c new file mode 100644 index 00000000000..117324b0edd --- /dev/null +++ b/sim/ppc/hw_memory.c @@ -0,0 +1,538 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1997, 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 _HW_MEMORY_C_ +#define _HW_MEMORY_C_ + +#ifndef STATIC_INLINE_HW_MEMORY +#define STATIC_INLINE_HW_MEMORY STATIC_INLINE +#endif + +#include "device_table.h" + +/* DEVICE + + + memory - description of system memory + + + DESCRIPTION + + + This device describes the size and location of the banks of + physical memory within the simulation. + + In addition, this device supports the "claim" and "release" methods + that can be used by OpenBoot client programs to manage the + allocation of physical memory. + + + PROPERTIES + + + reg = { <address> <size> } (required) + + Each pair specify one bank of memory. + + available = { <address> <size> } (automatic) + + Each pair specifies a block of memory that is currently unallocated. + + + BUGS + + + OpenFirmware doesn't make it clear if, when releasing memory the + same address + size pair as was used during the claim should be + specified. + + It is assumed that #size-cells and #address-cells for the parent + node of this device are both one i.e. an address or size can be + specified using a single memory cell (word). + + Significant work will be required before the <<memory>> device can + support 64bit addresses (#address-cells equal two). + + */ + +typedef struct _memory_reg_spec { + unsigned_cell base; + unsigned_cell size; +} memory_reg_spec; + +typedef struct _hw_memory_chunk hw_memory_chunk; +struct _hw_memory_chunk { + unsigned_word address; + unsigned_word size; + int available; + hw_memory_chunk *next; +}; + +typedef struct _hw_memory_device { + hw_memory_chunk *heap; +} hw_memory_device; + + +static void * +hw_memory_create(const char *name, + const device_unit *unit_address, + const char *args) +{ + hw_memory_device *hw_memory = ZALLOC(hw_memory_device); + return hw_memory; +} + + +static void +hw_memory_set_available(device *me, + hw_memory_device *hw_memory) +{ + hw_memory_chunk *chunk = NULL; + memory_reg_spec *available = NULL; + int nr_available = 0; + int curr = 0; + int sizeof_available = 0; + /* determine the nr of available chunks */ + chunk = hw_memory->heap; + nr_available = 0; + while (chunk != NULL) { + if (chunk->available) + nr_available += 1; + ASSERT(chunk->next == NULL + || chunk->address < chunk->next->address); + ASSERT(chunk->next == NULL + || chunk->address + chunk->size == chunk->next->address); + chunk = chunk->next; + } + /* now create the available struct */ + ASSERT(nr_available > 0); + sizeof_available = sizeof(memory_reg_spec) * nr_available; + available = zalloc(sizeof_available); + chunk = hw_memory->heap; + curr = 0; + while (chunk != NULL) { + if (chunk->available) { + available[curr].base = H2BE_cell(chunk->address); + available[curr].size = H2BE_cell(chunk->size); + curr += 1; + } + chunk = chunk->next; + } + /* update */ + device_set_array_property(me, "available", available, sizeof_available); + zfree(available); +} + + +static void +hw_memory_init_address(device *me) +{ + hw_memory_device *hw_memory = (hw_memory_device*)device_data(me); + + /* free up any previous structures */ + { + hw_memory_chunk *curr_chunk = hw_memory->heap; + hw_memory->heap = NULL; + while (curr_chunk != NULL) { + hw_memory_chunk *dead_chunk = curr_chunk; + curr_chunk = dead_chunk->next; + dead_chunk->next = NULL; + zfree(dead_chunk); + } + } + + /* attach memory regions according to the "reg" property */ + { + int reg_nr; + reg_property_spec reg; + for (reg_nr = 0; + device_find_reg_array_property(me, "reg", reg_nr, ®); + reg_nr++) { + int i; + /* check that the entry meets restrictions */ + for (i = 0; i < reg.address.nr_cells - 1; i++) + if (reg.address.cells[i] != 0) + device_error(me, "Only single celled addresses supported"); + for (i = 0; i < reg.size.nr_cells - 1; i++) + if (reg.size.cells[i] != 0) + device_error(me, "Only single celled sizes supported"); + /* attach the range */ + device_attach_address(device_parent(me), + attach_raw_memory, + 0 /*address space*/, + reg.address.cells[reg.address.nr_cells - 1], + reg.size.cells[reg.size.nr_cells - 1], + access_read_write_exec, + me); + } + } + + /* create the initial `available memory' data structure */ + if (device_find_property(me, "available") != NULL) { + hw_memory_chunk **curr_chunk = &hw_memory->heap; + int cell_nr; + unsigned_cell dummy; + int nr_cells = device_find_integer_array_property(me, "available", 0, &dummy); + if ((nr_cells % 2) != 0) + device_error(me, "property \"available\" invalid - contains an odd number of cells"); + for (cell_nr = 0; + cell_nr < nr_cells; + cell_nr += 2) { + hw_memory_chunk *new_chunk = ZALLOC(hw_memory_chunk); + device_find_integer_array_property(me, "available", cell_nr, + &new_chunk->address); + device_find_integer_array_property(me, "available", cell_nr + 1, + &new_chunk->size); + new_chunk->available = 1; + *curr_chunk = new_chunk; + curr_chunk = &new_chunk->next; + } + } + else { + hw_memory_chunk **curr_chunk = &hw_memory->heap; + int reg_nr; + reg_property_spec reg; + for (reg_nr = 0; + device_find_reg_array_property(me, "reg", reg_nr, ®); + reg_nr++) { + hw_memory_chunk *new_chunk; + new_chunk = ZALLOC(hw_memory_chunk); + new_chunk->address = reg.address.cells[reg.address.nr_cells - 1]; + new_chunk->size = reg.size.cells[reg.size.nr_cells - 1]; + new_chunk->available = 1; + *curr_chunk = new_chunk; + curr_chunk = &new_chunk->next; + } + } + + /* initialize the alloc property for this device */ + hw_memory_set_available(me, hw_memory); +} + +static void +hw_memory_instance_delete(device_instance *instance) +{ + return; +} + +static int +hw_memory_instance_claim(device_instance *instance, + int n_stack_args, + unsigned_cell stack_args[/*n_stack_args*/], + int n_stack_returns, + unsigned_cell stack_returns[/*n_stack_returns*/]) +{ + hw_memory_device *hw_memory = device_instance_data(instance); + device *me = device_instance_device(instance); + int stackp = 0; + unsigned_word alignment; + unsigned_cell size; + unsigned_cell address; + hw_memory_chunk *chunk = NULL; + + /* get the alignment from the stack */ + if (n_stack_args < stackp + 1) + device_error(me, "claim - incorrect number of arguments (alignment missing)"); + alignment = stack_args[stackp]; + stackp++; + + /* get the size from the stack */ + { + int i; + int nr_cells = device_nr_size_cells(device_parent(me)); + if (n_stack_args < stackp + nr_cells) + device_error(me, "claim - incorrect number of arguments (size missing)"); + for (i = 0; i < nr_cells - 1; i++) { + if (stack_args[stackp] != 0) + device_error(me, "claim - multi-cell sizes not supported"); + stackp++; + } + size = stack_args[stackp]; + stackp++; + } + + /* get the address from the stack */ + { + int nr_cells = device_nr_address_cells(device_parent(me)); + if (alignment != 0) { + if (n_stack_args != stackp) { + if (n_stack_args == stackp + nr_cells) + DTRACE(memory, ("claim - extra address argument ignored\n")); + else + device_error(me, "claim - incorrect number of arguments (optional addr)"); + } + address = 0; + } + else { + int i; + if (n_stack_args != stackp + nr_cells) + device_error(me, "claim - incorrect number of arguments (addr missing)"); + for (i = 0; i < nr_cells - 1; i++) { + if (stack_args[stackp] != 0) + device_error(me, "claim - multi-cell addresses not supported"); + stackp++; + } + address = stack_args[stackp]; + } + } + + /* check that there is space for the result */ + if (n_stack_returns != 0 + && n_stack_returns != device_nr_address_cells(device_parent(me))) + device_error(me, "claim - invalid number of return arguments"); + + /* find a chunk candidate, either according to address or alignment */ + if (alignment == 0) { + chunk = hw_memory->heap; + while (chunk != NULL) { + if ((address + size) <= (chunk->address + chunk->size)) + break; + chunk = chunk->next; + } + if (chunk == NULL || address < chunk->address || !chunk->available) + device_error(me, "failed to allocate %ld bytes at 0x%lx", + (unsigned long)size, (unsigned long)address); + DTRACE(memory, ("claim - address=0x%lx size=0x%lx\n", + (unsigned long)address, + (unsigned long)size)); + } + else { + /* adjust the alignment so that it is a power of two */ + unsigned_word align_mask = 1; + while (align_mask < alignment && align_mask != 0) + align_mask <<= 1; + if (align_mask == 0) + device_error(me, "alignment 0x%lx is to large", (unsigned long)alignment); + align_mask -= 1; + /* now find an aligned chunk that fits */ + chunk = hw_memory->heap; + while (chunk != NULL) { + address = ((chunk->address + align_mask) & ~align_mask); + if ((chunk->available) + && (chunk->address + chunk->size >= address + size)) + break; + chunk = chunk->next; + } + if (chunk == NULL) + device_error(me, "failed to allocate %ld bytes with alignment %ld", + (unsigned long)size, (unsigned long)alignment); + DTRACE(memory, ("claim - size=0x%lx alignment=%ld (0x%lx), address=0x%lx\n", + (unsigned long)size, + (unsigned long)alignment, + (unsigned long)alignment, + (unsigned long)address)); + } + + /* break off a bit before this chunk if needed */ + ASSERT(address >= chunk->address); + if (address > chunk->address) { + hw_memory_chunk *next_chunk = ZALLOC(hw_memory_chunk); + /* insert a new chunk */ + next_chunk->next = chunk->next; + chunk->next = next_chunk; + /* adjust the address/size */ + next_chunk->address = address; + next_chunk->size = chunk->address + chunk->size - next_chunk->address; + next_chunk->available = 1; + chunk->size = next_chunk->address - chunk->address; + /* make this new chunk the one to allocate */ + chunk = next_chunk; + } + ASSERT(address == chunk->address); + + /* break off a bit after this chunk if needed */ + ASSERT(address + size <= chunk->address + chunk->size); + if (address + size < chunk->address + chunk->size) { + hw_memory_chunk *next_chunk = ZALLOC(hw_memory_chunk); + /* insert it in to the list */ + next_chunk->next = chunk->next; + chunk->next = next_chunk; + /* adjust the address/size */ + next_chunk->address = address + size; + next_chunk->size = chunk->address + chunk->size - next_chunk->address; + next_chunk->available = 1; + chunk->size = next_chunk->address - chunk->address; + } + ASSERT(address + size == chunk->address + chunk->size); + + /* now allocate/return it */ + chunk->available = 0; + hw_memory_set_available(device_instance_device(instance), hw_memory); + if (n_stack_returns > 0) { + int i; + for (i = 0; i < n_stack_returns - 1; i++) + stack_returns[i] = 0; + stack_returns[n_stack_returns - 1] = address; + } + + return 0; +} + + +static int +hw_memory_instance_release(device_instance *instance, + int n_stack_args, + unsigned_cell stack_args[/*n_stack_args*/], + int n_stack_returns, + unsigned_cell stack_returns[/*n_stack_returns*/]) +{ + hw_memory_device *hw_memory = device_instance_data(instance); + device *me = device_instance_device(instance); + unsigned_word length; + unsigned_word address; + int stackp = 0; + hw_memory_chunk *chunk; + + /* get the length from the stack */ + { + int i; + int nr_cells = device_nr_size_cells(device_parent(me)); + if (n_stack_args < stackp + nr_cells) + device_error(me, "release - incorrect number of arguments (length missing)"); + for (i = 0; i < nr_cells - 1; i++) { + if (stack_args[stackp] != 0) + device_error(me, "release - multi-cell length not supported"); + stackp++; + } + length = stack_args[stackp]; + stackp++; + } + + /* get the address from the stack */ + { + int i; + int nr_cells = device_nr_address_cells(device_parent(me)); + if (n_stack_args != stackp + nr_cells) + device_error(me, "release - incorrect number of arguments (addr missing)"); + for (i = 0; i < nr_cells - 1; i++) { + if (stack_args[stackp] != 0) + device_error(me, "release - multi-cell addresses not supported"); + stackp++; + } + address = stack_args[stackp]; + } + + /* returns ok */ + if (n_stack_returns != 0) + device_error(me, "release - nonzero number of results"); + + /* try to free the corresponding memory chunk */ + chunk = hw_memory->heap; + while (chunk != NULL) { + if (chunk->address == address + && chunk->size == length) { + /* an exact match */ + if (chunk->available) + device_error(me, "memory chunk 0x%lx (size 0x%lx) already available", + (unsigned long)address, + (unsigned long)length); + else { + /* free this chunk */ + DTRACE(memory, ("release - address=0x%lx, length=0x%lx\n", + (unsigned long) address, + (unsigned long) length)); + chunk->available = 1; + break; + } + } + else if (chunk->address >= address + && chunk->address + chunk->size <= address + length) { + /* a sub region */ + if (!chunk->available) { + DTRACE(memory, ("release - address=0x%lx, size=0x%lx within region 0x%lx length 0x%lx\n", + (unsigned long) chunk->address, + (unsigned long) chunk->size, + (unsigned long) address, + (unsigned long) length)); + chunk->available = 1; + } + } + chunk = chunk->next; + } + if (chunk == NULL) { + printf_filtered("warning: released chunks within region 0x%lx..0x%lx\n", + (unsigned long)address, + (unsigned long)(address + length - 1)); + } + + /* check for the chance to merge two adjacent available memory chunks */ + chunk = hw_memory->heap; + while (chunk != NULL) { + if (chunk->available + && chunk->next != NULL && chunk->next->available) { + /* adjacent */ + hw_memory_chunk *delete = chunk->next; + ASSERT(chunk->address + chunk->size == delete->address); + chunk->size += delete->size; + chunk->next = delete->next; + zfree(delete); + } + else { + chunk = chunk->next; + } + } + + /* update the corresponding property */ + hw_memory_set_available(device_instance_device(instance), hw_memory); + + return 0; +} + + +static device_instance_methods hw_memory_instance_methods[] = { + { "claim", hw_memory_instance_claim }, + { "release", hw_memory_instance_release }, + { NULL, }, +}; + +static device_instance_callbacks const hw_memory_instance_callbacks = { + hw_memory_instance_delete, + NULL /*read*/, NULL /*write*/, NULL /*seek*/, + hw_memory_instance_methods +}; + +static device_instance * +hw_memory_create_instance(device *me, + const char *path, + const char *args) +{ + return device_create_instance_from(me, NULL, + device_data(me), /* nothing better */ + path, args, + &hw_memory_instance_callbacks); +} + +static device_callbacks const hw_memory_callbacks = { + { hw_memory_init_address, }, + { NULL, }, /* address */ + { NULL, }, /* IO */ + { NULL, }, /* DMA */ + { NULL, }, /* interrupt */ + { NULL, }, /* unit */ + hw_memory_create_instance, +}; + +const device_descriptor hw_memory_device_descriptor[] = { + { "memory", hw_memory_create, &hw_memory_callbacks }, + { NULL }, +}; + +#endif /* _HW_MEMORY_C_ */ diff --git a/sim/ppc/hw_nvram.c b/sim/ppc/hw_nvram.c new file mode 100644 index 00000000000..4c87d05d9be --- /dev/null +++ b/sim/ppc/hw_nvram.c @@ -0,0 +1,264 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1996, 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 _HW_NVRAM_C_ +#define _HW_NVRAM_C_ + +#ifndef STATIC_INLINE_HW_NVRAM +#define STATIC_INLINE_HW_NVRAM STATIC_INLINE +#endif + +#include "device_table.h" + +#ifdef HAVE_TIME_H +#include <time.h> +#endif + +#ifdef HAVE_STRING_H +#include <string.h> +#else +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif +#endif + +/* DEVICE + + + nvram - non-volatile memory with clock + + + DESCRIPTION + + + This device implements a small byte addressable non-volatile + memory. The top 8 bytes of this memory include a real-time clock. + + + PROPERTIES + + + reg = <address> <size> (required) + + Specify the address/size of this device within its parents address + space. + + + timezone = <integer> (optional) + + Adjustment to the hosts current GMT (in seconds) that should be + applied when updating the NVRAM's clock. If no timezone is + specified, zero (GMT or UCT) is assumed. + + + */ + +typedef struct _hw_nvram_device { + unsigned8 *memory; + unsigned sizeof_memory; +#ifdef HAVE_TIME_H + time_t host_time; +#else + long host_time; +#endif + unsigned timezone; + /* useful */ + unsigned addr_year; + unsigned addr_month; + unsigned addr_date; + unsigned addr_day; + unsigned addr_hour; + unsigned addr_minutes; + unsigned addr_seconds; + unsigned addr_control; +} hw_nvram_device; + +static void * +hw_nvram_create(const char *name, + const device_unit *unit_address, + const char *args) +{ + hw_nvram_device *nvram = ZALLOC(hw_nvram_device); + return nvram; +} + +typedef struct _hw_nvram_reg_spec { + unsigned32 base; + unsigned32 size; +} hw_nvram_reg_spec; + +static void +hw_nvram_init_address(device *me) +{ + hw_nvram_device *nvram = (hw_nvram_device*)device_data(me); + + /* use the generic init code to attach this device to its parent bus */ + generic_device_init_address(me); + + /* find the first non zero reg property and use that as the device + size */ + if (nvram->sizeof_memory == 0) { + reg_property_spec reg; + int reg_nr; + for (reg_nr = 0; + device_find_reg_array_property(me, "reg", reg_nr, ®); + reg_nr++) { + unsigned attach_size; + if (device_size_to_attach_size(device_parent(me), + ®.size, &attach_size, + me)) { + nvram->sizeof_memory = attach_size; + break; + } + } + if (nvram->sizeof_memory == 0) + device_error(me, "reg property must contain a non-zero phys-addr:size tupple"); + if (nvram->sizeof_memory < 8) + device_error(me, "NVRAM must be at least 8 bytes in size"); + } + + /* initialize the hw_nvram */ + if (nvram->memory == NULL) { + nvram->memory = zalloc(nvram->sizeof_memory); + } + else + memset(nvram->memory, nvram->sizeof_memory, 0); + + if (device_find_property(me, "timezone") == NULL) + nvram->timezone = 0; + else + nvram->timezone = device_find_integer_property(me, "timezone"); + + nvram->addr_year = nvram->sizeof_memory - 1; + nvram->addr_month = nvram->sizeof_memory - 2; + nvram->addr_date = nvram->sizeof_memory - 3; + nvram->addr_day = nvram->sizeof_memory - 4; + nvram->addr_hour = nvram->sizeof_memory - 5; + nvram->addr_minutes = nvram->sizeof_memory - 6; + nvram->addr_seconds = nvram->sizeof_memory - 7; + nvram->addr_control = nvram->sizeof_memory - 8; + +} + +static int +hw_nvram_bcd(int val) +{ + val = val % 100; + if (val < 0) + val += 100; + return ((val / 10) << 4) + (val % 10); +} + + +/* If reached an update interval and allowed, update the clock within + the hw_nvram. While this function could be implemented using events + it isn't on the assumption that the HW_NVRAM will hardly ever be + referenced and hence there is little need in keeping the clock + continually up-to-date */ + +static void +hw_nvram_update_clock(hw_nvram_device *nvram, + cpu *processor) +{ +#ifdef HAVE_TIME_H + if (!(nvram->memory[nvram->addr_control] & 0xc0)) { + time_t host_time = time(NULL); + if (nvram->host_time != host_time) { + time_t nvtime = host_time + nvram->timezone; + struct tm *clock = gmtime(&nvtime); + nvram->host_time = host_time; + nvram->memory[nvram->addr_year] = hw_nvram_bcd(clock->tm_year); + nvram->memory[nvram->addr_month] = hw_nvram_bcd(clock->tm_mon + 1); + nvram->memory[nvram->addr_date] = hw_nvram_bcd(clock->tm_mday); + nvram->memory[nvram->addr_day] = hw_nvram_bcd(clock->tm_wday + 1); + nvram->memory[nvram->addr_hour] = hw_nvram_bcd(clock->tm_hour); + nvram->memory[nvram->addr_minutes] = hw_nvram_bcd(clock->tm_min); + nvram->memory[nvram->addr_seconds] = hw_nvram_bcd(clock->tm_sec); + } + } +#else + error("fixme - where do I find out GMT\n"); +#endif +} + +static void +hw_nvram_set_clock(hw_nvram_device *nvram, cpu *processor) +{ + error ("fixme - how do I set the localtime\n"); +} + +static unsigned +hw_nvram_io_read_buffer(device *me, + void *dest, + int space, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + int i; + hw_nvram_device *nvram = (hw_nvram_device*)device_data(me); + for (i = 0; i < nr_bytes; i++) { + unsigned address = (addr + i) % nvram->sizeof_memory; + unsigned8 data = nvram->memory[address]; + hw_nvram_update_clock(nvram, processor); + ((unsigned8*)dest)[i] = data; + } + return nr_bytes; +} + +static unsigned +hw_nvram_io_write_buffer(device *me, + const void *source, + int space, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + int i; + hw_nvram_device *nvram = (hw_nvram_device*)device_data(me); + for (i = 0; i < nr_bytes; i++) { + unsigned address = (addr + i) % nvram->sizeof_memory; + unsigned8 data = ((unsigned8*)source)[i]; + if (address == nvram->addr_control + && (data & 0x80) == 0 + && (nvram->memory[address] & 0x80) == 0x80) + hw_nvram_set_clock(nvram, processor); + else + hw_nvram_update_clock(nvram, processor); + nvram->memory[address] = data; + } + return nr_bytes; +} + +static device_callbacks const hw_nvram_callbacks = { + { hw_nvram_init_address, }, + { NULL, }, /* address */ + { hw_nvram_io_read_buffer, hw_nvram_io_write_buffer }, /* IO */ +}; + +const device_descriptor hw_nvram_device_descriptor[] = { + { "nvram", hw_nvram_create, &hw_nvram_callbacks }, + { NULL }, +}; + +#endif /* _HW_NVRAM_C_ */ diff --git a/sim/ppc/hw_opic.c b/sim/ppc/hw_opic.c new file mode 100644 index 00000000000..c314347e5a5 --- /dev/null +++ b/sim/ppc/hw_opic.c @@ -0,0 +1,1827 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1996, 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 _HW_OPIC_C_ +#define _HW_OPIC_C_ + +#include "device_table.h" + +#ifdef HAVE_STRING_H +#include <string.h> +#else +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif +#endif + + +/* DEVICE + + + opic - Open Programmable Interrupt Controller (OpenPIC) + + + DESCRIPTION + + + This device implements the core of the OpenPIC interrupt controller + as described in the OpenPIC specification 1.2 and other related + documents. + + The model includes: + + o Up to 2048 external interrupt sources + + o The four count down timers + + o The four interprocessor multicast interrupts + + o multiprocessor support + + o Full tracing to assist help debugging + + o Support for all variations of edge/level x high/low polarity. + + + + PROPERTIES + + + reg = <address> <size> ... (required) + + Determine where the device lives in the parents address space. The + first <<address>> <<size>> pair specifies the address of the + interrupt destination unit (which might contain an interrupt source + unit) while successive reg entries specify additional interrupt + source units. + + Note that for an <<opic>> device attached to a <<pci>> bus, the + first <<reg>> entry may need to be ignored it will be the address + of the devices configuration registers. + + + interrupt-ranges = <int-number> <range> ... (required) + + A list of pairs. Each pair corresponds to a block of interrupt + source units (the address of which being specified by the + corresponding reg tupple). <<int-number>> is the number of the + first interrupt in the block while <<range>> is the number of + interrupts in the block. + + + timer-frequency = <integer> (optional) + + If present, specifies the default value of the timer frequency + reporting register. By default a value of 1 HZ is used. The value + is arbitrary, the timers are always updated once per machine cycle. + + + vendor-identification = <integer> (optional) + + If present, specifies the value to be returned when the vendor + identification register is read. + + + EXAMPLES + + + See the test suite directory: + + | psim-test/hw-opic + + + BUGS + + For an OPIC controller attached to a PCI bus, it is not clear what + the value of the <<reg>> and <<interrupt-ranges>> properties should + be. In particular, the PCI firmware bindings require the first + value of the <<reg>> property to specify the devices configuration + address while the OpenPIC bindings require that same entry to + specify the address of the Interrupt Delivery Unit. This + implementation checks for and, if present, ignores any + configuration address (and its corresponding <<interrupt-ranges>> + entry). + + The OpenPIC specification requires the controller to be fair when + distributing interrupts between processors. At present the + algorithm used isn't fair. It is biased towards processor zero. + + The OpenPIC specification includes a 8259 pass through mode. This + is not supported. + + + REFERENCES + + + PowerPC Multiprocessor Interrupt Controller (MPIC), January 19, + 1996. Available from IBM. + + + The Open Programmable Interrupt Controller (PIC) Register Interface + Specification Revision 1.2. Issue Date: Opctober 1995. Available + somewhere on AMD's web page (http://www.amd.com/) + + + PowerPC Microprocessor Common Hardware Reference Platform (CHRP) + System bindings to: IEEE Std 1275-1994 Standard for Boot + (Initialization, Configuration) Firmware. Revision 1.2b (INTERIM + DRAFT). April 22, 1996. Available on the Open Firmware web site + http://playground.sun.com/p1275/. + + + */ + + +/* forward types */ + +typedef struct _hw_opic_device hw_opic_device; + + +/* bounds */ + +enum { + max_nr_interrupt_sources = 2048, + max_nr_interrupt_destinations = 32, + max_nr_task_priorities = 16, +}; + + +enum { + opic_alignment = 16, +}; + + +/* global configuration register */ + +enum { + gcr0_8259_bit = 0x20000000, + gcr0_reset_bit = 0x80000000, +}; + + +/* offsets and sizes */ + +enum { + idu_isu_base = 0x10000, + sizeof_isu_register_block = 32, + idu_per_processor_register_base = 0x20000, + sizeof_idu_per_processor_register_block = 0x1000, + idu_timer_base = 0x01100, + sizeof_timer_register_block = 0x00040, +}; + + +/* Interrupt sources */ + +enum { + isu_mask_bit = 0x80000000, + isu_active_bit = 0x40000000, + isu_multicast_bit = 0x20000000, + isu_positive_polarity_bit = 0x00800000, + isu_level_triggered_bit = 0x00400000, + isu_priority_shift = 16, + isu_vector_bits = 0x000000ff, +}; + + +typedef struct _opic_interrupt_source { + unsigned is_masked; /* left in place */ + unsigned is_multicast; /* left in place */ + unsigned is_positive_polarity; /* left in place */ + unsigned is_level_triggered; /* left in place */ + unsigned priority; + unsigned vector; + /* misc */ + int nr; + unsigned destination; + unsigned pending; + unsigned in_service; +} opic_interrupt_source; + + +/* interrupt destinations (normally processors) */ + +typedef struct _opic_interrupt_destination { + int nr; + unsigned base_priority; + opic_interrupt_source *current_pending; + opic_interrupt_source *current_in_service; + unsigned bit; + int init_port; + int intr_port; +} opic_interrupt_destination; + + +/* address map descriptors */ + +typedef struct _opic_isu_block { /* interrupt source unit block */ + int space; + unsigned_word address; + unsigned size; + unsigned_cell int_number; + unsigned_cell range; + int reg; +} opic_isu_block; + + +typedef struct _opic_idu { /* interrupt delivery unit */ + int reg; + int space; + unsigned_word address; + unsigned size; +} opic_idu; + +typedef enum { + /* bad */ + invalid_opic_register, + /* interrupt source */ + interrupt_source_N_destination_register, + interrupt_source_N_vector_priority_register, + /* timers */ + timer_N_destination_register, + timer_N_vector_priority_register, + timer_N_base_count_register, + timer_N_current_count_register, + timer_frequency_reporting_register, + /* inter-processor interrupts */ + ipi_N_vector_priority_register, + ipi_N_dispatch_register, + /* global configuration */ + spurious_vector_register, + processor_init_register, + vendor_identification_register, + global_configuration_register_N, + feature_reporting_register_N, + /* per processor */ + end_of_interrupt_register_N, + interrupt_acknowledge_register_N, + current_task_priority_register_N, +} opic_register; + +static const char * +opic_register_name(opic_register type) +{ + switch (type) { + case invalid_opic_register: return "invalid_opic_register"; + case interrupt_source_N_destination_register: return "interrupt_source_N_destination_register"; + case interrupt_source_N_vector_priority_register: return "interrupt_source_N_vector_priority_register"; + case timer_N_destination_register: return "timer_N_destination_register"; + case timer_N_vector_priority_register: return "timer_N_vector_priority_register"; + case timer_N_base_count_register: return "timer_N_base_count_register"; + case timer_N_current_count_register: return "timer_N_current_count_register"; + case timer_frequency_reporting_register: return "timer_frequency_reporting_register"; + case ipi_N_vector_priority_register: return "ipi_N_vector_priority_register"; + case ipi_N_dispatch_register: return "ipi_N_dispatch_register"; + case spurious_vector_register: return "spurious_vector_register"; + case processor_init_register: return "processor_init_register"; + case vendor_identification_register: return "vendor_identification_register"; + case global_configuration_register_N: return "global_configuration_register_N"; + case feature_reporting_register_N: return "feature_reporting_register_N"; + case end_of_interrupt_register_N: return "end_of_interrupt_register_N"; + case interrupt_acknowledge_register_N: return "interrupt_acknowledge_register_N"; + case current_task_priority_register_N: return "current_task_priority_register_N"; + } + return NULL; +} + + + +/* timers */ + +typedef struct _opic_timer { + int nr; + device *me; /* find my way home */ + hw_opic_device *opic; /* ditto */ + unsigned base_count; + int inhibited; + signed64 count; /* *ONLY* if inhibited */ + event_entry_tag timeout_event; + opic_interrupt_source *interrupt_source; +} opic_timer; + + +/* the OPIC */ + +struct _hw_opic_device { + + /* vendor id */ + unsigned vendor_identification; + + /* interrupt destinations - processors */ + int nr_interrupt_destinations; + opic_interrupt_destination *interrupt_destination; + unsigned sizeof_interrupt_destination; + + /* bogus interrupts */ + int spurious_vector; + + /* interrupt sources - external interrupt source units + extra internal ones */ + int nr_interrupt_sources; + opic_interrupt_source *interrupt_source; + unsigned sizeof_interrupt_source; + + /* external interrupts */ + int nr_external_interrupts; + opic_interrupt_source *external_interrupt_source; + + /* inter-processor-interrupts */ + int nr_interprocessor_interrupts; + opic_interrupt_source *interprocessor_interrupt_source; + + /* timers */ + int nr_timer_interrupts; + opic_timer *timer; + unsigned sizeof_timer; + opic_interrupt_source *timer_interrupt_source; + unsigned timer_frequency; + + /* init register */ + unsigned32 init; + + /* address maps */ + opic_idu idu; + int nr_isu_blocks; + opic_isu_block *isu_block; +}; + + +static void +hw_opic_init_data(device *me) +{ + hw_opic_device *opic = (hw_opic_device*)device_data(me); + int isb; + int idu_reg; + int nr_isu_blocks; + int i; + + /* determine the first valid reg property entry (there could be + leading reg entries with invalid (zero) size fields) and the + number of isu entries found in the reg property. */ + idu_reg = 0; + nr_isu_blocks = 0; + while (1) { + reg_property_spec unit; + int attach_space; + unsigned_word attach_address; + unsigned attach_size; + if (!device_find_reg_array_property(me, "reg", idu_reg + nr_isu_blocks, + &unit)) + break; + if (nr_isu_blocks > 0 + || (device_address_to_attach_address(device_parent(me), &unit.address, + &attach_space, &attach_address, + me) + && device_size_to_attach_size(device_parent(me), &unit.size, + &attach_size, + me))) { + /* we count any thing once we've found one valid address/size pair */ + nr_isu_blocks += 1; + } + else { + idu_reg += 1; + } + } + + /* determine the number and location of the multiple interrupt + source units and the single interrupt delivery unit */ + if (opic->isu_block == NULL) { + int reg_nr; + opic->nr_isu_blocks = nr_isu_blocks; + opic->isu_block = zalloc(sizeof(opic_isu_block) * opic->nr_isu_blocks); + isb = 0; + reg_nr = idu_reg; + while (isb < opic->nr_isu_blocks) { + reg_property_spec reg; + if (!device_find_reg_array_property(me, "reg", reg_nr, ®)) + device_error(me, "reg property missing entry number %d", reg_nr); + opic->isu_block[isb].reg = reg_nr; + if (!device_address_to_attach_address(device_parent(me), ®.address, + &opic->isu_block[isb].space, + &opic->isu_block[isb].address, + me) + || !device_size_to_attach_size(device_parent(me), ®.size, + &opic->isu_block[isb].size, + me)) { + device_error(me, "reg property entry %d invalid", reg_nr); + } + if (!device_find_integer_array_property(me, "interrupt-ranges", + reg_nr * 2, + &opic->isu_block[isb].int_number) + || !device_find_integer_array_property(me, "interrupt-ranges", + reg_nr * 2 + 1, + &opic->isu_block[isb].range)) + device_error(me, "missing or invalid interrupt-ranges property entry %d", reg_nr); + /* first reg entry specifies the address of both the IDU and the + first set of ISU registers, adjust things accordingly */ + if (reg_nr == idu_reg) { + opic->idu.reg = opic->isu_block[isb].reg; + opic->idu.space = opic->isu_block[isb].space; + opic->idu.address = opic->isu_block[isb].address; + opic->idu.size = opic->isu_block[isb].size; + opic->isu_block[isb].address += idu_isu_base; + opic->isu_block[isb].size = opic->isu_block[isb].range * (16 + 16); + } + /* was this a valid reg entry? */ + if (opic->isu_block[isb].range == 0) { + opic->nr_isu_blocks -= 1; + } + else { + opic->nr_external_interrupts += opic->isu_block[isb].range; + isb++; + } + reg_nr++; + } + } + DTRACE(opic, ("interrupt source unit block - effective number of blocks %d\n", + (int)opic->nr_isu_blocks)); + + + /* the number of other interrupts */ + opic->nr_interprocessor_interrupts = 4; + opic->nr_timer_interrupts = 4; + + + /* create space for the interrupt source registers */ + if (opic->interrupt_source != NULL) { + memset(opic->interrupt_source, 0, opic->sizeof_interrupt_source); + } + else { + opic->nr_interrupt_sources = (opic->nr_external_interrupts + + opic->nr_interprocessor_interrupts + + opic->nr_timer_interrupts); + if (opic->nr_interrupt_sources > max_nr_interrupt_sources) + device_error(me, "number of interrupt sources exceeded"); + opic->sizeof_interrupt_source = (sizeof(opic_interrupt_source) + * opic->nr_interrupt_sources); + opic->interrupt_source = zalloc(opic->sizeof_interrupt_source); + opic->external_interrupt_source = opic->interrupt_source; + opic->interprocessor_interrupt_source = (opic->external_interrupt_source + + opic->nr_external_interrupts); + opic->timer_interrupt_source = (opic->interprocessor_interrupt_source + + opic->nr_interprocessor_interrupts); + } + for (i = 0; i < opic->nr_interrupt_sources; i++) { + opic_interrupt_source *source = &opic->interrupt_source[i]; + source->nr = i; + source->is_masked = isu_mask_bit; + } + DTRACE(opic, ("interrupt sources - external %d, timer %d, ipi %d, total %d\n", + opic->nr_external_interrupts, + opic->nr_timer_interrupts, + opic->nr_interprocessor_interrupts, + opic->nr_interrupt_sources)); + + + /* timers or interprocessor interrupts */ + if (opic->timer != NULL) + memset(opic->timer, 0, opic->sizeof_timer); + else { + opic->nr_timer_interrupts = 4; + opic->sizeof_timer = sizeof(opic_timer) * opic->nr_timer_interrupts; + opic->timer = zalloc(opic->sizeof_timer); + } + for (i = 0; i < opic->nr_timer_interrupts; i++) { + opic_timer *timer = &opic->timer[i]; + timer->nr = i; + timer->me = me; + timer->opic = opic; + timer->inhibited = 1; + timer->interrupt_source = &opic->timer_interrupt_source[i]; + } + if (device_find_property(me, "timer-frequency")) + opic->timer_frequency = device_find_integer_property(me, "timer-frequency"); + else + opic->timer_frequency = 1; + + + /* create space for the interrupt destination registers */ + if (opic->interrupt_destination != NULL) { + memset(opic->interrupt_destination, 0, opic->sizeof_interrupt_destination); + } + else { + opic->nr_interrupt_destinations = tree_find_integer_property(me, "/openprom/options/smp"); + opic->sizeof_interrupt_destination = (sizeof(opic_interrupt_destination) + * opic->nr_interrupt_destinations); + opic->interrupt_destination = zalloc(opic->sizeof_interrupt_destination); + if (opic->nr_interrupt_destinations > max_nr_interrupt_destinations) + device_error(me, "number of interrupt destinations exceeded"); + } + for (i = 0; i < opic->nr_interrupt_destinations; i++) { + opic_interrupt_destination *dest = &opic->interrupt_destination[i]; + dest->bit = (1 << i); + dest->nr = i; + dest->init_port = (device_interrupt_decode(me, "init0", output_port) + + i); + dest->intr_port = (device_interrupt_decode(me, "intr0", output_port) + + i); + dest->base_priority = max_nr_task_priorities - 1; + } + DTRACE(opic, ("interrupt destinations - total %d\n", + (int)opic->nr_interrupt_destinations)); + + + /* verify and print out the ISU's */ + for (isb = 0; isb < opic->nr_isu_blocks; isb++) { + unsigned correct_size; + if ((opic->isu_block[isb].address % opic_alignment) != 0) + device_error(me, "interrupt source unit %d address not aligned to %d byte boundary", + isb, opic_alignment); + correct_size = opic->isu_block[isb].range * sizeof_isu_register_block; + if (opic->isu_block[isb].size != correct_size) + device_error(me, "interrupt source unit %d (reg %d) has an incorrect size, should be 0x%x", + isb, opic->isu_block[isb].reg, correct_size); + DTRACE(opic, ("interrupt source unit block %ld - address %d:0x%lx, size 0x%lx, int-number %ld, range %ld\n", + (long)isb, + (int)opic->isu_block[isb].space, + (unsigned long)opic->isu_block[isb].address, + (unsigned long)opic->isu_block[isb].size, + (long)opic->isu_block[isb].int_number, + (long)opic->isu_block[isb].range)); + } + + + /* verify and print out the IDU */ + { + unsigned correct_size; + unsigned alternate_size; + if ((opic->idu.address % opic_alignment) != 0) + device_error(me, "interrupt delivery unit not aligned to %d byte boundary", + opic_alignment); + correct_size = (idu_per_processor_register_base + + (sizeof_idu_per_processor_register_block + * opic->nr_interrupt_destinations)); + alternate_size = (idu_per_processor_register_base + + (sizeof_idu_per_processor_register_block + * max_nr_interrupt_destinations)); + if (opic->idu.size != correct_size + && opic->idu.size != alternate_size) + device_error(me, "interrupt delivery unit has incorrect size, should be 0x%x or 0x%x", + correct_size, alternate_size); + DTRACE(opic, ("interrupt delivery unit - address %d:0x%lx, size 0x%lx\n", + (int)opic->idu.space, + (unsigned long)opic->idu.address, + (unsigned long)opic->idu.size)); + } + + /* initialize the init interrupts */ + opic->init = 0; + + + /* vendor ident */ + if (device_find_property(me, "vendor-identification") != NULL) + opic->vendor_identification = device_find_integer_property(me, "vendor-identification"); + else + opic->vendor_identification = 0; + + /* misc registers */ + opic->spurious_vector = 0xff; + +} + + +/* interrupt related actions */ + +static void +assert_interrupt(device *me, + hw_opic_device *opic, + opic_interrupt_destination *dest) +{ + ASSERT(dest >= opic->interrupt_destination); + ASSERT(dest < opic->interrupt_destination + opic->nr_interrupt_destinations); + DTRACE(opic, ("assert interrupt - intr port %d\n", dest->intr_port)); + device_interrupt_event(me, dest->intr_port, 1, NULL, 0); +} + + +static void +negate_interrupt(device *me, + hw_opic_device *opic, + opic_interrupt_destination *dest) +{ + ASSERT(dest >= opic->interrupt_destination); + ASSERT(dest < opic->interrupt_destination + opic->nr_interrupt_destinations); + DTRACE(opic, ("negate interrupt - intr port %d\n", dest->intr_port)); + device_interrupt_event(me, dest->intr_port, 0, NULL, 0); +} + + +static int +can_deliver(device *me, + opic_interrupt_source *source, + opic_interrupt_destination *dest) +{ + return (source != NULL && dest != NULL + && source->priority > dest->base_priority + && (dest->current_in_service == NULL + || source->priority > dest->current_in_service->priority)); +} + + +static unsigned +deliver_pending(device *me, + hw_opic_device *opic, + opic_interrupt_destination *dest) +{ + ASSERT(can_deliver(me, dest->current_pending, dest)); + dest->current_in_service = dest->current_pending; + dest->current_in_service->in_service |= dest->bit; + if (!dest->current_pending->is_level_triggered) { + if (dest->current_pending->is_multicast) + dest->current_pending->pending &= ~dest->bit; + else + dest->current_pending->pending = 0; + } + dest->current_pending = NULL; + negate_interrupt(me, opic, dest); + return dest->current_in_service->vector; +} + + +typedef enum { + pending_interrupt, + in_service_interrupt, +} interrupt_class; + +static opic_interrupt_source * +find_interrupt_for_dest(device *me, + hw_opic_device *opic, + opic_interrupt_destination *dest, + interrupt_class class) +{ + int i; + opic_interrupt_source *pending = NULL; + for (i = 0; i < opic->nr_interrupt_sources; i++) { + opic_interrupt_source *src = &opic->interrupt_source[i]; + /* is this a potential hit? */ + switch (class) { + case in_service_interrupt: + if ((src->in_service & dest->bit) == 0) + continue; + break; + case pending_interrupt: + if ((src->pending & dest->bit) == 0) + continue; + break; + } + /* see if it is the highest priority */ + if (pending == NULL) + pending = src; + else if (src->priority > pending->priority) + pending = src; + } + return pending; +} + + +static opic_interrupt_destination * +find_lowest_dest(device *me, + hw_opic_device *opic, + opic_interrupt_source *src) +{ + int i; + opic_interrupt_destination *lowest = NULL; + for (i = 0; i < opic->nr_interrupt_destinations; i++) { + opic_interrupt_destination *dest = &opic->interrupt_destination[i]; + if (src->destination & dest->bit) { + if (dest->base_priority < src->priority) { + if (lowest == NULL) + lowest = dest; + else if (lowest->base_priority > dest->base_priority) + lowest = dest; + else if (lowest->current_in_service != NULL + && dest->current_in_service == NULL) + lowest = dest; /* not doing anything */ + else if (lowest->current_in_service != NULL + && dest->current_in_service != NULL + && (lowest->current_in_service->priority + > dest->current_in_service->priority)) + lowest = dest; /* less urgent */ + /* FIXME - need to be more fair */ + } + } + } + return lowest; +} + + +static void +handle_interrupt(device *me, + hw_opic_device *opic, + opic_interrupt_source *src, + int asserted) +{ + if (src->is_masked) { + DTRACE(opic, ("interrupt %d - ignore masked\n", src->nr)); + } + else if (src->is_multicast) { + /* always try to deliver multicast interrupts - just easier */ + int i; + ASSERT(!src->is_level_triggered); + ASSERT(src->is_positive_polarity); + ASSERT(asserted); + for (i = 0; i < opic->nr_interrupt_destinations; i++) { + opic_interrupt_destination *dest = &opic->interrupt_destination[i]; + if (src->destination & dest->bit) { + if (src->pending & dest->bit) { + DTRACE(opic, ("interrupt %d - multicast still pending to %d\n", + src->nr, dest->nr)); + } + else if (can_deliver(me, src, dest)) { + dest->current_pending = src; + src->pending |= dest->bit; + assert_interrupt(me, opic, dest); + DTRACE(opic, ("interrupt %d - multicast to %d\n", + src->nr, dest->nr)); + } + else { + src->pending |= dest->bit; + DTRACE(opic, ("interrupt %d - multicast pending to %d\n", + src->nr, dest->nr)); + } + } + } + } + else if (src->is_level_triggered + && src->is_positive_polarity + && !asserted) { + if (src->pending) + DTRACE(opic, ("interrupt %d - ignore withdrawn (active high)\n", + src->nr)); + else + DTRACE(opic, ("interrupt %d - ignore low level (active high)\n", + src->nr)); + ASSERT(!src->is_multicast); + src->pending = 0; + } + else if (src->is_level_triggered + && !src->is_positive_polarity + && asserted) { + if (src->pending) + DTRACE(opic, ("interrupt %d - ignore withdrawn (active low)\n", + src->nr)); + else + DTRACE(opic, ("interrupt %d - ignore high level (active low)\n", + src->nr)); + + ASSERT(!src->is_multicast); + src->pending = 0; + } + else if (!src->is_level_triggered + && src->is_positive_polarity + && !asserted) { + DTRACE(opic, ("interrupt %d - ignore falling edge (positive edge trigered)\n", + src->nr)); + } + else if (!src->is_level_triggered + && !src->is_positive_polarity + && asserted) { + DTRACE(opic, ("interrupt %d - ignore rising edge (negative edge trigered)\n", + src->nr)); + } + else if (src->in_service != 0) { + /* leave the interrupt where it is */ + ASSERT(!src->is_multicast); + ASSERT(src->pending == 0 || src->pending == src->in_service); + src->pending = src->in_service; + DTRACE(opic, ("interrupt %ld - ignore already in service to 0x%lx\n", + (long)src->nr, (long)src->in_service)); + } + else if (src->pending != 0) { + DTRACE(opic, ("interrupt %ld - ignore still pending to 0x%lx\n", + (long)src->nr, (long)src->pending)); + } + else { + /* delivery is needed */ + opic_interrupt_destination *dest = find_lowest_dest(me, opic, src); + if (can_deliver(me, src, dest)) { + dest->current_pending = src; + src->pending = dest->bit; + DTRACE(opic, ("interrupt %d - delivered to %d\n", src->nr, dest->nr)); + assert_interrupt(me, opic, dest); + } + else { + src->pending = src->destination; /* any can take this */ + DTRACE(opic, ("interrupt %ld - pending to 0x%lx\n", + (long)src->nr, (long)src->pending)); + } + } +} + +static unsigned +do_interrupt_acknowledge_register_N_read(device *me, + hw_opic_device *opic, + int dest_nr) +{ + opic_interrupt_destination *dest = &opic->interrupt_destination[dest_nr]; + unsigned vector; + + ASSERT(dest_nr >= 0 && dest_nr < opic->nr_interrupt_destinations); + ASSERT(dest_nr == dest->nr); + + /* try the current pending */ + if (can_deliver(me, dest->current_pending, dest)) { + ASSERT(dest->current_pending->pending & dest->bit); + vector = deliver_pending(me, opic, dest); + DTRACE(opic, ("interrupt ack %d - entering %d (pending) - vector %d (%d), priority %d\n", + dest->nr, + dest->current_in_service->nr, + dest->current_in_service->vector, vector, + dest->current_in_service->priority)); + } + else { + /* try for something else */ + dest->current_pending = find_interrupt_for_dest(me, opic, dest, pending_interrupt); + if (can_deliver(me, dest->current_pending, dest)) { + vector = deliver_pending(me, opic, dest); + DTRACE(opic, ("interrupt ack %d - entering %d (not pending) - vector %d (%d), priority %d\n", + dest->nr, + dest->current_in_service->nr, + dest->current_in_service->vector, vector, + dest->current_in_service->priority)); + } + else { + dest->current_pending = NULL; + vector = opic->spurious_vector; + DTRACE(opic, ("interrupt ack %d - spurious interrupt %d\n", + dest->nr, vector)); + } + } + return vector; +} + + +static void +do_end_of_interrupt_register_N_write(device *me, + hw_opic_device *opic, + int dest_nr, + unsigned reg) +{ + opic_interrupt_destination *dest = &opic->interrupt_destination[dest_nr]; + + ASSERT(dest_nr >= 0 && dest_nr < opic->nr_interrupt_destinations); + ASSERT(dest_nr == dest->nr); + + /* check the value written is zero */ + if (reg != 0) { + DTRACE(opic, ("eoi %d - ignoring nonzero value\n", dest->nr)); + } + + /* user doing wierd things? */ + if (dest->current_in_service == NULL) { + DTRACE(opic, ("eoi %d - strange, no current interrupt\n", dest->nr)); + return; + } + + /* an internal stuff up? */ + if (!(dest->current_in_service->in_service & dest->bit)) { + device_error(me, "eoi %d - current interrupt not in service", dest->nr); + } + + /* find what was probably the previous in service interrupt */ + dest->current_in_service->in_service &= ~dest->bit; + DTRACE(opic, ("eoi %d - ending %d - priority %d, vector %d\n", + dest->nr, + dest->current_in_service->nr, + dest->current_in_service->priority, + dest->current_in_service->vector)); + dest->current_in_service = find_interrupt_for_dest(me, opic, dest, in_service_interrupt); + if (dest->current_in_service != NULL) + DTRACE(opic, ("eoi %d - resuming %d - priority %d, vector %d\n", + dest->nr, + dest->current_in_service->nr, + dest->current_in_service->priority, + dest->current_in_service->vector)); + else + DTRACE(opic, ("eoi %d - resuming none\n", dest->nr)); + + /* check to see if that shouldn't be interrupted */ + dest->current_pending = find_interrupt_for_dest(me, opic, dest, pending_interrupt); + if (can_deliver(me, dest->current_pending, dest)) { + ASSERT(dest->current_pending->pending & dest->bit); + assert_interrupt(me, opic, dest); + } + else { + dest->current_pending = NULL; + } +} + + +static void +decode_opic_address(device *me, + hw_opic_device *opic, + int space, + unsigned_word address, + unsigned nr_bytes, + opic_register *type, + int *index) +{ + int isb = 0; + + /* is the size valid? */ + if (nr_bytes != 4) { + *type = invalid_opic_register; + *index = -1; + return; + } + + /* try for a per-processor register within the interrupt delivery + unit */ + if (space == opic->idu.space + && address >= (opic->idu.address + idu_per_processor_register_base) + && address < (opic->idu.address + idu_per_processor_register_base + + (sizeof_idu_per_processor_register_block + * opic->nr_interrupt_destinations))) { + unsigned_word block_offset = (address + - opic->idu.address + - idu_per_processor_register_base); + unsigned_word offset = block_offset % sizeof_idu_per_processor_register_block; + *index = block_offset / sizeof_idu_per_processor_register_block; + switch (offset) { + case 0x040: + *type = ipi_N_dispatch_register; + *index = 0; + break; + case 0x050: + *type = ipi_N_dispatch_register; + *index = 1; + break; + case 0x060: + *type = ipi_N_dispatch_register; + *index = 2; + break; + case 0x070: + *type = ipi_N_dispatch_register; + *index = 3; + break; + case 0x080: + *type = current_task_priority_register_N; + break; + case 0x0a0: + *type = interrupt_acknowledge_register_N; + break; + case 0x0b0: + *type = end_of_interrupt_register_N; + break; + default: + *type = invalid_opic_register; + break; + } + DTRACE(opic, ("per-processor register %d:0x%lx - %s[%d]\n", + space, (unsigned long)address, + opic_register_name(*type), + *index)); + return; + } + + /* try for an interrupt source unit */ + for (isb = 0; isb < opic->nr_isu_blocks; isb++) { + if (opic->isu_block[isb].space == space + && address >= opic->isu_block[isb].address + && address < (opic->isu_block[isb].address + opic->isu_block[isb].size)) { + unsigned_word block_offset = address - opic->isu_block[isb].address; + unsigned_word offset = block_offset % sizeof_isu_register_block; + *index = (opic->isu_block[isb].int_number + + (block_offset / sizeof_isu_register_block)); + switch (offset) { + case 0x00: + *type = interrupt_source_N_vector_priority_register; + break; + case 0x10: + *type = interrupt_source_N_destination_register; + break; + default: + *type = invalid_opic_register; + break; + } + DTRACE(opic, ("isu register %d:0x%lx - %s[%d]\n", + space, (unsigned long)address, + opic_register_name(*type), + *index)); + return; + } + } + + /* try for a timer */ + if (space == opic->idu.space + && address >= (opic->idu.address + idu_timer_base) + && address < (opic->idu.address + idu_timer_base + + opic->nr_timer_interrupts * sizeof_timer_register_block)) { + unsigned_word offset = address % sizeof_timer_register_block; + *index = ((address - opic->idu.address - idu_timer_base) + / sizeof_timer_register_block); + switch (offset) { + case 0x00: + *type = timer_N_current_count_register; + break; + case 0x10: + *type = timer_N_base_count_register; + break; + case 0x20: + *type = timer_N_vector_priority_register; + break; + case 0x30: + *type = timer_N_destination_register; + break; + default: + *type = invalid_opic_register; + break; + } + DTRACE(opic, ("timer register %d:0x%lx - %s[%d]\n", + space, (unsigned long)address, + opic_register_name(*type), + *index)); + return; + } + + /* finally some other misc global register */ + if (space == opic->idu.space + && address >= opic->idu.address + && address < opic->idu.address + opic->idu.size) { + unsigned_word block_offset = address - opic->idu.address; + switch (block_offset) { + case 0x010f0: + *type = timer_frequency_reporting_register; + *index = -1; + break; + case 0x010e0: + *type = spurious_vector_register; + *index = -1; + break; + case 0x010d0: + case 0x010c0: + case 0x010b0: + case 0x010a0: + *type = ipi_N_vector_priority_register; + *index = (block_offset - 0x010a0) / 16; + break; + case 0x01090: + *type = processor_init_register; + *index = -1; + break; + case 0x01080: + *type = vendor_identification_register; + *index = -1; + break; + case 0x01020: + *type = global_configuration_register_N; + *index = 0; + break; + case 0x01000: + *type = feature_reporting_register_N; + *index = 0; + break; + default: + *type = invalid_opic_register; + *index = -1; + break; + } + DTRACE(opic, ("global register %d:0x%lx - %s[%d]\n", + space, (unsigned long)address, + opic_register_name(*type), + *index)); + return; + } + + /* nothing matched */ + *type = invalid_opic_register; + DTRACE(opic, ("invalid register %d:0x%lx\n", + space, (unsigned long)address)); + return; +} + + +/* Processor init register: + + The bits in this register (one per processor) are directly wired to + output "init" interrupt ports. */ + +static unsigned +do_processor_init_register_read(device *me, + hw_opic_device *opic) +{ + unsigned reg = opic->init; + DTRACE(opic, ("processor init register - read 0x%lx\n", + (long)reg)); + return reg; +} + +static void +do_processor_init_register_write(device *me, + hw_opic_device *opic, + unsigned reg) +{ + int i; + for (i = 0; i < opic->nr_interrupt_destinations; i++) { + opic_interrupt_destination *dest = &opic->interrupt_destination[i]; + if ((reg & dest->bit) != (opic->init & dest->bit)) { + if (reg & dest->bit) { + DTRACE(opic, ("processor init register - write 0x%lx - asserting init%d\n", + (long)reg, i)); + opic->init |= dest->bit; + device_interrupt_event(me, dest->init_port, 1, NULL, 0); + } + else { + DTRACE(opic, ("processor init register - write 0x%lx - negating init%d\n", + (long)reg, i)); + opic->init &= ~dest->bit; + device_interrupt_event(me, dest->init_port, 0, NULL, 0); + } + } + } +} + + + +/* Interrupt Source Vector/Priority Register: */ + +static unsigned +read_vector_priority_register(device *me, + hw_opic_device *opic, + opic_interrupt_source *interrupt, + const char *reg_name, + int reg_index) +{ + unsigned reg; + reg = 0; + reg |= interrupt->is_masked; + reg |= (interrupt->in_service || interrupt->pending + ? isu_active_bit : 0); /* active */ + reg |= interrupt->is_multicast; + reg |= interrupt->is_positive_polarity; + reg |= interrupt->is_level_triggered; /* sense? */ + reg |= interrupt->priority << isu_priority_shift; + reg |= interrupt->vector; + DTRACE(opic, ("%s %d vector/priority register - read 0x%lx\n", + reg_name, reg_index, (unsigned long)reg)); + return reg; +} + +static unsigned +do_interrupt_source_N_vector_priority_register_read(device *me, + hw_opic_device *opic, + int index) +{ + unsigned reg; + ASSERT(index < opic->nr_external_interrupts); + reg = read_vector_priority_register(me, opic, + &opic->interrupt_source[index], + "interrupt source", index); + return reg; +} + +static void +write_vector_priority_register(device *me, + hw_opic_device *opic, + opic_interrupt_source *interrupt, + unsigned reg, + const char *reg_name, + int reg_index) +{ + interrupt->is_masked = (reg & isu_mask_bit); + interrupt->is_multicast = (reg & isu_multicast_bit); + interrupt->is_positive_polarity = (reg & isu_positive_polarity_bit); + interrupt->is_level_triggered = (reg & isu_level_triggered_bit); + interrupt->priority = ((reg >> isu_priority_shift) + % max_nr_task_priorities); + interrupt->vector = (reg & isu_vector_bits); + DTRACE(opic, ("%s %d vector/priority register - write 0x%lx - %s%s%s-polarity, %s-triggered, priority %ld vector %ld\n", + reg_name, + reg_index, + (unsigned long)reg, + interrupt->is_masked ? "masked, " : "", + interrupt->is_multicast ? "multicast, " : "", + interrupt->is_positive_polarity ? "positive" : "negative", + interrupt->is_level_triggered ? "level" : "edge", + (long)interrupt->priority, + (long)interrupt->vector)); +} + +static void +do_interrupt_source_N_vector_priority_register_write(device *me, + hw_opic_device *opic, + int index, + unsigned reg) +{ + ASSERT(index < opic->nr_external_interrupts); + reg &= ~isu_multicast_bit; /* disable multicast */ + write_vector_priority_register(me, opic, + &opic->interrupt_source[index], + reg, "interrupt source", index); +} + + + +/* Interrupt Source Destination Register: */ + +static unsigned +read_destination_register(device *me, + hw_opic_device *opic, + opic_interrupt_source *interrupt, + const char *reg_name, + int reg_index) +{ + unsigned long reg; + reg = interrupt->destination; + DTRACE(opic, ("%s %d destination register - read 0x%lx\n", + reg_name, reg_index, reg)); + return reg; +} + +static unsigned +do_interrupt_source_N_destination_register_read(device *me, + hw_opic_device *opic, + int index) +{ + unsigned reg; + ASSERT(index < opic->nr_external_interrupts); + reg = read_destination_register(me, opic, &opic->external_interrupt_source[index], + "interrupt source", index); + return reg; +} + +static void +write_destination_register(device *me, + hw_opic_device *opic, + opic_interrupt_source *interrupt, + unsigned reg, + const char *reg_name, + int reg_index) +{ + reg &= (1 << opic->nr_interrupt_destinations) - 1; /* mask out invalid */ + DTRACE(opic, ("%s %d destination register - write 0x%x\n", + reg_name, reg_index, reg)); + interrupt->destination = reg; +} + +static void +do_interrupt_source_N_destination_register_write(device *me, + hw_opic_device *opic, + int index, + unsigned reg) +{ + ASSERT(index < opic->nr_external_interrupts); + write_destination_register(me, opic, &opic->external_interrupt_source[index], + reg, "interrupt source", index); +} + + + +/* Spurious vector register: */ + +static unsigned +do_spurious_vector_register_read(device *me, + hw_opic_device *opic) +{ + unsigned long reg = opic->spurious_vector; + DTRACE(opic, ("spurious vector register - read 0x%lx\n", reg)); + return reg; +} + +static void +do_spurious_vector_register_write(device *me, + hw_opic_device *opic, + unsigned reg) +{ + reg &= 0xff; /* mask off invalid */ + DTRACE(opic, ("spurious vector register - write 0x%x\n", reg)); + opic->spurious_vector = reg; +} + + + +/* current task priority register: */ + +static unsigned +do_current_task_priority_register_N_read(device *me, + hw_opic_device *opic, + int index) +{ + opic_interrupt_destination *interrupt_destination = &opic->interrupt_destination[index]; + unsigned reg; + ASSERT(index >= 0 && index < opic->nr_interrupt_destinations); + reg = interrupt_destination->base_priority; + DTRACE(opic, ("current task priority register %d - read 0x%x\n", index, reg)); + return reg; +} + +static void +do_current_task_priority_register_N_write(device *me, + hw_opic_device *opic, + int index, + unsigned reg) +{ + opic_interrupt_destination *interrupt_destination = &opic->interrupt_destination[index]; + ASSERT(index >= 0 && index < opic->nr_interrupt_destinations); + reg %= max_nr_task_priorities; + DTRACE(opic, ("current task priority register %d - write 0x%x\n", index, reg)); + interrupt_destination->base_priority = reg; +} + + + +/* Timer Frequency Reporting Register: */ + +static unsigned +do_timer_frequency_reporting_register_read(device *me, + hw_opic_device *opic) +{ + unsigned reg; + reg = opic->timer_frequency; + DTRACE(opic, ("timer frequency reporting register - read 0x%x\n", reg)); + return reg; +} + +static void +do_timer_frequency_reporting_register_write(device *me, + hw_opic_device *opic, + unsigned reg) +{ + DTRACE(opic, ("timer frequency reporting register - write 0x%x\n", reg)); + opic->timer_frequency = reg; +} + + +/* timer registers: */ + +static unsigned +do_timer_N_current_count_register_read(device *me, + hw_opic_device *opic, + int index) +{ + opic_timer *timer = &opic->timer[index]; + unsigned reg; + ASSERT(index >= 0 && index < opic->nr_timer_interrupts); + if (timer->inhibited) + reg = timer->count; /* stalled value */ + else + reg = timer->count - device_event_queue_time(me); /* time remaining */ + DTRACE(opic, ("timer %d current count register - read 0x%x\n", index, reg)); + return reg; +} + + +static unsigned +do_timer_N_base_count_register_read(device *me, + hw_opic_device *opic, + int index) +{ + opic_timer *timer = &opic->timer[index]; + unsigned reg; + ASSERT(index >= 0 && index < opic->nr_timer_interrupts); + reg = timer->base_count; + DTRACE(opic, ("timer %d base count register - read 0x%x\n", index, reg)); + return reg; +} + + +static void +timer_event(void *data) +{ + opic_timer *timer = data; + device *me = timer->me; + if (timer->inhibited) + device_error(timer->me, "internal-error - timer event occured when timer %d inhibited", + timer->nr); + handle_interrupt(timer->me, timer->opic, timer->interrupt_source, 1); + timer->timeout_event = device_event_queue_schedule(me, timer->base_count, + timer_event, timer); + DTRACE(opic, ("timer %d - interrupt at %ld, next at %d\n", + timer->nr, (long)device_event_queue_time(me), timer->base_count)); +} + + +static void +do_timer_N_base_count_register_write(device *me, + hw_opic_device *opic, + int index, + unsigned reg) +{ + opic_timer *timer = &opic->timer[index]; + int inhibit; + ASSERT(index >= 0 && index < opic->nr_timer_interrupts); + inhibit = reg & 0x80000000; + if (timer->inhibited && !inhibit) { + timer->inhibited = 0; + if (timer->timeout_event != NULL) + device_event_queue_deschedule(me, timer->timeout_event); + timer->count = device_event_queue_time(me) + reg; + timer->base_count = reg; + timer->timeout_event = device_event_queue_schedule(me, timer->base_count, + timer_event, (void*)timer); + DTRACE(opic, ("timer %d base count register - write 0x%x - timer started\n", + index, reg)); + } + else if (!timer->inhibited && inhibit) { + if (timer->timeout_event != NULL) + device_event_queue_deschedule(me, timer->timeout_event); + timer->count = timer->count - device_event_queue_time(me); + timer->inhibited = 1; + timer->base_count = reg; + DTRACE(opic, ("timer %d base count register - write 0x%x - timer stopped\n", + index, reg)); + } + else { + ASSERT((timer->inhibited && inhibit) || (!timer->inhibited && !inhibit)); + DTRACE(opic, ("timer %d base count register - write 0x%x\n", index, reg)); + timer->base_count = reg; + } +} + + +static unsigned +do_timer_N_vector_priority_register_read(device *me, + hw_opic_device *opic, + int index) +{ + unsigned reg; + ASSERT(index >= 0 && index < opic->nr_timer_interrupts); + reg = read_vector_priority_register(me, opic, + &opic->timer_interrupt_source[index], + "timer", index); + return reg; +} + +static void +do_timer_N_vector_priority_register_write(device *me, + hw_opic_device *opic, + int index, + unsigned reg) +{ + ASSERT(index >= 0 && index < opic->nr_timer_interrupts); + reg &= ~isu_level_triggered_bit; /* force edge trigger */ + reg |= isu_positive_polarity_bit; /* force rising (positive) edge */ + reg |= isu_multicast_bit; /* force multicast */ + write_vector_priority_register(me, opic, + &opic->timer_interrupt_source[index], + reg, "timer", index); +} + + +static unsigned +do_timer_N_destination_register_read(device *me, + hw_opic_device *opic, + int index) +{ + unsigned reg; + ASSERT(index >= 0 && index < opic->nr_timer_interrupts); + reg = read_destination_register(me, opic, &opic->timer_interrupt_source[index], + "timer", index); + return reg; +} + +static void +do_timer_N_destination_register_write(device *me, + hw_opic_device *opic, + int index, + unsigned reg) +{ + ASSERT(index >= 0 && index < opic->nr_timer_interrupts); + write_destination_register(me, opic, &opic->timer_interrupt_source[index], + reg, "timer", index); +} + + +/* IPI registers */ + +static unsigned +do_ipi_N_vector_priority_register_read(device *me, + hw_opic_device *opic, + int index) +{ + unsigned reg; + ASSERT(index >= 0 && index < opic->nr_interprocessor_interrupts); + reg = read_vector_priority_register(me, opic, + &opic->interprocessor_interrupt_source[index], + "ipi", index); + return reg; +} + +static void +do_ipi_N_vector_priority_register_write(device *me, + hw_opic_device *opic, + int index, + unsigned reg) +{ + ASSERT(index >= 0 && index < opic->nr_interprocessor_interrupts); + reg &= ~isu_level_triggered_bit; /* force edge trigger */ + reg |= isu_positive_polarity_bit; /* force rising (positive) edge */ + reg |= isu_multicast_bit; /* force a multicast source */ + write_vector_priority_register(me, opic, + &opic->interprocessor_interrupt_source[index], + reg, "ipi", index); +} + +static void +do_ipi_N_dispatch_register_write(device *me, + hw_opic_device *opic, + int index, + unsigned reg) +{ + opic_interrupt_source *source = &opic->interprocessor_interrupt_source[index]; + ASSERT(index >= 0 && index < opic->nr_interprocessor_interrupts); + DTRACE(opic, ("ipi %d interrupt dispatch register - write 0x%x\n", index, reg)); + source->destination = reg; + handle_interrupt(me, opic, source, 1); +} + + +/* vendor and other global registers */ + +static unsigned +do_vendor_identification_register_read(device *me, + hw_opic_device *opic) +{ + unsigned reg; + reg = opic->vendor_identification; + DTRACE(opic, ("vendor identification register - read 0x%x\n", reg)); + return reg; +} + +static unsigned +do_feature_reporting_register_N_read(device *me, + hw_opic_device *opic, + int index) +{ + unsigned reg = 0; + ASSERT(index == 0); + switch (index) { + case 0: + reg |= (opic->nr_external_interrupts << 16); + reg |= (opic->nr_interrupt_destinations << 8); + reg |= (2/*version 1.2*/); + break; + } + DTRACE(opic, ("feature reporting register %d - read 0x%x\n", index, reg)); + return reg; +} + +static unsigned +do_global_configuration_register_N_read(device *me, + hw_opic_device *opic, + int index) +{ + unsigned reg = 0; + ASSERT(index == 0); + switch (index) { + case 0: + reg |= gcr0_8259_bit; /* hardwire 8259 disabled */ + break; + } + DTRACE(opic, ("global configuration register %d - read 0x%x\n", index, reg)); + return reg; +} + +static void +do_global_configuration_register_N_write(device *me, + hw_opic_device *opic, + int index, + unsigned reg) +{ + ASSERT(index == 0); + if (reg & gcr0_reset_bit) { + DTRACE(opic, ("global configuration register %d - write 0x%x - reseting opic\n", index, reg)); + hw_opic_init_data(me); + } + if (!(reg & gcr0_8259_bit)) { + DTRACE(opic, ("global configuration register %d - write 0x%x - ignoring 8259 enable\n", index, reg)); + } +} + + + +/* register read-write */ + +static unsigned +hw_opic_io_read_buffer(device *me, + void *dest, + int space, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + hw_opic_device *opic = (hw_opic_device*)device_data(me); + opic_register type; + int index; + decode_opic_address(me, opic, space, addr, nr_bytes, &type, &index); + if (type == invalid_opic_register) { + device_error(me, "invalid opic read access to %d:0x%lx (%d bytes)", + space, (unsigned long)addr, nr_bytes); + } + else { + unsigned reg; + switch (type) { + case processor_init_register: + reg = do_processor_init_register_read(me, opic); + break; + case interrupt_source_N_vector_priority_register: + reg = do_interrupt_source_N_vector_priority_register_read(me, opic, index); + break; + case interrupt_source_N_destination_register: + reg = do_interrupt_source_N_destination_register_read(me, opic, index); + break; + case interrupt_acknowledge_register_N: + reg = do_interrupt_acknowledge_register_N_read(me, opic, index); + break; + case spurious_vector_register: + reg = do_spurious_vector_register_read(me, opic); + break; + case current_task_priority_register_N: + reg = do_current_task_priority_register_N_read(me, opic, index); + break; + case timer_frequency_reporting_register: + reg = do_timer_frequency_reporting_register_read(me, opic); + break; + case timer_N_current_count_register: + reg = do_timer_N_current_count_register_read(me, opic, index); + break; + case timer_N_base_count_register: + reg = do_timer_N_base_count_register_read(me, opic, index); + break; + case timer_N_vector_priority_register: + reg = do_timer_N_vector_priority_register_read(me, opic, index); + break; + case timer_N_destination_register: + reg = do_timer_N_destination_register_read(me, opic, index); + break; + case ipi_N_vector_priority_register: + reg = do_ipi_N_vector_priority_register_read(me, opic, index); + break; + case feature_reporting_register_N: + reg = do_feature_reporting_register_N_read(me, opic, index); + break; + case global_configuration_register_N: + reg = do_global_configuration_register_N_read(me, opic, index); + break; + case vendor_identification_register: + reg = do_vendor_identification_register_read(me, opic); + break; + default: + reg = 0; + device_error(me, "unimplemented read of register %s[%d]", + opic_register_name(type), index); + } + *(unsigned_4*)dest = H2LE_4(reg); + } + return nr_bytes; +} + + +static unsigned +hw_opic_io_write_buffer(device *me, + const void *source, + int space, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + hw_opic_device *opic = (hw_opic_device*)device_data(me); + opic_register type; + int index; + decode_opic_address(me, opic, space, addr, nr_bytes, &type, &index); + if (type == invalid_opic_register) { + device_error(me, "invalid opic write access to %d:0x%lx (%d bytes)", + space, (unsigned long)addr, nr_bytes); + } + else { + unsigned reg = LE2H_4(*(unsigned_4*)source); + switch (type) { + case processor_init_register: + do_processor_init_register_write(me, opic, reg); + break; + case interrupt_source_N_vector_priority_register: + do_interrupt_source_N_vector_priority_register_write(me, opic, index, reg); + break; + case interrupt_source_N_destination_register: + do_interrupt_source_N_destination_register_write(me, opic, index, reg); + break; + case end_of_interrupt_register_N: + do_end_of_interrupt_register_N_write(me, opic, index, reg); + break; + case spurious_vector_register: + do_spurious_vector_register_write(me, opic, reg); + break; + case current_task_priority_register_N: + do_current_task_priority_register_N_write(me, opic, index, reg); + break; + case timer_frequency_reporting_register: + do_timer_frequency_reporting_register_write(me, opic, reg); + break; + case timer_N_base_count_register: + do_timer_N_base_count_register_write(me, opic, index, reg); + break; + case timer_N_vector_priority_register: + do_timer_N_vector_priority_register_write(me, opic, index, reg); + break; + case timer_N_destination_register: + do_timer_N_destination_register_write(me, opic, index, reg); + break; + case ipi_N_dispatch_register: + do_ipi_N_dispatch_register_write(me, opic, index, reg); + break; + case ipi_N_vector_priority_register: + do_ipi_N_vector_priority_register_write(me, opic, index, reg); + break; + case global_configuration_register_N: + do_global_configuration_register_N_write(me, opic, index, reg); + break; + default: + device_error(me, "unimplemented write to register %s[%d]", + opic_register_name(type), index); + } + } + return nr_bytes; +} + + +static void +hw_opic_interrupt_event(device *me, + int my_port, + device *source, + int source_port, + int level, + cpu *processor, + unsigned_word cia) +{ + hw_opic_device *opic = (hw_opic_device*)device_data(me); + + int isb; + int src_nr = 0; + + /* find the corresponding internal input port */ + for (isb = 0; isb < opic->nr_isu_blocks; isb++) { + if (my_port >= opic->isu_block[isb].int_number + && my_port < opic->isu_block[isb].int_number + opic->isu_block[isb].range) { + src_nr += my_port - opic->isu_block[isb].int_number; + break; + } + else + src_nr += opic->isu_block[isb].range; + } + if (isb == opic->nr_isu_blocks) + device_error(me, "interrupt %d out of range", my_port); + DTRACE(opic, ("external-interrupt %d, internal %d, level %d\n", + my_port, src_nr, level)); + + /* pass it on */ + ASSERT(src_nr >= 0 && src_nr < opic->nr_external_interrupts); + handle_interrupt(me, opic, &opic->external_interrupt_source[src_nr], level); +} + + +static const device_interrupt_port_descriptor hw_opic_interrupt_ports[] = { + { "irq", 0, max_nr_interrupt_sources, input_port, }, + { "intr", 0, max_nr_interrupt_destinations, output_port, }, + { "init", max_nr_interrupt_destinations, max_nr_interrupt_destinations, output_port, }, + { NULL } +}; + + +static device_callbacks const hw_opic_callbacks = { + { generic_device_init_address, + hw_opic_init_data }, + { NULL, }, /* address */ + { hw_opic_io_read_buffer, + hw_opic_io_write_buffer }, /* IO */ + { NULL, }, /* DMA */ + { hw_opic_interrupt_event, NULL, hw_opic_interrupt_ports }, /* interrupt */ + { NULL, }, /* unit */ + NULL, /* instance */ +}; + +static void * +hw_opic_create(const char *name, + const device_unit *unit_address, + const char *args) +{ + hw_opic_device *opic = ZALLOC(hw_opic_device); + return opic; +} + + + +const device_descriptor hw_opic_device_descriptor[] = { + { "opic", hw_opic_create, &hw_opic_callbacks }, + { NULL }, +}; + +#endif /* _HW_OPIC_C_ */ diff --git a/sim/ppc/hw_pal.c b/sim/ppc/hw_pal.c new file mode 100644 index 00000000000..a4256b94f3a --- /dev/null +++ b/sim/ppc/hw_pal.c @@ -0,0 +1,366 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1996, 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 _HW_PAL_C_ +#define _HW_PAL_C_ + +#ifndef STATIC_INLINE_HW_PAL +#define STATIC_INLINE_HW_PAL STATIC_INLINE +#endif + +#include "device_table.h" + +#include "cpu.h" + +#ifdef HAVE_STRING_H +#include <string.h> +#else +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif +#endif + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + + +/* DEVICE + + + pal - glue logic device containing assorted junk + + + DESCRIPTION + + + Typical hardware dependant hack. This device allows the firmware + to gain access to all the things the firmware needs (but the OS + doesn't). + + The pal contains the following registers. Except for the interrupt + level register, each of the below is 8 bytes in size and must be + accessed using correct alignment. For 16 and 32 bit accesses the + bytes not directed to the register are ignored: + + |0 reset register (write) + |4 processor id register (read) + |8 interrupt port (write) + |9 interrupt level (write) + |12 processor count register (read) + |16 tty input fifo register (read) + |20 tty input status register (read) + |24 tty output fifo register (write) + |28 tty output status register (read) + + Reset register (write) halts the simulator exiting with the + value written. + + Processor id register (read) returns the processor number (0 + .. N-1) of the processor performing the read. + + The interrupt registers should be accessed as a pair (using a 16 or + 32 bit store). The low byte specifies the interrupt port while the + high byte specifies the level to drive that port at. By + convention, the pal's interrupt ports (int0, int1, ...) are wired + up to the corresponding processor's level sensative external + interrupt pin. Eg: A two byte write to address 8 of 0x0102 + (big-endian) will result in processor 2's external interrupt pin to + be asserted. + + Processor count register (read) returns the total number of + processors active in the current simulation. + + TTY input fifo register (read), if the TTY input status register + indicates a character is available by being nonzero, returns the + next available character from the pal's tty input port. + + Similarly, the TTY output fifo register (write), if the TTY output + status register indicates the output fifo is not full by being + nonzero, outputs the character written to the tty's output port. + + + PROPERTIES + + + reg = <address> <size> (required) + + Specify the address (within the parent bus) that this device is to + live. + + + */ + + +enum { + hw_pal_reset_register = 0x0, + hw_pal_cpu_nr_register = 0x4, + hw_pal_int_register = 0x8, + hw_pal_nr_cpu_register = 0xa, + hw_pal_read_fifo = 0x10, + hw_pal_read_status = 0x14, + hw_pal_write_fifo = 0x18, + hw_pal_write_status = 0x1a, + hw_pal_address_mask = 0x1f, +}; + + +typedef struct _hw_pal_console_buffer { + char buffer; + int status; +} hw_pal_console_buffer; + +typedef struct _hw_pal_device { + hw_pal_console_buffer input; + hw_pal_console_buffer output; + device *disk; +} hw_pal_device; + + +/* check the console for an available character */ +static void +scan_hw_pal(hw_pal_device *hw_pal) +{ + char c; + int count; + count = sim_io_read_stdin(&c, sizeof(c)); + switch (count) { + case sim_io_not_ready: + case sim_io_eof: + hw_pal->input.buffer = 0; + hw_pal->input.status = 0; + break; + default: + hw_pal->input.buffer = c; + hw_pal->input.status = 1; + } +} + +/* write the character to the hw_pal */ +static void +write_hw_pal(hw_pal_device *hw_pal, + char val) +{ + sim_io_write_stdout(&val, 1); + hw_pal->output.buffer = val; + hw_pal->output.status = 1; +} + + +static unsigned +hw_pal_io_read_buffer_callback(device *me, + void *dest, + int space, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + hw_pal_device *hw_pal = (hw_pal_device*)device_data(me); + unsigned_1 val; + switch (addr & hw_pal_address_mask) { + case hw_pal_cpu_nr_register: + val = cpu_nr(processor); + DTRACE(pal, ("read - cpu-nr %d\n", val)); + break; + case hw_pal_nr_cpu_register: + val = tree_find_integer_property(me, "/openprom/options/smp"); + DTRACE(pal, ("read - nr-cpu %d\n", val)); + break; + case hw_pal_read_fifo: + val = hw_pal->input.buffer; + DTRACE(pal, ("read - input-fifo %d\n", val)); + break; + case hw_pal_read_status: + scan_hw_pal(hw_pal); + val = hw_pal->input.status; + DTRACE(pal, ("read - input-status %d\n", val)); + break; + case hw_pal_write_fifo: + val = hw_pal->output.buffer; + DTRACE(pal, ("read - output-fifo %d\n", val)); + break; + case hw_pal_write_status: + val = hw_pal->output.status; + DTRACE(pal, ("read - output-status %d\n", val)); + break; + default: + val = 0; + DTRACE(pal, ("read - ???\n")); + } + memset(dest, 0, nr_bytes); + *(unsigned_1*)dest = val; + return nr_bytes; +} + + +static unsigned +hw_pal_io_write_buffer_callback(device *me, + const void *source, + int space, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + hw_pal_device *hw_pal = (hw_pal_device*)device_data(me); + unsigned_1 *byte = (unsigned_1*)source; + + switch (addr & hw_pal_address_mask) { + case hw_pal_reset_register: + cpu_halt(processor, cia, was_exited, byte[0]); + break; + case hw_pal_int_register: + device_interrupt_event(me, + byte[0], /*port*/ + (nr_bytes > 1 ? byte[1] : 0), /* val */ + processor, cia); + break; + case hw_pal_read_fifo: + hw_pal->input.buffer = byte[0]; + DTRACE(pal, ("write - input-fifo %d\n", byte[0])); + break; + case hw_pal_read_status: + hw_pal->input.status = byte[0]; + DTRACE(pal, ("write - input-status %d\n", byte[0])); + break; + case hw_pal_write_fifo: + write_hw_pal(hw_pal, byte[0]); + DTRACE(pal, ("write - output-fifo %d\n", byte[0])); + break; + case hw_pal_write_status: + hw_pal->output.status = byte[0]; + DTRACE(pal, ("write - output-status %d\n", byte[0])); + break; + } + return nr_bytes; +} + + +/* instances of the hw_pal device */ + +static void +hw_pal_instance_delete_callback(device_instance *instance) +{ + /* nothing to delete, the hw_pal is attached to the device */ + return; +} + +static int +hw_pal_instance_read_callback(device_instance *instance, + void *buf, + unsigned_word len) +{ + DITRACE(pal, ("read - %s (%ld)", (const char*)buf, (long int)len)); + return sim_io_read_stdin(buf, len); +} + +static int +hw_pal_instance_write_callback(device_instance *instance, + const void *buf, + unsigned_word len) +{ + int i; + const char *chp = buf; + hw_pal_device *hw_pal = device_instance_data(instance); + DITRACE(pal, ("write - %s (%ld)", (const char*)buf, (long int)len)); + for (i = 0; i < len; i++) + write_hw_pal(hw_pal, chp[i]); + sim_io_flush_stdoutput(); + return i; +} + +static const device_instance_callbacks hw_pal_instance_callbacks = { + hw_pal_instance_delete_callback, + hw_pal_instance_read_callback, + hw_pal_instance_write_callback, +}; + +static device_instance * +hw_pal_create_instance(device *me, + const char *path, + const char *args) +{ + return device_create_instance_from(me, NULL, + device_data(me), + path, args, + &hw_pal_instance_callbacks); +} + +static const device_interrupt_port_descriptor hw_pal_interrupt_ports[] = { + { "int", 0, MAX_NR_PROCESSORS }, + { NULL } +}; + + +static void +hw_pal_attach_address(device *me, + attach_type attach, + int space, + unsigned_word addr, + unsigned nr_bytes, + access_type access, + device *client) +{ + hw_pal_device *pal = (hw_pal_device*)device_data(me); + pal->disk = client; +} + + +static device_callbacks const hw_pal_callbacks = { + { generic_device_init_address, }, + { hw_pal_attach_address, }, /* address */ + { hw_pal_io_read_buffer_callback, + hw_pal_io_write_buffer_callback, }, + { NULL, }, /* DMA */ + { NULL, NULL, hw_pal_interrupt_ports }, /* interrupt */ + { generic_device_unit_decode, + generic_device_unit_encode, + generic_device_address_to_attach_address, + generic_device_size_to_attach_size }, + hw_pal_create_instance, +}; + + +static void * +hw_pal_create(const char *name, + const device_unit *unit_address, + const char *args) +{ + /* create the descriptor */ + hw_pal_device *hw_pal = ZALLOC(hw_pal_device); + hw_pal->output.status = 1; + hw_pal->output.buffer = '\0'; + hw_pal->input.status = 0; + hw_pal->input.buffer = '\0'; + return hw_pal; +} + + +const device_descriptor hw_pal_device_descriptor[] = { + { "pal", hw_pal_create, &hw_pal_callbacks }, + { NULL }, +}; + +#endif /* _HW_PAL_C_ */ diff --git a/sim/ppc/hw_phb.c b/sim/ppc/hw_phb.c new file mode 100644 index 00000000000..0071f59faf3 --- /dev/null +++ b/sim/ppc/hw_phb.c @@ -0,0 +1,1068 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1996, 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 _HW_PHB_C_ +#define _HW_PHB_C_ + +#include "device_table.h" + +#include "hw_phb.h" + +#include "corefile.h" + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#include <ctype.h> + + +/* DEVICE + + + phb - PCI Host Bridge + + + DESCRIPTION + + + PHB implements a model of the PCI-host bridge described in the PPCP + document. + + For bridge devices, Open Firmware specifies that the <<ranges>> + property be used to specify the mapping of address spaces between a + bridges parent and child busses. This PHB model configures itsself + according to the information specified in its ranges property. The + <<ranges>> property is described in detail in the Open Firmware + documentation. + + For DMA transfers, any access to a PCI address space which falls + outside of the mapped memory space is assumed to be a transfer + intended for the parent bus. + + + PROPERTIES + + + ranges = <my-phys-addr> <parent-phys-addr> <my-size> ... (required) + + Define a number of mappings from the parent bus to one of this + devices PCI busses. The exact format of the <<parent-phys-addr>> + is parent bus dependant. The format of <<my-phys-addr>> is + described in the Open Firmware PCI bindings document (note that the + address must be non-relocatable). + + + #address-cells = 3 (required) + + Number of cells used by an Open Firmware PCI address. This + property must be defined before specifying the <<ranges>> property. + + + #size-cells = 2 (required) + + Number of cells used by an Open Firmware PCI size. This property + must be defined before specifying the <<ranges>> property. + + + EXAMPLES + + + Enable tracing: + + | $ psim \ + | -t phb-device \ + + + Since device tree entries that are specified on the command line + are added before most of the device tree has been built it is often + necessary to explictly add certain device properties and thus + ensure they are already present in the device tree. For the + <<phb>> one such property is parent busses <<#address-cells>>. + + | -o '/#address-cells 1' \ + + + Create the PHB remembering to include the cell size properties: + + | -o '/phb@0x80000000/#address-cells 3' \ + | -o '/phb@0x80000000/#size-cells 2' \ + + + Specify that the memory address range <<0x80000000>> to + <<0x8fffffff>> should map directly onto the PCI memory address + space while the processor address range <<0xc0000000>> to + <<0xc000ffff>> should map onto the PCI I/O address range starting + at location zero: + + | -o '/phb@0x80000000/ranges \ + | nm0,0,0,80000000 0x80000000 0x10000000 \ + | ni0,0,0,0 0xc0000000 0x10000' \ + + + Insert a 4k <<nvram>> into slot zero of the PCI bus. Have it + directly accessible in both the I/O (address <<0x100>>) and memory + (address 0x80001000) spaces: + + | -o '/phb@0x80000000/nvram@0/assigned-addresses \ + | nm0,0,10,80001000 4096 \ + | ni0,0,14,100 4096' + | -o '/phb@0x80000000/nvram@0/reg \ + | 0 0 \ + | i0,0,14,0 4096' + | -o '/phb@0x80000000/nvram@0/alternate-reg \ + | 0 0 \ + | m0,0,10,0 4096' + + The <<assigned-address>> property corresponding to what (if it were + implemented) be found in the config base registers while the + <<reg>> and <<alternative-reg>> properties indicating the location + of registers within each address space. + + Of the possible addresses, only the non-relocatable versions are + used when attaching the device to the bus. + + + BUGS + + + The implementation of the PCI configuration space is left as an + exercise for the reader. Such a restriction should only impact on + systems wanting to dynamically configure devices on the PCI bus. + + The <<CHRP>> document specfies additional (optional) functionality + of the primary PHB. The implementation of such functionality is + left as an exercise for the reader. + + The Open Firmware PCI bus bindings document (rev 1.6 and 2.0) is + unclear on the value of the "ss" bits for a 64bit memory address. + The correct value, as used by this module, is 0b11. + + The Open Firmware PCI bus bindings document (rev 1.6) suggests that + the register field of non-relocatable PCI address should be zero. + Unfortunatly, PCI addresses specified in the <<assigned-addresses>> + property must be both non-relocatable and have non-zero register + fields. + + The unit-decode method is not inserting a bus number into any + address that it decodes. Instead the bus-number is left as zero. + + Support for aliased memory and I/O addresses is left as an exercise + for the reader. + + Support for interrupt-ack and special cycles are left as an + exercise for the reader. One issue to consider when attempting + this exercise is how to specify the address of the int-ack and + special cycle register. Hint: <</8259-interrupt-ackowledge>> is + the wrong answer. + + Children of this node can only use the client callback interface + when attaching themselves to the <<phb>>. + + + REFERENCES + + + http://playground.sun.com/1275/home.html#OFDbusPCI + + + */ + + +typedef struct _phb_space { + core *map; + core_map *readable; + core_map *writeable; + unsigned_word parent_base; + int parent_space; + unsigned_word my_base; + int my_space; + unsigned size; + const char *name; +} phb_space; + +typedef struct _hw_phb_device { + phb_space space[nr_hw_phb_spaces]; +} hw_phb_device; + + +static const char * +hw_phb_decode_name(hw_phb_decode level) +{ + switch (level) { + case hw_phb_normal_decode: return "normal"; + case hw_phb_subtractive_decode: return "subtractive"; + case hw_phb_master_abort_decode: return "master-abort"; + default: return "invalid decode"; + } +} + + +static void +hw_phb_init_address(device *me) +{ + hw_phb_device *phb = device_data(me); + + /* check some basic properties */ + if (device_nr_address_cells(me) != 3) + device_error(me, "incorrect #address-cells"); + if (device_nr_size_cells(me) != 2) + device_error(me, "incorrect #size-cells"); + + /* (re) initialize each PCI space */ + { + hw_phb_spaces space_nr; + for (space_nr = 0; space_nr < nr_hw_phb_spaces; space_nr++) { + phb_space *pci_space = &phb->space[space_nr]; + core_init(pci_space->map); + pci_space->size = 0; + } + } + + /* decode each of the ranges properties entering the information + into the space table */ + { + range_property_spec range; + int ranges_entry; + + for (ranges_entry = 0; + device_find_range_array_property(me, "ranges", ranges_entry, + &range); + ranges_entry++) { + int my_attach_space; + unsigned_word my_attach_address; + int parent_attach_space; + unsigned_word parent_attach_address; + unsigned size; + phb_space *pci_space; + /* convert the addresses into something meaningful */ + device_address_to_attach_address(me, &range.child_address, + &my_attach_space, + &my_attach_address, + me); + device_address_to_attach_address(device_parent(me), + &range.parent_address, + &parent_attach_space, + &parent_attach_address, + me); + device_size_to_attach_size(me, &range.size, &size, me); + if (my_attach_space < 0 || my_attach_space >= nr_hw_phb_spaces) + device_error(me, "ranges property contains an invalid address space"); + pci_space = &phb->space[my_attach_space]; + if (pci_space->size != 0) + device_error(me, "ranges property contains duplicate mappings for %s address space", + pci_space->name); + pci_space->parent_base = parent_attach_address; + pci_space->parent_space = parent_attach_space; + pci_space->my_base = my_attach_address; + pci_space->my_space = my_attach_space; + pci_space->size = size; + device_attach_address(device_parent(me), + attach_callback, + parent_attach_space, parent_attach_address, size, + access_read_write_exec, + me); + DTRACE(phb, ("map %d:0x%lx to %s:0x%lx (0x%lx bytes)\n", + (int)parent_attach_space, + (unsigned long)parent_attach_address, + pci_space->name, + (unsigned long)my_attach_address, + (unsigned long)size)); + } + + if (ranges_entry == 0) { + device_error(me, "Missing or empty ranges property"); + } + + } + +} + +static void +hw_phb_attach_address(device *me, + attach_type type, + int space, + unsigned_word addr, + unsigned nr_bytes, + access_type access, + device *client) /*callback/default*/ +{ + hw_phb_device *phb = device_data(me); + phb_space *pci_space; + /* sanity checks */ + if (space < 0 || space >= nr_hw_phb_spaces) + device_error(me, "attach space (%d) specified by %s invalid", + space, device_path(client)); + pci_space = &phb->space[space]; + if (addr + nr_bytes > pci_space->my_base + pci_space->size + || addr < pci_space->my_base) + device_error(me, "attach addr (0x%lx) specified by %s outside of bus address range", + (unsigned long)addr, device_path(client)); + if (type != hw_phb_normal_decode + && type != hw_phb_subtractive_decode) + device_error(me, "attach type (%d) specified by %s invalid", + type, device_path(client)); + /* attach it to the relevent bus */ + DTRACE(phb, ("attach %s - %s %s:0x%lx (0x%lx bytes)\n", + device_path(client), + hw_phb_decode_name(type), + pci_space->name, + (unsigned long)addr, + (unsigned long)nr_bytes)); + core_attach(pci_space->map, + type, + space, + access, + addr, + nr_bytes, + client); +} + + +/* Extract/set various fields from a PCI unit address. + + Note: only the least significant 32 bits of each cell is used. + + Note: for PPC MSB is 0 while for PCI it is 31. */ + + +/* relocatable bit n */ + +static unsigned +extract_n(const device_unit *address) +{ + return EXTRACTED32(address->cells[0], 0, 0); +} + +static void +set_n(device_unit *address) +{ + BLIT32(address->cells[0], 0, 1); +} + + +/* prefetchable bit p */ + +static unsigned +extract_p(const device_unit *address) +{ + ASSERT(address->nr_cells == 3); + return EXTRACTED32(address->cells[0], 1, 1); +} + +static void +set_p(device_unit *address) +{ + BLIT32(address->cells[0], 1, 1); +} + + +/* aliased bit t */ + +static unsigned +extract_t(const device_unit *address) +{ + ASSERT(address->nr_cells == 3); + return EXTRACTED32(address->cells[0], 2, 2); +} + +static void +set_t(device_unit *address) +{ + BLIT32(address->cells[0], 2, 1); +} + + +/* space code ss */ + +typedef enum { + ss_config_code = 0, + ss_io_code = 1, + ss_32bit_memory_code = 2, + ss_64bit_memory_code = 3, +} ss_type; + +static ss_type +extract_ss(const device_unit *address) +{ + ASSERT(address->nr_cells == 3); + return EXTRACTED32(address->cells[0], 6, 7); +} + +static void +set_ss(device_unit *address, ss_type val) +{ + MBLIT32(address->cells[0], 6, 7, val); +} + + +/* bus number bbbbbbbb */ + +#if 0 +static unsigned +extract_bbbbbbbb(const device_unit *address) +{ + ASSERT(address->nr_cells == 3); + return EXTRACTED32(address->cells[0], 8, 15); +} +#endif + +#if 0 +static void +set_bbbbbbbb(device_unit *address, unsigned val) +{ + MBLIT32(address->cells[0], 8, 15, val); +} +#endif + + +/* device number ddddd */ + +static unsigned +extract_ddddd(const device_unit *address) +{ + ASSERT(address->nr_cells == 3); + return EXTRACTED32(address->cells[0], 16, 20); +} + +static void +set_ddddd(device_unit *address, unsigned val) +{ + MBLIT32(address->cells[0], 16, 20, val); +} + + +/* function number fff */ + +static unsigned +extract_fff(const device_unit *address) +{ + ASSERT(address->nr_cells == 3); + return EXTRACTED32(address->cells[0], 21, 23); +} + +static void +set_fff(device_unit *address, unsigned val) +{ + MBLIT32(address->cells[0], 21, 23, val); +} + + +/* register number rrrrrrrr */ + +static unsigned +extract_rrrrrrrr(const device_unit *address) +{ + ASSERT(address->nr_cells == 3); + return EXTRACTED32(address->cells[0], 24, 31); +} + +static void +set_rrrrrrrr(device_unit *address, unsigned val) +{ + MBLIT32(address->cells[0], 24, 31, val); +} + + +/* MSW of 64bit address hh..hh */ + +static unsigned +extract_hh_hh(const device_unit *address) +{ + ASSERT(address->nr_cells == 3); + return address->cells[1]; +} + +static void +set_hh_hh(device_unit *address, unsigned val) +{ + address->cells[2] = val; +} + + +/* LSW of 64bit address ll..ll */ + +static unsigned +extract_ll_ll(const device_unit *address) +{ + ASSERT(address->nr_cells == 3); + return address->cells[2]; +} + +static void +set_ll_ll(device_unit *address, unsigned val) +{ + address->cells[2] = val; +} + + +/* Convert PCI textual bus address into a device unit */ + +static int +hw_phb_unit_decode(device *me, + const char *unit, + device_unit *address) +{ + char *end = NULL; + const char *chp = unit; + unsigned long val; + + if (device_nr_address_cells(me) != 3) + device_error(me, "PCI bus should have #address-cells == 3"); + memset(address, 0, sizeof(*address)); + + if (unit == NULL) + return 0; + + address->nr_cells = 3; + + if (isxdigit(*chp)) { + set_ss(address, ss_config_code); + } + else { + + /* non-relocatable? */ + if (*chp == 'n') { + set_n(address); + chp++; + } + + /* address-space? */ + if (*chp == 'i') { + set_ss(address, ss_io_code); + chp++; + } + else if (*chp == 'm') { + set_ss(address, ss_32bit_memory_code); + chp++; + } + else if (*chp == 'x') { + set_ss(address, ss_64bit_memory_code); + chp++; + } + else + device_error(me, "Problem parsing PCI address %s", unit); + + /* possible alias */ + if (*chp == 't') { + if (extract_ss(address) == ss_64bit_memory_code) + device_error(me, "Invalid alias bit in PCI address %s", unit); + set_t(address); + chp++; + } + + /* possible p */ + if (*chp == 'p') { + if (extract_ss(address) != ss_32bit_memory_code) + device_error(me, "Invalid prefetchable bit (p) in PCI address %s", + unit); + set_p(address); + chp++; + } + + } + + /* required DD */ + if (!isxdigit(*chp)) + device_error(me, "Missing device number in PCI address %s", unit); + val = strtoul(chp, &end, 16); + if (chp == end) + device_error(me, "Problem parsing device number in PCI address %s", unit); + if ((val & 0x1f) != val) + device_error(me, "Device number (0x%lx) out of range (0..0x1f) in PCI address %s", + val, unit); + set_ddddd(address, val); + chp = end; + + /* For config space, the F is optional */ + if (extract_ss(address) == ss_config_code + && (isspace(*chp) || *chp == '\0')) + return chp - unit; + + /* function number F */ + if (*chp != ',') + device_error(me, "Missing function number in PCI address %s", unit); + chp++; + val = strtoul(chp, &end, 10); + if (chp == end) + device_error(me, "Problem parsing function number in PCI address %s", + unit); + if ((val & 7) != val) + device_error(me, "Function number (%ld) out of range (0..7) in PCI address %s", + (long)val, unit); + set_fff(address, val); + chp = end; + + /* for config space, must be end */ + if (extract_ss(address) == ss_config_code) { + if (!isspace(*chp) && *chp != '\0') + device_error(me, "Problem parsing PCI config address %s", + unit); + return chp - unit; + } + + /* register number RR */ + if (*chp != ',') + device_error(me, "Missing register number in PCI address %s", unit); + chp++; + val = strtoul(chp, &end, 16); + if (chp == end) + device_error(me, "Problem parsing register number in PCI address %s", + unit); + switch (extract_ss(address)) { + case ss_io_code: +#if 0 + if (extract_n(address) && val != 0) + device_error(me, "non-relocatable I/O register must be zero in PCI address %s", unit); + else if (!extract_n(address) + && val != 0x10 && val != 0x14 && val != 0x18 + && val != 0x1c && val != 0x20 && val != 0x24) + device_error(me, "I/O register invalid in PCI address %s", unit); +#endif + break; + case ss_32bit_memory_code: +#if 0 + if (extract_n(address) && val != 0) + device_error(me, "non-relocatable memory register must be zero in PCI address %s", unit); + else if (!extract_n(address) + && val != 0x10 && val != 0x14 && val != 0x18 + && val != 0x1c && val != 0x20 && val != 0x24 && val != 0x30) + device_error(me, "I/O register (0x%lx) invalid in PCI address %s", + val, unit); +#endif + break; + case ss_64bit_memory_code: + if (extract_n(address) && val != 0) + device_error(me, "non-relocatable 32bit memory register must be zero in PCI address %s", unit); + else if (!extract_n(address) + && val != 0x10 && val != 0x18 && val != 0x20) + device_error(me, "Register number (0x%lx) invalid in 64bit PCI address %s", + val, unit); + case ss_config_code: + device_error(me, "internal error"); + } + if ((val & 0xff) != val) + device_error(me, "Register number (0x%lx) out of range (0..0xff) in PCI address %s", + val, unit); + set_rrrrrrrr(address, val); + chp = end; + + /* address */ + if (*chp != ',') + device_error(me, "Missing address in PCI address %s", unit); + chp++; + switch (extract_ss(address)) { + case ss_io_code: + case ss_32bit_memory_code: + val = strtoul(chp, &end, 16); + if (chp == end) + device_error(me, "Problem parsing address in PCI address %s", unit); + switch (extract_ss(address)) { + case ss_io_code: + if (extract_n(address) && extract_t(address) + && (val & 1024) != val) + device_error(me, "10bit aliased non-relocatable address (0x%lx) out of range in PCI address %s", + val, unit); + if (!extract_n(address) && extract_t(address) + && (val & 0xffff) != val) + device_error(me, "64k relocatable address (0x%lx) out of range in PCI address %s", + val, unit); + break; + case ss_32bit_memory_code: + if (extract_t(address) && (val & 0xfffff) != val) + device_error(me, "1mb memory address (0x%lx) out of range in PCI address %s", + val, unit); + if (!extract_t(address) && (val & 0xffffffff) != val) + device_error(me, "32bit memory address (0x%lx) out of range in PCI address %s", + val, unit); + break; + case ss_64bit_memory_code: + case ss_config_code: + device_error(me, "internal error"); + } + set_ll_ll(address, val); + chp = end; + break; + case ss_64bit_memory_code: + device_error(me, "64bit addresses unimplemented"); + set_hh_hh(address, val); + set_ll_ll(address, val); + break; + case ss_config_code: + device_error(me, "internal error"); + break; + } + + /* finished? */ + if (!isspace(*chp) && *chp != '\0') + device_error(me, "Problem parsing PCI address %s", unit); + + return chp - unit; +} + + +/* Convert PCI device unit into its corresponding textual + representation */ + +static int +hw_phb_unit_encode(device *me, + const device_unit *unit_address, + char *buf, + int sizeof_buf) +{ + if (unit_address->nr_cells != 3) + device_error(me, "Incorrect number of cells in PCI unit address"); + if (device_nr_address_cells(me) != 3) + device_error(me, "PCI bus should have #address-cells == 3"); + if (extract_ss(unit_address) == ss_config_code + && extract_fff(unit_address) == 0 + && extract_rrrrrrrr(unit_address) == 0 + && extract_hh_hh(unit_address) == 0 + && extract_ll_ll(unit_address) == 0) { + /* DD - Configuration Space address */ + sprintf(buf, "%x", + extract_ddddd(unit_address)); + } + else if (extract_ss(unit_address) == ss_config_code + && extract_fff(unit_address) != 0 + && extract_rrrrrrrr(unit_address) == 0 + && extract_hh_hh(unit_address) == 0 + && extract_ll_ll(unit_address) == 0) { + /* DD,F - Configuration Space */ + sprintf(buf, "%x,%d", + extract_ddddd(unit_address), + extract_fff(unit_address)); + } + else if (extract_ss(unit_address) == ss_io_code + && extract_hh_hh(unit_address) == 0) { + /* [n]i[t]DD,F,RR,NNNNNNNN - 32bit I/O space */ + sprintf(buf, "%si%s%x,%d,%x,%x", + extract_n(unit_address) ? "n" : "", + extract_t(unit_address) ? "t" : "", + extract_ddddd(unit_address), + extract_fff(unit_address), + extract_rrrrrrrr(unit_address), + extract_ll_ll(unit_address)); + } + else if (extract_ss(unit_address) == ss_32bit_memory_code + && extract_hh_hh(unit_address) == 0) { + /* [n]m[t][p]DD,F,RR,NNNNNNNN - 32bit memory space */ + sprintf(buf, "%sm%s%s%x,%d,%x,%x", + extract_n(unit_address) ? "n" : "", + extract_t(unit_address) ? "t" : "", + extract_p(unit_address) ? "p" : "", + extract_ddddd(unit_address), + extract_fff(unit_address), + extract_rrrrrrrr(unit_address), + extract_ll_ll(unit_address)); + } + else if (extract_ss(unit_address) == ss_32bit_memory_code) { + /* [n]x[p]DD,F,RR,NNNNNNNNNNNNNNNN - 64bit memory space */ + sprintf(buf, "%sx%s%x,%d,%x,%x%08x", + extract_n(unit_address) ? "n" : "", + extract_p(unit_address) ? "p" : "", + extract_ddddd(unit_address), + extract_fff(unit_address), + extract_rrrrrrrr(unit_address), + extract_hh_hh(unit_address), + extract_ll_ll(unit_address)); + } + else { + device_error(me, "Invalid PCI unit address 0x%08lx 0x%08lx 0x%08lx", + (unsigned long)unit_address->cells[0], + (unsigned long)unit_address->cells[1], + (unsigned long)unit_address->cells[2]); + } + if (strlen(buf) > sizeof_buf) + error("buffer overflow"); + return strlen(buf); +} + + +static int +hw_phb_address_to_attach_address(device *me, + const device_unit *address, + int *attach_space, + unsigned_word *attach_address, + device *client) +{ + if (address->nr_cells != 3) + device_error(me, "attach address has incorrect number of cells"); + if (address->cells[1] != 0) + device_error(me, "64bit attach address unsupported"); + + /* directly decode the address/space */ + *attach_address = address->cells[2]; + switch (extract_ss(address)) { + case ss_config_code: + *attach_space = hw_phb_config_space; + break; + case ss_io_code: + *attach_space = hw_phb_io_space; + break; + case ss_32bit_memory_code: + case ss_64bit_memory_code: + *attach_space = hw_phb_memory_space; + break; + } + + /* if non-relocatable finished */ + if (extract_n(address)) + return 1; + + /* make memory and I/O addresses absolute */ + if (*attach_space == hw_phb_io_space + || *attach_space == hw_phb_memory_space) { + int reg_nr; + reg_property_spec assigned; + if (extract_ss(address) == ss_64bit_memory_code) + device_error(me, "64bit memory address not unsuported"); + for (reg_nr = 0; + device_find_reg_array_property(client, "assigned-addresses", reg_nr, + &assigned); + reg_nr++) { + if (!extract_n(&assigned.address) + || extract_rrrrrrrr(&assigned.address) == 0) + device_error(me, "client %s has invalid assigned-address property", + device_path(client)); + if (extract_rrrrrrrr(address) == extract_rrrrrrrr(&assigned.address)) { + /* corresponding base register */ + if (extract_ss(address) != extract_ss(&assigned.address)) + device_error(me, "client %s has conflicting types for base register 0x%lx", + device_path(client), + (unsigned long)extract_rrrrrrrr(address)); + *attach_address += assigned.address.cells[2]; + return 0; + } + } + device_error(me, "client %s missing base address register 0x%lx in assigned-addresses property", + device_path(client), + (unsigned long)extract_rrrrrrrr(address)); + } + + return 0; +} + + +static int +hw_phb_size_to_attach_size(device *me, + const device_unit *size, + unsigned *nr_bytes, + device *client) +{ + if (size->nr_cells != 2) + device_error(me, "size has incorrect number of cells"); + if (size->cells[0] != 0) + device_error(me, "64bit size unsupported"); + *nr_bytes = size->cells[1]; + return size->cells[1]; +} + + +static const phb_space * +find_phb_space(hw_phb_device *phb, + unsigned_word addr, + unsigned nr_bytes) +{ + hw_phb_spaces space; + /* find the space that matches the address */ + for (space = 0; space < nr_hw_phb_spaces; space++) { + phb_space *pci_space = &phb->space[space]; + if (addr >= pci_space->parent_base + && (addr + nr_bytes) <= (pci_space->parent_base + pci_space->size)) { + return pci_space; + } + } + return NULL; +} + + +static unsigned_word +map_phb_addr(const phb_space *space, + unsigned_word addr) +{ + return addr - space->parent_base + space->my_base; +} + + + +static unsigned +hw_phb_io_read_buffer(device *me, + void *dest, + int space, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + hw_phb_device *phb = (hw_phb_device*)device_data(me); + const phb_space *pci_space = find_phb_space(phb, addr, nr_bytes); + unsigned_word bus_addr; + if (pci_space == NULL) + return 0; + bus_addr = map_phb_addr(pci_space, addr); + DTRACE(phb, ("io read - %d:0x%lx -> %s:0x%lx (%u bytes)\n", + space, (unsigned long)addr, pci_space->name, (unsigned long)bus_addr, + nr_bytes)); + return core_map_read_buffer(pci_space->readable, + dest, bus_addr, nr_bytes); +} + + +static unsigned +hw_phb_io_write_buffer(device *me, + const void *source, + int space, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + hw_phb_device *phb = (hw_phb_device*)device_data(me); + const phb_space *pci_space = find_phb_space(phb, addr, nr_bytes); + unsigned_word bus_addr; + if (pci_space == NULL) + return 0; + bus_addr = map_phb_addr(pci_space, addr); + DTRACE(phb, ("io write - %d:0x%lx -> %s:0x%lx (%u bytes)\n", + space, (unsigned long)addr, pci_space->name, (unsigned long)bus_addr, + nr_bytes)); + return core_map_write_buffer(pci_space->writeable, source, + bus_addr, nr_bytes); +} + + +static unsigned +hw_phb_dma_read_buffer(device *me, + void *dest, + int space, + unsigned_word addr, + unsigned nr_bytes) +{ + hw_phb_device *phb = (hw_phb_device*)device_data(me); + const phb_space *pci_space; + /* find the space */ + if (space != hw_phb_memory_space) + device_error(me, "invalid dma address space %d", space); + pci_space = &phb->space[space]; + /* check out the address */ + if ((addr >= pci_space->my_base + && addr <= pci_space->my_base + pci_space->size) + || (addr + nr_bytes >= pci_space->my_base + && addr + nr_bytes <= pci_space->my_base + pci_space->size)) + device_error(me, "Do not support DMA into own bus"); + /* do it */ + DTRACE(phb, ("dma read - %s:0x%lx (%d bytes)\n", + pci_space->name, addr, nr_bytes)); + return device_dma_read_buffer(device_parent(me), + dest, pci_space->parent_space, + addr, nr_bytes); +} + + +static unsigned +hw_phb_dma_write_buffer(device *me, + const void *source, + int space, + unsigned_word addr, + unsigned nr_bytes, + int violate_read_only_section) +{ + hw_phb_device *phb = (hw_phb_device*)device_data(me); + const phb_space *pci_space; + /* find the space */ + if (space != hw_phb_memory_space) + device_error(me, "invalid dma address space %d", space); + pci_space = &phb->space[space]; + /* check out the address */ + if ((addr >= pci_space->my_base + && addr <= pci_space->my_base + pci_space->size) + || (addr + nr_bytes >= pci_space->my_base + && addr + nr_bytes <= pci_space->my_base + pci_space->size)) + device_error(me, "Do not support DMA into own bus"); + /* do it */ + DTRACE(phb, ("dma write - %s:0x%lx (%d bytes)\n", + pci_space->name, addr, nr_bytes)); + return device_dma_write_buffer(device_parent(me), + source, pci_space->parent_space, + addr, nr_bytes, + violate_read_only_section); +} + + +static device_callbacks const hw_phb_callbacks = { + { hw_phb_init_address, }, + { hw_phb_attach_address, }, + { hw_phb_io_read_buffer, hw_phb_io_write_buffer }, + { hw_phb_dma_read_buffer, hw_phb_dma_write_buffer }, + { NULL, }, /* interrupt */ + { hw_phb_unit_decode, + hw_phb_unit_encode, + hw_phb_address_to_attach_address, + hw_phb_size_to_attach_size } +}; + + +static void * +hw_phb_create(const char *name, + const device_unit *unit_address, + const char *args) +{ + /* create the descriptor */ + hw_phb_device *phb = ZALLOC(hw_phb_device); + + /* create the core maps now */ + hw_phb_spaces space_nr; + for (space_nr = 0; space_nr < nr_hw_phb_spaces; space_nr++) { + phb_space *pci_space = &phb->space[space_nr]; + pci_space->map = core_create(); + pci_space->readable = core_readable(pci_space->map); + pci_space->writeable = core_writeable(pci_space->map); + switch (space_nr) { + case hw_phb_memory_space: + pci_space->name = "memory"; + break; + case hw_phb_io_space: + pci_space->name = "I/O"; + break; + case hw_phb_config_space: + pci_space->name = "config"; + break; + case hw_phb_special_space: + pci_space->name = "special"; + break; + default: + error ("internal error"); + break; + } + } + + return phb; +} + + +const device_descriptor hw_phb_device_descriptor[] = { + { "phb", hw_phb_create, &hw_phb_callbacks }, + { "pci", NULL, &hw_phb_callbacks }, + { NULL, }, +}; + +#endif /* _HW_PHB_ */ diff --git a/sim/ppc/hw_phb.h b/sim/ppc/hw_phb.h new file mode 100644 index 00000000000..030fe0a8194 --- /dev/null +++ b/sim/ppc/hw_phb.h @@ -0,0 +1,40 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1996, 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 _HW_PHB_H_ +#define _HW_PHB_H_ + +typedef enum { + hw_phb_memory_space, + hw_phb_io_space, + hw_phb_config_space, + hw_phb_special_space, + nr_hw_phb_spaces, +} hw_phb_spaces; + +typedef enum { + hw_phb_normal_decode = attach_callback, + hw_phb_subtractive_decode, + hw_phb_master_abort_decode, +} hw_phb_decode; + + +#endif /* _HW_PHB_H_ */ diff --git a/sim/ppc/hw_register.c b/sim/ppc/hw_register.c new file mode 100644 index 00000000000..f361e462527 --- /dev/null +++ b/sim/ppc/hw_register.c @@ -0,0 +1,131 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1996, 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 _HW_REGISTER_C_ +#define _HW_REGISTER_C_ + +#include "device_table.h" +#include <stdlib.h> +#include "psim.h" + +/* DEVICE + + + register - dummy device to initialize processor registers + + + DESCRIPTION + + The properties of this device are used, during initialization, to + specify the initial value of various processor registers. The + property name specifying the register to be initialized with the + special form <cpu-nr>.<register> being used to initialize a + specific processor's register (eg 0.pc). + + Because, when the device tree is created, overriding properties are + entered into the tree before any default values, this device must + initialize registers in newest (default) to oldest (overriding) + property order. + + The actual registers (for a given target) are defined in the file + registers.c. + + This device is normally a child of the /openprom/init node. + + + EXAMPLES + + + Given a device tree containing the entry: + + | /openprom/init/register/pc 0xfff00cf0 + + then specifying the command line option: + + | -o '/openprom/init/register/pc 0x0' + + would override the initial value of processor zero's program + counter. The resultant device tree tree containing: + + | /openprom/init/register/0.pc 0x0 + | /openprom/init/register/pc 0xfff00cf0 + + and would be processed last to first resulting in the sequence: set + all program counters to 0xfff00cf0; set processor zero's program + counter to zero. + + */ + +static void +do_register_init(device *me, + const device_property *prop) +{ + psim *system = device_system(me); + if (prop != NULL) { + const char *name = prop->name; + unsigned32 value = device_find_integer_property(me, name); + int processor; + + do_register_init(me, device_next_property(prop)); + + if (strchr(name, '.') == NULL) { + processor = -1; + DTRACE(register, ("%s=0x%lx\n", name, (unsigned long)value)); + } + else { + char *end; + processor = strtoul(name, &end, 0); + ASSERT(end[0] == '.'); + name = end+1; + DTRACE(register, ("%d.%s=0x%lx\n", processor, name, + (unsigned long)value)); + } + psim_write_register(system, processor, /* all processors */ + &value, + name, + cooked_transfer); + } +} + + +static void +register_init_data_callback(device *me) +{ + const device_property *prop = device_find_property(me, NULL); + do_register_init(me, prop); +} + + +static device_callbacks const register_callbacks = { + { NULL, register_init_data_callback, }, + { NULL, }, /* address */ + { NULL, }, /* IO */ + { NULL, }, /* DMA */ + { NULL, }, /* interrupt */ + { NULL, }, /* unit */ +}; + +const device_descriptor hw_register_device_descriptor[] = { + { "register", NULL, ®ister_callbacks }, + { NULL }, +}; + +#endif _HW_REGISTER_C_ diff --git a/sim/ppc/hw_trace.c b/sim/ppc/hw_trace.c new file mode 100644 index 00000000000..a46483a5191 --- /dev/null +++ b/sim/ppc/hw_trace.c @@ -0,0 +1,103 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1996, 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 _HW_TRACE_C_ +#define _HW_TRACE_C_ + +#include "device_table.h" +#include <stdarg.h> + +/* DEVICE + + trace - the properties of this dummy device specify trace options + + DESCRIPTION + + The properties of this device are used, during initialization, to + specify the value various simulation trace options. The + initialization can occure implicitly (during device tree init) or + explicitly using this devices ioctl method. + + The actual options and their default values (for a given target) + are defined in the file debug. + + This device is normally a child of the /openprom node. + + EXAMPLE + + The trace option dump-device-tree can be enabled by specifying the + option: + + | -o '/openprom/trace/dump-device-tree 0x1' + + Alternativly the shorthand version: + + | -t dump-device-tree + + can be used. */ + + +/* Hook to allow the initialization of the trace options at any time */ + +static int +hw_trace_ioctl(device *me, + cpu *processor, + unsigned_word cia, + device_ioctl_request request, + va_list ap) +{ + switch (request) { + case device_ioctl_set_trace: + { + const device_property *prop = device_find_property(me, NULL); + while (prop != NULL) { + const char *name = prop->name; + unsigned32 value = device_find_integer_property(me, name); + trace_option(name, value); + prop = device_next_property(prop); + } + } + break; + default: + device_error(me, "insupported ioctl request"); + break; + } + return 0; +} + + +static device_callbacks const hw_trace_callbacks = { + { NULL, }, /* init */ + { NULL, }, /* address */ + { NULL, }, /* IO */ + { NULL, }, /* DMA */ + { NULL, }, /* interrupt */ + { NULL, }, /* unit */ + NULL, /* instance-create */ + hw_trace_ioctl, +}; + +const device_descriptor hw_trace_device_descriptor[] = { + { "trace", NULL, &hw_trace_callbacks }, + { NULL }, +}; + +#endif _HW_TRACE_C_ diff --git a/sim/ppc/hw_vm.c b/sim/ppc/hw_vm.c new file mode 100644 index 00000000000..e42b8002347 --- /dev/null +++ b/sim/ppc/hw_vm.c @@ -0,0 +1,275 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1996, 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 _HW_VM_C_ +#define _HW_VM_C_ + +#include "device_table.h" +#include "cpu.h" + +#include <signal.h> + +/* DEVICE + + vm - virtual memory device for user simulation modes + + DESCRIPTION + + In user mode, mapped text, data and stack addresses are managed by + the core. Unmapped addresses are passed onto this device (because + it establishes its self as the fallback device) for processing. + + During initialization, children of this device will request the + mapping of the initial text and data segments. Those requests are + passed onto the core device so that that may establish the initial + memory regions. + + Once the simulation has started (as noted above) any access to an + unmapped address range will be passed down to this device as an IO + access. This device will then either attach additional memory to + the core device or signal the access as being invalid. + + The IOCTL function is used to notify this device of any changes to + the users `brk' point. + + PROPERTIES + + stack-base = <number> + + Specifies the lower address of the stack segment in the users + virtual address space. The initial stack page is defined by + stack-base + nr-bytes. + + nr-bytes = <number> + + Specifies the maximum size of the stack segment in the users + address space. + + */ + +typedef struct _hw_vm_device { + /* area of memory valid for stack addresses */ + unsigned_word stack_base; /* min possible stack value */ + unsigned_word stack_bound; + unsigned_word stack_lower_limit; + /* area of memory valid for heap addresses */ + unsigned_word heap_base; + unsigned_word heap_bound; + unsigned_word heap_upper_limit; +} hw_vm_device; + + +static void +hw_vm_init_address_callback(device *me) +{ + hw_vm_device *vm = (hw_vm_device*)device_data(me); + + /* revert the stack/heap variables to their defaults */ + vm->stack_base = device_find_integer_property(me, "stack-base"); + vm->stack_bound = (vm->stack_base + + device_find_integer_property(me, "nr-bytes")); + vm->stack_lower_limit = vm->stack_bound; + vm->heap_base = 0; + vm->heap_bound = 0; + vm->heap_upper_limit = 0; + + /* establish this device as the default memory handler */ + device_attach_address(device_parent(me), + attach_callback + 1, + 0 /*address space - ignore*/, + 0 /*addr - ignore*/, + (((unsigned)0)-1) /*nr_bytes - ignore*/, + access_read_write /*access*/, + me); +} + + +static void +hw_vm_attach_address(device *me, + attach_type attach, + int space, + unsigned_word addr, + unsigned nr_bytes, + access_type access, + device *client) /*callback/default*/ +{ + hw_vm_device *vm = (hw_vm_device*)device_data(me); + /* update end of bss if necessary */ + if (vm->heap_base < addr + nr_bytes) { + vm->heap_base = addr + nr_bytes; + vm->heap_bound = addr + nr_bytes; + vm->heap_upper_limit = addr + nr_bytes; + } + device_attach_address(device_parent(me), + attach_raw_memory, + 0 /*address space*/, + addr, + nr_bytes, + access, + me); +} + + +static unsigned +hw_vm_add_space(device *me, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + hw_vm_device *vm = (hw_vm_device*)device_data(me); + unsigned_word block_addr; + unsigned block_nr_bytes; + + /* an address in the stack area, allocate just down to the addressed + page */ + if (addr >= vm->stack_base && addr < vm->stack_lower_limit) { + block_addr = FLOOR_PAGE(addr); + block_nr_bytes = vm->stack_lower_limit - block_addr; + vm->stack_lower_limit = block_addr; + } + /* an address in the heap area, allocate all of the required heap */ + else if (addr >= vm->heap_upper_limit && addr < vm->heap_bound) { + block_addr = vm->heap_upper_limit; + block_nr_bytes = vm->heap_bound - vm->heap_upper_limit; + vm->heap_upper_limit = vm->heap_bound; + } + /* oops - an invalid address - abort the cpu */ + else if (processor != NULL) { + cpu_halt(processor, cia, was_signalled, SIGSEGV); + return 0; + } + /* 2*oops - an invalid address and no processor */ + else { + return 0; + } + + /* got the parameters, allocate the space */ + device_attach_address(device_parent(me), + attach_raw_memory, + 0 /*address space*/, + block_addr, + block_nr_bytes, + access_read_write, + me); + return block_nr_bytes; +} + + +static unsigned +hw_vm_io_read_buffer_callback(device *me, + void *dest, + int space, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + if (hw_vm_add_space(me, addr, nr_bytes, processor, cia) >= nr_bytes) { + memset(dest, 0, nr_bytes); /* always initialized to zero */ + return nr_bytes; + } + else + return 0; +} + + +static unsigned +hw_vm_io_write_buffer_callback(device *me, + const void *source, + int space, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + if (hw_vm_add_space(me, addr, nr_bytes, processor, cia) >= nr_bytes) { + return device_dma_write_buffer(device_parent(me), source, + space, addr, + nr_bytes, + 0/*violate_read_only*/); + } + else + return 0; +} + + +static int +hw_vm_ioctl(device *me, + cpu *processor, + unsigned_word cia, + device_ioctl_request request, + va_list ap) +{ + /* While the caller is notified that the heap has grown by the + requested amount, the heap is actually extended out to a page + boundary. */ + hw_vm_device *vm = (hw_vm_device*)device_data(me); + switch (request) { + case device_ioctl_break: + { + unsigned_word requested_break = va_arg(ap, unsigned_word); + unsigned_word new_break = ALIGN_8(requested_break); + unsigned_word old_break = vm->heap_bound; + signed_word delta = new_break - old_break; + if (delta > 0) + vm->heap_bound = ALIGN_PAGE(new_break); + break; + } + default: + device_error(me, "Unsupported ioctl request"); + break; + } + return 0; + +} + + +static device_callbacks const hw_vm_callbacks = { + { hw_vm_init_address_callback, }, + { hw_vm_attach_address, + passthrough_device_address_detach, }, + { hw_vm_io_read_buffer_callback, + hw_vm_io_write_buffer_callback, }, + { NULL, passthrough_device_dma_write_buffer, }, + { NULL, }, /* interrupt */ + { generic_device_unit_decode, + generic_device_unit_encode, }, + NULL, /* instance */ + hw_vm_ioctl, +}; + + +static void * +hw_vm_create(const char *name, + const device_unit *address, + const char *args) +{ + hw_vm_device *vm = ZALLOC(hw_vm_device); + return vm; +} + +const device_descriptor hw_vm_device_descriptor[] = { + { "vm", hw_vm_create, &hw_vm_callbacks }, + { NULL }, +}; + +#endif _HW_VM_C_ diff --git a/sim/ppc/idecode_branch.h b/sim/ppc/idecode_branch.h new file mode 100644 index 00000000000..ecae98eb8e7 --- /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_expression.h b/sim/ppc/idecode_expression.h new file mode 100644 index 00000000000..1f9b7259add --- /dev/null +++ b/sim/ppc/idecode_expression.h @@ -0,0 +1,410 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1997, 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. + + */ + + +/* 32bit target expressions: + + Each calculation is performed three times using each of the + signed64, unsigned64 and long integer types. The macro ALU_END + (in _ALU_RESULT_VAL) then selects which of the three alternative + results will be used in the final assignment of the target + register. As this selection is determined at compile time by + fields in the instruction (OE, EA, Rc) the compiler has sufficient + information to firstly simplify the selection code into a single + case and then back anotate the equations and hence eliminate any + resulting dead code. That dead code being the calculations that, + as it turned out were not in the end needed. + + 64bit arrithemetic is used firstly because it allows the use of + gcc's efficient long long operators (typically efficiently output + inline) and secondly because the resultant answer will contain in + the low 32bits the answer while in the high 32bits is either carry + or status information. */ + +/* 64bit target expressions: + + Unfortunatly 128bit arrithemetic isn't that common. Consequently + the 32/64 bit trick can not be used. Instead all calculations are + required to retain carry/overflow information in separate + variables. Even with this restriction it is still possible for the + trick of letting the compiler discard the calculation of unneeded + values */ + + +/* Macro's to type cast 32bit constants to 64bits */ +#define SIGNED64(val) ((signed64)(signed32)(val)) +#define UNSIGNED64(val) ((unsigned64)(unsigned32)(val)) + + +/* Start a section of ALU code */ + +#define ALU_BEGIN(val) \ +{ \ + natural_word alu_val; \ + unsigned64 alu_carry_val; \ + signed64 alu_overflow_val; \ + ALU_SET(val) + + +/* assign the result to the target register */ + +#define ALU_END(TARG,CA,OE,Rc) \ +{ /* select the result to use */ \ + signed_word const alu_result = _ALU_RESULT_VAL(CA,OE,Rc); \ + /* determine the overflow bit if needed */ \ + if (OE) { \ + if ((((unsigned64)(alu_overflow_val & BIT64(0))) \ + >> 32) \ + == (alu_overflow_val & BIT64(32))) \ + XER &= (~xer_overflow); \ + else \ + XER |= (xer_summary_overflow | xer_overflow); \ + } \ + /* Update the carry bit if needed */ \ + if (CA) { \ + XER = ((XER & ~xer_carry) \ + | SHUFFLED32((alu_carry_val >> 32), 31, xer_carry_bit)); \ + /* if (alu_carry_val & BIT64(31)) \ + XER |= (xer_carry); \ + else \ + XER &= (~xer_carry); */ \ + } \ + TRACE(trace_alu, (" Result = %ld (0x%lx), XER = %ld\n", \ + (long)alu_result, (long)alu_result, (long)XER)); \ + /* Update the Result Conditions if needed */ \ + CR0_COMPARE(alu_result, 0, Rc); \ + /* assign targ same */ \ + TARG = alu_result; \ +}} + +/* select the result from the different options */ + +#define _ALU_RESULT_VAL(CA,OE,Rc) (WITH_TARGET_WORD_BITSIZE == 64 \ + ? alu_val \ + : (OE \ + ? alu_overflow_val \ + : (CA \ + ? alu_carry_val \ + : alu_val))) + + +/* More basic alu operations */ +#if (WITH_TARGET_WORD_BITSIZE == 64) +#define ALU_SET(val) \ +do { \ + alu_val = val; \ + alu_carry_val = ((unsigned64)alu_val) >> 32; \ + alu_overflow_val = ((signed64)alu_val) >> 32; \ +} while (0) +#endif +#if (WITH_TARGET_WORD_BITSIZE == 32) +#define ALU_SET(val) \ +do { \ + alu_val = val; \ + alu_carry_val = (unsigned32)(alu_val); \ + alu_overflow_val = (signed32)(alu_val); \ +} while (0) +#endif + +#if (WITH_TARGET_WORD_BITSIZE == 64) +#define ALU_ADD(val) \ +do { \ + unsigned64 alu_lo = (UNSIGNED64(alu_val) \ + + UNSIGNED64(val)); \ + signed alu_carry = ((alu_lo & BIT(31)) != 0); \ + alu_carry_val = (alu_carry_val \ + + UNSIGNED64(EXTRACTED(val, 0, 31)) \ + + alu_carry); \ + alu_overflow_val = (alu_overflow_val \ + + SIGNED64(EXTRACTED(val, 0, 31)) \ + + alu_carry); \ + alu_val = alu_val + val; \ +} while (0) +#endif +#if (WITH_TARGET_WORD_BITSIZE == 32) +#define ALU_ADD(val) \ +do { \ + alu_val += val; \ + alu_carry_val += (unsigned32)(val); \ + alu_overflow_val += (signed32)(val); \ +} while (0) +#endif + + +#if (WITH_TARGET_WORD_BITSIZE == 64) +#define ALU_ADD_CA \ +do { \ + signed carry = MASKED32(XER, xer_carry_bit, xer_carry_bit) != 0; \ + ALU_ADD(carry); \ +} while (0) +#endif +#if (WITH_TARGET_WORD_BITSIZE == 32) +#define ALU_ADD_CA \ +do { \ + signed carry = MASKED32(XER, xer_carry_bit, xer_carry_bit) != 0; \ + ALU_ADD(carry); \ +} while (0) +#endif + + +#if 0 +#if (WITH_TARGET_WORD_BITSIZE == 64) +#endif +#if (WITH_TARGET_WORD_BITSIZE == 32) +#define ALU_SUB(val) \ +do { \ + alu_val -= val; \ + alu_carry_val -= (unsigned32)(val); \ + alu_overflow_val -= (signed32)(val); \ +} while (0) +#endif +#endif + +#if (WITH_TARGET_WORD_BITSIZE == 64) +#endif +#if (WITH_TARGET_WORD_BITSIZE == 32) +#define ALU_OR(val) \ +do { \ + alu_val |= val; \ + alu_carry_val = (unsigned32)(alu_val); \ + alu_overflow_val = (signed32)(alu_val); \ +} while (0) +#endif + + +#if (WITH_TARGET_WORD_BITSIZE == 64) +#endif +#if (WITH_TARGET_WORD_BITSIZE == 32) +#define ALU_XOR(val) \ +do { \ + alu_val ^= val; \ + alu_carry_val = (unsigned32)(alu_val); \ + alu_overflow_val = (signed32)(alu_val); \ +} while (0) +#endif + + +#if 0 +#if (WITH_TARGET_WORD_BITSIZE == 64) +#endif +#if (WITH_TARGET_WORD_BITSIZE == 32) +#define ALU_NEGATE \ +do { \ + alu_val = -alu_val; \ + alu_carry_val = -alu_carry_val; \ + alu_overflow_val = -alu_overflow_val; \ +} while(0) +#endif +#endif + + +#if (WITH_TARGET_WORD_BITSIZE == 64) +#endif +#if (WITH_TARGET_WORD_BITSIZE == 32) +#define ALU_AND(val) \ +do { \ + alu_val &= val; \ + alu_carry_val = (unsigned32)(alu_val); \ + alu_overflow_val = (signed32)(alu_val); \ +} while (0) +#endif + + +#if (WITH_TARGET_WORD_BITSIZE == 64) +#define ALU_NOT \ +do { \ + signed64 new_alu_val = ~alu_val; \ + ALU_SET(new_alu_val); \ +} while (0) +#endif +#if (WITH_TARGET_WORD_BITSIZE == 32) +#define ALU_NOT \ +do { \ + signed new_alu_val = ~alu_val; \ + ALU_SET(new_alu_val); \ +} while(0) +#endif + + +/* Macros for updating the condition register */ + +#define CR1_UPDATE(Rc) \ +do { \ + if (Rc) { \ + CR_SET(1, EXTRACTED32(FPSCR, fpscr_fx_bit, fpscr_ox_bit)); \ + } \ +} while (0) + + +#define _DO_CR_COMPARE(LHS, RHS) \ +(((LHS) < (RHS)) \ + ? cr_i_negative \ + : (((LHS) > (RHS)) \ + ? cr_i_positive \ + : cr_i_zero)) + +#define CR_SET(REG, VAL) MBLIT32(CR, REG*4, REG*4+3, VAL) +#define CR_FIELD(REG) EXTRACTED32(CR, REG*4, REG*4+3) +#define CR_SET_XER_SO(REG, VAL) \ +do { \ + creg new_bits = ((XER & xer_summary_overflow) \ + ? (cr_i_summary_overflow | VAL) \ + : VAL); \ + CR_SET(REG, new_bits); \ +} while(0) + +#define CR_COMPARE(REG, LHS, RHS) \ +do { \ + creg new_bits = ((XER & xer_summary_overflow) \ + ? (cr_i_summary_overflow | _DO_CR_COMPARE(LHS,RHS)) \ + : _DO_CR_COMPARE(LHS,RHS)); \ + CR_SET(REG, new_bits); \ +} while (0) + +#define CR0_COMPARE(LHS, RHS, Rc) \ +do { \ + if (Rc) { \ + CR_COMPARE(0, LHS, RHS); \ + TRACE(trace_alu, \ + ("CR=0x%08lx, LHS=%ld, RHS=%ld\n", \ + (unsigned long)CR, (long)LHS, (long)RHS)); \ + } \ +} while (0) + + + +/* Bring data in from the cold */ + +#define MEM(SIGN, EA, NR_BYTES) \ +((SIGN##_##NR_BYTES) vm_data_map_read_##NR_BYTES(cpu_data_map(processor), EA, \ + processor, cia)) \ + +#define STORE(EA, NR_BYTES, VAL) \ +do { \ + vm_data_map_write_##NR_BYTES(cpu_data_map(processor), EA, VAL, \ + processor, cia); \ +} while (0) + + + +/* some FPSCR update macros. */ + +#define FPSCR_BEGIN \ +{ \ + fpscreg old_fpscr UNUSED = FPSCR + +#define FPSCR_END(Rc) { \ + /* always update VX */ \ + if ((FPSCR & fpscr_vx_bits)) \ + FPSCR |= fpscr_vx; \ + else \ + FPSCR &= ~fpscr_vx; \ + /* always update FEX */ \ + if (((FPSCR & fpscr_vx) && (FPSCR & fpscr_ve)) \ + || ((FPSCR & fpscr_ox) && (FPSCR & fpscr_oe)) \ + || ((FPSCR & fpscr_ux) && (FPSCR & fpscr_ue)) \ + || ((FPSCR & fpscr_zx) && (FPSCR & fpscr_ze)) \ + || ((FPSCR & fpscr_xx) && (FPSCR & fpscr_xe))) \ + FPSCR |= fpscr_fex; \ + else \ + FPSCR &= ~fpscr_fex; \ + CR1_UPDATE(Rc); \ + /* interrupt enabled? */ \ + if ((MSR & (msr_floating_point_exception_mode_0 \ + | msr_floating_point_exception_mode_1)) \ + && (FPSCR & fpscr_fex)) \ + program_interrupt(processor, cia, \ + floating_point_enabled_program_interrupt); \ +}} + +#define FPSCR_SET(REG, VAL) MBLIT32(FPSCR, REG*4, REG*4+3, VAL) +#define FPSCR_FIELD(REG) EXTRACTED32(FPSCR, REG*4, REG*4+3) + +#define FPSCR_SET_FPCC(VAL) MBLIT32(FPSCR, fpscr_fpcc_bit, fpscr_fpcc_bit+3, VAL) + +/* Handle various exceptions */ + +#define FPSCR_OR_VX(VAL) \ +do { \ + /* NOTE: VAL != 0 */ \ + FPSCR |= (VAL); \ + FPSCR |= fpscr_fx; \ +} while (0) + +#define FPSCR_SET_OX(COND) \ +do { \ + if (COND) { \ + FPSCR |= fpscr_ox; \ + FPSCR |= fpscr_fx; \ + } \ + else \ + FPSCR &= ~fpscr_ox; \ +} while (0) + +#define FPSCR_SET_UX(COND) \ +do { \ + if (COND) { \ + FPSCR |= fpscr_ux; \ + FPSCR |= fpscr_fx; \ + } \ + else \ + FPSCR &= ~fpscr_ux; \ +} while (0) + +#define FPSCR_SET_ZX(COND) \ +do { \ + if (COND) { \ + FPSCR |= fpscr_zx; \ + FPSCR |= fpscr_fx; \ + } \ + else \ + FPSCR &= ~fpscr_zx; \ +} while (0) + +#define FPSCR_SET_XX(COND) \ +do { \ + if (COND) { \ + FPSCR |= fpscr_xx; \ + FPSCR |= fpscr_fx; \ + } \ +} while (0) + +/* Note: code using SET_FI must also explicitly call SET_XX */ + +#define FPSCR_SET_FR(COND) do { \ + if (COND) \ + FPSCR |= fpscr_fr; \ + else \ + FPSCR &= ~fpscr_fr; \ +} while (0) + +#define FPSCR_SET_FI(COND) \ +do { \ + if (COND) { \ + FPSCR |= fpscr_fi; \ + } \ + else \ + FPSCR &= ~fpscr_fi; \ +} while (0) + +#define FPSCR_SET_FPRF(VAL) \ +do { \ + FPSCR = (FPSCR & ~fpscr_fprf) | (VAL); \ +} while (0) diff --git a/sim/ppc/idecode_fields.h b/sim/ppc/idecode_fields.h new file mode 100644 index 00000000000..1c449bc9769 --- /dev/null +++ b/sim/ppc/idecode_fields.h @@ -0,0 +1,105 @@ +/* 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 is_64bit_implementation (WITH_TARGET_WORD_BITSIZE == 64) +#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) + +#define GOTO(dest) goto XCONCAT4(label__,dest,__,MY_PREFIX) +#define LABEL(dest) XCONCAT4(label__,dest,__,MY_PREFIX) diff --git a/sim/ppc/igen.c b/sim/ppc/igen.c new file mode 100644 index 00000000000..dc87087e23b --- /dev/null +++ b/sim/ppc/igen.c @@ -0,0 +1,516 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1997, 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. + + */ + + + +#include <getopt.h> + +#include "misc.h" +#include "lf.h" +#include "table.h" +#include "config.h" + +#include "filter.h" + +#include "ld-decode.h" +#include "ld-cache.h" +#include "ld-insn.h" + +#include "igen.h" + +#include "gen-model.h" +#include "gen-icache.h" +#include "gen-itable.h" +#include "gen-idecode.h" +#include "gen-semantics.h" +#include "gen-support.h" + +int hi_bit_nr; +int insn_bit_size = max_insn_bit_size; + +igen_code code = generate_calls; + +int generate_expanded_instructions; +int icache_size = 1024; +int generate_smp; + +/****************************************************************/ + +static int +print_insn_bits(lf *file, insn_bits *bits) +{ + int nr = 0; + if (bits == NULL) + return nr; + nr += print_insn_bits(file, bits->last); + nr += lf_putchr(file, '_'); + nr += lf_putstr(file, bits->field->val_string); + if (bits->opcode->is_boolean && bits->value == 0) + nr += lf_putint(file, bits->opcode->boolean_constant); + else if (!bits->opcode->is_boolean) { + if (bits->opcode->last < bits->field->last) + nr += lf_putint(file, bits->value << (bits->field->last - bits->opcode->last)); + else + nr += lf_putint(file, bits->value); + } + return nr; +} + +extern int +print_function_name(lf *file, + const char *basename, + insn_bits *expanded_bits, + lf_function_name_prefixes prefix) +{ + int nr = 0; + /* the prefix */ + switch (prefix) { + case function_name_prefix_semantics: + nr += lf_putstr(file, "semantic_"); + break; + case function_name_prefix_idecode: + nr += lf_printf(file, "idecode_"); + break; + case function_name_prefix_itable: + nr += lf_putstr(file, "itable_"); + break; + case function_name_prefix_icache: + nr += lf_putstr(file, "icache_"); + break; + default: + break; + } + + /* the function name */ + { + const char *pos; + for (pos = basename; + *pos != '\0'; + pos++) { + switch (*pos) { + case '/': + case '-': + break; + case ' ': + nr += lf_putchr(file, '_'); + break; + default: + nr += lf_putchr(file, *pos); + break; + } + } + } + + /* the suffix */ + if (generate_expanded_instructions) + nr += print_insn_bits(file, expanded_bits); + + return nr; +} + + +void +print_my_defines(lf *file, + insn_bits *expanded_bits, + table_entry *file_entry) +{ + /* #define MY_INDEX xxxxx */ + lf_indent_suppress(file); + lf_printf(file, "#undef MY_INDEX\n"); + lf_indent_suppress(file); + lf_printf(file, "#define MY_INDEX "); + print_function_name(file, + file_entry->fields[insn_name], + NULL, + function_name_prefix_itable); + lf_printf(file, "\n"); + /* #define MY_PREFIX xxxxxx */ + lf_indent_suppress(file); + lf_printf(file, "#undef MY_PREFIX\n"); + lf_indent_suppress(file); + lf_printf(file, "#define MY_PREFIX "); + print_function_name(file, + file_entry->fields[insn_name], + expanded_bits, + function_name_prefix_none); + lf_printf(file, "\n"); +} + + +void +print_itrace(lf *file, + table_entry *file_entry, + int idecode) +{ + lf_print__external_reference(file, file_entry->line_nr, file_entry->file_name); + lf_printf(file, "ITRACE(trace_%s, (\"%s %s\\n\"));\n", + (idecode ? "idecode" : "semantics"), + (idecode ? "idecode" : "semantics"), + file_entry->fields[insn_name]); + lf_print__internal_reference(file); +} + + +/****************************************************************/ + + +static void +gen_semantics_h(insn_table *table, + lf *file, + igen_code generate) +{ + lf_printf(file, "typedef %s idecode_semantic\n(%s);\n", + SEMANTIC_FUNCTION_TYPE, + SEMANTIC_FUNCTION_FORMAL); + lf_printf(file, "\n"); + if ((code & generate_calls)) { + lf_printf(file, "#ifdef WITH_OPTION_MPC860C0\n"); + lf_printf(file, "extern int option_mpc860c0;\n"); + lf_printf(file, "#define PAGE_SIZE 0x1000\n"); + lf_printf(file, "\n"); + lf_printf(file, "EXTERN_SEMANTICS(void)\n"); + lf_printf(file, "semantic_init(device* root);\n"); + lf_printf(file, "\n"); + lf_printf(file, "#endif // WITH_OPTION_MPC860C0\n"); + if (generate_expanded_instructions) + insn_table_traverse_tree(table, + file, NULL, + 1, + NULL, /* start */ + print_semantic_declaration, /* leaf */ + NULL, /* end */ + NULL); /* padding */ + else + insn_table_traverse_insn(table, + file, NULL, + print_semantic_declaration); + + } + else { + lf_print__this_file_is_empty(file); + } +} + + +static void +gen_semantics_c(insn_table *table, + cache_table *cache_rules, + lf *file, + igen_code generate) +{ + if ((code & generate_calls)) { + 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, "#include \"support.h\"\n"); + lf_printf(file, "\n"); + lf_printf(file, "#ifdef WITH_OPTION_MPC860C0\n"); + lf_printf(file, "int option_mpc860c0 = 0;\n"); + lf_printf(file, "\n"); + lf_printf(file, "EXTERN_SEMANTICS(void)\n"); + lf_printf(file, "semantic_init(device* root)\n"); + lf_printf(file, "{\n"); + lf_printf(file, " option_mpc860c0 = 0;\n"); + lf_printf(file, " if (tree_find_property(root, \"/options/mpc860c0\"))\n"); + lf_printf(file, " option_mpc860c0 = tree_find_integer_property(root, \"/options/mpc860c0\");\n"); + lf_printf(file, "}\n"); + lf_printf(file, "\n"); + lf_printf(file, "#endif // WITH_OPTION_MPC860C0\n"); + if (generate_expanded_instructions) + insn_table_traverse_tree(table, + file, cache_rules, + 1, + NULL, /* start */ + print_semantic_definition, /* leaf */ + NULL, /* end */ + NULL); /* padding */ + else + insn_table_traverse_insn(table, + file, cache_rules, + print_semantic_definition); + + } + else { + lf_print__this_file_is_empty(file); + } +} + + +/****************************************************************/ + + +static void +gen_icache_h(insn_table *table, + lf *file, + igen_code generate) +{ + lf_printf(file, "typedef %s idecode_icache\n(%s);\n", + ICACHE_FUNCTION_TYPE, + ICACHE_FUNCTION_FORMAL); + lf_printf(file, "\n"); + if ((code & generate_calls) + && (code & generate_with_icache)) { + insn_table_traverse_function(table, + file, NULL, + print_icache_internal_function_declaration); + if (generate_expanded_instructions) + insn_table_traverse_tree(table, + file, NULL, + 1, + NULL, /* start */ + print_icache_declaration, /* leaf */ + NULL, /* end */ + NULL); /* padding */ + else + insn_table_traverse_insn(table, + file, NULL, + print_icache_declaration); + + } + else { + lf_print__this_file_is_empty(file); + } +} + +static void +gen_icache_c(insn_table *table, + cache_table *cache_rules, + lf *file, + igen_code generate) +{ + /* output `internal' invalid/floating-point unavailable functions + where needed */ + if ((code & generate_calls) + && (code & generate_with_icache)) { + 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, "#include \"icache.h\"\n"); + lf_printf(file, "#include \"support.h\"\n"); + lf_printf(file, "\n"); + insn_table_traverse_function(table, + file, NULL, + print_icache_internal_function_definition); + lf_printf(file, "\n"); + if (generate_expanded_instructions) + insn_table_traverse_tree(table, + file, cache_rules, + 1, + NULL, /* start */ + print_icache_definition, /* leaf */ + NULL, /* end */ + NULL); /* padding */ + else + insn_table_traverse_insn(table, + file, cache_rules, + print_icache_definition); + + } + else { + lf_print__this_file_is_empty(file); + } +} + + +/****************************************************************/ + + +int +main(int argc, + char **argv, + char **envp) +{ + cache_table *cache_rules = NULL; + lf_file_references file_references = lf_include_references; + decode_table *decode_rules = NULL; + filter *filters = NULL; + insn_table *instructions = NULL; + char *real_file_name = NULL; + int is_header = 0; + int ch; + + if (argc == 1) { + printf("Usage:\n"); + printf(" igen <config-opts> ... <input-opts>... <output-opts>...\n"); + printf("Config options:\n"); + printf(" -F <filter-out-flag> eg -F 64 to skip 64bit instructions\n"); + printf(" -E Expand (duplicate) semantic functions\n"); + printf(" -I <icache-size> Generate cracking cache version\n"); + printf(" -C Include semantics in cache functions\n"); + printf(" -S Include insn (instruction) in icache\n"); + printf(" -R Use defines to reference cache vars\n"); + printf(" -L Supress line numbering in output files\n"); + printf(" -B <bit-size> Set the number of bits in an instruction\n"); + printf(" -H <high-bit> Set the nr of the high (msb bit)\n"); + printf(" -N <nr-cpus> Specify the max number of cpus the simulation will support\n"); + printf(" -J Use jumps instead of function calls\n"); + printf(" -T <mechanism> Override the mechanism used to decode an instruction\n"); + printf(" using <mechanism> instead of what was specified in the\n"); + printf(" decode-rules input file\n"); + printf("\n"); + printf("Input options (ucase version also dumps loaded table):\n"); + printf(" -o <decode-rules>\n"); + printf(" -k <cache-rules>\n"); + printf(" -i <instruction-table>\n"); + printf("\n"); + printf("Output options:\n"); + printf(" -n <real-name> Specify the real name of for the next output file\n"); + printf(" -h Generate header file\n"); + printf(" -c <output-file> output icache\n"); + printf(" -d <output-file> output idecode\n"); + printf(" -m <output-file> output model\n"); + printf(" -s <output-file> output schematic\n"); + printf(" -t <output-file> output itable\n"); + printf(" -f <output-file> output support functions\n"); + } + + while ((ch = getopt(argc, argv, + "F:EI:RSLJT:CB:H:N:o:k:i:n:hc:d:m:s:t:f:")) + != -1) { + fprintf(stderr, "\t-%c %s\n", ch, (optarg ? optarg : "")); + switch(ch) { + case 'C': + code |= generate_with_icache; + code |= generate_with_semantic_icache; + break; + case 'S': + code |= generate_with_icache; + code |= generate_with_insn_in_icache; + break; + case 'L': + file_references = lf_omit_references; + break; + case 'E': + generate_expanded_instructions = 1; + break; + case 'I': + icache_size = a2i(optarg); + code |= generate_with_icache; + break; + case 'N': + generate_smp = a2i(optarg); + break; + case 'R': + code |= generate_with_direct_access; + break; + case 'B': + insn_bit_size = a2i(optarg); + ASSERT(insn_bit_size > 0 && insn_bit_size <= max_insn_bit_size + && (hi_bit_nr == insn_bit_size-1 || hi_bit_nr == 0)); + break; + case 'H': + hi_bit_nr = a2i(optarg); + ASSERT(hi_bit_nr == insn_bit_size-1 || hi_bit_nr == 0); + break; + case 'F': + filters = new_filter(optarg, filters); + break; + case 'J': + code &= ~generate_calls; + code |= generate_jumps; + break; + case 'T': + force_decode_gen_type(optarg); + break; + case 'i': + if (decode_rules == NULL || cache_rules == NULL) { + fprintf(stderr, "Must specify decode and cache tables\n"); + exit (1); + } + instructions = load_insn_table(optarg, decode_rules, filters); + fprintf(stderr, "\texpanding ...\n"); + insn_table_expand_insns(instructions); + break; + case 'o': + decode_rules = load_decode_table(optarg, hi_bit_nr); + break; + case 'k': + cache_rules = load_cache_table(optarg, hi_bit_nr); + break; + case 'n': + real_file_name = strdup(optarg); + break; + case 'h': + is_header = 1; + break; + case 's': + case 'd': + case 'm': + case 't': + case 'f': + case 'c': + { + lf *file = lf_open(optarg, real_file_name, file_references, + (is_header ? lf_is_h : lf_is_c), + argv[0]); + lf_print__file_start(file); + ASSERT(instructions != NULL); + switch (ch) { + case 's': + if(is_header) + gen_semantics_h(instructions, file, code); + else + gen_semantics_c(instructions, cache_rules, file, code); + break; + case 'd': + if (is_header) + gen_idecode_h(file, instructions, cache_rules); + else + gen_idecode_c(file, instructions, cache_rules); + break; + case 'm': + if (is_header) + gen_model_h(instructions, file); + else + gen_model_c(instructions, file); + break; + case 't': + if (is_header) + gen_itable_h(instructions, file); + else + gen_itable_c(instructions, file); + break; + case 'f': + if (is_header) + gen_support_h(instructions, file); + else + gen_support_c(instructions, file); + break; + case 'c': + if (is_header) + gen_icache_h(instructions, file, code); + else + gen_icache_c(instructions, cache_rules, file, code); + break; + } + lf_print__file_finish(file); + lf_close(file); + is_header = 0; + } + real_file_name = NULL; + break; + default: + error("unknown option\n"); + } + } + return 0; +} diff --git a/sim/ppc/igen.h b/sim/ppc/igen.h new file mode 100644 index 00000000000..d3462098905 --- /dev/null +++ b/sim/ppc/igen.h @@ -0,0 +1,199 @@ +/* This file is part of the program psim. + + Copyright (C) 1994,1995,1996, 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. + + */ + + +/* What does the instruction look like - bit ordering and size */ +extern int hi_bit_nr; +extern int insn_bit_size; + + +/* generation options: */ + + +enum { + generate_with_direct_access = 0x1, + generate_with_icache = 0x2, + generate_with_semantic_icache = 0x4, + generate_with_insn_in_icache = 0x8, +}; + + +typedef enum { + + /* Transfer control to an instructions semantic code using the the + standard call/return mechanism */ + + generate_calls = 0x100, + + /* In addition, when refering to fields access them directly instead + of via variables */ + + generate_calls_with_direct_access + = generate_calls | generate_with_direct_access, + + /* In addition, pre-decode an instructions opcode fields (entering + them into an icache) so that semantic code can avoid the need to + re-decode fields each time it is executed */ + + generate_calls_with_icache + = generate_calls | generate_with_icache, + + /* In addition, the instruction decode code includes a duplicated + copy of the instructions semantic code. This avoids the need to + perform two calls (one to decode an instructions opcode fields + and one to execute the instruction) when there is a miss of the + icache */ + + generate_calls_with_semantic_icache + = generate_calls_with_icache | generate_with_semantic_icache, + + /* In addition, the semantic function refers to icache entries + directly instead of first moving them into local variables */ + + generate_calls_with_direct_access_icache + = generate_calls_with_icache | generate_with_direct_access, + + generate_calls_with_direct_access_semantic_icache + = generate_calls_with_direct_access_icache | generate_with_semantic_icache, + + + /* Transfer control to an instructions semantic code using + (computed) goto's instead of the more conventional call/return + mechanism */ + + generate_jumps = 0x200, + + /* As for generate jumps but with instruction fields accessed + directly */ + + generate_jumps_with_direct_access + = generate_jumps | generate_with_direct_access, + + /* As for generate_calls_with_icache but applies to jumping code */ + + generate_jumps_with_icache + = generate_jumps | generate_with_icache, + + /* As for generate_calls_with_semantic_icache but applies to jumping + code */ + + generate_jumps_with_semantic_icache + = generate_jumps_with_icache | generate_with_semantic_icache, + + /* As for generate_calls_with_direct_access */ + + generate_jumps_with_direct_access_icache + = generate_jumps_with_icache | generate_with_direct_access, + + generate_jumps_with_direct_access_semantic_icache + = generate_jumps_with_direct_access_icache | generate_with_semantic_icache, + +} igen_code; + +extern igen_code code; + + + + +extern int icache_size; + + +/* Instruction expansion? + + Should the semantic code for each instruction, when the oportunity + arrises, be expanded according to the variable opcode files that + the instruction decode process renders constant */ + +extern int generate_expanded_instructions; + + +/* SMP? + + Should the generated code include SMP support (>0) and if so, for + how many processors? */ + +extern int generate_smp; + + + + +/* Misc junk */ + + + +/* Function header definitions */ + + +/* Cache functions: */ + +#define ICACHE_FUNCTION_FORMAL \ +"cpu *processor,\n\ + instruction_word instruction,\n\ + unsigned_word cia,\n\ + idecode_cache *cache_entry" + +#define ICACHE_FUNCTION_ACTUAL "processor, instruction, cia, cache_entry" + +#define ICACHE_FUNCTION_TYPE \ +((code & generate_with_semantic_icache) \ + ? SEMANTIC_FUNCTION_TYPE \ + : "idecode_semantic *") + + +/* Semantic functions: */ + +#define SEMANTIC_FUNCTION_FORMAL \ +((code & generate_with_icache) \ + ? "cpu *processor,\n idecode_cache *cache_entry,\n unsigned_word cia" \ + : "cpu *processor,\n instruction_word instruction,\n unsigned_word cia") + +#define SEMANTIC_FUNCTION_ACTUAL \ +((code & generate_with_icache) \ + ? "processor, instruction, cia, cache_entry" \ + : "processor, instruction, cia") + +#define SEMANTIC_FUNCTION_TYPE "unsigned_word" + + + +extern void print_my_defines +(lf *file, + insn_bits *expanded_bits, + table_entry *file_entry); + +extern void print_itrace +(lf *file, + table_entry *file_entry, + int idecode); + + +typedef enum { + function_name_prefix_semantics, + function_name_prefix_idecode, + function_name_prefix_itable, + function_name_prefix_icache, + function_name_prefix_none +} lf_function_name_prefixes; + +extern int print_function_name +(lf *file, + const char *basename, + insn_bits *expanded_bits, + lf_function_name_prefixes prefix); diff --git a/sim/ppc/inline.c b/sim/ppc/inline.c new file mode 100644 index 00000000000..43595a53424 --- /dev/null +++ b/sim/ppc/inline.c @@ -0,0 +1,98 @@ +/* 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 _INLINE_C_ +#define _INLINE_C_ + +#include "config.h" +#include "ppc-config.h" + +#include "inline.h" + +#if (BITS_INLINE & INCLUDE_MODULE) +#include "bits.c" +#endif + +#if (SIM_ENDIAN_INLINE & INCLUDE_MODULE) +#include "sim-endian.c" +#endif + +#if (ICACHE_INLINE & INCLUDE_MODULE) +#include "icache.c" +#endif + +#if (CORE_INLINE & INCLUDE_MODULE) +#include "corefile.c" +#endif + +#if (VM_INLINE & INCLUDE_MODULE) +#include "vm.c" +#endif + +#if (EVENTS_INLINE & INCLUDE_MODULE) +#include "events.c" +#endif + +#if (MODEL_INLINE & INCLUDE_MODULE) +#include "model.c" +#endif + +#if (OPTIONS_INLINE & INCLUDE_MODULE) +#include "options.c" +#endif + +#if (MON_INLINE & INCLUDE_MODULE) +#include "mon.c" +#endif + +#if (REGISTERS_INLINE & INCLUDE_MODULE) +#include "registers.c" +#endif + +#if (INTERRUPTS_INLINE & INCLUDE_MODULE) +#include "interrupts.c" +#endif + +#if (DEVICE_INLINE & INCLUDE_MODULE) +#include "device.c" +#endif + +#if (TREE_INLINE & INCLUDE_MODULE) +#include "tree.c" +#endif + +#if (SPREG_INLINE & INCLUDE_MODULE) +#include "spreg.c" +#endif + +#if (SEMANTICS_INLINE & INCLUDE_MODULE) +#include "semantics.c" +#endif + +#if (IDECODE_INLINE & INCLUDE_MODULE) +#include "idecode.c" +#endif + +#if (OS_EMUL_INLINE & INCLUDE_MODULE) +#include "os_emul.c" +#endif + +#endif diff --git a/sim/ppc/inline.h b/sim/ppc/inline.h new file mode 100644 index 00000000000..f88cd9e4904 --- /dev/null +++ b/sim/ppc/inline.h @@ -0,0 +1,490 @@ +/* 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 _INLINE_H_ +#define _INLINE_H_ + + +#define STATIC(TYPE) static TYPE + + +/* sim_endian is always inlined */ + +#if !defined(_SIM_ENDIAN_C_) && (SIM_ENDIAN_INLINE & INCLUDE_MODULE) +# if (SIM_ENDIAN_INLINE & INLINE_MODULE) +# define INLINE_SIM_ENDIAN(TYPE) static INLINE TYPE UNUSED +# define EXTERN_SIM_ENDIAN(TYPE) static TYPE UNUSED +# else +# define INLINE_SIM_ENDIAN(TYPE) static TYPE UNUSED +# define EXTERN_SIM_ENDIAN(TYPE) static TYPE UNUSED +# endif +#else +# define INLINE_SIM_ENDIAN(TYPE) TYPE +# define EXTERN_SIM_ENDIAN(TYPE) TYPE +#endif + +#if (SIM_ENDIAN_INLINE & INLINE_LOCALS) +# define STATIC_INLINE_SIM_ENDIAN(TYPE) static INLINE TYPE +#else +# define STATIC_INLINE_SIM_ENDIAN(TYPE) static TYPE +#endif + + +/* bits is always inlined */ + +#if !defined(_BITS_C_) && (BITS_INLINE & INCLUDE_MODULE) +# if (BITS_INLINE & INLINE_MODULE) +# define INLINE_BITS(TYPE) static INLINE TYPE UNUSED +# define EXTERN_BITS(TYPE) static TYPE UNUSED +# else +# define INLINE_BITS(TYPE) static TYPE UNUSED +# define EXTERN_BITS(TYPE) static TYPE UNUSED +# endif +#else +# define INLINE_BITS(TYPE) TYPE +# define EXTERN_BITS(TYPE) TYPE +#endif + +#if (BITS_INLINE & INLINE_LOCALS) +# define STATIC_INLINE_BITS(TYPE) static INLINE TYPE +#else +# define STATIC_INLINE_BITS(TYPE) static TYPE +#endif + + +/* core is inlined with inline.c */ + +#if defined(_INLINE_C_) && !defined(_CORE_C_) && (CORE_INLINE & INCLUDE_MODULE) +# if (CORE_INLINE & INLINE_MODULE) +# define INLINE_CORE(TYPE) static INLINE TYPE UNUSED +# define EXTERN_CORE(TYPE) static TYPE UNUSED +#else +# define INLINE_CORE(TYPE) static TYPE UNUSED +# define EXTERN_CORE(TYPE) static TYPE UNUSED +#endif +#else +# define INLINE_CORE(TYPE) TYPE +# define EXTERN_CORE(TYPE) TYPE +#endif + +#if (CORE_INLINE & INLINE_LOCALS) +# define STATIC_INLINE_CORE(TYPE) static INLINE TYPE +#else +# define STATIC_INLINE_CORE(TYPE) static TYPE +#endif + + +/* vm is inlined with inline.c */ + +#if defined(_INLINE_C_) && !defined(_VM_C_) && (VM_INLINE & INCLUDE_MODULE) +# if (VM_INLINE & INLINE_MODULE) +# define INLINE_VM(TYPE) static INLINE TYPE UNUSED +# define EXTERN_VM(TYPE) static TYPE UNUSED +#else +# define INLINE_VM(TYPE) static TYPE UNUSED +# define EXTERN_VM(TYPE) static TYPE UNUSED +#endif +#else +# define INLINE_VM(TYPE) TYPE +# define EXTERN_VM(TYPE) TYPE +#endif + +#if (VM_INLINE & INLINE_LOCALS) +# define STATIC_INLINE_VM(TYPE) static INLINE TYPE +#else +# define STATIC_INLINE_VM(TYPE) static TYPE +#endif + + +/* cpu is always inlined */ + +#if !defined(_CPU_C_) && (CPU_INLINE & INCLUDE_MODULE) +# if (CPU_INLINE & INLINE_MODULE) +# define INLINE_CPU(TYPE) static INLINE TYPE UNUSED +# define EXTERN_CPU(TYPE) static TYPE UNUSED +#else +# define INLINE_CPU(TYPE) static TYPE UNUSED +# define EXTERN_CPU(TYPE) static TYPE UNUSED +#endif +#else +# define INLINE_CPU(TYPE) TYPE +# define EXTERN_CPU(TYPE) TYPE +#endif + +#if (CPU_INLINE & INLINE_LOCALS) +# define STATIC_INLINE_CPU(TYPE) static INLINE TYPE +#else +# define STATIC_INLINE_CPU(TYPE) static TYPE +#endif + + +/* model is inlined with inline.c */ + +#if defined(_INLINE_C_) && !defined(_MODEL_C_) && (MODEL_INLINE & INCLUDE_MODULE) +# if (MODEL_INLINE & INLINE_MODULE) +# define INLINE_MODEL(TYPE) static INLINE TYPE UNUSED +# define EXTERN_MODEL(TYPE) static TYPE UNUSED +#else +# define INLINE_MODEL(TYPE) static TYPE UNUSED +# define EXTERN_MODEL(TYPE) static TYPE UNUSED +#endif +#else +# define INLINE_MODEL(TYPE) TYPE +# define EXTERN_MODEL(TYPE) TYPE +#endif + +#if (MODEL_INLINE & INLINE_LOCALS) +# define STATIC_INLINE_MODEL(TYPE) static INLINE TYPE +#else +# define STATIC_INLINE_MODEL(TYPE) static TYPE +#endif + + +/* events is inlined with inline.c */ + +#if defined(_INLINE_C_) && !defined(_EVENTS_C_) && (EVENTS_INLINE & INCLUDE_MODULE) +# if (EVENTS_INLINE & INLINE_MODULE) +# define INLINE_EVENTS(TYPE) static INLINE TYPE UNUSED +# define EXTERN_EVENTS(TYPE) static TYPE UNUSED +#else +# define INLINE_EVENTS(TYPE) static TYPE UNUSED +# define EXTERN_EVENTS(TYPE) static TYPE UNUSED +#endif +#else +# define INLINE_EVENTS(TYPE) TYPE +# define EXTERN_EVENTS(TYPE) TYPE +#endif + +#if (EVENTS_INLINE & INLINE_LOCALS) +# define STATIC_INLINE_EVENTS(TYPE) static INLINE TYPE +#else +# define STATIC_INLINE_EVENTS(TYPE) static TYPE +#endif + + +/* mon is inlined with inline.c */ + +#if defined(_INLINE_C_) && !defined(_MON_C_) && (MON_INLINE & INCLUDE_MODULE) +# if (MON_INLINE & INLINE_MODULE) +# define INLINE_MON(TYPE) static INLINE TYPE UNUSED +# define EXTERN_MON(TYPE) static TYPE UNUSED +#else +# define INLINE_MON(TYPE) static TYPE UNUSED +# define EXTERN_MON(TYPE) static TYPE UNUSED +#endif +#else +# define INLINE_MON(TYPE) TYPE +# define EXTERN_MON(TYPE) TYPE +#endif + +#if (MON_INLINE & INLINE_LOCALS) +# define STATIC_INLINE_MON(TYPE) static INLINE TYPE +#else +# define STATIC_INLINE_MON(TYPE) static TYPE +#endif + + +/* registers is inlined with inline.c */ + +#if defined(_INLINE_C_) && !defined(_REGISTERS_C_) && (REGISTERS_INLINE & INCLUDE_MODULE) +# if (REGISTERS_INLINE & INLINE_MODULE) +# define INLINE_REGISTERS(TYPE) static INLINE TYPE UNUSED +# define EXTERN_REGISTERS(TYPE) static TYPE UNUSED +#else +# define INLINE_REGISTERS(TYPE) static TYPE UNUSED +# define EXTERN_REGISTERS(TYPE) static TYPE UNUSED +#endif +#else +# define INLINE_REGISTERS(TYPE) TYPE +# define EXTERN_REGISTERS(TYPE) TYPE +#endif + +#if (REGISTERS_INLINE & INLINE_LOCALS) +# define STATIC_INLINE_REGISTERS(TYPE) static INLINE TYPE +#else +# define STATIC_INLINE_REGISTERS(TYPE) static TYPE +#endif + + +/* interrupts is inlined with inline.c */ + +#if defined(_INLINE_C_) && !defined(_INTERRUPTS_C_) && (INTERRUPTS_INLINE & INCLUDE_MODULE) +# if (INTERRUPTS_INLINE & INLINE_MODULE) +# define INLINE_INTERRUPTS(TYPE) static INLINE TYPE UNUSED +# define EXTERN_INTERRUPTS(TYPE) static TYPE UNUSED +#else +# define INLINE_INTERRUPTS(TYPE) static TYPE UNUSED +# define EXTERN_INTERRUPTS(TYPE) static TYPE UNUSED +#endif +#else +# define INLINE_INTERRUPTS(TYPE) TYPE +# define EXTERN_INTERRUPTS(TYPE) TYPE +#endif + +#if (INTERRUPTS_INLINE & INLINE_LOCALS) +# define STATIC_INLINE_INTERRUPTS(TYPE) static INLINE TYPE +#else +# define STATIC_INLINE_INTERRUPTS(TYPE) static TYPE +#endif + + +/* device is inlined with inline.c */ + +#if defined(_INLINE_C_) && !defined(_DEVICE_C_) && (DEVICE_INLINE & INCLUDE_MODULE) +# if (DEVICE_INLINE & INLINE_MODULE) +# define INLINE_DEVICE(TYPE) static INLINE TYPE UNUSED +# define EXTERN_DEVICE(TYPE) static TYPE UNUSED +#else +# define INLINE_DEVICE(TYPE) static TYPE UNUSED +# define EXTERN_DEVICE(TYPE) static TYPE UNUSED +#endif +#else +# define INLINE_DEVICE(TYPE) TYPE +# define EXTERN_DEVICE(TYPE) TYPE +#endif + +#if (DEVICE_INLINE & INLINE_LOCALS) +# define STATIC_INLINE_DEVICE(TYPE) static INLINE TYPE +#else +# define STATIC_INLINE_DEVICE(TYPE) static TYPE +#endif + + +/* tree is inlined with inline.c */ + +#if defined(_INLINE_C_) && !defined(_TREE_C_) && (TREE_INLINE & INCLUDE_MODULE) +# if (TREE_INLINE & INLINE_MODULE) +# define INLINE_TREE(TYPE) static INLINE TYPE UNUSED +# define EXTERN_TREE(TYPE) static TYPE UNUSED +#else +# define INLINE_TREE(TYPE) static TYPE UNUSED +# define EXTERN_TREE(TYPE) static TYPE UNUSED +#endif +#else +# define INLINE_TREE(TYPE) TYPE +# define EXTERN_TREE(TYPE) TYPE +#endif + +#if (TREE_INLINE & INLINE_LOCALS) +# define STATIC_INLINE_TREE(TYPE) static INLINE TYPE +#else +# define STATIC_INLINE_TREE(TYPE) static TYPE +#endif + + +/* spreg is inlined with inline.c */ + +#if defined(_INLINE_C_) && !defined(_SPREG_C_) && (SPREG_INLINE & INCLUDE_MODULE) +# if (SPREG_INLINE & INLINE_MODULE) +# define INLINE_SPREG(TYPE) static INLINE TYPE UNUSED +# define EXTERN_SPREG(TYPE) static TYPE UNUSED +#else +# define INLINE_SPREG(TYPE) static TYPE UNUSED +# define EXTERN_SPREG(TYPE) static TYPE UNUSED +#endif +#else +# define INLINE_SPREG(TYPE) TYPE +# define EXTERN_SPREG(TYPE) TYPE +#endif + +#if (SPREG_INLINE & INLINE_LOCALS) +# define STATIC_INLINE_SPREG(TYPE) static INLINE TYPE +#else +# define STATIC_INLINE_SPREG(TYPE) static TYPE +#endif + + +/* semantics is inlined with inline.c */ + +#if defined(_INLINE_C_) && !defined(_SEMANTICS_C_) && (SEMANTICS_INLINE & INCLUDE_MODULE) +# if (SEMANTICS_INLINE & INLINE_MODULE) +# define INLINE_SEMANTICS(TYPE) static INLINE TYPE UNUSED +# define EXTERN_SEMANTICS(TYPE) static TYPE UNUSED REGPARM +#else +# define INLINE_SEMANTICS(TYPE) static TYPE UNUSED REGPARM +# define EXTERN_SEMANTICS(TYPE) static TYPE UNUSED REGPARM +#endif +#else +# define INLINE_SEMANTICS(TYPE) TYPE REGPARM +# define EXTERN_SEMANTICS(TYPE) TYPE REGPARM +#endif + +#if (SEMANTICS_INLINE & INLINE_LOCALS) +# define STATIC_INLINE_SEMANTICS(TYPE) static INLINE TYPE +#else +# define STATIC_INLINE_SEMANTICS(TYPE) static TYPE REGPARM +#endif + + +/* idecode is actually not inlined */ + +#if defined(_INLINE_C_) && !defined(_IDECODE_C_) && (IDECODE_INLINE & INCLUDE_MODULE) +# if (IDECODE_INLINE & INLINE_MODULE) +# define INLINE_IDECODE(TYPE) static INLINE TYPE UNUSED +# define EXTERN_IDECODE(TYPE) static TYPE UNUSED REGPARM +#else +# define INLINE_IDECODE(TYPE) static TYPE UNUSED REGPARM +# define EXTERN_IDECODE(TYPE) static TYPE UNUSED REGPARM +#endif +#else +# define INLINE_IDECODE(TYPE) TYPE REGPARM +# define EXTERN_IDECODE(TYPE) TYPE REGPARM +#endif + +#if (IDECODE_INLINE & INLINE_LOCALS) +# define STATIC_INLINE_IDECODE(TYPE) static INLINE TYPE +#else +# define STATIC_INLINE_IDECODE(TYPE) static TYPE REGPARM +#endif + + +/* icache is inlined with inline.c */ + +#if defined(_INLINE_C_) && !defined(_ICACHE_C_) && (ICACHE_INLINE & INCLUDE_MODULE) +# if (ICACHE_INLINE & INLINE_MODULE) +# define INLINE_ICACHE(TYPE) static INLINE TYPE UNUSED +# define EXTERN_ICACHE(TYPE) static TYPE UNUSED REGPARM +#else +# define INLINE_ICACHE(TYPE) static TYPE UNUSED REGPARM +# define EXTERN_ICACHE(TYPE) static TYPE UNUSED REGPARM +#endif +#else +# define INLINE_ICACHE(TYPE) TYPE REGPARM +# define EXTERN_ICACHE(TYPE) TYPE REGPARM +#endif + +#if (ICACHE_INLINE & INLINE_LOCALS) +# define STATIC_INLINE_ICACHE(TYPE) static INLINE TYPE +#else +# define STATIC_INLINE_ICACHE(TYPE) static TYPE REGPARM +#endif + + +/* support is always inlined */ + +#if !defined(_SUPPORT_C_) && (SUPPORT_INLINE & INCLUDE_MODULE) +# if (SUPPORT_INLINE & INLINE_MODULE) +# define INLINE_SUPPORT(TYPE) static INLINE TYPE UNUSED +# define EXTERN_SUPPORT(TYPE) static TYPE UNUSED +#else +# define INLINE_SUPPORT(TYPE) static TYPE UNUSED +# define EXTERN_SUPPORT(TYPE) static TYPE UNUSED +#endif +#else +# define INLINE_SUPPORT(TYPE) TYPE +# define EXTERN_SUPPORT(TYPE) TYPE +#endif + +#if (SUPPORT_INLINE & INLINE_LOCALS) +# define STATIC_INLINE_SUPPORT(TYPE) static INLINE TYPE +#else +# define STATIC_INLINE_SUPPORT(TYPE) static TYPE +#endif + + +/* options is inlined with inline.c */ + +#if defined(_INLINE_C_) && !defined(_OPTIONS_C_) && (OPTIONS_INLINE & INCLUDE_MODULE) +# if (OPTIONS_INLINE & INLINE_MODULE) +# define INLINE_OPTIONS(TYPE) static INLINE TYPE UNUSED +# define EXTERN_OPTIONS(TYPE) static TYPE UNUSED +#else +# define INLINE_OPTIONS(TYPE) static TYPE UNUSED +# define EXTERN_OPTIONS(TYPE) static TYPE UNUSED +#endif +#else +# define INLINE_OPTIONS(TYPE) TYPE +# define EXTERN_OPTIONS(TYPE) TYPE +#endif + +#if (OPTIONS_INLINE & INLINE_LOCALS) +# define STATIC_INLINE_OPTIONS(TYPE) static INLINE TYPE +#else +# define STATIC_INLINE_OPTIONS(TYPE) static TYPE +#endif + + +/* os_emul is inlined with inline.c */ + +#if defined(_INLINE_C_) && !defined(_OS_EMUL_C_) && (OS_EMUL_INLINE & INCLUDE_MODULE) +# if (OS_EMUL_INLINE & INLINE_MODULE) +# define INLINE_OS_EMUL(TYPE) static INLINE TYPE UNUSED +# define EXTERN_OS_EMUL(TYPE) static TYPE UNUSED +#else +# define INLINE_OS_EMUL(TYPE) static TYPE UNUSED +# define EXTERN_OS_EMUL(TYPE) static TYPE UNUSED +#endif +#else +# define INLINE_OS_EMUL(TYPE) TYPE +# define EXTERN_OS_EMUL(TYPE) TYPE +#endif + +#if (OS_EMUL_INLINE & INLINE_LOCALS) +# define STATIC_INLINE_OS_EMUL(TYPE) static INLINE TYPE +#else +# define STATIC_INLINE_OS_EMUL(TYPE) static TYPE +#endif + + +/* psim is actually not inlined */ + +#if defined(_INLINE_C_) && !defined(_PSIM_C_) && (PSIM_INLINE & INCLUDE_MODULE) +# if (PSIM_INLINE & INLINE_MODULE) +# define INLINE_PSIM(TYPE) static INLINE TYPE UNUSED +# define EXTERN_PSIM(TYPE) static TYPE UNUSED +#else +# define INLINE_PSIM(TYPE) static TYPE UNUSED +# define EXTERN_PSIM(TYPE) static TYPE UNUSED +#endif +#else +# define INLINE_PSIM(TYPE) TYPE +# define EXTERN_PSIM(TYPE) TYPE +#endif + +#if (PSIM_INLINE & INLINE_LOCALS) +# define STATIC_INLINE_PSIM(TYPE) static INLINE TYPE +#else +# define STATIC_INLINE_PSIM(TYPE) static TYPE +#endif + + +/* cap is inlined with inline.c */ + +#if defined(_INLINE_C_) && !defined(_CAP_C_) && (CAP_INLINE & INCLUDE_MODULE) +# if (CAP_INLINE & INLINE_MODULE) +# define INLINE_CAP(TYPE) static INLINE TYPE UNUSED +# define EXTERN_CAP(TYPE) static TYPE UNUSED +#else +# define INLINE_CAP(TYPE) static TYPE UNUSED +# define EXTERN_CAP(TYPE) static TYPE UNUSED +#endif +#else +# define INLINE_CAP(TYPE) TYPE +# define EXTERN_CAP(TYPE) TYPE +#endif + +#if (CAP_INLINE & INLINE_LOCALS) +# define STATIC_INLINE_CAP(TYPE) static INLINE TYPE +#else +# define STATIC_INLINE_CAP(TYPE) static TYPE +#endif + +#endif diff --git a/sim/ppc/interrupts.c b/sim/ppc/interrupts.c new file mode 100644 index 00000000000..7a13f7609b2 --- /dev/null +++ b/sim/ppc/interrupts.c @@ -0,0 +1,545 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1997, 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 _INTERRUPTS_C_ +#define _INTERRUPTS_C_ + +#include <signal.h> + +#include "cpu.h" +#include "idecode.h" +#include "os_emul.h" + + +/* Operating environment support code + + Unlike the VEA, the OEA must fully model the effect an interrupt + has on the processors state. + + Each function below return updated values for registers effected by + interrupts */ + + +STATIC_INLINE_INTERRUPTS\ +(msreg) +interrupt_msr(msreg old_msr, + msreg msr_clear, + msreg msr_set) +{ + msreg msr_set_to_0 = (msr_branch_trace_enable + | msr_data_relocate + | msr_external_interrupt_enable + | msr_floating_point_exception_mode_0 + | msr_floating_point_exception_mode_1 + | msr_floating_point_available + | msr_instruction_relocate + | msr_power_management_enable + | msr_problem_state + | msr_recoverable_interrupt + | msr_single_step_trace_enable); + /* remember, in 32bit mode msr_64bit_mode is zero */ + msreg new_msr = ((((old_msr & ~msr_set_to_0) + | msr_64bit_mode) + & ~msr_clear) + | msr_set); + return new_msr; +} + + +STATIC_INLINE_INTERRUPTS\ +(msreg) +interrupt_srr1(msreg old_msr, + msreg srr1_clear, + msreg srr1_set) +{ + spreg srr1_mask = (MASK(0,32) + | MASK(37, 41) + | MASK(48, 63)); + spreg srr1 = (old_msr & srr1_mask & ~srr1_clear) | srr1_set; + return srr1; +} + + +STATIC_INLINE_INTERRUPTS\ +(unsigned_word) +interrupt_base_ea(msreg msr) +{ + if (msr & msr_interrupt_prefix) + return MASK(0, 43); + else + return 0; +} + + +/* finish off an interrupt for the OEA model, updating all registers + and forcing a restart of the processor */ + +STATIC_INLINE_INTERRUPTS\ +(unsigned_word) +perform_oea_interrupt(cpu *processor, + unsigned_word cia, + unsigned_word vector_offset, + msreg msr_clear, + msreg msr_set, + msreg srr1_clear, + msreg srr1_set) +{ + msreg old_msr = MSR; + msreg new_msr = interrupt_msr(old_msr, msr_clear, msr_set); + unsigned_word nia; + if (!(old_msr & msr_recoverable_interrupt)) { + cpu_error(processor, cia, + "double interrupt - MSR[RI] bit clear when attempting to deliver interrupt, cia=0x%lx, msr=0x%lx; srr0=0x%lx(cia), srr1=0x%lx(msr); trap-vector=0x%lx, trap-msr=0x%lx", + (unsigned long)cia, + (unsigned long)old_msr, + (unsigned long)SRR0, + (unsigned long)SRR1, + (unsigned long)vector_offset, + (unsigned long)new_msr); + } + SRR0 = (spreg)(cia); + SRR1 = interrupt_srr1(old_msr, srr1_clear, srr1_set); + MSR = new_msr; + nia = interrupt_base_ea(new_msr) + vector_offset; + cpu_synchronize_context(processor, cia); + return nia; +} + + +INLINE_INTERRUPTS\ +(void) +machine_check_interrupt(cpu *processor, + unsigned_word cia) +{ + switch (CURRENT_ENVIRONMENT) { + + case USER_ENVIRONMENT: + case VIRTUAL_ENVIRONMENT: + cpu_error(processor, cia, "machine-check interrupt"); + + case OPERATING_ENVIRONMENT: + TRACE(trace_interrupts, ("machine-check interrupt - cia=0x%lx\n", + (unsigned long)cia)); + cia = perform_oea_interrupt(processor, cia, 0x00200, 0, 0, 0, 0); + cpu_restart(processor, cia); + + default: + error("internal error - machine_check_interrupt - bad switch"); + + } +} + + +INLINE_INTERRUPTS\ +(void) +data_storage_interrupt(cpu *processor, + unsigned_word cia, + unsigned_word ea, + storage_interrupt_reasons reason, + int is_store) +{ + switch (CURRENT_ENVIRONMENT) { + + case USER_ENVIRONMENT: + case VIRTUAL_ENVIRONMENT: + error("internal error - data_storage_interrupt - should not be called in VEA mode"); + break; + + case OPERATING_ENVIRONMENT: + { + spreg direction = (is_store ? dsisr_store_operation : 0); + switch (reason) { + case direct_store_storage_interrupt: + DSISR = dsisr_direct_store_error_exception | direction; + break; + case hash_table_miss_storage_interrupt: + DSISR = dsisr_hash_table_or_dbat_miss | direction; + break; + case protection_violation_storage_interrupt: + DSISR = dsisr_protection_violation | direction; + break; + case earwax_violation_storage_interrupt: + DSISR = dsisr_earwax_violation | direction; + break; + case segment_table_miss_storage_interrupt: + DSISR = dsisr_segment_table_miss | direction; + break; + case earwax_disabled_storage_interrupt: + DSISR = dsisr_earwax_disabled | direction; + break; + default: + error("internal error - data_storage_interrupt - reason %d not implemented", reason); + break; + } + DAR = (spreg)ea; + TRACE(trace_interrupts, ("data storage interrupt - cia=0x%lx DAR=0x%lx DSISR=0x%lx\n", + (unsigned long)cia, + (unsigned long)DAR, + (unsigned long)DSISR)); + cia = perform_oea_interrupt(processor, cia, 0x00300, 0, 0, 0, 0); + cpu_restart(processor, cia); + } + + default: + error("internal error - data_storage_interrupt - bad switch"); + + } +} + + +INLINE_INTERRUPTS\ +(void) +instruction_storage_interrupt(cpu *processor, + unsigned_word cia, + storage_interrupt_reasons reason) +{ + switch (CURRENT_ENVIRONMENT) { + + case USER_ENVIRONMENT: + case VIRTUAL_ENVIRONMENT: + error("internal error - instruction_storage_interrupt - should not be called in VEA mode"); + + case OPERATING_ENVIRONMENT: + { + msreg srr1_set; + switch(reason) { + case hash_table_miss_storage_interrupt: + srr1_set = srr1_hash_table_or_ibat_miss; + break; + case direct_store_storage_interrupt: + srr1_set = srr1_direct_store_error_exception; + break; + case protection_violation_storage_interrupt: + srr1_set = srr1_protection_violation; + break; + case segment_table_miss_storage_interrupt: + srr1_set = srr1_segment_table_miss; + break; + default: + srr1_set = 0; + error("internal error - instruction_storage_interrupt - reason %d not implemented"); + break; + } + TRACE(trace_interrupts, ("instruction storage interrupt - cia=0x%lx SRR1|=0x%lx\n", + (unsigned long)cia, + (unsigned long)srr1_set)); + cia = perform_oea_interrupt(processor, cia, 0x00400, 0, 0, 0, srr1_set); + cpu_restart(processor, cia); + } + + default: + error("internal error - instruction_storage_interrupt - bad switch"); + + } +} + + + +INLINE_INTERRUPTS\ +(void) +alignment_interrupt(cpu *processor, + unsigned_word cia, + unsigned_word ra) +{ + switch (CURRENT_ENVIRONMENT) { + + case USER_ENVIRONMENT: + case VIRTUAL_ENVIRONMENT: + cpu_error(processor, cia, "alignment interrupt - ra=0x%lx", ra); + + case OPERATING_ENVIRONMENT: + DAR = (spreg)ra; + DSISR = 0; /* FIXME */ + TRACE(trace_interrupts, ("alignment interrupt - cia=0x%lx DAR=0x%lx DSISR=0x%lx\n", + (unsigned long)cia, + (unsigned long)DAR, + (unsigned long)DSISR)); + cia = perform_oea_interrupt(processor, cia, 0x00600, 0, 0, 0, 0); + cpu_restart(processor, cia); + + default: + error("internal error - alignment_interrupt - bad switch"); + + } +} + + + + +INLINE_INTERRUPTS\ +(void) +program_interrupt(cpu *processor, + unsigned_word cia, + program_interrupt_reasons reason) +{ + switch (CURRENT_ENVIRONMENT) { + + case USER_ENVIRONMENT: + case VIRTUAL_ENVIRONMENT: + switch (reason) { + case floating_point_enabled_program_interrupt: + cpu_error(processor, cia, "program interrupt - %s", + "floating point enabled"); + break; + case illegal_instruction_program_interrupt: + cpu_error(processor, cia, "program interrupt - %s", + "illegal instruction"); + break; + case privileged_instruction_program_interrupt: + cpu_error(processor, cia, "program interrupt - %s", + "privileged instruction"); + break; + case trap_program_interrupt: + cpu_error(processor, cia, "program interrupt - %s", + "trap"); + break; + case optional_instruction_program_interrupt: + cpu_error(processor, cia, "program interrupt - %s", + "illegal instruction (optional instruction not supported)"); + break; +#ifdef WITH_OPTION_MPC860C0 + case mpc860c0_instruction_program_interrupt: + cpu_error(processor, cia, "program interrupt - %s", + "problematic branch detected, see MPC860 C0 errata"); + break; +#endif // WITH_OPTION_MPC860C0 + default: + error("internal error - program_interrupt - reason %d not implemented", reason); + } + + case OPERATING_ENVIRONMENT: + { + msreg srr1_set; + switch (reason) { + case floating_point_enabled_program_interrupt: + srr1_set = srr1_floating_point_enabled; + break; + case optional_instruction_program_interrupt: + case illegal_instruction_program_interrupt: + srr1_set = srr1_illegal_instruction; + break; + case privileged_instruction_program_interrupt: + srr1_set = srr1_priviliged_instruction; + break; + case trap_program_interrupt: + srr1_set = srr1_trap; + break; +#ifdef WITH_OPTION_MPC860C0 + case mpc860c0_instruction_program_interrupt: + srr1_set = 0; + error(processor, cia, "program interrupt - %s", + "problematic branch detected, see MPC860 C0 errata"); + break; +#endif // WITH_OPTION_MPC860C0 + default: + srr1_set = 0; + error("internal error - program_interrupt - reason %d not implemented", reason); + break; + } + TRACE(trace_interrupts, ("program interrupt - cia=0x%lx SRR1|=0x%lx\n", + (unsigned long)cia, + (unsigned long)srr1_set)); + cia = perform_oea_interrupt(processor, cia, 0x00700, 0, 0, 0, srr1_set); + cpu_restart(processor, cia); + } + + default: + error("internal error - program_interrupt - bad switch"); + + } +} + + +INLINE_INTERRUPTS\ +(void) +floating_point_unavailable_interrupt(cpu *processor, + unsigned_word cia) +{ + switch (CURRENT_ENVIRONMENT) { + + case USER_ENVIRONMENT: + case VIRTUAL_ENVIRONMENT: + cpu_error(processor, cia, "floating-point unavailable interrupt"); + + case OPERATING_ENVIRONMENT: + TRACE(trace_interrupts, ("floating-point unavailable interrupt - cia=0x%lx\n", + (unsigned long)cia)); + cia = perform_oea_interrupt(processor, cia, 0x00800, 0, 0, 0, 0); + cpu_restart(processor, cia); + + default: + error("internal error - floating_point_unavailable_interrupt - bad switch"); + + } +} + + +INLINE_INTERRUPTS\ +(void) +system_call_interrupt(cpu *processor, + unsigned_word cia) +{ + TRACE(trace_interrupts, ("system-call interrupt - cia=0x%lx\n", (unsigned long)cia)); + + switch (CURRENT_ENVIRONMENT) { + + case USER_ENVIRONMENT: + case VIRTUAL_ENVIRONMENT: + os_emul_system_call(processor, cia); + cpu_restart(processor, cia+4); + + case OPERATING_ENVIRONMENT: + cia = perform_oea_interrupt(processor, cia+4, 0x00c00, 0, 0, 0, 0); + cpu_restart(processor, cia); + + default: + error("internal error - system_call_interrupt - bad switch"); + + } +} + +INLINE_INTERRUPTS\ +(void) +floating_point_assist_interrupt(cpu *processor, + unsigned_word cia) +{ + switch (CURRENT_ENVIRONMENT) { + + case USER_ENVIRONMENT: + case VIRTUAL_ENVIRONMENT: + cpu_error(processor, cia, "floating-point assist interrupt"); + + case OPERATING_ENVIRONMENT: + TRACE(trace_interrupts, ("floating-point assist interrupt - cia=0x%lx\n", (unsigned long)cia)); + cia = perform_oea_interrupt(processor, cia, 0x00e00, 0, 0, 0, 0); + cpu_restart(processor, cia); + + default: + error("internal error - floating_point_assist_interrupt - bad switch"); + + } +} + + + +/* handle an externally generated event or an interrupt that has just + been enabled through changes to the MSR. */ + +STATIC_INLINE_INTERRUPTS\ +(void) +deliver_hardware_interrupt(void *data) +{ + cpu *processor = (cpu*)data; + interrupts *ints = cpu_interrupts(processor); + ints->delivery_scheduled = NULL; + if ((cpu_registers(processor)->msr & (msr_floating_point_exception_mode_0 + | msr_floating_point_exception_mode_1)) + && cpu_registers(processor)->fpscr & fpscr_fex) { + msreg srr1_set = srr1_floating_point_enabled | srr1_subsequent_instruction; + unsigned_word cia = cpu_get_program_counter(processor); + unsigned_word nia = perform_oea_interrupt(processor, + cia, 0x00700, 0, 0, 0, srr1_set); + cpu_set_program_counter(processor, nia); + } + else if (cpu_registers(processor)->msr & msr_external_interrupt_enable) { + /* external interrupts have a high priority and remain pending */ + if (ints->pending_interrupts & external_interrupt_pending) { + unsigned_word cia = cpu_get_program_counter(processor); + unsigned_word nia = perform_oea_interrupt(processor, + cia, 0x00500, 0, 0, 0, 0); + TRACE(trace_interrupts, ("external interrupt - cia=0x%lx\n", (unsigned long)cia)); + cpu_set_program_counter(processor, nia); + } + /* decrementer interrupts have a lower priority and are once only */ + else if (ints->pending_interrupts & decrementer_interrupt_pending) { + unsigned_word cia = cpu_get_program_counter(processor); + unsigned_word nia = perform_oea_interrupt(processor, + cia, 0x00900, 0, 0, 0, 0); + TRACE(trace_interrupts, ("decrementer interrupt - cia=0x%lx time=0x%lx\n", + (unsigned long)cia, + (unsigned long)event_queue_time(psim_event_queue(cpu_system(processor))) + )); + cpu_set_program_counter(processor, nia); + ints->pending_interrupts &= ~decrementer_interrupt_pending; + } + } +} + +STATIC_INLINE_INTERRUPTS\ +(void) +schedule_hardware_interrupt_delivery(cpu *processor) +{ + interrupts *ints = cpu_interrupts(processor); + if (ints->delivery_scheduled == NULL) { + ints->delivery_scheduled = + event_queue_schedule(psim_event_queue(cpu_system(processor)), + 0, deliver_hardware_interrupt, processor); + } +} + + +INLINE_INTERRUPTS\ +(void) +check_masked_interrupts(cpu *processor) +{ + if (((cpu_registers(processor)->msr & (msr_floating_point_exception_mode_0 + | msr_floating_point_exception_mode_1)) + && cpu_registers(processor)->fpscr & fpscr_fex) + || ((cpu_registers(processor)->msr & msr_external_interrupt_enable) + && (cpu_interrupts(processor)->pending_interrupts))) + schedule_hardware_interrupt_delivery(processor); +} + +INLINE_INTERRUPTS\ +(void) +decrementer_interrupt(cpu *processor) +{ + interrupts *ints = cpu_interrupts(processor); + ints->pending_interrupts |= decrementer_interrupt_pending; + if (cpu_registers(processor)->msr & msr_external_interrupt_enable) { + schedule_hardware_interrupt_delivery(processor); + } +} + +INLINE_INTERRUPTS\ +(void) +external_interrupt(cpu *processor, + int is_asserted) +{ + interrupts *ints = cpu_interrupts(processor); + if (is_asserted) { + if (!ints->pending_interrupts & external_interrupt_pending) { + ints->pending_interrupts |= external_interrupt_pending; + if (cpu_registers(processor)->msr & msr_external_interrupt_enable) + schedule_hardware_interrupt_delivery(processor); + } + else { + /* check that we haven't missed out on a chance to deliver an + interrupt */ + ASSERT(!(cpu_registers(processor)->msr & msr_external_interrupt_enable)); + } + } + else { + ints->pending_interrupts &= ~external_interrupt_pending; + } +} + +#endif /* _INTERRUPTS_C_ */ diff --git a/sim/ppc/interrupts.h b/sim/ppc/interrupts.h new file mode 100644 index 00000000000..6e87b77de5a --- /dev/null +++ b/sim/ppc/interrupts.h @@ -0,0 +1,172 @@ +/* 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 _INTERRUPTS_H_ +#define _INTERRUPTS_H_ + +/* Interrupts: + + The code below handles two different types of interrupts. + Synchronous and Asynchronous. + + Synchronous: + + Interrupts that must immediately force either an abort or restart + of a current instruction are implemented by forcing an instruction + restart. (or to put it another way, long jump). In looking at the + code it may occure to you that, for some interrupts, they could + return instead of restarting the cpu (eg system_call). While true + (it once was like that) I've decided to make the behavour of all + interrupt routines roughly identical. + + Because, a cpu's recorded state (ie what is in the cpu structure) + is allowed to lag behind the cpu's true current state (eg PC not + updated) sycnronous interrupt handers are parameterized with the + the cpu being interrupted so that, as part of moddeling the + interrupt, the cpu's state can be updated. + + Asynchronous: + + Interrupts such as reset or external exception are delivered using + more normal (returning) functions. It is assumed that these + functions are called out side of the normal processor execution + cycle. */ + + +/* Software generated interrupts. + + The below are generated by software driven events. For instance, + an invalid instruction or access (virtual or physical) to an + invalid address */ + +typedef enum { + direct_store_storage_interrupt, + hash_table_miss_storage_interrupt, + protection_violation_storage_interrupt, + earwax_violation_storage_interrupt, + segment_table_miss_storage_interrupt, + earwax_disabled_storage_interrupt, + vea_storage_interrupt, +} storage_interrupt_reasons; + + +INLINE_INTERRUPTS\ +(void) data_storage_interrupt +(cpu *processor, + unsigned_word cia, + unsigned_word ea, + storage_interrupt_reasons reason, + int is_store); + +INLINE_INTERRUPTS\ +(void) instruction_storage_interrupt +(cpu *processor, + unsigned_word cia, + storage_interrupt_reasons reason); + +INLINE_INTERRUPTS\ +(void) alignment_interrupt +(cpu *processor, + unsigned_word cia, + unsigned_word ra); + +typedef enum { + floating_point_enabled_program_interrupt, + illegal_instruction_program_interrupt, + privileged_instruction_program_interrupt, + trap_program_interrupt, + optional_instruction_program_interrupt, /* subset of illegal instruction */ +#ifdef WITH_OPTION_MPC860C0 + mpc860c0_instruction_program_interrupt, /* fwd br, taken but not predicted, near EO page */ +#endif // WITH_OPTION_MPC860C0 + nr_program_interrupt_reasons +} program_interrupt_reasons; + +INLINE_INTERRUPTS\ +(void) program_interrupt +(cpu *processor, + unsigned_word cia, + program_interrupt_reasons reason); + +INLINE_INTERRUPTS\ +(void) floating_point_unavailable_interrupt +(cpu *processor, + unsigned_word cia); + +INLINE_INTERRUPTS\ +(void) system_call_interrupt +(cpu *processor, + unsigned_word cia); + +INLINE_INTERRUPTS\ +(void) floating_point_assist_interrupt +(cpu *processor, + unsigned_word cia); + +INLINE_INTERRUPTS\ +(void) machine_check_interrupt +(cpu *processor, + unsigned_word cia); + +/* Hardware generated interrupts: + + These asynchronous hardware generated interrupts may be called at + any time. It is the responsibility of this (the interrupts) module + to ensure that interrupts are delivered correctly (when possible). + The delivery of these interrupts is controlled by the MSR's + external interrupt enable bit. When ever the MSR's value is + changed, the processor must call the check_masked_interrupts() + function in case delivery has been made possible. + + decrementer_interrupt is `edge' sensitive. Multiple edges arriving + before the first edge has been delivered result in only one + interrupt. + + external_interrupt is `level' sensitive. An external interrupt + will only be delivered when the external interrupt port is + `asserted'. While interrupts are disabled, the external interrupt + can be asserted and then de-asserted without an interrupt + eventually being delivered. */ + +enum { + external_interrupt_pending = 1, + decrementer_interrupt_pending = 2, +}; + +typedef struct _interrupts { + event_entry_tag delivery_scheduled; + int pending_interrupts; +} interrupts; + +INLINE_INTERRUPTS\ +(void) check_masked_interrupts +(cpu *processor); + +INLINE_INTERRUPTS\ +(void) decrementer_interrupt +(cpu *processor); + +INLINE_INTERRUPTS\ +(void) external_interrupt +(cpu *processor, + int is_asserted); + +#endif /* _INTERRUPTS_H_ */ diff --git a/sim/ppc/ld-cache.c b/sim/ppc/ld-cache.c new file mode 100644 index 00000000000..135013e5f23 --- /dev/null +++ b/sim/ppc/ld-cache.c @@ -0,0 +1,115 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1997, 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. + + */ + + +#include "misc.h" +#include "lf.h" +#include "table.h" +#include "ld-cache.h" + +#ifndef NULL +#define NULL 0 +#endif + + +enum { + ca_type, + ca_field_name, + ca_derived_name, + ca_type_def, + ca_expression, + nr_cache_rule_fields, +}; + +static const name_map cache_type_map[] = { + { "cache", cache_value }, + { "compute", compute_value }, + { "scratch", scratch_value }, + { NULL, 0 }, +}; + + +cache_table * +load_cache_table(char *file_name, + int hi_bit_nr) +{ + table *file = table_open(file_name, nr_cache_rule_fields, 0); + table_entry *entry; + cache_table *table = NULL; + cache_table **curr_rule = &table; + while ((entry = table_entry_read(file)) != NULL) { + cache_table *new_rule = ZALLOC(cache_table); + new_rule->type = name2i(entry->fields[ca_type], cache_type_map); + new_rule->field_name = entry->fields[ca_field_name]; + new_rule->derived_name = entry->fields[ca_derived_name]; + new_rule->type_def = (strlen(entry->fields[ca_type_def]) + ? entry->fields[ca_type_def] + : NULL); + new_rule->expression = (strlen(entry->fields[ca_expression]) > 0 + ? entry->fields[ca_expression] + : NULL); + new_rule->file_entry = entry; + *curr_rule = new_rule; + curr_rule = &new_rule->next; + } + return table; +} + + + +#ifdef MAIN + +static void +dump_cache_rule(cache_table* rule, + int indent) +{ + dumpf(indent, "((cache_table*)0x%x\n", rule); + dumpf(indent, " (type %s)\n", i2name(rule->type, cache_type_map)); + dumpf(indent, " (field_name \"%s\")\n", rule->field_name); + dumpf(indent, " (derived_name \"%s\")\n", rule->derived_name); + dumpf(indent, " (type-def \"%s\")\n", rule->type_def); + dumpf(indent, " (expression \"%s\")\n", rule->expression); + dumpf(indent, " (next 0x%x)\n", rule->next); + dumpf(indent, " )\n"); +} + + +static void +dump_cache_rules(cache_table* rule, + int indent) +{ + while (rule) { + dump_cache_rule(rule, indent); + rule = rule->next; + } +} + + +int +main(int argc, char **argv) +{ + cache_table *rules; + if (argc != 3) + error("Usage: cache <cache-file> <hi-bit-nr>\n"); + rules = load_cache_table(argv[1], a2i(argv[2])); + dump_cache_rules(rules, 0); + return 0; +} +#endif diff --git a/sim/ppc/ld-cache.h b/sim/ppc/ld-cache.h new file mode 100644 index 00000000000..72e39504380 --- /dev/null +++ b/sim/ppc/ld-cache.h @@ -0,0 +1,81 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1997, 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 unpacking: + + Once the instruction has been decoded, the register (and other) + fields within the instruction need to be extracted. + + The table that follows determines how each field should be treated. + Importantly it considers the case where the extracted field is to + be used immediatly or stored in an instruction cache. + + <type> + + Indicates what to do with the cache entry. If a cache is to be + used. SCRATCH and CACHE values are defined when a cache entry is + being filled while CACHE and COMPUTE values are defined in the + semantic code. + + Zero marks the end of the table. More importantly 1. indicates + that the entry is valid and can be cached. 2. indicates that that + the entry is valid but can not be cached. + + <field_name> + + The field name as given in the instruction spec. + + <derived_name> + + A new name for <field_name> once it has been extracted from the + instruction (and possibly stored in the instruction cache). + + <type> + + String specifying the storage type for <new_name> (the extracted + field>. + + <expression> + + Specifies how to get <new_name> from <old_name>. If null, old and + new name had better be the same. */ + + +typedef enum { + scratch_value, + cache_value, + compute_value, +} cache_rule_type; + +typedef struct _cache_table cache_table; +struct _cache_table { + cache_rule_type type; + char *field_name; + char *derived_name; + char *type_def; + char *expression; + table_entry *file_entry; + cache_table *next; +}; + + +extern cache_table *load_cache_table +(char *file_name, + int hi_bit_nr); diff --git a/sim/ppc/ld-decode.c b/sim/ppc/ld-decode.c new file mode 100644 index 00000000000..3964960af26 --- /dev/null +++ b/sim/ppc/ld-decode.c @@ -0,0 +1,155 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1997, 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. + + */ + +/* load the opcode stat structure */ + +#include "misc.h" +#include "lf.h" +#include "table.h" +#include "ld-decode.h" + +#ifndef NULL +#define NULL 0 +#endif + +enum { + op_options, + op_first, + op_last, + op_force_first, + op_force_last, + op_force_expansion, + op_special_mask, + op_special_value, + op_special_constant, + nr_decode_fields, +}; + +static const name_map decode_type_map[] = { + { "normal", normal_decode_rule }, + { "expand-forced", expand_forced_rule }, + { "boolean", boolean_rule }, + { NULL, normal_decode_rule }, +}; + +static const name_map decode_gen_map[] = { + { "array", array_gen }, + { "switch", switch_gen }, + { "padded-switch", padded_switch_gen }, + { "goto-switch", goto_switch_gen }, + { NULL, -1 }, +}; + +static const name_map decode_slash_map[] = { + { "variable-slash", 0 }, + { "constant-slash", 1 }, + { NULL }, +}; + + +static decode_gen_type overriding_gen_type = invalid_gen; + +void +force_decode_gen_type(const char *type) +{ + overriding_gen_type = name2i(type, decode_gen_map); +} + + +decode_table * +load_decode_table(char *file_name, + int hi_bit_nr) +{ + table *file = table_open(file_name, nr_decode_fields, 0); + table_entry *entry; + decode_table *table = NULL; + decode_table **curr_rule = &table; + while ((entry = table_entry_read(file)) != NULL) { + decode_table *new_rule = ZALLOC(decode_table); + new_rule->type = name2i(entry->fields[op_options], decode_type_map); + new_rule->gen = (overriding_gen_type != invalid_gen + ? overriding_gen_type + : name2i(entry->fields[op_options], decode_gen_map)); + new_rule->force_slash = name2i(entry->fields[op_options], decode_slash_map); + new_rule->first = target_a2i(hi_bit_nr, entry->fields[op_first]); + new_rule->last = target_a2i(hi_bit_nr, entry->fields[op_last]); + new_rule->force_first = (strlen(entry->fields[op_force_first]) + ? target_a2i(hi_bit_nr, entry->fields[op_force_first]) + : new_rule->last + 1); + new_rule->force_last = (strlen(entry->fields[op_force_last]) + ? target_a2i(hi_bit_nr, entry->fields[op_force_last]) + : new_rule->first - 1); + new_rule->force_expansion = entry->fields[op_force_expansion]; + new_rule->special_mask = a2i(entry->fields[op_special_mask]); + new_rule->special_value = a2i(entry->fields[op_special_value]); + new_rule->special_constant = a2i(entry->fields[op_special_constant]); + *curr_rule = new_rule; + curr_rule = &new_rule->next; + } + return table; +} + + +void +dump_decode_rule(decode_table *rule, + int indent) +{ + dumpf(indent, "((decode_table*)%p\n", rule); + if (rule) { + dumpf(indent, " (type %s)\n", i2name(rule->type, decode_type_map)); + dumpf(indent, " (gen %s)\n", i2name(rule->gen, decode_gen_map)); + dumpf(indent, " (force_slash %d)\n", rule->force_slash); + 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_expansion \"%s\")\n", rule->force_expansion); + dumpf(indent, " (special_mask 0x%x)\n", rule->special_mask); + dumpf(indent, " (special_value 0x%x)\n", rule->special_value); + dumpf(indent, " (special_constant 0x%x)\n", rule->special_constant); + dumpf(indent, " (next 0x%x)\n", rule->next); + } + dumpf(indent, " )\n"); +} + + +#ifdef MAIN + +static void +dump_decode_rules(decode_table *rule, + int indent) +{ + while (rule) { + dump_decode_rule(rule, indent); + rule = rule->next; + } +} + +int +main(int argc, char **argv) +{ + decode_table *rules; + if (argc != 3) + error("Usage: decode <decode-file> <hi-bit-nr>\n"); + rules = load_decode_table(argv[1], a2i(argv[2])); + dump_decode_rules(rules, 0); + return 0; +} +#endif diff --git a/sim/ppc/ld-decode.h b/sim/ppc/ld-decode.h new file mode 100644 index 00000000000..de23181ba37 --- /dev/null +++ b/sim/ppc/ld-decode.h @@ -0,0 +1,143 @@ +/* This file is part of the program psim. + + Copyright (C) 1994,1995,1996, 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 decode table: + + <options>:<first>:<last>:<force-first>:<force-last>:<force-expand>:<special>... + + + + Ignore the below: + + + The instruction decode table contains rules that dictate how igen + is going to firstly break down the opcode table and secondly + + The table that follows is used by gen to construct a decision tree + that can identify each possible instruction. Gen then outputs this + decision tree as (according to config) a table or switch statement + as the function idecode. + + In parallel to this, as mentioned above, WITH_EXPANDED_SEMANTICS + determines of the semantic functions themselves should be expanded + in a similar way. + + <first> + <last> + + Range of bits (within the instruction) that should be searched for + an instruction field. Within such ranges, gen looks for opcodes + (constants), registers (strings) and reserved bits (slash) and + according to the rules that follows includes or excludes them from + a possible instruction field. + + <force_first> + <force_last> + + If an instruction field was found, enlarge the field size so that + it is forced to at least include bits starting from <force_first> + (<force_last>). To stop this occuring, use <force_first> = <last> + + 1 and <force_last> = <first> - 1. + + <force_slash> + + Treat `/' fields as a constant instead of variable when looking for + an instruction field. + + <force_expansion> + + Treat any contained register (string) fields as constant when + determining the instruction field. For the instruction decode (and + controled by IDECODE_EXPAND_SEMANTICS) this forces the expansion of + what would otherwize be non constant bits of an instruction. + + <use_switch> + + Should this table be expanded using a switch statement (val 1) and + if so, should it be padded with entries so as to force the compiler + to generate a jump table (val 2). Or a branch table (val 3). + + <special_mask> + <special_value> + <special_rule> + <special_constant> + + Special rule to fine tune how specific (or groups) of instructions + are expanded. The applicability of the rule is determined by + + <special_mask> != 0 && (instruction> & <special_mask>) == <special_value> + + Where <instruction> is obtained by looking only at constant fields + with in an instructions spec. When determining an expansion, the + rule is only considered when a node contains a single instruction. + <special_rule> can be any of: + + 0: for this instruction, expand by earlier rules + 1: expand bits <force_low> .. <force_hi> only + 2: boolean expansion of only zero/non-zero cases + 3: boolean expansion of equality of special constant + + */ + + +typedef enum { + normal_decode_rule, + expand_forced_rule, + boolean_rule, + nr_decode_rules +} decode_special_type; + +typedef enum { + invalid_gen, + array_gen, + switch_gen, + padded_switch_gen, + goto_switch_gen, + nr_decode_gen_types, +} decode_gen_type; + + +typedef struct _decode_table decode_table; +struct _decode_table { + decode_special_type type; + decode_gen_type gen; + int first; + int last; + int force_first; + int force_last; + int force_slash; + char *force_expansion; + unsigned special_mask; + unsigned special_value; + unsigned special_constant; + decode_table *next; +}; + + +extern void force_decode_gen_type +(const char *type); + +extern decode_table *load_decode_table +(char *file_name, + int hi_bit_nr); + +extern void dump_decode_rule +(decode_table *rule, + int indent); diff --git a/sim/ppc/ld-insn.c b/sim/ppc/ld-insn.c new file mode 100644 index 00000000000..a190d85a83f --- /dev/null +++ b/sim/ppc/ld-insn.c @@ -0,0 +1,925 @@ +/* This file is part of the program psim. + + Copyright (C) 1994,1995,1996, 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. + + */ + + +#include "misc.h" +#include "lf.h" +#include "table.h" +#include "filter.h" +#include "ld-decode.h" +#include "ld-cache.h" +#include "ld-insn.h" + +#include "igen.h" + +static void +update_depth(insn_table *entry, + lf *file, + void *data, + insn *instruction, + 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, + NULL, + &depth, + 1, + NULL, /*start*/ + update_depth, + NULL, /*end*/ + NULL); /*padding*/ + return depth; +} + + +static insn_fields * +parse_insn_format(table_entry *entry, + char *format) +{ + char *chp; + insn_fields *fields = ZALLOC(insn_fields); + + /* create a leading sentinal */ + fields->first = ZALLOC(insn_field); + fields->first->first = -1; + fields->first->last = -1; + fields->first->width = 0; + + /* and a trailing sentinal */ + fields->last = ZALLOC(insn_field); + fields->last->first = insn_bit_size; + fields->last->last = insn_bit_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)) { + error("%s:%d: missing position field at `%s'\n", + entry->file_name, entry->line_nr, chp); + } + + /* break out the bit position */ + start_pos = chp; + while (isdigit(*chp)) + chp++; + strlen_pos = chp - start_pos; + if (*chp == '.' && strlen_pos > 0) + chp++; + else { + error("%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) { + error("%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 = ZALLOC(insn_field); + 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*)zalloc(strlen_val+1); + strncpy(new_field->val_string, start_val, strlen_val); + if (isdigit(*new_field->val_string)) { + new_field->val_int = a2i(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*)zalloc(strlen_pos+1); + strncpy(new_field->pos_string, start_pos, strlen_pos); + new_field->first = target_a2i(hi_bit_nr, 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_bit_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_bit_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_bit_size; + field = field->next) { + fields->value <<= field->width; + if (field->is_int) + fields->value |= field->val_int; + } + } + return fields; +} + + +static void +model_table_insert(insn_table *table, + table_entry *file_entry) +{ + int len; + + /* create a new model */ + model *new_model = ZALLOC(model); + + new_model->name = file_entry->fields[model_identifer]; + new_model->printable_name = file_entry->fields[model_name]; + new_model->insn_default = file_entry->fields[model_default]; + + while (*new_model->insn_default && isspace(*new_model->insn_default)) + new_model->insn_default++; + + len = strlen(new_model->insn_default); + if (max_model_fields_len < len) + max_model_fields_len = len; + + /* append it to the end of the model list */ + if (last_model) + last_model->next = new_model; + else + models = new_model; + last_model = new_model; +} + +static void +model_table_insert_specific(insn_table *table, + table_entry *file_entry, + insn **start_ptr, + insn **end_ptr) +{ + insn *ptr = ZALLOC(insn); + ptr->file_entry = file_entry; + if (*end_ptr) + (*end_ptr)->next = ptr; + else + (*start_ptr) = ptr; + (*end_ptr) = ptr; +} + + +static void +insn_table_insert_function(insn_table *table, + table_entry *file_entry) +{ + /* create a new function */ + insn *new_function = ZALLOC(insn); + new_function->file_entry = file_entry; + + /* append it to the end of the function list */ + if (table->last_function) + table->last_function->next = new_function; + else + table->functions = new_function; + table->last_function = new_function; +} + +extern void +insn_table_insert_insn(insn_table *table, + table_entry *file_entry, + insn_fields *fields) +{ + insn **ptr_to_cur_insn = &table->insns; + insn *cur_insn = *ptr_to_cur_insn; + table_model_entry *insn_model_ptr; + model *model_ptr; + + /* create a new instruction */ + insn *new_insn = ZALLOC(insn); + new_insn->file_entry = file_entry; + new_insn->fields = fields; + + /* Check out any model information returned to make sure the model + is correct. */ + for(insn_model_ptr = file_entry->model_first; insn_model_ptr; insn_model_ptr = insn_model_ptr->next) { + char *name = insn_model_ptr->fields[insn_model_name]; + int len = strlen (insn_model_ptr->fields[insn_model_fields]); + + while (len > 0 && isspace(*insn_model_ptr->fields[insn_model_fields])) { + len--; + insn_model_ptr->fields[insn_model_fields]++; + } + + if (max_model_fields_len < len) + max_model_fields_len = len; + + for(model_ptr = models; model_ptr; model_ptr = model_ptr->next) { + if (strcmp(name, model_ptr->printable_name) == 0) { + + /* Replace the name field with that of the global model, so that when we + want to print it out, we can just compare pointers. */ + insn_model_ptr->fields[insn_model_name] = model_ptr->printable_name; + break; + } + } + + if (!model_ptr) + error("%s:%d: machine model `%s' was not known about\n", + file_entry->file_name, file_entry->line_nr, name); + } + + /* 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++; +} + + + +insn_table * +load_insn_table(const char *file_name, + decode_table *decode_rules, + filter *filters) +{ + table *file = table_open(file_name, nr_insn_table_fields, nr_insn_model_table_fields); + insn_table *table = ZALLOC(insn_table); + table_entry *file_entry; + table->opcode_rule = decode_rules; + + while ((file_entry = table_entry_read(file)) != NULL) { + if (it_is("function", file_entry->fields[insn_flags]) + || it_is("internal", file_entry->fields[insn_flags])) { + insn_table_insert_function(table, file_entry); + } + else if (it_is("model", file_entry->fields[insn_flags])) { + model_table_insert(table, file_entry); + } + else if (it_is("model-macro", file_entry->fields[insn_flags])) { + model_table_insert_specific(table, file_entry, &model_macros, &last_model_macro); + } + else if (it_is("model-function", file_entry->fields[insn_flags])) { + model_table_insert_specific(table, file_entry, &model_functions, &last_model_function); + } + else if (it_is("model-internal", file_entry->fields[insn_flags])) { + model_table_insert_specific(table, file_entry, &model_internal, &last_model_internal); + } + else if (it_is("model-static", file_entry->fields[insn_flags])) { + model_table_insert_specific(table, file_entry, &model_static, &last_model_static); + } + else if (it_is("model-data", file_entry->fields[insn_flags])) { + model_table_insert_specific(table, file_entry, &model_data, &last_model_data); + } + else { + insn_fields *fields; + /* skip instructions that aren't relevant to the mode */ + if (is_filtered_out(file_entry->fields[insn_flags], filters)) { + fprintf(stderr, "Dropping %s - %s\n", + file_entry->fields[insn_name], + file_entry->fields[insn_flags]); + } + else { + /* 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; +} + + +extern void +insn_table_traverse_tree(insn_table *table, + lf *file, + void *data, + int depth, + leaf_handler *start, + insn_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, file, 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, file, 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, file, data, depth+1, + start, leaf, end, padding); + } + else if (depth >= 0) { + if (leaf != NULL) + leaf(entry, file, data, entry->insns, depth); + } + entry = entry->sibling; + } + } + if (end != NULL && depth >= 0) + end(table, file, data, depth); +} + + +extern void +insn_table_traverse_function(insn_table *table, + lf *file, + void *data, + function_handler *leaf) +{ + insn *function; + for (function = table->functions; + function != NULL; + function = function->next) { + leaf(table, file, data, function->file_entry); + } +} + +extern void +insn_table_traverse_insn(insn_table *table, + lf *file, + void *data, + insn_handler *handler) +{ + insn *instruction; + for (instruction = table->insns; + instruction != NULL; + instruction = instruction->next) { + handler(table, file, data, instruction, 0); + } +} + + +/****************************************************************/ + +typedef enum { + field_constant_int = 1, + field_constant_slash = 2, + field_constant_string = 3 +} constant_field_types; + + +static int +insn_field_is_constant(insn_field *field, + decode_table *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; +} + + +static opcode_field * +insn_table_find_opcode_field(insn *insns, + decode_table *rule, + int string_only) +{ + opcode_field *curr_opcode = ZALLOC(opcode_field); + insn *entry; + ASSERT(rule); + + curr_opcode->first = insn_bit_size; + curr_opcode->last = -1; + 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); + 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_bit_size-1) { + curr_opcode->last = insn_bit_size-1; + } + + /* handle any special cases */ + switch (rule->type) { + case normal_decode_rule: + /* let the above apply */ + break; + case expand_forced_rule: + /* expand a limited nr of bits, ignoring the rest */ + curr_opcode->first = rule->force_first; + curr_opcode->last = rule->force_last; + break; + case boolean_rule: + curr_opcode->is_boolean = 1; + curr_opcode->boolean_constant = rule->special_constant; + break; + default: + error("Something is going wrong\n"); + } + + return curr_opcode; +} + + +static 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 = ZALLOC(insn_table); + new_entry->opcode_nr = new_opcode_nr; + new_entry->expanded_bits = new_bits; + new_entry->opcode_rule = table->opcode_rule->next; + 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); +} + +static 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 = ZALLOC(insn_bits); + 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); + } + } + } +} + +static void +insn_table_insert_expanding(insn_table *table, + insn *entry) +{ + insn_table_expand_opcode(table, + entry, + table->opcode->first, + 0, + table->expanded_bits); +} + + +extern void +insn_table_expand_insns(insn_table *table) +{ + + ASSERT(table->nr_insn >= 1); + + /* determine a valid opcode */ + while (table->opcode_rule) { + /* specials only for single instructions */ + if ((table->nr_insn > 1 + && table->opcode_rule->special_mask == 0 + && table->opcode_rule->type == normal_decode_rule) + || (table->nr_insn == 1 + && table->opcode_rule->special_mask != 0 + && ((table->insns->fields->value + & table->opcode_rule->special_mask) + == table->opcode_rule->special_value)) + || (generate_expanded_instructions + && table->opcode_rule->special_mask == 0 + && table->opcode_rule->type == normal_decode_rule)) + 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 = table->opcode_rule->next; + } + + /* 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); + } + } +} + + + + +#ifdef MAIN + +static void +dump_insn_field(insn_field *field, + int indent) +{ + + printf("(insn_field*)0x%x\n", (unsigned)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); + + +} + +static void +dump_insn_fields(insn_fields *fields, + int indent) +{ + int i; + + printf("(insn_fields*)%p\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_bit_size; i++) { + dumpf(indent, "(bits[%d] ", i, fields->bits[i]); + dump_insn_field(fields->bits[i], indent+1); + dumpf(indent, " )\n"); + } + +} + + +static void +dump_opcode_field(opcode_field *field, int indent, int levels) +{ + printf("(opcode_field*)%p\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); + } +} + + +static void +dump_insn_bits(insn_bits *bits, int indent, int levels) +{ + printf("(insn_bits*)%p\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); + } +} + + + +static void +dump_insn(insn *entry, int indent, int levels) +{ + printf("(insn*)%p\n", entry); + + if (levels && entry != NULL) { + + dumpf(indent, "(file_entry "); + dump_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"); + + } + +} + + +static void +dump_insn_table(insn_table *table, + int indent, int levels) +{ + + printf("(insn_table*)%p\n", table); + + if (levels && table != NULL) { + + 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_decode_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"); + + } +} + +int insn_bit_size = max_insn_bit_size; +int hi_bit_nr; +int generate_expanded_instructions; + +int +main(int argc, char **argv) +{ + filter *filters = NULL; + decode_table *decode_rules = NULL; + insn_table *instructions = NULL; + + if (argc != 5) + error("Usage: insn <filter> <hi-bit-nr> <decode-table> <insn-table>\n"); + + filters = new_filter(argv[1], filters); + hi_bit_nr = a2i(argv[2]); + ASSERT(hi_bit_nr < insn_bit_size); + decode_rules = load_decode_table(argv[3], hi_bit_nr); + instructions = load_insn_table(argv[4], decode_rules, filters); + insn_table_expand_insns(instructions); + + dump_insn_table(instructions, 0, -1); + return 0; +} + +#endif diff --git a/sim/ppc/ld-insn.h b/sim/ppc/ld-insn.h new file mode 100644 index 00000000000..e800f0dac23 --- /dev/null +++ b/sim/ppc/ld-insn.h @@ -0,0 +1,281 @@ +/* This file is part of the program psim. + + Copyright (C) 1994,1995,1996, 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. + + */ + +/* +# -- +# +# +# Fields: +# +# 1 Instruction format as a `start-bit,content' pairs. +# the content is one of a digit, field name or `/' (aka.0) +# +# 2 Format specifier +# +# 3 Flags: 64 - 64bit only +# f - floating point enabled required +# +# 4 short name +# +# 5 Description +# +# +# For flags marked 'model', the fields are interpreted as follows: +# +# 1 Not used +# +# 2 Not used +# +# 3 "macro" +# +# 4 String name for model +# +# 5 Specific CPU model, must be an identifier +# +# 6 Comma separated list of functional units + +*/ + + +/* Global constants */ + +enum { + max_insn_bit_size = 32, +}; + + +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[max_insn_bit_size]; + insn_field *first; + insn_field *last; + unsigned value; +}; + + +/****************************************************************/ + +typedef struct _opcode_field opcode_field; +struct _opcode_field { + int first; + int last; + int is_boolean; + unsigned boolean_constant; + opcode_field *parent; +}; + + +/****************************************************************/ + +typedef struct _insn_bits insn_bits; +struct _insn_bits { + int is_expanded; + int value; + insn_field *field; + opcode_field *opcode; + insn_bits *last; +}; + + +/****************************************************************/ + + +typedef enum { + insn_format, + insn_form, + insn_flags, + insn_mnemonic, + insn_name, + insn_comment, + nr_insn_table_fields +} insn_table_fields; + +typedef enum { + function_type = insn_format, + function_name = insn_name, + function_param = insn_comment +} function_table_fields; + +typedef enum { + model_name = insn_mnemonic, + model_identifer = insn_name, + model_default = insn_comment, +} model_table_fields; + +typedef struct _insn insn; +struct _insn { + table_entry *file_entry; + insn_fields *fields; + insn *next; +}; + +typedef struct _insn_undef insn_undef; +struct _insn_undef { + insn_undef *next; + char *name; +}; + +typedef struct _model model; +struct _model { + model *next; + char *name; + char *printable_name; + char *insn_default; + table_model_entry *func_unit_start; + table_model_entry *func_unit_end; +}; + +typedef struct _insn_table insn_table; +struct _insn_table { + int opcode_nr; + insn_bits *expanded_bits; + int nr_insn; + insn *insns; + insn *functions; + insn *last_function; + decode_table *opcode_rule; + opcode_field *opcode; + int nr_entries; + insn_table *entries; + insn_table *sibling; + insn_table *parent; +}; + +typedef enum { + insn_model_name, + insn_model_fields, + nr_insn_model_table_fields +} insn_model_table_fields; + + +extern insn_table *load_insn_table +(const char *file_name, + decode_table *decode_rules, + filter *filters); + +model *models; +model *last_model; + +insn *model_macros; +insn *last_model_macro; + +insn *model_functions; +insn *last_model_function; + +insn *model_internal; +insn *last_model_internal; + +insn *model_static; +insn *last_model_static; + +insn *model_data; +insn *last_model_data; + +int max_model_fields_len; + +extern void insn_table_insert_insn +(insn_table *table, + table_entry *file_entry, + insn_fields *fields); + + +/****************************************************************/ + +/****************************************************************/ + +typedef void leaf_handler +(insn_table *entry, + lf *file, + void *data, + int depth); + +typedef void insn_handler +(insn_table *table, + lf *file, + void *data, + insn *instruction, + int depth); + +typedef void padding_handler +(insn_table *table, + lf *file, + void *data, + int depth, + int opcode_nr); + + +extern void insn_table_traverse_tree +(insn_table *table, + lf *file, + void *data, + int depth, + leaf_handler *start, + insn_handler *handler, + leaf_handler *end, + padding_handler *padding); + + +extern void insn_table_traverse_insn +(insn_table *table, + lf *file, + void *data, + insn_handler *handler); + + + +/****************************************************************/ + +typedef void function_handler +(insn_table *table, + lf *file, + void *data, + table_entry *function); + +extern void +insn_table_traverse_function +(insn_table *table, + lf *file, + void *data, + function_handler *leaf); + +/****************************************************************/ + + + +extern void insn_table_expand_insns +(insn_table *table); + +extern int insn_table_depth +(insn_table *table); diff --git a/sim/ppc/lf.c b/sim/ppc/lf.c new file mode 100644 index 00000000000..2a420151efa --- /dev/null +++ b/sim/ppc/lf.c @@ -0,0 +1,440 @@ +/* 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. + + */ + + +#include <stdio.h> +#include <stdarg.h> +#include <ctype.h> + +#include "config.h" +#include "misc.h" +#include "lf.h" + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#ifdef HAVE_STRING_H +#include <string.h> +#else +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif +#endif + +struct _lf { + FILE *stream; + int line_nr; /* nr complete lines written, curr line is line_nr+1 */ + int indent; + int line_blank; + const char *name; + const char *program; + lf_file_references references; + lf_file_type type; +}; + + +lf * +lf_open(char *name, + char *real_name, + lf_file_references references, + lf_file_type type, + const char *program) +{ + /* create a file object */ + lf *new_lf = ZALLOC(lf); + ASSERT(new_lf != NULL); + new_lf->references = references; + new_lf->type = type; + new_lf->name = (real_name == NULL ? name : real_name); + new_lf->program = program; + /* attach to stdout if pipe */ + if (!strcmp(name, "-")) { + new_lf->stream = stdout; + } + else { + /* create a new file */ + new_lf->stream = fopen(name, "w"); + if (new_lf->stream == NULL) { + perror(name); + exit(1); + } + } + return new_lf; +} + + +void +lf_close(lf *file) +{ + if (file->stream != stdout) { + if (fclose(file->stream)) { + perror("lf_close.fclose"); + exit(1); + } + free(file); + } +} + + +int +lf_putchr(lf *file, + const char chr) +{ + int nr = 0; + 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); + nr += file->indent; + file->line_blank = 0; + } + putc(chr, file->stream); + nr += 1; + return nr; +} + +void +lf_indent_suppress(lf *file) +{ + file->line_blank = 0; +} + + +int +lf_putstr(lf *file, + const char *string) +{ + int nr = 0; + const char *chp; + if (string != NULL) { + for (chp = string; *chp != '\0'; chp++) { + nr += lf_putchr(file, *chp); + } + } + return nr; +} + +static int +do_lf_putunsigned(lf *file, + unsigned u) +{ + int nr = 0; + if (u > 0) { + nr += do_lf_putunsigned(file, u / 10); + nr += lf_putchr(file, (u % 10) + '0'); + } + return nr; +} + + +int +lf_putint(lf *file, + int decimal) +{ + int nr = 0; + if (decimal == 0) + nr += lf_putchr(file, '0'); + else if (decimal < 0) { + nr += lf_putchr(file, '-'); + nr += do_lf_putunsigned(file, -decimal); + } + else if (decimal > 0) { + nr += do_lf_putunsigned(file, decimal); + } + else + ASSERT(0); + return nr; +} + + +int +lf_printf(lf *file, + const char *fmt, + ...) +{ + int nr = 0; + char buf[1024]; + va_list ap; + + 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)); + nr += lf_putstr(file, buf); + va_end(ap); + return nr; +} + + +int +lf_print__c_code(lf *file, + const char *code) +{ + int nr = 0; + const 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; + nr += lf_putchr(file, '_'); + } + else if (in_bit_field && chp[0] == ':') { + nr += lf_putchr(file, '_'); + } + else if (in_bit_field && *chp == '}') { + nr += lf_putchr(file, '_'); + in_bit_field = 0; + } + else { + nr += lf_putchr(file, *chp); + } + chp++; + } + if (in_bit_field) + error("bit field paren miss match some where\n"); + if (*chp == '\n') { + nr += lf_putchr(file, '\n'); + chp++; + } + } + nr += lf_putchr(file, '\n'); + return nr; +} + + +int +lf_print__external_reference(lf *file, + int line_nr, + const char *file_name) +{ + int nr = 0; + switch (file->references) { + case lf_include_references: + lf_indent_suppress(file); + nr += lf_putstr(file, "#line "); + nr += lf_putint(file, line_nr); + nr += lf_putstr(file, " \""); + nr += lf_putstr(file, file_name); + nr += lf_putstr(file, "\"\n"); + break; + case lf_omit_references: + break; + } + return nr; +} + +int +lf_print__internal_reference(lf *file) +{ + int nr = 0; + nr += lf_print__external_reference(file, file->line_nr+2, file->name); + /* line_nr == last_line, want to number from next */ + return nr; +} + +void +lf_indent(lf *file, int delta) +{ + file->indent += delta; +} + + +int +lf_print__gnu_copyleft(lf *file) +{ + int nr = 0; + switch (file->type) { + case lf_is_c: + case lf_is_h: + nr += lf_printf(file, "\ +/* 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. + + -- + + This file was generated by the program %s */ +", filter_filename(file->program)); + break; + default: + ASSERT(0); + break; + } + return nr; +} + + +int +lf_putbin(lf *file, int decimal, int width) +{ + int nr = 0; + int bit; + ASSERT(width > 0); + for (bit = 1 << (width-1); bit != 0; bit >>= 1) { + if (decimal & bit) + nr += lf_putchr(file, '1'); + else + nr += lf_putchr(file, '0'); + } + return nr; +} + +int +lf_print__this_file_is_empty(lf *file) +{ + int nr = 0; + switch (file->type) { + case lf_is_c: + case lf_is_h: + nr += lf_printf(file, + "/* This generated file (%s) is intentionally left blank */\n", + file->name); + break; + default: + ASSERT(0); + } + return nr; +} + +int +lf_print__ucase_filename(lf *file) +{ + int nr = 0; + const char *chp = file->name; + while (*chp != '\0') { + char ch = *chp; + if (islower(ch)) { + nr += lf_putchr(file, toupper(ch)); + } + else if (ch == '.') + nr += lf_putchr(file, '_'); + else + nr += lf_putchr(file, ch); + chp++; + } + return nr; +} + +int +lf_print__file_start(lf *file) +{ + int nr = 0; + switch (file->type) { + case lf_is_h: + case lf_is_c: + nr += lf_print__gnu_copyleft(file); + nr += lf_printf(file, "\n"); + nr += lf_printf(file, "#ifndef _"); + nr += lf_print__ucase_filename(file); + nr += lf_printf(file, "_\n"); + nr += lf_printf(file, "#define _"); + nr += lf_print__ucase_filename(file); + nr += lf_printf(file, "_\n"); + nr += lf_printf(file, "\n"); + break; + default: + ASSERT(0); + } + return nr; +} + + +int +lf_print__file_finish(lf *file) +{ + int nr = 0; + switch (file->type) { + case lf_is_h: + case lf_is_c: + nr += lf_printf(file, "\n"); + nr += lf_printf(file, "#endif /* _"); + nr += lf_print__ucase_filename(file); + nr += lf_printf(file, "_*/\n"); + break; + default: + ASSERT(0); + } + return nr; +} + + +int +lf_print_function_type(lf *file, + const char *type, + const char *prefix, + const char *trailing_space) +{ + int nr = 0; + nr += lf_printf(file, "%s\\\n(%s)", prefix, type); + if (trailing_space != NULL) + nr += lf_printf(file, "%s", trailing_space); +#if 0 + const char *type_pointer = strrchr(type, '*'); + int type_pointer_offset = (type_pointer != NULL + ? type_pointer - type + : 0); + if (type_pointer == NULL) { + lf_printf(file, "%s %s", type, prefix); + } + else { + char *munged_type = (char*)zalloc(strlen(type) + + strlen(prefix) + + strlen(" * ") + + 1); + strcpy(munged_type, type); + munged_type[type_pointer_offset] = '\0'; + if (type_pointer_offset > 0 && type[type_pointer_offset-1] != ' ') + strcat(munged_type, " "); + strcat(munged_type, prefix); + strcat(munged_type, " "); + strcat(munged_type, type + type_pointer_offset); + lf_printf(file, "%s", munged_type); + free(munged_type); + } + if (trailing_space != NULL && type_pointer_offset < strlen(type) - 1) + lf_printf(file, trailing_space); +#endif + return nr; +} + diff --git a/sim/ppc/lf.h b/sim/ppc/lf.h new file mode 100644 index 00000000000..7bf6d1cafff --- /dev/null +++ b/sim/ppc/lf.h @@ -0,0 +1,129 @@ +/* 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. + + */ + + +/* LF: Line Numbered Output Stream */ + +typedef struct _lf lf; + +typedef enum { + lf_is_h, + lf_is_c, + lf_is_text, +} lf_file_type; + + +typedef enum { + lf_include_references, + lf_omit_references, +} lf_file_references; + + +/* Open the file NAME for writing. REAL_NAME is to be included in any + line number outputs. The output of line number information can be + suppressed with LINE_NUMBERS */ + +extern lf *lf_open +(char *name, + char *real_name, + lf_file_references file_references, + lf_file_type type, + const char *program); + +extern void lf_close +(lf *file); + + +/* Basic output functions */ + +extern int lf_putchr +(lf *file, + const char ch); + +extern int lf_putstr +(lf *file, + const char *string); + +extern int lf_putint +(lf *file, + int decimal); + +extern int lf_putbin +(lf *file, + int decimal, + int width); + +extern int lf_printf +(lf *file, + const char *fmt, + ...) __attribute__((format(printf, 2, 3))); + + +/* Indentation control. + + lf_indent_suppress suppresses indentation on the next line (current + line if that has not yet been started) */ + +extern void lf_indent_suppress +(lf *file); + +extern void lf_indent +(lf *file, + int delta); + + +/* Print generic text: */ + + +extern int lf_print__gnu_copyleft +(lf *file); + +extern int lf_print__file_start +(lf *file); + +extern int lf_print__this_file_is_empty +(lf *file); + +extern int lf_print__file_finish +(lf *file); + +extern int lf_print__internal_reference +(lf *file); + +extern int lf_print__external_reference +(lf *file, + int line_nr, + const char *file_name); + +extern int lf_print__ucase_filename +(lf *file); + +/* Tab prefix is suppressed */ + +extern int lf_print__c_code +(lf *file, + const char *code); + + +extern int lf_print_function_type +(lf *file, + const char *type, + const char *prefix, + const char *trailing_space); diff --git a/sim/ppc/main.c b/sim/ppc/main.c new file mode 100644 index 00000000000..a2948e0a6b0 --- /dev/null +++ b/sim/ppc/main.c @@ -0,0 +1,318 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1997, 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. + + */ + + +#include <stdarg.h> +#include <stdio.h> +#include <fcntl.h> + +#include <signal.h> + +#include "psim.h" +#include "options.h" +#include "device.h" /* FIXME: psim should provide the interface */ +#include "events.h" /* FIXME: psim should provide the interface */ + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#ifdef HAVE_STRING_H +#include <string.h> +#else +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif +#endif + +#include <errno.h> + +#if !defined(O_NDELAY) || !defined(F_GETFL) || !defined(F_SETFL) +#undef WITH_STDIO +#define WITH_STDIO DO_USE_STDIO +#endif + + +extern char **environ; + +static psim *simulation = NULL; + + +void +sim_io_poll_quit (void) +{ + /* nothing to do */ +} + +void +sim_io_printf_filtered(const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); + vprintf(msg, ap); + va_end(ap); +} + +void +error (const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); + vprintf(msg, ap); + printf("\n"); + va_end(ap); + + /* any final clean up */ + if (ppc_trace[trace_print_info] && simulation != NULL) + psim_print_info (simulation, ppc_trace[trace_print_info]); + + exit (1); +} + +int +sim_io_write_stdout(const char *buf, + int sizeof_buf) +{ + switch (CURRENT_STDIO) { + case DO_USE_STDIO: + { + int i; + for (i = 0; i < sizeof_buf; i++) { + putchar(buf[i]); + } + return i; + } + break; + case DONT_USE_STDIO: + return write(1, buf, sizeof_buf); + break; + default: + error("sim_io_write_stdout: invalid switch\n"); + } + return 0; +} + +int +sim_io_write_stderr(const char *buf, + int sizeof_buf) +{ + switch (CURRENT_STDIO) { + case DO_USE_STDIO: + { + int i; + for (i = 0; i < sizeof_buf; i++) { + fputc(buf[i], stderr); + } + return i; + } + break; + case DONT_USE_STDIO: + return write(2, buf, sizeof_buf); + break; + default: + error("sim_io_write_stdout: invalid switch\n"); + } + return 0; +} + +int +sim_io_read_stdin(char *buf, + int sizeof_buf) +{ + switch (CURRENT_STDIO) { + case DO_USE_STDIO: + if (sizeof_buf > 1) { + if (fgets(buf, sizeof_buf, stdin) != NULL) + return strlen(buf); + } + else if (sizeof_buf == 1) { + char b[2]; + if (fgets(b, sizeof(b), stdin) != NULL) { + memcpy(buf, b, strlen(b)); + return strlen(b); + } + } + else if (sizeof_buf == 0) + return 0; + return sim_io_eof; + break; + case DONT_USE_STDIO: +#if defined(O_NDELAY) && defined(F_GETFL) && defined(F_SETFL) + { + /* check for input */ + int flags; + int status; + int nr_read; + int result; + /* get the old status */ + flags = fcntl(0, F_GETFL, 0); + if (flags == -1) { + perror("sim_io_read_stdin"); + return sim_io_eof; + } + /* temp, disable blocking IO */ + status = fcntl(0, F_SETFL, flags | O_NDELAY); + if (status == -1) { + perror("sim_io_read_stdin"); + return sim_io_eof; + } + /* try for input */ + nr_read = read(0, buf, sizeof_buf); + if (nr_read > 0 + || (nr_read == 0 && sizeof_buf == 0)) + result = nr_read; + else if (nr_read == 0) + result = sim_io_eof; + else { /* nr_read < 0 */ + if (errno == EAGAIN) + result = sim_io_not_ready; + else + result = sim_io_eof; + } + /* return to regular vewing */ + status = fcntl(0, F_SETFL, flags); + if (status == -1) { + perror("sim_io_read_stdin"); + return sim_io_eof; + } + return result; + } + break; +#endif + default: + error("sim_io_read_stdin: invalid switch\n"); + break; + } + return 0; +} + +void +sim_io_flush_stdoutput(void) +{ + switch (CURRENT_STDIO) { + case DO_USE_STDIO: + fflush (stdout); + break; + case DONT_USE_STDIO: + break; + default: + error("sim_io_flush_stdoutput: invalid switch\n"); + break; + } +} + + +void * +zalloc(long size) +{ + void *memory = malloc(size); + if (memory == NULL) + error("zmalloc failed\n"); + memset(memory, 0, size); + return memory; +} + +void +zfree(void *chunk) +{ + free(chunk); +} + +/* When a CNTRL-C occures, queue an event to shut down the simulation */ + +static RETSIGTYPE +cntrl_c(int sig) +{ + psim_stop (simulation); +} + + +int +main(int argc, char **argv) +{ + const char *name_of_file; + char *arg_; + psim_status status; + device *root = psim_tree(); + + /* parse the arguments */ + argv = psim_options(root, argv + 1); + if (argv[0] == NULL) { + if (ppc_trace[trace_opts]) { + print_options (); + return 0; + } else { + psim_usage(0); + } + } + name_of_file = argv[0]; + + if (ppc_trace[trace_opts]) + print_options (); + + /* create the simulator */ + simulation = psim_create(name_of_file, root); + + /* fudge the environment so that _=prog-name */ + arg_ = (char*)zalloc(strlen(argv[0]) + strlen("_=") + 1); + strcpy(arg_, "_="); + strcat(arg_, argv[0]); + putenv(arg_); + + /* initialize it */ + psim_init(simulation); + psim_stack(simulation, argv, environ); + + { + RETSIGTYPE (*prev) (); + prev = signal(SIGINT, cntrl_c); + psim_run(simulation); + signal(SIGINT, prev); + } + + /* any final clean up */ + if (ppc_trace[trace_print_info]) + psim_print_info (simulation, ppc_trace[trace_print_info]); + + /* why did we stop */ + status = psim_get_status(simulation); + switch (status.reason) { + case was_continuing: + error("psim: continuing while stoped!\n"); + return 0; + case was_trap: + error("psim: no trap insn\n"); + return 0; + case was_exited: + return status.signal; + case was_signalled: + printf ("%s: Caught signal %d at address 0x%lx\n", + name_of_file, (int)status.signal, + (long)status.program_counter); + return status.signal; + default: + error("unknown halt condition\n"); + return 0; + } +} diff --git a/sim/ppc/misc.c b/sim/ppc/misc.c new file mode 100644 index 00000000000..d8e54ed497b --- /dev/null +++ b/sim/ppc/misc.c @@ -0,0 +1,215 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1997, 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. + + */ + + +#include <stdio.h> +#include <stdarg.h> +#include <ctype.h> + +#include "config.h" +#include "misc.h" + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#ifdef HAVE_STRING_H +#include <string.h> +#else +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif +#endif + +void +error (char *msg, ...) +{ + va_list ap; + va_start(ap, msg); + vprintf(msg, ap); + va_end(ap); + exit (1); +} + +void * +zalloc(long size) +{ + void *memory = malloc(size); + if (memory == NULL) + error("zalloc failed\n"); + memset(memory, 0, 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); +} + + +unsigned +a2i(const char *a) +{ + int neg = 0; + int base = 10; + unsigned num = 0; + int looping; + + while (isspace (*a)) + a++; + + if (*a == '-') { + neg = 1; + a++; + } + + if (*a == '0') { + if (a[1] == 'x' || a[1] == 'X') { + a += 2; + base = 16; + } + else + base = 8; + } + + looping = 1; + while (looping) { + int ch = *a++; + + switch (base) { + default: + looping = 0; + break; + + case 10: + if (ch >= '0' && ch <= '9') { + num = (num * 10) + (ch - '0'); + } else { + looping = 0; + } + break; + + case 8: + if (ch >= '0' && ch <= '7') { + num = (num * 8) + (ch - '0'); + } else { + looping = 0; + } + break; + + case 16: + if (ch >= '0' && ch <= '9') { + num = (num * 16) + (ch - '0'); + } else if (ch >= 'a' && ch <= 'f') { + num = (num * 16) + (ch - 'a' + 10); + } else if (ch >= 'A' && ch <= 'F') { + num = (num * 16) + (ch - 'A' + 10); + } else { + looping = 0; + } + break; + } + } + + if (neg) + num = - num; + + return num; +} + +unsigned +target_a2i(int ms_bit_nr, + const char *a) +{ + if (ms_bit_nr) + return (ms_bit_nr - a2i(a)); + else + return a2i(a); +} + +unsigned +i2target(int ms_bit_nr, + unsigned bit) +{ + if (ms_bit_nr) + return ms_bit_nr - bit; + else + return bit; +} + + +int +name2i(const char *names, + const name_map *map) +{ + const name_map *curr; + const char *name = names; + while (*name != '\0') { + /* find our name */ + char *end = strchr(name, ','); + char *next; + int len; + if (end == NULL) { + end = strchr(name, '\0'); + next = end; + } + else { + next = end + 1; + } + len = end - name; + /* look it up */ + curr = map; + while (curr->name != NULL) { + if (strncmp(curr->name, name, len) == 0 + && strlen(curr->name) == len) + return curr->i; + curr++; + } + name = next; + } + /* nothing found, possibly return a default */ + curr = map; + while (curr->name != NULL) + curr++; + if (curr->i >= 0) + return curr->i; + else + error("%s contains no valid names\n", names); + return 0; +} + +const char * +i2name(const int i, + const name_map *map) +{ + while (map->name != NULL) { + if (map->i == i) + return map->name; + map++; + } + error("map lookup failed for %d\n", i); + return NULL; +} diff --git a/sim/ppc/misc.h b/sim/ppc/misc.h new file mode 100644 index 00000000000..041bd6d2cac --- /dev/null +++ b/sim/ppc/misc.h @@ -0,0 +1,94 @@ +/* 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. + + */ + + +/* Frustrating header junk */ + +#include "config.h" + +#include <stdio.h> +#include <ctype.h> + +#ifdef HAVE_STRING_H +#include <string.h> +#else +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif +#endif + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#if !defined (__attribute__) && (!defined(__GNUC__) || __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7)) +#define __attribute__(arg) +#endif + + + +#include "filter_filename.h" + +extern void error +(char *msg, ...); + +#define ASSERT(EXPRESSION) \ +do { \ + if (!(EXPRESSION)) { \ + error("%s:%d: assertion failed - %s\n", \ + filter_filename (__FILE__), __LINE__, #EXPRESSION); \ + } \ +} while (0) + +#define ZALLOC(TYPE) (TYPE*)zalloc(sizeof(TYPE)) + +extern void *zalloc +(long size); + +extern void dumpf +(int indent, char *msg, ...); + +extern unsigned target_a2i +(int ms_bit_nr, + const char *a); + +extern unsigned i2target +(int ms_bit_nr, + unsigned bit); + +extern unsigned a2i +(const char *a); + +/* Try looking for name in the map table (returning the corresponding + integer value). If that fails, try converting the name into an + integer */ + +typedef struct _name_map { + const char *name; + int i; +} name_map; + +extern int name2i +(const char *name, + const name_map *map); + +extern const char *i2name +(const int i, + const name_map *map); diff --git a/sim/ppc/mon.c b/sim/ppc/mon.c new file mode 100644 index 00000000000..2bdaf474158 --- /dev/null +++ b/sim/ppc/mon.c @@ -0,0 +1,445 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1997, 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 _MON_C_ +#define _MON_C_ + +#include "basics.h" +#include "cpu.h" +#include "mon.h" +#include <stdio.h> + +#ifdef HAVE_STRING_H +#include <string.h> +#else +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif +#endif + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif + +#ifdef HAVE_TIME_H +#include <time.h> +#endif + +#ifdef HAVE_SYS_TIMES_H +#include <sys/times.h> +#endif + +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif + +#ifdef HAVE_SYS_RESOURCE_H +#include <sys/resource.h> +int getrusage(); +#endif + +#define MAX_BYTE_READWRITE 9 +#define MAX_SHIFT_READWRITE 3 + +struct _cpu_mon { + count_type issue_count[nr_itable_entries]; + count_type read_count; + count_type read_byte_count[MAX_BYTE_READWRITE]; + count_type write_count; + count_type write_byte_count[MAX_BYTE_READWRITE]; + count_type unaligned_read_count; + count_type unaligned_write_count; + count_type event_count[nr_mon_events]; +}; + +struct _mon { + int nr_cpus; + cpu_mon cpu_monitor[MAX_NR_PROCESSORS]; +}; + + +INLINE_MON\ +(mon *) +mon_create(void) +{ + mon *monitor = ZALLOC(mon); + return monitor; +} + + +INLINE_MON\ +(cpu_mon *) +mon_cpu(mon *monitor, + int cpu_nr) +{ + if (cpu_nr < 0 || cpu_nr >= MAX_NR_PROCESSORS) + error("mon_cpu() - invalid cpu number\n"); + return &monitor->cpu_monitor[cpu_nr]; +} + + +INLINE_MON\ +(void) +mon_init(mon *monitor, + int nr_cpus) +{ + memset(monitor, 0, sizeof(*monitor)); + monitor->nr_cpus = nr_cpus; +} + + +INLINE_MON\ +(void) +mon_issue(itable_index index, + cpu *processor, + unsigned_word cia) +{ + cpu_mon *monitor = cpu_monitor(processor); + ASSERT(index <= nr_itable_entries); + monitor->issue_count[index] += 1; +} + + +INLINE_MON\ +(void) +mon_read(unsigned_word ea, + unsigned_word ra, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + cpu_mon *monitor = cpu_monitor(processor); + monitor->read_count += 1; + monitor->read_byte_count[nr_bytes] += 1; + if ((nr_bytes - 1) & ea) + monitor->unaligned_read_count += 1; +} + + +INLINE_MON\ +(void) +mon_write(unsigned_word ea, + unsigned_word ra, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + cpu_mon *monitor = cpu_monitor(processor); + monitor->write_count += 1; + monitor->write_byte_count[nr_bytes] += 1; + if ((nr_bytes - 1) & ea) + monitor->unaligned_write_count += 1; +} + +INLINE_MON\ +(void) +mon_event(mon_events event, + cpu *processor, + unsigned_word cia) +{ + cpu_mon *monitor = cpu_monitor(processor); + ASSERT(event < nr_mon_events); + monitor->event_count[event] += 1; +} + +INLINE_MON\ +(unsigned) +mon_get_number_of_insns(mon *monitor, + int cpu_nr) +{ + itable_index index; + unsigned total_insns = 0; + ASSERT(cpu_nr >= 0 && cpu_nr < monitor->nr_cpus); + for (index = 0; index < nr_itable_entries; index++) + total_insns += monitor->cpu_monitor[cpu_nr].issue_count[index]; + return total_insns; +} + +STATIC_INLINE_MON\ +(int) +mon_sort_instruction_names(const void *ptr_a, const void *ptr_b) +{ + itable_index a = *(const itable_index *)ptr_a; + itable_index b = *(const itable_index *)ptr_b; + + return strcmp (itable[a].name, itable[b].name); +} + +STATIC_INLINE_MON\ +(char *) +mon_add_commas(char *buf, + int sizeof_buf, + count_type value) +{ + int comma = 3; + char *endbuf = buf + sizeof_buf - 1; + + *--endbuf = '\0'; + do { + if (comma-- == 0) + { + *--endbuf = ','; + comma = 2; + } + + *--endbuf = (value % 10) + '0'; + } while ((value /= 10) != 0); + + ASSERT(endbuf >= buf); + return endbuf; +} + + +INLINE_MON\ +(void) +mon_print_info(psim *system, + mon *monitor, + int verbose) +{ + char buffer[20]; + char buffer1[20]; + char buffer2[20]; + char buffer4[20]; + char buffer8[20]; + int cpu_nr; + int len_cpu; + int len_num = 0; + int len_sub_num[MAX_BYTE_READWRITE]; + int len; + int i; + long total_insns = 0; + long cpu_insns_second = 0; + long total_sim_cycles = 0; + long sim_cycles_second = 0; + double cpu_time = 0.0; + + for (i = 0; i < MAX_BYTE_READWRITE; i++) + len_sub_num[i] = 0; + + for (cpu_nr = 0; cpu_nr < monitor->nr_cpus; cpu_nr++) { + count_type num_insns = mon_get_number_of_insns(monitor, cpu_nr); + + total_insns += num_insns; + len = strlen (mon_add_commas(buffer, sizeof(buffer), num_insns)); + if (len_num < len) + len_num = len; + + for (i = 0; i <= MAX_SHIFT_READWRITE; i++) { + int size = 1<<i; + len = strlen (mon_add_commas(buffer, sizeof(buffer), + monitor->cpu_monitor[cpu_nr].read_byte_count[size])); + if (len_sub_num[size] < len) + len_sub_num[size] = len; + + len = strlen (mon_add_commas(buffer, sizeof(buffer), + monitor->cpu_monitor[cpu_nr].write_byte_count[size])); + if (len_sub_num[size] < len) + len_sub_num[size] = len; + } + } + + sprintf (buffer, "%d", (int)monitor->nr_cpus + 1); + len_cpu = strlen (buffer); + +#ifdef HAVE_GETRUSAGE + { + struct rusage mytime; + if (getrusage (RUSAGE_SELF, &mytime) == 0 + && (mytime.ru_utime.tv_sec > 0 || mytime.ru_utime.tv_usec > 0)) { + + cpu_time = (double)mytime.ru_utime.tv_sec + (((double)mytime.ru_utime.tv_usec) / 1000000.0); + } + } + if (WITH_EVENTS) + total_sim_cycles = event_queue_time(psim_event_queue(system)) - 1; + if (cpu_time > 0) { + if (total_insns > 0) + cpu_insns_second = (long)(((double)total_insns / cpu_time) + 0.5); + if (total_sim_cycles) { + sim_cycles_second = (long)(((double)total_sim_cycles / cpu_time) + 0.5); + } + } +#endif + + for (cpu_nr = 0; cpu_nr < monitor->nr_cpus; cpu_nr++) { + + if (verbose > 1) { + itable_index sort_insns[nr_itable_entries]; + int nr_sort_insns = 0; + itable_index index; + int index2; + + if (cpu_nr) + printf_filtered ("\n"); + + for (index = 0; index < nr_itable_entries; index++) { + if (monitor->cpu_monitor[cpu_nr].issue_count[index]) { + sort_insns[nr_sort_insns++] = index; + } + } + + qsort((void *)sort_insns, nr_sort_insns, sizeof(sort_insns[0]), mon_sort_instruction_names); + + for (index2 = 0; index2 < nr_sort_insns; index2++) { + index = sort_insns[index2]; + printf_filtered("CPU #%*d executed %*s %s instruction%s.\n", + len_cpu, cpu_nr+1, + len_num, mon_add_commas(buffer, + sizeof(buffer), + monitor->cpu_monitor[cpu_nr].issue_count[index]), + itable[index].name, + (monitor->cpu_monitor[cpu_nr].issue_count[index] == 1) ? "" : "s"); + } + + printf_filtered ("\n"); + } + + if (CURRENT_MODEL_ISSUE > 0) + { + model_data *model_ptr = cpu_model(psim_cpu(system, cpu_nr)); + model_print *ptr = model_mon_info(model_ptr); + model_print *orig_ptr = ptr; + + while (ptr) { + if (ptr->count) + printf_filtered("CPU #%*d executed %*s %s%s.\n", + len_cpu, cpu_nr+1, + len_num, mon_add_commas(buffer, + sizeof(buffer), + ptr->count), + ptr->name, + ((ptr->count == 1) + ? ptr->suffix_singular + : ptr->suffix_plural)); + + ptr = ptr->next; + } + + model_mon_info_free(model_ptr, orig_ptr); + } + + if (monitor->cpu_monitor[cpu_nr].read_count) + printf_filtered ("CPU #%*d executed %*s read%s (%*s 1-byte, %*s 2-byte, %*s 4-byte, %*s 8-byte).\n", + len_cpu, cpu_nr+1, + len_num, mon_add_commas(buffer, + sizeof(buffer), + monitor->cpu_monitor[cpu_nr].read_count), + (monitor->cpu_monitor[cpu_nr].read_count == 1) ? "" : "s", + len_sub_num[1], mon_add_commas(buffer1, + sizeof(buffer1), + monitor->cpu_monitor[cpu_nr].read_byte_count[1]), + len_sub_num[2], mon_add_commas(buffer2, + sizeof(buffer2), + monitor->cpu_monitor[cpu_nr].read_byte_count[2]), + len_sub_num[4], mon_add_commas(buffer4, + sizeof(buffer4), + monitor->cpu_monitor[cpu_nr].read_byte_count[4]), + len_sub_num[8], mon_add_commas(buffer8, + sizeof(buffer8), + monitor->cpu_monitor[cpu_nr].read_byte_count[8])); + + if (monitor->cpu_monitor[cpu_nr].write_count) + printf_filtered ("CPU #%*d executed %*s write%s (%*s 1-byte, %*s 2-byte, %*s 4-byte, %*s 8-byte).\n", + len_cpu, cpu_nr+1, + len_num, mon_add_commas(buffer, + sizeof(buffer), + monitor->cpu_monitor[cpu_nr].write_count), + (monitor->cpu_monitor[cpu_nr].write_count == 1) ? "" : "s", + len_sub_num[1], mon_add_commas(buffer1, + sizeof(buffer1), + monitor->cpu_monitor[cpu_nr].write_byte_count[1]), + len_sub_num[2], mon_add_commas(buffer2, + sizeof(buffer2), + monitor->cpu_monitor[cpu_nr].write_byte_count[2]), + len_sub_num[4], mon_add_commas(buffer4, + sizeof(buffer4), + monitor->cpu_monitor[cpu_nr].write_byte_count[4]), + len_sub_num[8], mon_add_commas(buffer8, + sizeof(buffer8), + monitor->cpu_monitor[cpu_nr].write_byte_count[8])); + + if (monitor->cpu_monitor[cpu_nr].unaligned_read_count) + printf_filtered ("CPU #%*d executed %*s unaligned read%s.\n", + len_cpu, cpu_nr+1, + len_num, mon_add_commas(buffer, + sizeof(buffer), + monitor->cpu_monitor[cpu_nr].unaligned_read_count), + (monitor->cpu_monitor[cpu_nr].unaligned_read_count == 1) ? "" : "s"); + + if (monitor->cpu_monitor[cpu_nr].unaligned_write_count) + printf_filtered ("CPU #%*d executed %*s unaligned write%s.\n", + len_cpu, cpu_nr+1, + len_num, mon_add_commas(buffer, + sizeof(buffer), + monitor->cpu_monitor[cpu_nr].unaligned_write_count), + (monitor->cpu_monitor[cpu_nr].unaligned_write_count == 1) ? "" : "s"); + + if (monitor->cpu_monitor[cpu_nr].event_count[mon_event_icache_miss]) + printf_filtered ("CPU #%*d executed %*s icache miss%s.\n", + len_cpu, cpu_nr+1, + len_num, mon_add_commas(buffer, + sizeof(buffer), + monitor->cpu_monitor[cpu_nr].event_count[mon_event_icache_miss]), + (monitor->cpu_monitor[cpu_nr].event_count[mon_event_icache_miss] == 1) ? "" : "es"); + + { + long nr_insns = mon_get_number_of_insns(monitor, cpu_nr); + if (nr_insns > 0) + printf_filtered("CPU #%*d executed %*s instructions in total.\n", + len_cpu, cpu_nr+1, + len_num, mon_add_commas(buffer, + sizeof(buffer), + nr_insns)); + } + } + + if (total_insns > 0) { + if (monitor->nr_cpus > 1) + printf_filtered("\nAll CPUs executed %s instructions in total.\n", + mon_add_commas(buffer, sizeof(buffer), total_insns)); + } + else if (total_sim_cycles > 0) { + printf_filtered("\nSimulator performed %s simulation cycles.\n", + mon_add_commas(buffer, sizeof(buffer), total_sim_cycles)); + } + + if (cpu_insns_second) + printf_filtered ("%sSimulator speed was %s instructions/second.\n", + (monitor->nr_cpus > 1) ? "" : "\n", + mon_add_commas(buffer, sizeof(buffer), cpu_insns_second)); + else if (sim_cycles_second) + printf_filtered ("Simulator speed was %s simulation cycles/second\n", + mon_add_commas(buffer, sizeof(buffer), sim_cycles_second)); + else if (cpu_time > 0.0) + printf_filtered ("%sSimulator executed for %.2f seconds\n", + (monitor->nr_cpus > 1) ? "" : "\n", cpu_time); + +} + +#endif /* _MON_C_ */ diff --git a/sim/ppc/mon.h b/sim/ppc/mon.h new file mode 100644 index 00000000000..ab64b8e13c4 --- /dev/null +++ b/sim/ppc/mon.h @@ -0,0 +1,108 @@ +/* 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 _MON_H_ +#define _MON_H_ + +#include "basics.h" +#include "itable.h" + +/* monitor/logger: counts what the simulation is up to */ + +typedef unsigned long count_type; + +/* Interfact to model to return model specific information */ +typedef struct _model_print model_print; +struct _model_print { + model_print *next; + const char *name; + const char *suffix_singular; + const char *suffix_plural; + count_type count; +}; + +/* Additional events to monitor */ +typedef enum _mon_events { + mon_event_icache_miss, + nr_mon_events +} mon_events; + +typedef struct _mon mon; +typedef struct _cpu_mon cpu_mon; + +INLINE_MON\ +(mon *) mon_create +(void); + +INLINE_MON\ +(cpu_mon *) mon_cpu +(mon *monitor, + int cpu_nr); + +INLINE_MON\ +(void) mon_init +(mon *monitor, + int nr_cpus); + +INLINE_MON\ +(void) mon_issue +(itable_index index, + cpu *processor, + unsigned_word cia); + +/* NOTE - there is no mon_iload - it is made reduntant by mon_issue() + and besides when the cpu's have their own cache, the information is + wrong */ + +INLINE_MON\ +(void) mon_read +(unsigned_word ea, + unsigned_word ra, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia); + +INLINE_MON\ +(void) mon_write +(unsigned_word ea, + unsigned_word ra, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia); + +INLINE_MON\ +(void) mon_event +(mon_events event, + cpu *processor, + unsigned_word cia); + +INLINE_MON\ +(unsigned) mon_get_number_of_insns +(mon *monitor, + int cpu_nr); + +INLINE_MON\ +(void) mon_print_info +(psim *system, + mon *monitor, + int verbose); + +#endif diff --git a/sim/ppc/options.c b/sim/ppc/options.c new file mode 100644 index 00000000000..2d31124752a --- /dev/null +++ b/sim/ppc/options.c @@ -0,0 +1,247 @@ +/* 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 _OPTIONS_C_ +#define _OPTIONS_C_ + +#include "cpu.h" +#include "options.h" + +STATIC_INLINE_OPTIONS\ +(const char *) +options_byte_order (int order) +{ + switch (order) { + case 0: return "0"; + case BIG_ENDIAN: return "BIG_ENDIAN"; + case LITTLE_ENDIAN: return "LITTLE_ENDIAN"; + } + + return "UNKNOWN"; +} + +STATIC_INLINE_OPTIONS\ +(const char *) +options_env (int env) +{ + switch (env) { + case OPERATING_ENVIRONMENT: return "OPERATING"; + case VIRTUAL_ENVIRONMENT: return "VIRTUAL"; + case USER_ENVIRONMENT: return "USER"; + case 0: return "0"; + } + + return "UNKNOWN"; +} + +STATIC_INLINE_OPTIONS\ +(const char *) +options_align (int align) +{ + switch (align) { + case NONSTRICT_ALIGNMENT: return "NONSTRICT"; + case STRICT_ALIGNMENT: return "STRICT"; + case 0: return "0"; + } + + return "UNKNOWN"; +} + +STATIC_INLINE_OPTIONS\ +(const char *) +options_float (int float_type) +{ + switch (float_type) { + case SOFT_FLOATING_POINT: return "SOFTWARE"; + case HARD_FLOATING_POINT: return "HARDWARE"; + } + + return "UNKNOWN"; +} + +STATIC_INLINE_OPTIONS\ +(const char *) +options_mon (int mon) +{ + switch (mon) { + case MONITOR_INSTRUCTION_ISSUE|MONITOR_LOAD_STORE_UNIT: return "ALL"; + case MONITOR_INSTRUCTION_ISSUE: return "INSTRUCTION"; + case MONITOR_LOAD_STORE_UNIT: return "MEMORY"; + case 0: return "0"; + } + + return "UNKNOWN"; +} + +STATIC_INLINE_OPTIONS\ +(const char *) +options_inline (int in) +{ + switch (in) { + case /*0*/ 0: return "0"; + case /*1*/ REVEAL_MODULE: return "REVEAL_MODULE"; + case /*2*/ INLINE_MODULE: return "INLINE_MODULE"; + case /*3*/ REVEAL_MODULE|INLINE_MODULE: return "REVEAL_MODULE|INLINE_MODULE"; + case /*4*/ INLINE_LOCALS: return "LOCALS_INLINE"; + case /*5*/ INLINE_LOCALS|REVEAL_MODULE: return "INLINE_LOCALS|REVEAL_MODULE"; + case /*6*/ INLINE_LOCALS|INLINE_MODULE: return "INLINE_LOCALS|INLINE_MODULE"; + case /*7*/ ALL_INLINE: return "ALL_INLINE"; + } + return "0"; +} + + +INLINE_OPTIONS\ +(void) +print_options (void) +{ +#if defined(_GNUC_) && defined(__VERSION__) + printf_filtered ("Compiled by GCC %s on %s %s\n", __VERSION__, __DATE__, __TIME__); +#else + printf_filtered ("Compiled on %s %s\n", __DATE__, __TIME__); +#endif + + printf_filtered ("WITH_HOST_BYTE_ORDER = %s\n", options_byte_order (WITH_HOST_BYTE_ORDER)); + printf_filtered ("WITH_TARGET_BYTE_ORDER = %s\n", options_byte_order (WITH_TARGET_BYTE_ORDER)); + printf_filtered ("WITH_XOR_ENDIAN = %d\n", WITH_XOR_ENDIAN); + printf_filtered ("WITH_BSWAP = %d\n", WITH_BSWAP); + printf_filtered ("WITH_SMP = %d\n", WITH_SMP); + printf_filtered ("WITH_HOST_WORD_BITSIZE = %d\n", WITH_HOST_WORD_BITSIZE); + printf_filtered ("WITH_TARGET_WORD_BITSIZE = %d\n", WITH_TARGET_WORD_BITSIZE); + printf_filtered ("WITH_ENVIRONMENT = %s\n", options_env(WITH_ENVIRONMENT)); + printf_filtered ("WITH_EVENTS = %d\n", WITH_EVENTS); + printf_filtered ("WITH_TIME_BASE = %d\n", WITH_TIME_BASE); + printf_filtered ("WITH_CALLBACK_MEMORY = %d\n", WITH_CALLBACK_MEMORY); + printf_filtered ("WITH_ALIGNMENT = %s\n", options_align (WITH_ALIGNMENT)); + printf_filtered ("WITH_FLOATING_POINT = %s\n", options_float (WITH_FLOATING_POINT)); + printf_filtered ("WITH_TRACE = %d\n", WITH_TRACE); + printf_filtered ("WITH_ASSERT = %d\n", WITH_ASSERT); + printf_filtered ("WITH_MON = %s\n", options_mon (WITH_MON)); + printf_filtered ("WITH_DEFAULT_MODEL = %s\n", model_name[WITH_DEFAULT_MODEL]); + printf_filtered ("WITH_MODEL = %s\n", model_name[WITH_MODEL]); + printf_filtered ("WITH_MODEL_ISSUE = %d\n", WITH_MODEL_ISSUE); + printf_filtered ("WITH_RESERVED_BITS = %d\n", WITH_RESERVED_BITS); + printf_filtered ("WITH_STDIO = %d\n", WITH_STDIO); + printf_filtered ("WITH_REGPARM = %d\n", WITH_REGPARM); + printf_filtered ("WITH_STDCALL = %d\n", WITH_STDCALL); + printf_filtered ("DEFAULT_INLINE = %s\n", options_inline (DEFAULT_INLINE)); + printf_filtered ("SIM_ENDIAN_INLINE = %s\n", options_inline (SIM_ENDIAN_INLINE)); + printf_filtered ("BITS_INLINE = %s\n", options_inline (BITS_INLINE)); + printf_filtered ("CPU_INLINE = %s\n", options_inline (CPU_INLINE)); + printf_filtered ("VM_INLINE = %s\n", options_inline (VM_INLINE)); + printf_filtered ("CORE_INLINE = %s\n", options_inline (CORE_INLINE)); + printf_filtered ("EVENTS_INLINE = %s\n", options_inline (EVENTS_INLINE)); + printf_filtered ("MON_INLINE = %s\n", options_inline (MON_INLINE)); + printf_filtered ("INTERRUPTS_INLINE = %s\n", options_inline (INTERRUPTS_INLINE)); + printf_filtered ("REGISTERS_INLINE = %s\n", options_inline (REGISTERS_INLINE)); + printf_filtered ("DEVICE_INLINE = %s\n", options_inline (DEVICE_INLINE)); + printf_filtered ("SPREG_INLINE = %s\n", options_inline (SPREG_INLINE)); + printf_filtered ("SEMANTICS_INLINE = %s\n", options_inline (SEMANTICS_INLINE)); + printf_filtered ("IDECODE_INLINE = %s\n", options_inline (IDECODE_INLINE)); + printf_filtered ("OPTIONS_INLINE = %s\n", options_inline (OPTIONS_INLINE)); + printf_filtered ("OS_EMUL_INLINE = %s\n", options_inline (OS_EMUL_INLINE)); + printf_filtered ("SUPPORT_INLINE = %s\n", options_inline (SUPPORT_INLINE)); + +#ifdef OPCODE_RULES + printf_filtered ("OPCODE rules = %s\n", OPCODE_RULES); +#endif + +#ifdef IGEN_FLAGS + printf_filtered ("IGEN_FLAGS = %s\n", IGEN_FLAGS); +#endif + +#ifdef DGEN_FLAGS + printf_filtered ("DGEN_FLAGS = %s\n", DGEN_FLAGS); +#endif + + { + static const char *const defines[] = { +#ifdef __GNUC__ + "__GNUC__", +#endif + +#ifdef __STRICT_ANSI__ + "__STRICT_ANSI__", +#endif + +#ifdef __CHAR_UNSIGNED__ + "__CHAR_UNSIGNED__", +#endif + +#ifdef __OPTIMIZE__ + "__OPTIMIZE__", +#endif + +#ifdef STDC_HEADERS + "STDC_HEADERS", +#endif + +#include "defines.h" + +#ifdef HAVE_TERMIOS_CLINE + "HAVE_TERMIOS_CLINE", +#endif + +#ifdef HAVE_TERMIOS_STRUCTURE + "HAVE_TERMIOS_STRUCTURE", +#endif + +#ifdef HAVE_TERMIO_CLINE + "HAVE_TERMIO_CLINE", +#endif + +#ifdef HAVE_TERMIO_STRUCTURE + "HAVE_TERMIO_STRUCTURE", +#endif + +#ifdef HAVE_DEVZERO + "HAVE_DEVZERO", +#endif + }; + + int i; + int max_len = 0; + int cols; + + for (i = 0; i < sizeof (defines) / sizeof (defines[0]); i++) { + int len = strlen (defines[i]); + if (len > max_len) + max_len = len; + } + + cols = 78 / (max_len + 2); + if (cols < 0) + cols = 1; + + printf_filtered ("\n#defines:"); + for (i = 0; i < sizeof (defines) / sizeof (defines[0]); i++) { + const char *const prefix = ((i % cols) == 0) ? "\n" : ""; + printf_filtered ("%s %s%*s", prefix, defines[i], + (((i == (sizeof (defines) / sizeof (defines[0])) - 1) + || (((i + 1) % cols) == 0)) + ? 0 + : max_len + 4 - strlen (defines[i])), + ""); + } + printf_filtered ("\n"); + } +} + +#endif /* _OPTIONS_C_ */ diff --git a/sim/ppc/options.h b/sim/ppc/options.h new file mode 100644 index 00000000000..5adb9ebcf1b --- /dev/null +++ b/sim/ppc/options.h @@ -0,0 +1,30 @@ +/* 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 _OPTIONS_H_ +#define _OPTIONS_H_ + +#ifndef INLINE_OPTIONS +#define INLINE_OPTIONS +#endif + +INLINE_OPTIONS(void) print_options (void); + +#endif /* _OPTIONS_H_ */ diff --git a/sim/ppc/os_emul.c b/sim/ppc/os_emul.c new file mode 100644 index 00000000000..dc60c8f6641 --- /dev/null +++ b/sim/ppc/os_emul.c @@ -0,0 +1,146 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1996, 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 _OS_EMUL_C_ +#define _OS_EMUL_C_ + +#include "cpu.h" +#include "idecode.h" +#include "os_emul.h" + +#include "emul_generic.h" +#include "emul_netbsd.h" +#include "emul_unix.h" +#include "emul_chirp.h" +#include "emul_bugapi.h" + +static const os_emul *(os_emulations[]) = { + &emul_chirp, + &emul_bugapi, + &emul_netbsd, + &emul_solaris, + &emul_linux, + 0 +}; + + +INLINE_OS_EMUL\ +(os_emul *) +os_emul_create(const char *file_name, + device *root) +{ + const char *emulation_name = NULL; + bfd *image; + os_emul *chosen_emulation = NULL; + + bfd_init(); /* would never hurt */ + + /* open the file */ + image = bfd_openr(file_name, NULL); + if (image == NULL) { + bfd_perror(file_name); + error("nothing loaded\n"); + } + + /* check it is an executable */ + if (!bfd_check_format(image, bfd_object)) { + TRACE(trace_tbd, + ("FIXME - should check more than just bfd_check_format\n")); + TRACE(trace_os_emul, + ("%s not an executable, assumeing a device file\n", file_name)); + bfd_close(image); + image = NULL; + } + + /* if a device file, load that before trying the emulations on */ + if (image == NULL) { + psim_merge_device_file(root, file_name); + } + + /* see if the device tree already specifies the required emulation */ + if (tree_find_property(root, "/openprom/options/os-emul") != NULL) + emulation_name = + tree_find_string_property(root, "/openprom/options/os-emul"); + else + emulation_name = NULL; + + /* go through each emulation to see if they reconize it. FIXME - + should have some sort of imported table from a separate file */ + { + os_emul_data *emul_data; + const os_emul **possible_emulation; + chosen_emulation = NULL; + for (possible_emulation = os_emulations, emul_data = NULL; + *possible_emulation != NULL && emul_data == NULL; + possible_emulation++) { + emul_data = (*possible_emulation)->create(root, + image, + emulation_name); + if (emul_data != NULL) { + chosen_emulation = ZALLOC(os_emul); + *chosen_emulation = **possible_emulation; + chosen_emulation->data = emul_data; + } + } + } + + /* clean up */ + if (image != NULL) + bfd_close(image); + return chosen_emulation; +} + +INLINE_OS_EMUL\ +(void) +os_emul_init(os_emul *emulation, + int nr_cpus) +{ + if (emulation != (os_emul*)0) + emulation->init(emulation->data, nr_cpus); +} + +INLINE_OS_EMUL\ +(void) +os_emul_system_call(cpu *processor, + unsigned_word cia) +{ + os_emul *emulation = cpu_os_emulation(processor); + if (emulation != (os_emul*)0 && emulation->system_call != 0) + emulation->system_call(processor, cia, emulation->data); + else + error("System call emulation not available\n"); +} + +INLINE_OS_EMUL\ +(int) +os_emul_instruction_call(cpu *processor, + unsigned_word cia, + unsigned_word ra) +{ + os_emul *emulation = cpu_os_emulation(processor); + if (emulation != (os_emul*)0 && emulation->instruction_call != 0) + return emulation->instruction_call(processor, cia, ra, emulation->data); + else + return 0; +} + + +#endif /* _OS_EMUL_C_ */ diff --git a/sim/ppc/os_emul.h b/sim/ppc/os_emul.h new file mode 100644 index 00000000000..6d74e9c6f93 --- /dev/null +++ b/sim/ppc/os_emul.h @@ -0,0 +1,60 @@ +/* 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 _OS_EMUL_H_ +#define _OS_EMUL_H_ + +typedef struct _os_emul os_emul; + +INLINE_OS_EMUL\ +(os_emul *) os_emul_create +(const char *file_name, + device *root); + +INLINE_OS_EMUL\ +(void) os_emul_init +(os_emul *emulation, + int nr_cpus); + + +/* System-call emulation - for user code. Instead of trapping system + calls to kernel mode, the simulator emulates the kernels behavour */ + +INLINE_OS_EMUL\ +(void) os_emul_system_call +(cpu *processor, + unsigned_word cia); + + +/* Instruction emulation - for kernel code. Extra (normally illegal) + instructions are added to the instruction table that when executed + call this emulation function. The instruction call emulator should + verify the address that the instruction appears before emulating + the required behavour. If the verification fails, a zero value + should be returned (indicating instruction illegal). */ + +INLINE_OS_EMUL\ +(int) os_emul_instruction_call +(cpu *processor, + unsigned_word cia, + unsigned_word ra); + +#endif diff --git a/sim/ppc/pk_disklabel.c b/sim/ppc/pk_disklabel.c new file mode 100644 index 00000000000..91dcf7ce562 --- /dev/null +++ b/sim/ppc/pk_disklabel.c @@ -0,0 +1,401 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1997, 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 _PK_DISKLABEL_C_ +#define _PK_DISKLABEL_C_ + +#ifndef STATIC_INLINE_PK_DISKLABEL +#define STATIC_INLINE_PK_DISKLABEL STATIC_INLINE +#endif + +#include "device_table.h" + +#include "pk.h" + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + + + +/* PACKAGE + + disk-label - all knowing disk I/O package + + DESCRIPTION + + The disk-label package provides a generic interface to disk + devices. It uses the arguments specified when an instance is being + created to determine if the raw disk, a partition, or a file within + a partition should be opened. + + An instance create call to disk-label could result, for instance, + in the opening of a DOS file system contained within a dos + partition contained within a physical disk. + + */ + +/* taken from bfd/ppcboot.c by Michael Meissner */ + +/* PPCbug location structure */ +typedef struct ppcboot_location { + unsigned8 ind; + unsigned8 head; + unsigned8 sector; + unsigned8 cylinder; +} ppcboot_location_t; + +/* PPCbug partition table layout */ +typedef struct ppcboot_partition { + ppcboot_location_t partition_begin; /* partition begin */ + ppcboot_location_t partition_end; /* partition end */ + unsigned8 sector_begin[4]; /* 32-bit start RBA (zero-based), little endian */ + unsigned8 sector_length[4]; /* 32-bit RBA count (one-based), little endian */ +} ppcboot_partition_t; + +#if 0 +/* PPCbug boot layout. */ +typedef struct ppcboot_hdr { + unsigned8 pc_compatibility[446]; /* x86 instruction field */ + ppcboot_partition_t partition[4]; /* partition information */ + unsigned8 signature[2]; /* 0x55 and 0xaa */ + unsigned8 entry_offset[4]; /* entry point offset, little endian */ + unsigned8 length[4]; /* load image length, little endian */ + unsigned8 flags; /* flag field */ + unsigned8 os_id; /* OS_ID */ + char partition_name[32]; /* partition name */ + unsigned8 reserved1[470]; /* reserved */ +} ppcboot_hdr_t; +#endif + + +typedef struct _disklabel { + device_instance *parent; + device_instance *raw_disk; + unsigned_word pos; + unsigned_word sector_begin; + unsigned_word sector_length; +} disklabel; + + +static unsigned_word +sector2uw(unsigned8 s[4]) +{ + return ((s[3] << 24) + + (s[2] << 16) + + (s[1] << 8) + + (s[0] << 0)); +} + + +static void +disklabel_delete(device_instance *instance) +{ + disklabel *label = device_instance_data(instance); + device_instance_delete(label->raw_disk); + zfree(label); +} + + +static int +disklabel_read(device_instance *instance, + void *buf, + unsigned_word len) +{ + disklabel *label = device_instance_data(instance); + int nr_read; + if (label->pos + len > label->sector_length) + len = label->sector_length - label->pos; + if (device_instance_seek(label->raw_disk, 0, + label->sector_begin + label->pos) < 0) + return -1; + nr_read = device_instance_read(label->raw_disk, buf, len); + if (nr_read > 0) + label->pos += nr_read; + return nr_read; +} + +static int +disklabel_write(device_instance *instance, + const void *buf, + unsigned_word len) +{ + disklabel *label = device_instance_data(instance); + int nr_written; + if (label->pos + len > label->sector_length) + len = label->sector_length - label->pos; + if (device_instance_seek(label->raw_disk, 0, + label->sector_begin + label->pos) < 0) + return -1; + nr_written = device_instance_write(label->raw_disk, buf, len); + if (nr_written > 0) + label->pos += nr_written; + return nr_written; +} + +static int +disklabel_seek(device_instance *instance, + unsigned_word pos_hi, + unsigned_word pos_lo) +{ + disklabel *label = device_instance_data(instance); + if (pos_lo >= label->sector_length || pos_hi != 0) + return -1; + label->pos = pos_lo; + return 0; +} + + +static const device_instance_callbacks package_disklabel_callbacks = { + disklabel_delete, + disklabel_read, + disklabel_write, + disklabel_seek, +}; + +/* Reconize different types of boot block */ + +static int +block0_is_bpb(const unsigned8 block[]) +{ + const char ebdic_ibma[] = { 0xc9, 0xc2, 0xd4, 0xc1 }; + /* ref PowerPC Microprocessor CHRP bindings 1.2b - page 47 */ + /* can't start with IBMA */ + if (memcmp(block, ebdic_ibma, sizeof(ebdic_ibma)) == 0) + return 0; + /* must have LE 0xAA55 signature at offset 510 */ + if (block[511] != 0xAA && block[510] != 0x55) + return 0; + /* valid 16 bit LE bytes per sector - 256, 512, 1024 */ + if (block[11] != 0 + || (block[12] != 1 && block[12] != 2 && block[12] != 4)) + return 0; + /* nr fats is 1 or 2 */ + if (block[16] != 1 && block[16] != 2) + return 0; + return 1; +} + + +/* Verify that the device contains an ISO-9660 File system */ + +static int +is_iso9660(device_instance *raw_disk) +{ + /* ref PowerPC Microprocessor CHRP bindings 1.2b - page 47 */ + unsigned8 block[512]; + if (device_instance_seek(raw_disk, 0, 512 * 64) < 0) + return 0; + if (device_instance_read(raw_disk, block, sizeof(block)) != sizeof(block)) + return 0; + if (block[0] == 0x01 + && block[1] == 'C' + && block[2] == 'D' + && block[3] == '0' + && block[4] == '0' + && block[5] == '1') + return 1; + return 0; +} + + +/* Verify that the disk block contains a valid DOS partition table. + While we're at it have a look around for active partitions etc. + + Return 0: invalid + Return 1..4: valid, value returned is the first active partition + Return -1: no active partition */ + +static int +block0_is_fdisk(const unsigned8 block[]) +{ + const int partition_type_fields[] = { 0, 0x1c2, 0x1d2, 0x1e2, 0x1f2 }; + const int partition_active_fields[] = { 0, 0x1be, 0x1ce, 0x1de, 0xee }; + int partition; + int active = -1; + /* ref PowerPC Microprocessor CHRP bindings 1.2b - page 47 */ + /* must have LE 0xAA55 signature at offset 510 */ + if (block[511/*0x1ff*/] != 0xAA && block[510/*0x1fe*/] != 0x55) + return 0; + /* must contain valid partition types */ + for (partition = 1; partition <= 4 && active != 0; partition++) { + int partition_type = block[partition_type_fields[partition]]; + int is_active = block[partition_active_fields[partition]] == 0x80; + const char *type; + switch (partition_type) { + case 0x00: + type = "UNUSED"; + break; + case 0x01: + type = "FAT 12 File system"; + break; + case 0x04: + type = "FAT 16 File system"; + break; + case 0x05: + case 0x06: + type = "rejected - extended/chained partition not supported"; + active = 0; + break; + case 0x41: + type = "Single program image"; + break; + case 0x82: + type = "Solaris?"; + break; + case 0x96: + type = "ISO 9660 File system"; + break; + default: + type = "rejected - unknown type"; + active = 0; + break; + } + PTRACE(disklabel, ("partition %d of type 0x%02x - %s%s\n", + partition, + partition_type, + type, + is_active && active != 0 ? " (active)" : "")); + if (partition_type != 0 && is_active && active < 0) + active = partition; + } + return active; +} + + +/* Verify that block0 corresponds to a MAC disk */ + +static int +block0_is_mac_disk(const unsigned8 block[]) +{ + /* ref PowerPC Microprocessor CHRP bindings 1.2b - page 47 */ + /* signature - BEx4552 at offset 0 */ + if (block[0] != 0x45 || block[1] != 0x52) + return 0; + return 1; +} + + +/* Open a logical disk/file */ + +device_instance * +pk_disklabel_create_instance(device_instance *raw_disk, + const char *args) +{ + int partition; + char *filename; + + /* parse the arguments */ + if (args == NULL) { + partition = 0; + filename = NULL; + } + else { + partition = strtoul((char*)args, &filename, 0); + if (filename == args) + partition = -1; /* not specified */ + if (*filename == ',') + filename++; + if (*filename == '\0') + filename = NULL; /* easier */ + } + + if (partition == 0) { + /* select the raw disk */ + return raw_disk; + } + else { + unsigned8 boot_block[512]; + /* get the boot block for examination */ + if (device_instance_seek(raw_disk, 0, 0) < 0) + device_error(device_instance_device(raw_disk), + "Problem seeking on raw disk"); + if (device_instance_read(raw_disk, &boot_block, sizeof(boot_block)) + != sizeof(boot_block)) + device_error(device_instance_device(raw_disk), "Problem reading boot block"); + + if (partition < 0) { + /* select the active partition */ + if (block0_is_bpb(boot_block)) { + device_error(device_instance_device(raw_disk), "Unimplemented active BPB"); + } + else if (block0_is_fdisk(boot_block)) { + int active = block0_is_fdisk(boot_block); + device_error(device_instance_device(raw_disk), "Unimplemented active FDISK (%d)", + active); + } + else if (is_iso9660(raw_disk)) { + device_error(device_instance_device(raw_disk), "Unimplemented active ISO9660"); + } + else if (block0_is_mac_disk(boot_block)) { + device_error(device_instance_device(raw_disk), "Unimplemented active MAC DISK"); + } + else { + device_error(device_instance_device(raw_disk), "Unreconized bootblock"); + } + } + else { + /* select the specified disk partition */ + if (block0_is_bpb(boot_block)) { + device_error(device_instance_device(raw_disk), "Unimplemented BPB"); + } + else if (block0_is_fdisk(boot_block)) { + /* return an instance */ + ppcboot_partition_t *partition_table = (ppcboot_partition_t*) &boot_block[446]; + ppcboot_partition_t *partition_entry; + disklabel *label; + if (partition > 4) + device_error(device_instance_device(raw_disk), + "Only FDISK partitions 1..4 supported"); + partition_entry = &partition_table[partition - 1]; + label = ZALLOC(disklabel); + label->raw_disk = raw_disk; + label->pos = 0; + label->sector_begin = 512 * sector2uw(partition_entry->sector_begin); + label->sector_length = 512 * sector2uw(partition_entry->sector_length); + PTRACE(disklabel, ("partition %ld, sector-begin %ld, length %ld\n", + (long)partition, + (long)label->sector_begin, + (long)label->sector_length)); + if (filename != NULL) + device_error(device_instance_device(raw_disk), + "FDISK file names not yet supported"); + return device_create_instance_from(NULL, raw_disk, + label, + NULL, args, + &package_disklabel_callbacks); + } + else if (block0_is_mac_disk(boot_block)) { + device_error(device_instance_device(raw_disk), "Unimplemented MAC DISK"); + } + else { + device_error(device_instance_device(raw_disk), + "Unreconized bootblock"); + } + } + } + + return NULL; +} + + +#endif /* _PK_DISKLABEL_C_ */ + diff --git a/sim/ppc/ppc-cache-rules b/sim/ppc/ppc-cache-rules new file mode 100644 index 00000000000..2b5f4722175 --- /dev/null +++ b/sim/ppc/ppc-cache-rules @@ -0,0 +1,65 @@ +# +# 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. +# +cache:RA:RA:: +cache:RA:rA:signed_word *:(cpu_registers(processor)->gpr + RA) +cache:RA:RA_BITMASK:unsigned32:(1 << RA) +compute:RA:RA_is_0:int:(RA == 0) +cache:RT:RT:: +cache:RT:rT:signed_word *:(cpu_registers(processor)->gpr + RT) +cache:RT:RT_BITMASK:unsigned32:(1 << RT) +cache:RS:RS:: +cache:RS:rS:signed_word *:(cpu_registers(processor)->gpr + RS) +cache:RS:RS_BITMASK:unsigned32:(1 << RS) +cache:RB:RB:: +cache:RB:rB:signed_word *:(cpu_registers(processor)->gpr + RB) +cache:RB:RB_BITMASK:unsigned32:(1 << RB) +scratch:FRA:FRA:: +cache:FRA:frA:unsigned64 *:(cpu_registers(processor)->fpr + FRA) +cache:FRA:FRA_BITMASK:unsigned32:(1 << FRA) +scratch:FRB:FRB:: +cache:FRB:frB:unsigned64 *:(cpu_registers(processor)->fpr + FRB) +cache:FRB:FRB_BITMASK:unsigned32:(1 << FRB) +scratch:FRC:FRC:: +cache:FRC:frC:unsigned64 *:(cpu_registers(processor)->fpr + FRC) +cache:FRC:FRC_BITMASK:unsigned32:(1 << FRC) +scratch:FRS:FRS:: +cache:FRS:frS:unsigned64 *:(cpu_registers(processor)->fpr + FRS) +cache:FRS:FRS_BITMASK:unsigned32:(1 << FRS) +scratch:FRT:FRT:: +cache:FRT:frT:unsigned64 *:(cpu_registers(processor)->fpr + FRT) +cache:FRT:FRT_BITMASK:unsigned32:(1 << FRT) +cache:SI:EXTS_SI:unsigned_word:((signed_word)(signed16)instruction) +scratch:BI:BI:: +cache:BI:BIT32_BI::BIT32(BI) +cache:BF:BF:: +cache:BF:BF_BITMASK:unsigned32:(1 << BF) +scratch:BA:BA:: +cache:BA:BIT32_BA::BIT32(BA) +cache:BA:BA_BITMASK:unsigned32:(1 << BA) +scratch:BB:BB:: +cache:BB:BIT32_BB::BIT32(BB) +cache:BB:BB_BITMASK:unsigned32:(1 << BB) +cache:BT:BT:: +cache:BT:BT_BITMASK:unsigned32:(1 << BT) +cache:BD:EXTS_BD_0b00:unsigned_word:(((signed_word)(signed16)instruction) & ~3) +cache:LI:EXTS_LI_0b00:unsigned_word:((((signed_word)(signed32)(instruction << 6)) >> 6) & ~0x3) +cache:D:EXTS_D:unsigned_word:((signed_word)(signed16)(instruction)) +cache:DS:EXTS_DS_0b00:unsigned_word:(((signed_word)(signed16)instruction) & ~0x3) +#compute:SPR:SPR_is_256:int:(SPR == 256) diff --git a/sim/ppc/ppc-instructions b/sim/ppc/ppc-instructions new file mode 100644 index 00000000000..6ebe0812e7a --- /dev/null +++ b/sim/ppc/ppc-instructions @@ -0,0 +1,4933 @@ +# +# This file is part of the program psim. +# +# Copyright (C) 1994-1997, Andrew Cagney <cagney@highland.com.au> +# +# -- +# +# The pseudo-code that appears below, translated into C, was copied +# by Andrew Cagney of Moss Vale, Australia. +# +# This pseudo-code is copied by permission from the publication +# "The PowerPC Architecture: A Specification for A New Family of +# RISC Processors" (ISBN 1-55860-316-6) copyright 1993, 1994 by +# International Business Machines Corporation. +# +# THIS PERMISSION IS PROVIDED WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +# +# -- +# +# 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. +# + +# PowerPC models +::model:604:ppc604: PPC_UNIT_BAD, PPC_UNIT_BAD, 1, 1, 0 +::model:603e:ppc603e:PPC_UNIT_BAD, PPC_UNIT_BAD, 1, 1, 0 +::model:603:ppc603: PPC_UNIT_BAD, PPC_UNIT_BAD, 1, 1, 0 +::model:601:ppc601: PPC_UNIT_BAD, PPC_UNIT_BAD, 1, 1, 0 + +# Flags for model.h +::model-macro::: + #define PPC_INSN_INT(OUT_MASK, IN_MASK, RC) \ + do { \ + if (CURRENT_MODEL_ISSUE > 0) { \ + if (RC) \ + ppc_insn_int_cr(MY_INDEX, cpu_model(processor), OUT_MASK, IN_MASK, 1 << 0); \ + else \ + ppc_insn_int(MY_INDEX, cpu_model(processor), OUT_MASK, IN_MASK); \ + } \ + } while (0) + + #define PPC_INSN_INT_CR(OUT_MASK, IN_MASK, CR_MASK) \ + do { \ + if (CURRENT_MODEL_ISSUE > 0) \ + ppc_insn_int_cr(MY_INDEX, cpu_model(processor), OUT_MASK, IN_MASK, CR_MASK); \ + } while (0) + + #define PPC_INSN_CR(OUT_MASK, IN_MASK) \ + do { \ + if (CURRENT_MODEL_ISSUE > 0) \ + ppc_insn_cr(MY_INDEX, cpu_model(processor), OUT_MASK, IN_MASK); \ + } while (0) + + #define PPC_INSN_FLOAT(OUT_MASK, IN_MASK, RC) \ + do { \ + if (CURRENT_MODEL_ISSUE > 0) { \ + if (RC) \ + ppc_insn_float(MY_INDEX, cpu_model(processor), OUT_MASK, IN_MASK); \ + else \ + ppc_insn_float_cr(MY_INDEX, cpu_model(processor), OUT_MASK, IN_MASK, 1 << 0); \ + } \ + } while (0) + + #define PPC_INSN_FLOAT_CR(OUT_MASK, IN_MASK, CR_MASK) \ + do { \ + if (CURRENT_MODEL_ISSUE > 0) \ + ppc_insn_float_cr(MY_INDEX, cpu_model(processor), OUT_MASK, IN_MASK, CR_MASK); \ + } while (0) + + #define PPC_INSN_INT_FLOAT(OUT_INT_MASK, OUT_FP_MASK, IN_INT_MASK, IN_FP_MASK) \ + do { \ + if (CURRENT_MODEL_ISSUE > 0) \ + ppc_insn_int_float(MY_INDEX, cpu_model(processor), OUT_INT_MASK, OUT_FP_MASK, IN_INT_MASK, IN_FP_MASK); \ + } while (0) + + #define PPC_INSN_FROM_SPR(INT_MASK, SPR) \ + do { \ + if (CURRENT_MODEL_ISSUE > 0) \ + ppc_insn_from_spr(MY_INDEX, cpu_model(processor), INT_MASK, SPR); \ + } while (0) + + #define PPC_INSN_TO_SPR(INT_MASK, SPR) \ + do { \ + if (CURRENT_MODEL_ISSUE > 0) \ + ppc_insn_to_spr(MY_INDEX, cpu_model(processor), INT_MASK, SPR); \ + } while (0) + + #define PPC_INSN_MFCR(INT_MASK) \ + do { \ + if (CURRENT_MODEL_ISSUE > 0) \ + ppc_insn_mfcr(MY_INDEX, cpu_model(processor), INT_MASK); \ + } while (0) + + #define PPC_INSN_MTCR(INT_MASK, FXM) \ + do { \ + if (CURRENT_MODEL_ISSUE > 0) \ + ppc_insn_mtcr(MY_INDEX, cpu_model(processor), INT_MASK, FXM); \ + } while (0) + +::model-data::: + typedef enum _ppc_function_unit { + PPC_UNIT_BAD, /* unknown function unit */ + PPC_UNIT_IU, /* integer unit (601/603 style) */ + PPC_UNIT_SRU, /* system register unit (601/603 style) */ + PPC_UNIT_SCIU1, /* 1st single cycle integer unit (604 style) */ + PPC_UNIT_SCIU2, /* 2nd single cycle integer unit (604 style) */ + PPC_UNIT_MCIU, /* multiple cycle integer unit (604 style) */ + PPC_UNIT_FPU, /* floating point unit */ + PPC_UNIT_LSU, /* load/store unit */ + PPC_UNIT_BPU, /* branch unit */ + nr_ppc_function_units + } ppc_function_unit; + + /* Structure to hold timing information on a per instruction basis */ + struct _model_time { + ppc_function_unit first_unit; /* first functional unit this insn could use */ + ppc_function_unit second_unit; /* second functional unit this insn could use */ + signed16 issue; /* # cycles before function unit can process other insns */ + signed16 done; /* # cycles before insn is done */ + unsigned32 flags; /* any flags that are needed */ + }; + + /* Register mappings in status masks */ + #define PPC_CR_REG 0 /* start of CR0 .. CR7 */ + #define PPC_FPSCR_REG (PPC_CR_REG + 8) /* start of fpscr register */ + + #define PPC_NO_SPR (-1) /* flag for no SPR register */ + + /* Return if 1 bit set */ + #define PPC_ONE_BIT_SET_P(x) (((x) & ((x)-1)) == 0) + + /* Structure for each functional unit that is busy */ + typedef struct _model_busy model_busy; + struct _model_busy { + model_busy *next; /* next function unit */ + ppc_function_unit unit; /* function unit name */ + unsigned32 int_busy; /* int registers that are busy */ + unsigned32 fp_busy; /* floating point registers that are busy */ + unsigned32 cr_fpscr_busy; /* CR/FPSCR registers that are busy */ + signed16 spr_busy; /* SPR register that is busy or PPC_NO_SPR */ + signed16 issue; /* # of cycles until unit can accept another insn */ + signed16 done; /* # of cycles until insn is done */ + signed16 nr_writebacks; /* # of registers this unit writes back */ + }; + + /* Structure to hold the current state information for the simulated CPU model */ + struct _model_data { + cpu *processor; /* point back to processor */ + const char *name; /* model name */ + const model_time *timing; /* timing information */ + model_busy busy_head; /* dummy entry to head list of busy function units */ + model_busy *busy_tail; /* tail of list of busy function units */ + model_busy *free_list; /* list of model_busy structs not in use */ + count_type nr_cycles; /* # cycles */ + count_type nr_branches; /* # branches */ + count_type nr_branches_fallthrough; /* # conditional branches that fell through */ + count_type nr_branch_predict_trues; /* # branches predicted correctly */ + count_type nr_branch_predict_falses; /* # branches predicted incorrectly */ + count_type nr_branch_conditional[32]; /* # of each type of bc */ + count_type nr_mtcrf_crs[9]; /* # of CR's moved in a mtcrf instruction */ + count_type nr_stalls_data; /* # of stalls for data */ + count_type nr_stalls_unit; /* # of stalls waiting for a function unit */ + count_type nr_stalls_serialize; /* # of stalls waiting for things to quiet down */ + count_type nr_stalls_writeback; /* # of stalls waiting for a writeback slot */ + count_type nr_units[nr_ppc_function_units]; /* function unit counts */ + int max_nr_writebacks; /* max # of writeback slots available */ + unsigned32 int_busy; /* int registers that are busy */ + unsigned32 fp_busy; /* floating point registers that are busy */ + unsigned32 cr_fpscr_busy; /* CR/FPSCR registers that are busy */ + unsigned8 spr_busy[nr_of_sprs]; /* SPR registers that are busy */ + unsigned8 busy[nr_ppc_function_units]; /* whether a function is busy or not */ + }; + + static const char *const ppc_function_unit_name[ (int)nr_ppc_function_units ] = { + "unknown functional unit instruction", + "integer functional unit instruction", + "system register functional unit instruction", + "1st single cycle integer functional unit instruction", + "2nd single cycle integer functional unit instruction", + "multiple cycle integer functional unit instruction", + "floating point functional unit instruction", + "load/store functional unit instruction", + "branch functional unit instruction", + }; + + static const char *const ppc_branch_conditional_name[32] = { + "branch if --CTR != 0 and condition is FALSE", /* 0000y */ + "branch if --CTR != 0 and condition is FALSE, reverse branch likely", + "branch if --CTR == 0 and condition is FALSE", /* 0001y */ + "branch if --CTR == 0 and condition is FALSE, reverse branch likely", + "branch if the condition is FALSE", /* 001zy */ + "branch if the condition is FALSE, reverse branch likely", + "branch if the condition is FALSE (ignored bit 1 set to 1)", /* 001zy */ + "branch if the condition is FALSE, reverse branch likely (ignored bit 4 set to 1)", + "branch if --CTR != 0 and condition is TRUE", /* 0100y */ + "branch if --CTR != 0 and condition is TRUE, reverse branch likely", + "branch if --CTR == 0 and condition is TRUE", /* 0101y */ + "branch if --CTR == 0 and condition is TRUE, reverse branch likely", + "branch if the condition is TRUE", /* 011zy */ + "branch if the condition is TRUE, reverse branch likely", + "branch if the condition is TRUE (ignored bit 1 set to 1)", /* 011zy */ + "branch if the condition is TRUE, reverse branch likely (ignored bit 4 set to 1)", + "branch if --CTR != 0", /* 1z00y */ + "branch if --CTR != 0, reverse branch likely", + "branch if --CTR == 0", /* 1z01y */ + "branch if --CTR == 0, reverse branch likely", + "branch always", /* 1z1zz */ + "branch always (ignored bit 5 set to 1)", + "branch always (ignored bit 4 set to 1)", /* 1z1zz */ + "branch always (ignored bits 4,5 set to 1)", + "branch if --CTR != 0 (ignored bit 1 set to 1)", /* 1z00y */ + "branch if --CTR != 0, reverse branch likely (ignored bit 1 set to 1)", + "branch if --CTR == 0 (ignored bit 1 set to 1)", /* 1z01y */ + "branch if --CTR == 0, reverse branch likely (ignored bit 1 set to 1)", + "branch always (ignored bit 1 set to 1)", /* 1z1zz */ + "branch always (ignored bits 1,5 set to 1)", + "branch always (ignored bits 1,4 set to 1)", /* 1z1zz */ + "branch always (ignored bits 1,4,5 set to 1)", + }; + + static const char *const ppc_nr_mtcrf_crs[9] = { + "mtcrf moving 0 CRs", + "mtcrf moving 1 CR", + "mtcrf moving 2 CRs", + "mtcrf moving 3 CRs", + "mtcrf moving 4 CRs", + "mtcrf moving 5 CRs", + "mtcrf moving 6 CRs", + "mtcrf moving 7 CRs", + "mtcrf moving all CRs", + }; + +# Trace releasing resources +void::model-static::model_trace_release:model_data *model_ptr, model_busy *busy + int i; + TRACE(trace_model,("done, %s, %d writeback%s\n", ppc_function_unit_name[busy->unit], + busy->nr_writebacks, busy->nr_writebacks == 1 ? "" : "s")); + if (busy->int_busy) { + for(i = 0; i < 32; i++) { + if (((1 << i) & busy->int_busy) != 0) { + TRACE(trace_model, ("Register r%d is now available.\n", i)); + } + } + } + if (busy->fp_busy) { + for(i = 0; i < 32; i++) { + if (((1 << i) & busy->fp_busy) != 0) { + TRACE(trace_model, ("Register f%d is now available.\n", i)); + } + } + } + if (busy->cr_fpscr_busy) { + for(i = 0; i < 8; i++) { + if (((1 << i) & busy->cr_fpscr_busy) != 0) { + TRACE(trace_model, ("Register cr%d is now available.\n", i)); + } + } + if (busy->cr_fpscr_busy & 0x100) + TRACE(trace_model, ("Register fpscr is now available.\n")); + } + if (busy->spr_busy != PPC_NO_SPR) + TRACE(trace_model, ("Register %s is now available.\n", spr_name(busy->spr_busy))); + +# Trace making registers busy +void::model-static::model_trace_make_busy:model_data *model_ptr, unsigned32 int_mask, unsigned32 fp_mask, unsigned32 cr_mask + int i; + if (int_mask) { + for(i = 0; i < 32; i++) { + if (((1 << i) & int_mask) != 0) { + TRACE(trace_model, ("Register r%d is now busy.\n", i)); + } + } + } + if (fp_mask) { + for(i = 0; i < 32; i++) { + if (((1 << i) & fp_mask) != 0) { + TRACE(trace_model, ("Register f%d is now busy.\n", i)); + } + } + } + if (cr_mask) { + for(i = 0; i < 8; i++) { + if (((1 << i) & cr_mask) != 0) { + TRACE(trace_model, ("Register cr%d is now busy.\n", i)); + } + } + } + +# Trace waiting for registers to become available +void::model-static::model_trace_busy_p:model_data *model_ptr, unsigned32 int_busy, unsigned32 fp_busy, unsigned32 cr_or_fpscr_busy, int spr_busy + int i; + if (int_busy) { + int_busy &= model_ptr->int_busy; + for(i = 0; i < 32; i++) { + if (((1 << i) & int_busy) != 0) { + TRACE(trace_model, ("Waiting for register r%d.\n", i)); + } + } + } + if (fp_busy) { + fp_busy &= model_ptr->fp_busy; + for(i = 0; i < 32; i++) { + if (((1 << i) & fp_busy) != 0) { + TRACE(trace_model, ("Waiting for register f%d.\n", i)); + } + } + } + if (cr_or_fpscr_busy) { + cr_or_fpscr_busy &= model_ptr->cr_fpscr_busy; + for(i = 0; i < 8; i++) { + if (((1 << i) & cr_or_fpscr_busy) != 0) { + TRACE(trace_model, ("Waiting for register cr%d.\n", i)); + } + } + if (cr_or_fpscr_busy & 0x100) + TRACE(trace_model, ("Waiting for register fpscr.\n")); + } + if (spr_busy != PPC_NO_SPR && model_ptr->spr_busy[spr_busy]) + TRACE(trace_model, ("Waiting for register %s.\n", spr_name(spr_busy))); + +# Advance state to next cycle, releasing any registers allocated +void::model-internal::model_new_cycle:model_data *model_ptr + model_busy *cur_busy = model_ptr->busy_head.next; + model_busy *free_list = model_ptr->free_list; + model_busy *busy_tail = &model_ptr->busy_head; + int nr_writebacks = model_ptr->max_nr_writebacks; + model_busy *next; + + model_ptr->nr_cycles++; + TRACE(trace_model,("New cycle %lu\n", (unsigned long)model_ptr->nr_cycles)); + for ( ; cur_busy; cur_busy = next) { + next = cur_busy->next; + if (--cur_busy->done <= 0) { /* function unit done, release registers if we have writeback slots */ + nr_writebacks -= cur_busy->nr_writebacks; + if (nr_writebacks >= 0) { + model_ptr->int_busy &= ~cur_busy->int_busy; + model_ptr->fp_busy &= ~cur_busy->fp_busy; + model_ptr->cr_fpscr_busy &= ~cur_busy->cr_fpscr_busy; + if (cur_busy->spr_busy != PPC_NO_SPR) + model_ptr->spr_busy[cur_busy->spr_busy] = 0; + + if (WITH_TRACE && ppc_trace[trace_model]) + model_trace_release(model_ptr, cur_busy); + + model_ptr->busy[cur_busy->unit] = 0; + cur_busy->next = free_list; + free_list = cur_busy; + } + else { /* writeback slots not available */ + TRACE(trace_model,("%d writeback slot%s not available for %s\n", + cur_busy->nr_writebacks, + cur_busy->nr_writebacks == 1 ? " is" : "s are", + ppc_function_unit_name[cur_busy->unit])); + cur_busy->done++; /* undo -- above */ + model_ptr->nr_stalls_writeback++; + busy_tail->next = cur_busy; + busy_tail = cur_busy; + } + } + else if (--cur_busy->issue <= 0) { /* function unit pipelined, allow new use */ + TRACE(trace_model,("pipeline, %s ready for next client\n", ppc_function_unit_name[cur_busy->unit])); + model_ptr->busy[cur_busy->unit] = 0; + busy_tail->next = cur_busy; + busy_tail = cur_busy; + } + else { + TRACE(trace_model,("%s still working, issue = %d, done = %d\n", + ppc_function_unit_name[cur_busy->unit], + cur_busy->issue, + cur_busy->done)); + busy_tail->next = cur_busy; + busy_tail = cur_busy; + } + } + + busy_tail->next = (model_busy *)0; + model_ptr->busy_tail = busy_tail; + model_ptr->free_list = free_list; + +# Mark a function unit as busy, return the busy structure +model_busy *::model-internal::model_make_busy:model_data *model_ptr, ppc_function_unit unit, int issue, int done + model_busy *busy; + + TRACE(trace_model,("unit = %s, issue = %d, done = %d\n", ppc_function_unit_name[unit], issue, done)); + + if (!model_ptr->free_list) { + busy = ZALLOC(model_busy); + } + else { + busy = model_ptr->free_list; + model_ptr->free_list = busy->next; + busy->next = (model_busy *)0; + busy->int_busy = 0; + busy->fp_busy = 0; + busy->cr_fpscr_busy = 0; + busy->nr_writebacks = 0; + } + + busy->unit = unit; + busy->issue = issue; + busy->done = done; + busy->spr_busy = PPC_NO_SPR; + model_ptr->busy_tail->next = busy; + model_ptr->busy_tail = busy; + model_ptr->busy[unit] = 1; + model_ptr->nr_units[unit]++; + return busy; + +# Wait until a function unit is non-busy, and then allocate a busy pointer & return the pointer +model_busy *::model-internal::model_wait_for_unit:itable_index index, model_data *const model_ptr, const model_time *const time_ptr + ppc_function_unit first_unit = time_ptr->first_unit; + ppc_function_unit second_unit = time_ptr->second_unit; + int stall_increment = 0; + + for (;;) { + if (!model_ptr->busy[first_unit]) + return model_make_busy(model_ptr, first_unit, + model_ptr->timing[index].issue, + model_ptr->timing[index].done); + + if (!model_ptr->busy[second_unit]) + return model_make_busy(model_ptr, second_unit, + model_ptr->timing[index].issue, + model_ptr->timing[index].done); + + TRACE(trace_model,("all function units are busy for %s\n", itable[index].name)); + model_ptr->nr_stalls_unit += stall_increment; /* don't count first stall */ + stall_increment = 1; + model_new_cycle(model_ptr); + } + +# Serialize the processor, waiting for all instructions to drain out before adding an instruction. +void::model-function::model_serialize:itable_index index, model_data *model_ptr + while (model_ptr->busy_head.next) { + TRACE(trace_model,("waiting for pipeline to empty\n")); + model_ptr->nr_stalls_serialize++; + model_new_cycle(model_ptr); + } + (void) model_make_busy(model_ptr, + model_ptr->timing[index].first_unit, + model_ptr->timing[index].issue, + model_ptr->timing[index].done); + +# Wait for a CR to become unbusy +void::model-function::model_wait_for_cr:model_data *model_ptr, unsigned CRBIT + unsigned u; + unsigned32 cr_mask; + int cr_var = 0; + for (u = 0xc0000000; (u != 0) && (CRBIT & u) == 0; u >>= 4 ) + cr_var++; + + cr_mask = (1 << cr_var); + while ((model_ptr->cr_fpscr_busy & cr_mask) != 0) { + TRACE(trace_model,("waiting for CR %d\n", cr_var)); + model_ptr->nr_stalls_data++; + model_new_cycle(model_ptr); + } + +# Schedule an instruction that takes integer input registers and produces output registers +void::model-function::ppc_insn_int:itable_index index, model_data *model_ptr, const unsigned32 out_mask, const unsigned32 in_mask + const unsigned32 int_mask = out_mask | in_mask; + model_busy *busy_ptr; + + if ((model_ptr->int_busy & int_mask) != 0) { + model_new_cycle(model_ptr); /* don't count first dependency as a stall */ + + while ((model_ptr->int_busy & int_mask) != 0) { + if (WITH_TRACE && ppc_trace[trace_model]) + model_trace_busy_p(model_ptr, int_mask, 0, 0, PPC_NO_SPR); + + model_ptr->nr_stalls_data++; + model_new_cycle(model_ptr); + } + } + + busy_ptr = model_wait_for_unit(index, model_ptr, &model_ptr->timing[index]); + model_ptr->int_busy |= out_mask; + busy_ptr->int_busy |= out_mask; + if (out_mask) + busy_ptr->nr_writebacks = (PPC_ONE_BIT_SET_P(out_mask)) ? 1 : 2; + + if (WITH_TRACE && ppc_trace[trace_model]) + model_trace_make_busy(model_ptr, out_mask, 0, 0); + +# Schedule an instruction that takes integer input registers and produces output registers & sets a CR register +void::model-function::ppc_insn_int_cr:itable_index index, model_data *model_ptr, const unsigned32 out_mask, const unsigned32 in_mask, const unsigned32 cr_mask + const unsigned32 int_mask = out_mask | in_mask; + model_busy *busy_ptr; + + if ((model_ptr->int_busy & int_mask) || (model_ptr->cr_fpscr_busy & cr_mask)) { + model_new_cycle(model_ptr); /* don't count first dependency as a stall */ + + while ((model_ptr->int_busy & int_mask) || (model_ptr->cr_fpscr_busy & cr_mask)) { + if (WITH_TRACE && ppc_trace[trace_model]) + model_trace_busy_p(model_ptr, int_mask, 0, cr_mask, PPC_NO_SPR); + + model_ptr->nr_stalls_data++; + model_new_cycle(model_ptr); + } + } + + busy_ptr = model_wait_for_unit(index, model_ptr, &model_ptr->timing[index]); + model_ptr->int_busy |= out_mask; + busy_ptr->int_busy |= out_mask; + model_ptr->cr_fpscr_busy |= cr_mask; + busy_ptr->cr_fpscr_busy |= cr_mask; + if (out_mask) + busy_ptr->nr_writebacks = (PPC_ONE_BIT_SET_P(out_mask)) ? 1 : 2; + + if (cr_mask) + busy_ptr->nr_writebacks++; + + if (WITH_TRACE && ppc_trace[trace_model]) + model_trace_make_busy(model_ptr, out_mask, 0, cr_mask); + + +# Schedule an instruction that takes CR input registers and produces output CR registers +void::model-function::ppc_insn_cr:itable_index index, model_data *model_ptr, const unsigned32 out_mask, const unsigned32 in_mask + const unsigned32 cr_mask = out_mask | in_mask; + model_busy *busy_ptr; + + if ((model_ptr->cr_fpscr_busy & cr_mask) != 0) { + model_new_cycle(model_ptr); /* don't count first dependency as a stall */ + + while ((model_ptr->cr_fpscr_busy & cr_mask) != 0) { + if (WITH_TRACE && ppc_trace[trace_model]) + model_trace_busy_p(model_ptr, 0, 0, cr_mask, PPC_NO_SPR); + + model_ptr->nr_stalls_data++; + model_new_cycle(model_ptr); + } + } + + busy_ptr = model_wait_for_unit(index, model_ptr, &model_ptr->timing[index]); + model_ptr->cr_fpscr_busy |= out_mask; + busy_ptr->cr_fpscr_busy |= out_mask; + if (out_mask) + busy_ptr->nr_writebacks = 1; + + if (WITH_TRACE && ppc_trace[trace_model]) + model_trace_make_busy(model_ptr, 0, 0, out_mask); + + +# Schedule an instruction that takes floating point input registers and produces an output fp register +void::model-function::ppc_insn_float:itable_index index, model_data *model_ptr, const unsigned32 out_mask, const unsigned32 in_mask + const unsigned32 fp_mask = out_mask | in_mask; + model_busy *busy_ptr; + + if ((model_ptr->fp_busy & fp_mask) != 0) { + model_new_cycle(model_ptr); /* don't count first dependency as a stall */ + + while ((model_ptr->fp_busy & fp_mask) != 0) { + if (WITH_TRACE && ppc_trace[trace_model]) + model_trace_busy_p(model_ptr, 0, fp_mask, 0, PPC_NO_SPR); + + model_ptr->nr_stalls_data++; + model_new_cycle(model_ptr); + } + } + + busy_ptr = model_wait_for_unit(index, model_ptr, &model_ptr->timing[index]); + model_ptr->fp_busy |= out_mask; + busy_ptr->fp_busy |= out_mask; + busy_ptr->nr_writebacks = 1; + if (WITH_TRACE && ppc_trace[trace_model]) + model_trace_make_busy(model_ptr, 0, out_mask, 0); + + +# Schedule an instruction that takes floating point input registers and produces an output fp register & sets a CR reg +void::model-function::ppc_insn_float_cr:itable_index index, model_data *model_ptr, const unsigned32 out_mask, const unsigned32 in_mask, const unsigned32 cr_mask + const unsigned32 fp_mask = out_mask | in_mask; + model_busy *busy_ptr; + + if ((model_ptr->fp_busy & fp_mask) || (model_ptr->cr_fpscr_busy & cr_mask)) { + model_new_cycle(model_ptr); /* don't count first dependency as a stall */ + + while ((model_ptr->fp_busy & fp_mask) || (model_ptr->cr_fpscr_busy & cr_mask)) { + if (WITH_TRACE && ppc_trace[trace_model]) + model_trace_busy_p(model_ptr, 0, fp_mask, cr_mask, PPC_NO_SPR); + + model_ptr->nr_stalls_data++; + model_new_cycle(model_ptr); + } + } + + busy_ptr = model_wait_for_unit(index, model_ptr, &model_ptr->timing[index]); + model_ptr->fp_busy |= out_mask; + busy_ptr->fp_busy |= out_mask; + model_ptr->cr_fpscr_busy |= cr_mask; + busy_ptr->cr_fpscr_busy |= cr_mask; + busy_ptr->nr_writebacks = (cr_mask) ? 2 : 1; + if (WITH_TRACE && ppc_trace[trace_model]) + model_trace_make_busy(model_ptr, 0, out_mask, cr_mask); + + +# Schedule an instruction that takes both int/float input registers and produces output int/float registers +void::model-function::ppc_insn_int_float:itable_index index, model_data *model_ptr, const unsigned32 out_int_mask, const unsigned32 out_fp_mask, const unsigned32 in_int_mask, const unsigned32 in_fp_mask + const unsigned32 int_mask = out_int_mask | in_int_mask; + const unsigned32 fp_mask = out_fp_mask | in_fp_mask; + model_busy *busy_ptr; + + if ((model_ptr->int_busy & int_mask) || (model_ptr->fp_busy & fp_mask)) { + model_new_cycle(model_ptr); /* don't count first dependency as a stall */ + + while ((model_ptr->int_busy & int_mask) || (model_ptr->fp_busy & fp_mask)) { + if (WITH_TRACE && ppc_trace[trace_model]) + model_trace_busy_p(model_ptr, int_mask, fp_mask, 0, PPC_NO_SPR); + + model_ptr->nr_stalls_data++; + model_new_cycle(model_ptr); + } + + busy_ptr = model_wait_for_unit(index, model_ptr, &model_ptr->timing[index]); + model_ptr->int_busy |= out_int_mask; + busy_ptr->int_busy |= out_int_mask; + model_ptr->fp_busy |= out_fp_mask; + busy_ptr->fp_busy |= out_fp_mask; + busy_ptr->nr_writebacks = ((out_int_mask) ? 1 : 0) + ((out_fp_mask) ? 1 : 0); + if (WITH_TRACE && ppc_trace[trace_model]) + model_trace_make_busy(model_ptr, out_int_mask, out_fp_mask, 0); + return; + } + +# Schedule an MFSPR instruction that takes 1 special purpose register and produces an integer output register +void::model-function::ppc_insn_from_spr:itable_index index, model_data *model_ptr, const unsigned32 int_mask, const unsigned nSPR + model_busy *busy_ptr; + + while ((model_ptr->int_busy & int_mask) != 0 || model_ptr->spr_busy[nSPR] != 0) { + if (WITH_TRACE && ppc_trace[trace_model]) + model_trace_busy_p(model_ptr, int_mask, 0, 0, nSPR); + + model_ptr->nr_stalls_data++; + model_new_cycle(model_ptr); + } + + busy_ptr = model_wait_for_unit(index, model_ptr, &model_ptr->timing[index]); + model_ptr->int_busy |= int_mask; + busy_ptr->int_busy |= int_mask; + busy_ptr->nr_writebacks = 1; + if (WITH_TRACE && ppc_trace[trace_model]) + model_trace_make_busy(model_ptr, int_mask, 0, 0); + +# Schedule an MTSPR instruction that takes 1 integer register and produces a special purpose output register +void::model-function::ppc_insn_to_spr:itable_index index, model_data *model_ptr, const unsigned32 int_mask, const unsigned nSPR + model_busy *busy_ptr; + + while ((model_ptr->int_busy & int_mask) != 0 || model_ptr->spr_busy[nSPR] != 0) { + if (WITH_TRACE && ppc_trace[trace_model]) + model_trace_busy_p(model_ptr, int_mask, 0, 0, nSPR); + + model_ptr->nr_stalls_data++; + model_new_cycle(model_ptr); + } + + busy_ptr = model_wait_for_unit(index, model_ptr, &model_ptr->timing[index]); + busy_ptr->spr_busy = nSPR; + model_ptr->spr_busy[nSPR] = 1; + busy_ptr->nr_writebacks = 1; + TRACE(trace_model,("Making register %s busy.\n", spr_name(nSPR))); + +# Schedule a MFCR instruction that moves the CR into an integer regsiter +void::model-function::ppc_insn_mfcr:itable_index index, model_data *model_ptr, unsigned32 int_mask + const unsigned32 cr_mask = 0xff; + model_busy *busy_ptr; + + while (((model_ptr->int_busy & int_mask) | (model_ptr->cr_fpscr_busy & cr_mask)) != 0) { + if (WITH_TRACE && ppc_trace[trace_model]) + model_trace_busy_p(model_ptr, int_mask, 0, cr_mask, PPC_NO_SPR); + + model_ptr->nr_stalls_data++; + model_new_cycle(model_ptr); + } + + busy_ptr = model_wait_for_unit(index, model_ptr, &model_ptr->timing[index]); + model_ptr->int_busy |= int_mask; + busy_ptr->int_busy |= int_mask; + busy_ptr->nr_writebacks = 1; + if (WITH_TRACE && ppc_trace[trace_model]) + model_trace_make_busy(model_ptr, int_mask, 0, 0); + +# Schedule a MTCR instruction that moves an integer register into the CR +void::model-function::ppc_insn_mtcr:itable_index index, model_data *model_ptr, unsigned32 int_mask, unsigned FXM + int f; + int nr_crs = 0; + unsigned32 cr_mask = 0; + const model_time *normal_time = &model_ptr->timing[index]; + static const model_time ppc604_1bit_time = { PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 }; + model_busy *busy_ptr; + + for (f = 0; f < 8; f++) { + if (FXM & (0x80 >> f)) { + cr_mask |= (1 << f); + nr_crs++; + } + } + + while (((model_ptr->int_busy & int_mask) | (model_ptr->cr_fpscr_busy & cr_mask)) != 0) { + if (WITH_TRACE && ppc_trace[trace_model]) + model_trace_busy_p(model_ptr, int_mask, 0, cr_mask, PPC_NO_SPR); + + model_ptr->nr_stalls_data++; + model_new_cycle(model_ptr); + } + + /* If only one CR is being moved, use the SCIU, not the MCIU on the 604 */ + if (CURRENT_MODEL == MODEL_ppc604 && nr_crs == 1) { + normal_time = &ppc604_1bit_time; + } + + busy_ptr = model_wait_for_unit(index, model_ptr, normal_time); + busy_ptr->cr_fpscr_busy |= cr_mask; + model_ptr->cr_fpscr_busy |= cr_mask; + model_ptr->nr_mtcrf_crs[nr_crs]++; + busy_ptr->nr_writebacks = 1; + if (WITH_TRACE && ppc_trace[trace_model]) + model_trace_make_busy(model_ptr, 0, 0, cr_mask); + +model_data *::model-function::model_create:cpu *processor + model_data *model_ptr = ZALLOC(model_data); + model_ptr->name = model_name[CURRENT_MODEL]; + model_ptr->timing = model_time_mapping[CURRENT_MODEL]; + model_ptr->processor = processor; + model_ptr->nr_cycles = 1; + model_ptr->busy_tail = &model_ptr->busy_head; + switch (CURRENT_MODEL) { + case MODEL_ppc601: model_ptr->max_nr_writebacks = 1; break; /* ??? */ + case MODEL_ppc603: model_ptr->max_nr_writebacks = 2; break; + case MODEL_ppc603e: model_ptr->max_nr_writebacks = 2; break; + case MODEL_ppc604: model_ptr->max_nr_writebacks = 2; break; + default: error ("Unknown model %d\n", CURRENT_MODEL); + } + return model_ptr; + +void::model-function::model_init:model_data *model_ptr + +void::model-function::model_halt:model_data *model_ptr + /* Let pipeline drain */ + while (model_ptr->busy_head.next) + model_new_cycle(model_ptr); + +unsigned_word::model-function::model_get_number_of_stalls:model_data *model_ptr + return (model_ptr->nr_stalls_data + + model_ptr->nr_stalls_unit + + model_ptr->nr_stalls_serialize + + model_ptr->nr_stalls_writeback); + +unsigned_word::model-function::model_get_number_of_cycles:model_data *model_ptr + return (model_ptr->nr_cycles); + +model_print *::model-function::model_mon_info:model_data *model_ptr + model_print *head; + model_print *tail; + ppc_function_unit i; + count_type nr_insns; + int j; + + head = tail = ZALLOC(model_print); + tail->count = model_ptr->nr_cycles; + tail->name = "cycle"; + tail->suffix_plural = "s"; + tail->suffix_singular = ""; + + if (model_ptr->nr_stalls_data) { + tail->next = ZALLOC(model_print); + tail = tail->next; + tail->count = model_ptr->nr_stalls_data; + tail->name = "stall"; + tail->suffix_plural = "s waiting for data"; + tail->suffix_singular = " waiting for data"; + } + + if (model_ptr->nr_stalls_unit) { + tail->next = ZALLOC(model_print); + tail = tail->next; + tail->count = model_ptr->nr_stalls_unit; + tail->name = "stall"; + tail->suffix_plural = "s waiting for a function unit"; + tail->suffix_singular = " waiting for a function unit"; + } + + if (model_ptr->nr_stalls_serialize) { + tail->next = ZALLOC(model_print); + tail = tail->next; + tail->count = model_ptr->nr_stalls_serialize; + tail->name = "stall"; + tail->suffix_plural = "s waiting for serialization"; + tail->suffix_singular = " waiting for serialization"; + } + + if (model_ptr->nr_stalls_writeback) { + tail->next = ZALLOC(model_print); + tail = tail->next; + tail->count = model_ptr->nr_stalls_writeback; + tail->name = ""; + tail->suffix_plural = "times a write-back slot was unavailable"; + tail->suffix_singular = "time a writeback was unavailable"; + } + + if (model_ptr->nr_branches) { + tail->next = ZALLOC(model_print); + tail = tail->next; + tail->count = model_ptr->nr_branches; + tail->name = "branch"; + tail->suffix_plural = "es"; + tail->suffix_singular = ""; + } + + if (model_ptr->nr_branches_fallthrough) { + tail->next = ZALLOC(model_print); + tail = tail->next; + tail->count = model_ptr->nr_branches_fallthrough; + tail->name = "conditional branch"; + tail->suffix_plural = "es fell through"; + tail->suffix_singular = " fell through"; + } + + if (model_ptr->nr_branch_predict_trues) { + tail->next = ZALLOC(model_print); + tail = tail->next; + tail->count = model_ptr->nr_branch_predict_trues; + tail->name = "successful branch prediction"; + tail->suffix_plural = "s"; + tail->suffix_singular = ""; + } + + if (model_ptr->nr_branch_predict_falses) { + tail->next = ZALLOC(model_print); + tail = tail->next; + tail->count = model_ptr->nr_branch_predict_falses; + tail->name = "unsuccessful branch prediction"; + tail->suffix_plural = "s"; + tail->suffix_singular = ""; + } + + for (j = 0; j < (sizeof(ppc_branch_conditional_name) / sizeof(ppc_branch_conditional_name[0])) ; j++) { + if (model_ptr->nr_branch_conditional[j]) { + tail->next = ZALLOC(model_print); + tail = tail->next; + tail->count = model_ptr->nr_branch_conditional[j]; + tail->name = ppc_branch_conditional_name[j]; + tail->suffix_plural = " conditional branches"; + tail->suffix_singular = " conditional branch"; + } + } + + for (j = 0; j < 9; j++) { + if (model_ptr->nr_mtcrf_crs[j]) { + tail->next = ZALLOC(model_print); + tail = tail->next; + tail->count = model_ptr->nr_mtcrf_crs[j]; + tail->name = ppc_nr_mtcrf_crs[j]; + tail->suffix_plural = " instructions"; + tail->suffix_singular = " instruction"; + } + } + + nr_insns = 0; + for (i = PPC_UNIT_BAD; i < nr_ppc_function_units; i++) { + if (model_ptr->nr_units[i]) { + nr_insns += model_ptr->nr_units[i]; + tail->next = ZALLOC(model_print); + tail = tail->next; + tail->count = model_ptr->nr_units[i]; + tail->name = ppc_function_unit_name[i]; + tail->suffix_plural = "s"; + tail->suffix_singular = ""; + } + } + + tail->next = ZALLOC(model_print); + tail = tail->next; + tail->count = nr_insns; + tail->name = "instruction"; + tail->suffix_plural = "s that were accounted for in timing info"; + tail->suffix_singular = " that was accounted for in timing info"; + + tail->next = (model_print *)0; + return head; + +void::model-function::model_mon_info_free:model_data *model_ptr, model_print *ptr + while (ptr) { + model_print *next = ptr->next; + free((void *)ptr); + ptr = next; + } + +void::model-function::model_branches:model_data *model_ptr, int failed, int conditional + model_ptr->nr_units[PPC_UNIT_BPU]++; + if (failed) + model_ptr->nr_branches_fallthrough++; + else + model_ptr->nr_branches++; + if (conditional >= 0) + model_ptr->nr_branch_conditional[conditional]++; + model_new_cycle(model_ptr); /* A branch always ends the current cycle */ + +void::model-function::model_branch_predict:model_data *model_ptr, int success + if (success) + model_ptr->nr_branch_predict_trues++; + else + model_ptr->nr_branch_predict_falses++; + + +# The following (illegal) instruction is `known' by gen and is +# called when ever an illegal instruction is encountered +::internal::illegal + program_interrupt(processor, cia, + illegal_instruction_program_interrupt); + + +# The following (floating point unavailable) instruction is `known' by gen +# and is called when ever an a floating point instruction is to be +# executed but floating point is make unavailable by the MSR +::internal::floating_point_unavailable + floating_point_unavailable_interrupt(processor, cia); + + +# +# Floating point support functions +# + +# Convert 32bit single to 64bit double +unsigned64::function::DOUBLE:unsigned32 WORD + unsigned64 FRT; + if (EXTRACTED32(WORD, 1, 8) > 0 + && EXTRACTED32(WORD, 1, 8) < 255) { + /* normalized operand */ + int not_word_1_1 = !EXTRACTED32(WORD, 1, 1); /*2.6.3 bug*/ + FRT = (INSERTED64(EXTRACTED32(WORD, 0, 1), 0, 1) + | INSERTED64(not_word_1_1, 2, 2) + | INSERTED64(not_word_1_1, 3, 3) + | INSERTED64(not_word_1_1, 4, 4) + | INSERTED64(EXTRACTED32(WORD, 2, 31), 5, (63 - 29))); + } + else if (EXTRACTED32(WORD, 1, 8) == 0 + && EXTRACTED32(WORD, 9, 31) != 0) { + /* denormalized operand */ + int sign = EXTRACTED32(WORD, 0, 0); + int exp = -126; + unsigned64 frac = INSERTED64(EXTRACTED32(WORD, 9, 31), 1, (52 - 29)); + /* normalize the operand */ + while (MASKED64(frac, 0, 0) == 0) { + frac <<= 1; + exp -= 1; + } + FRT = (INSERTED64(sign, 0, 0) + | INSERTED64(exp + 1023, 1, 11) + | INSERTED64(EXTRACTED64(frac, 1, 52), 12, 63)); + } + else if (EXTRACTED32(WORD, 1, 8) == 255 + || EXTRACTED32(WORD, 1, 31) == 0) { + FRT = (INSERTED64(EXTRACTED32(WORD, 0, 1), 0, 1) + | INSERTED64(EXTRACTED32(WORD, 1, 1), 2, 2) + | INSERTED64(EXTRACTED32(WORD, 1, 1), 3, 3) + | INSERTED64(EXTRACTED32(WORD, 1, 1), 4, 4) + | INSERTED64(EXTRACTED32(WORD, 2, 31), 5, (63 - 29))); + } + else { + error("DOUBLE - unknown case\n"); + FRT = 0; + } + return FRT; + +# Convert 64bit single to 32bit double +unsigned32::function::SINGLE:unsigned64 FRS + unsigned32 WORD; + if (EXTRACTED64(FRS, 1, 11) > 896 + || EXTRACTED64(FRS, 1, 63) == 0) { + /* no denormalization required (includes Zero/Infinity/NaN) */ + WORD = (INSERTED32(EXTRACTED64(FRS, 0, 1), 0, 1) + | INSERTED32(EXTRACTED64(FRS, 5, 34), 2, 31)); + } + else if (874 <= EXTRACTED64(FRS, 1, 11) + && EXTRACTED64(FRS, 1, 11) <= 896) { + /* denormalization required */ + int sign = EXTRACTED64(FRS, 0, 0); + int exp = EXTRACTED64(FRS, 1, 11) - 1023; + unsigned64 frac = (BIT64(0) + | INSERTED64(EXTRACTED64(FRS, 12, 63), 1, 52)); + /* denormalize the operand */ + while (exp < -126) { + frac = INSERTED64(EXTRACTED64(frac, 0, 62), 1, 63); + exp += 1; + } + WORD = (INSERTED32(sign, 0, 0) + | INSERTED32(0x00, 1, 8) + | INSERTED32(EXTRACTED64(frac, 1, 23), 9, 31)); + } + else { + WORD = 0x0; /* ??? */ + } + return WORD; + + +# round 64bit double to 64bit but single +void::function::Round_Single:cpu *processor, int sign, int *exp, unsigned64 *frac_grx + /* comparisons ignore u bits */ + unsigned64 out; + int inc = 0; + int lsb = EXTRACTED64(*frac_grx, 23, 23); + int gbit = EXTRACTED64(*frac_grx, 24, 24); + int rbit = EXTRACTED64(*frac_grx, 25, 25); + int xbit = EXTRACTED64(*frac_grx, 26, 55) != 0; + if ((FPSCR & fpscr_rn) == fpscr_rn_round_to_nearest) { + if (lsb == 1 && gbit == 1) inc = 1; + if (lsb == 0 && gbit == 1 && rbit == 1) inc = 1; + if (lsb == 0 && gbit == 1 && xbit == 1) inc = 1; + } + if ((FPSCR & fpscr_rn) == fpscr_rn_round_towards_pos_infinity) { + if (sign == 0 && gbit == 1) inc = 1; + if (sign == 0 && rbit == 1) inc = 1; + if (sign == 0 && xbit == 1) inc = 1; + } + if ((FPSCR & fpscr_rn) == fpscr_rn_round_towards_neg_infinity) { + if (sign == 1 && gbit == 1) inc = 1; + if (sign == 1 && rbit == 1) inc = 1; + if (sign == 1 && xbit == 1) inc = 1; + } + /* work out addition in low 25 bits of out */ + out = EXTRACTED64(*frac_grx, 0, 23) + inc; + *frac_grx = INSERTED64(out, 0, 23); + if (out & BIT64(64 - 23 - 1 - 1)) { + *frac_grx = (BIT64(0) | + INSERTED64(EXTRACTED64(*frac_grx, 0, 22), 1, 23)); + *exp = *exp + 1; + } + /* frac_grx[24:52] = 0 already */ + FPSCR_SET_FR(inc); + FPSCR_SET_FI(gbit || rbit || xbit); + + +# +void::function::Round_Integer:cpu *processor, int sign, unsigned64 *frac, int *frac64, int gbit, int rbit, int xbit, fpscreg round_mode + int inc = 0; + if (round_mode == fpscr_rn_round_to_nearest) { + if (*frac64 == 1 && gbit == 1) inc = 1; + if (*frac64 == 0 && gbit == 1 && rbit == 1) inc = 1; + if (*frac64 == 0 && gbit == 1 && xbit == 1) inc = 1; + } + if (round_mode == fpscr_rn_round_towards_pos_infinity) { + if (sign == 0 && gbit == 1) inc = 1; + if (sign == 0 && rbit == 1) inc = 1; + if (sign == 0 && xbit == 1) inc = 1; + } + if (round_mode == fpscr_rn_round_towards_neg_infinity) { + if (sign == 1 && gbit == 1) inc = 1; + if (sign == 1 && rbit == 1) inc = 1; + if (sign == 1 && xbit == 1) inc = 1; + } + /* frac[0:64] = frac[0:64} + inc */ + *frac += (*frac64 && inc ? 1 : 0); + *frac64 = (*frac64 + inc) & 0x1; + FPSCR_SET_FR(inc); + FPSCR_SET_FI(gbit | rbit | xbit); + + +void::function::Round_Float:cpu *processor, int sign, int *exp, unsigned64 *frac, fpscreg round_mode + int carry_out; + int inc = 0; + int lsb = EXTRACTED64(*frac, 52, 52); + int gbit = EXTRACTED64(*frac, 53, 53); + int rbit = EXTRACTED64(*frac, 54, 54); + int xbit = EXTRACTED64(*frac, 55, 55); + if (round_mode == fpscr_rn_round_to_nearest) { + if (lsb == 1 && gbit == 1) inc = 1; + if (lsb == 0 && gbit == 1 && rbit == 1) inc = 1; + if (lsb == 0 && gbit == 1 && xbit == 1) inc = 1; + } + if (round_mode == fpscr_rn_round_towards_pos_infinity) { + if (sign == 0 && gbit == 1) inc = 1; + if (sign == 0 && rbit == 1) inc = 1; + if (sign == 0 && xbit == 1) inc = 1; + } + if (round_mode == fpscr_rn_round_towards_neg_infinity) { + if (sign == 1 && gbit == 1) inc = 1; + if (sign == 1 && rbit == 1) inc = 1; + if (sign == 1 && xbit == 1) inc = 1; + } + /* frac//carry_out = frac + inc */ + *frac = (*frac >> 1) + (INSERTED64(inc, 52, 52) >> 1); + carry_out = EXTRACTED64(*frac, 0, 0); + *frac <<= 1; + if (carry_out == 1) *exp = *exp + 1; + FPSCR_SET_FR(inc); + FPSCR_SET_FI(gbit | rbit | xbit); + FPSCR_SET_XX(FPSCR & fpscr_fi); + + +# conversion of FP to integer +void::function::convert_to_integer:cpu *processor, unsigned_word cia, unsigned64 *frt, unsigned64 frb, fpscreg round_mode, int tgt_precision + int i; + int exp = 0; + unsigned64 frac = 0; + int frac64 = 0; + int gbit = 0; + int rbit = 0; + int xbit = 0; + int sign = EXTRACTED64(frb, 0, 0); + /***/ + if (EXTRACTED64(frb, 1, 11) == 2047 && EXTRACTED64(frb, 12, 63) == 0) + GOTO(Infinity_Operand); + if (EXTRACTED64(frb, 1, 11) == 2047 && EXTRACTED64(frb, 12, 12) == 0) + GOTO(SNaN_Operand); + if (EXTRACTED64(frb, 1, 11) == 2047 && EXTRACTED64(frb, 12, 12) == 1) + GOTO(QNaN_Operand); + if (EXTRACTED64(frb, 1, 11) > 1086) GOTO(Large_Operand); + if (EXTRACTED64(frb, 1, 11) > 0) exp = EXTRACTED64(frb, 1, 11) - 1023; + if (EXTRACTED64(frb, 1, 11) == 0) exp = -1022; + if (EXTRACTED64(frb, 1, 11) > 0) { /* normal */ + frac = BIT64(1) | INSERTED64(EXTRACTED64(frb, 12, 63), 2, 53); + frac64 = 0; + } + if (EXTRACTED64(frb, 1, 11) == 0) { /* denorm */ + frac = INSERTED64(EXTRACTED64(frb, 12, 63), 2, 53); + frac64 = 0; + } + gbit = 0, rbit = 0, xbit = 0; + for (i = 1; i <= 63 - exp; i++) { + xbit = rbit | xbit; + rbit = gbit; + gbit = frac64; + frac64 = EXTRACTED64(frac, 63, 63); + frac = INSERTED64(EXTRACTED64(frac, 0, 62), 1, 63); + } + Round_Integer(processor, sign, &frac, &frac64, gbit, rbit, xbit, round_mode); + if (sign == 1) { /* frac[0:64] = ~frac[0:64] + 1 */ + frac = ~frac; + frac64 ^= 1; + frac += (frac64 ? 1 : 0); + frac64 = (frac64 + 1) & 0x1; + } + if (tgt_precision == 32 /* can ignore frac64 in compare */ + && (signed64)frac > (signed64)MASK64(33+1, 63)/*2^31-1 >>1*/) + GOTO(Large_Operand); + if (tgt_precision == 64 /* can ignore frac64 in compare */ + && (signed64)frac > (signed64)MASK64(1+1, 63)/*2^63-1 >>1*/) + GOTO(Large_Operand); + if (tgt_precision == 32 /* can ignore frac64 in compare */ + && (signed64)frac < (signed64)MASK64(0, 32+1)/*-2^31 >>1*/) + GOTO(Large_Operand); + if (tgt_precision == 64 /* can ignore frac64 in compare */ + && (signed64)frac < (signed64)MASK64(0, 0+1)/*-2^63 >>1*/) + GOTO(Large_Operand); + FPSCR_SET_XX(FPSCR & fpscr_fi); + if (tgt_precision == 32) + *frt = MASKED64(*frt, 0, 31) | (EXTRACTED64(frac, 33, 63) << 1) | frac64; + if (tgt_precision == 64) + *frt = (EXTRACTED64(frac, 1, 63) << 1) | frac64; + /*FPSCR[fprf] = undefined */ + GOTO(Done); + /**/ + LABEL(Infinity_Operand): + FPSCR_SET_FR(0); + FPSCR_SET_FI(0); + FPSCR_OR_VX(fpscr_vxcvi); + if ((FPSCR & fpscr_ve) == 0) { + if (tgt_precision == 32) { + if (sign == 0) *frt = MASKED64(*frt, 0, 31) | 0x7FFFFFFF; + if (sign == 1) *frt = MASKED64(*frt, 0, 31) | 0x80000000; + } + else { + if (sign == 0) *frt = MASK64(1, 63); /*0x7FFF_FFFF_FFFF_FFFF*/ + if (sign == 1) *frt = BIT64(0); /*0x8000_0000_0000_0000*/ + } + /* FPSCR[FPRF] = undefined */ + } + GOTO(Done); + /**/ + LABEL(SNaN_Operand): + FPSCR_SET_FR(0); + FPSCR_SET_FI(0); + FPSCR_OR_VX(fpscr_vxsnan | fpscr_vxcvi); + if ((FPSCR & fpscr_ve) == 0) { + if (tgt_precision == 32) *frt = MASKED64(*frt, 0, 31) | 0x80000000; + if (tgt_precision == 64) *frt = BIT64(0); /*0x8000_0000_0000_0000*/ + /* FPSCR[fprf] = undefined */ + } + GOTO(Done); + /**/ + LABEL(QNaN_Operand): + FPSCR_SET_FR(0); + FPSCR_SET_FI(0); + FPSCR_OR_VX(fpscr_vxcvi); + if ((FPSCR & fpscr_ve) == 0) { + if (tgt_precision == 32) *frt = MASKED64(*frt, 0, 31) | 0x80000000; + if (tgt_precision == 64) *frt = BIT64(0);/*0x8000_0000_0000_0000*/ + /* FPSCR[fprf] = undefined */ + } + GOTO(Done); + /**/ + LABEL(Large_Operand): + FPSCR_SET_FR(0); + FPSCR_SET_FI(0); + FPSCR_OR_VX(fpscr_vxcvi); + if ((FPSCR & fpscr_ve) == 0) { + if (tgt_precision == 32) { + if (sign == 0) *frt = MASKED64(*frt, 0, 31) | 0x7fffffff; + if (sign == 1) *frt = MASKED64(*frt, 0, 31) | 0x80000000; + } + else { + if (sign == 0) *frt = MASK64(1, 63); /*0x7FFF_FFFF_FFFF_FFFF*/ + if (sign == 1) *frt = BIT64(0); /*0x8000_0000_0000_0000*/ + } + /* FPSCR[fprf] = undefined */ + } + /**/ + LABEL(Done): + + +# extract out raw fields of a FP number +int::function::sign:unsigned64 FRS + return (MASKED64(FRS, 0, 0) + ? -1 + : 1); +int::function::biased_exp:unsigned64 frs, int single + if (single) + return EXTRACTED64(frs, 1, 8); + else + return EXTRACTED64(frs, 1, 11); +unsigned64::function::fraction:unsigned64 frs, int single + if (single) + return EXTRACTED64(frs, 9, 31); + else + return EXTRACTED64(frs, 12, 63); + +# a number?, each of the below return +1 or -1 (based on sign bit) +# if true. +int::function::is_nor:unsigned64 frs, int single + int exp = biased_exp(frs, single); + return (exp >= 1 + && exp <= (single ? 254 : 2046)); +int::function::is_zero:unsigned64 FRS + return (MASKED64(FRS, 1, 63) == 0 + ? sign(FRS) + : 0); +int::function::is_den:unsigned64 frs, int single + int exp = biased_exp(frs, single); + unsigned64 frac = fraction(frs, single); + return (exp == 0 && frac != 0 + ? sign(frs) + : 0); +int::function::is_inf:unsigned64 frs, int single + int exp = biased_exp(frs, single); + unsigned64 frac = fraction(frs, single); + return (exp == (single ? 255 : 2047) && frac == 0 + ? sign(frs) + : 0); +int::function::is_NaN:unsigned64 frs, int single + int exp = biased_exp(frs, single); + unsigned64 frac = fraction(frs, single); + return (exp == (single ? 255 : 2047) && frac != 0 + ? sign(frs) + : 0); +int::function::is_SNaN:unsigned64 frs, int single + return (is_NaN(frs, single) + && !(frs & (single ? MASK64(9, 9) : MASK64(12, 12))) + ? sign(frs) + : 0); +int::function::is_QNaN:unsigned64 frs, int single + return (is_NaN(frs, single) && !is_SNaN(frs, single)); +int::function::is_less_than:unsigned64 *fra, unsigned64 *frb + return *(double*)fra < *(double*)frb; +int::function::is_greater_than:unsigned64 *fra, unsigned64 *frb + return *(double*)fra > *(double*)frb; +int::function::is_equan_to:unsigned64 *fra, unsigned64 *frb + return *(double*)fra == *(double*)frb; + + +# which quiet nan should become the result +unsigned64::function::select_qnan:unsigned64 fra, unsigned64 frb, unsigned64 frc, int instruction_is_frsp, int generate_qnan, int single + unsigned64 frt = 0; + if (is_NaN(fra, single)) + frt = fra; + else if (is_NaN(frb, single)) + if (instruction_is_frsp) + frt = MASKED64(frb, 0, 34); + else + frt = frb; + else if (is_NaN(frc, single)) + frt = frc; + else if (generate_qnan) + frt = MASK64(1, 12); /* 0x7FF8_0000_0000_0000 */ + else + error("select_qnan - default reached\n"); + return frt; + + +# detect invalid operation +int::function::is_invalid_operation:cpu *processor, unsigned_word cia, unsigned64 fra, unsigned64 frb, fpscreg check, int single, int negate + int fail = 0; + if ((check & fpscr_vxsnan) + && (is_SNaN(fra, single) || is_SNaN(frb, single))) { + FPSCR_OR_VX(fpscr_vxsnan); + fail = 1; + } + if ((check & fpscr_vxisi) + && (is_inf(fra, single) && is_inf(frb, single)) + && ((negate && sign(fra) != sign(frb)) + || (!negate && sign(fra) == sign(frb)))) { + /*FIXME: don't handle inf-inf VS inf+-inf */ + FPSCR_OR_VX(fpscr_vxisi); + fail = 1; + } + if ((check & fpscr_vxidi) + && (is_inf(fra, single) && is_inf(frb, single))) { + FPSCR_OR_VX(fpscr_vxidi); + fail = 1; + } + if ((check & fpscr_vxzdz) + && (is_zero(fra) && is_zero(frb))) { + FPSCR_OR_VX(fpscr_vxzdz); + fail = 1; + } + if ((check & fpscr_vximz) + && (is_zero(fra) && is_inf(frb, single))) { + FPSCR_OR_VX(fpscr_vximz); + fail = 1; + } + if ((check & fpscr_vxvc) + && (is_NaN(fra, single) || is_NaN(frb, single))) { + FPSCR_OR_VX(fpscr_vxvc); + fail = 1; + } + if ((check & fpscr_vxsoft)) { + FPSCR_OR_VX(fpscr_vxsoft); + fail = 1; + } + if ((check & fpscr_vxsqrt) + && sign(fra) < 0) { + FPSCR_OR_VX(fpscr_vxsqrt); + fail = 1; + } + /* if ((check && fpscr_vxcvi) { + && (is_inf(fra, single) || is_NaN(fra, single) || is_large(fra, single))) + FPSCR_OR_VX(fpscr_vxcvi); + fail = 1; + } + */ + return fail; + + + + + +# handle case of invalid operation +void::function::invalid_arithemetic_operation:cpu *processor, unsigned_word cia, unsigned64 *frt, unsigned64 fra, unsigned64 frb, unsigned64 frc, int instruction_is_frsp, int instruction_is_convert_to_64bit, int instruction_is_convert_to_32bit, int single + if (FPSCR & fpscr_ve) { + /* invalid operation exception enabled */ + /* FRT unchaged */ + FPSCR_SET_FR(0); + FPSCR_SET_FI(0); + /* fpscr_FPRF unchanged */ + } + else { + /* invalid operation exception disabled */ + if (instruction_is_convert_to_64bit) { + error("oopsi"); + } + else if (instruction_is_convert_to_32bit) { + error("oopsi"); + } + else { /* arrith, frsp */ + *frt = select_qnan(fra, frb, frc, + instruction_is_frsp, 1/*generate*/, single); + FPSCR_SET_FR(0); + FPSCR_SET_FI(0); + FPSCR_SET_FPRF(fpscr_rf_quiet_nan); + } + } + + + + +# detect divide by zero +int::function::is_invalid_zero_divide:cpu *processor, unsigned_word cia, unsigned64 fra, unsigned64 frb, int single + int fail = 0; + if (is_zero (frb)) { + FPSCR_SET_ZX (1); + fail = 1; + } + return fail; + + + + +# handle case of invalid operation +void::function::invalid_zero_divide_operation:cpu *processor, unsigned_word cia, unsigned64 *frt, unsigned64 fra, unsigned64 frb, int single + if (FPSCR & fpscr_ze) { + /* zero-divide exception enabled */ + /* FRT unchaged */ + FPSCR_SET_FR(0); + FPSCR_SET_FI(0); + /* fpscr_FPRF unchanged */ + } + else { + /* zero-divide exception disabled */ + FPSCR_SET_FR(0); + FPSCR_SET_FI(0); + if ((sign (fra) < 0 && sign (frb) < 0) + || (sign (fra) > 0 && sign (frb) > 0)) { + *frt = MASK64 (1, 11); /* 0 : 2047 : 0..0 */ + FPSCR_SET_FPRF(fpscr_rf_pos_infinity); + } + else { + *frt = MASK64 (0, 11); /* 1 : 2047 : 0..0 */ + FPSCR_SET_FPRF(fpscr_rf_neg_infinity); + } + } + + + + + +# +# 0.0.0.0 Illegal instruction used for kernel mode emulation +# +0.0,6./,11./,16./,21./,31.1:X:::instruction_call + if (!os_emul_instruction_call(processor, cia, real_addr(cia, 1))) + program_interrupt(processor, cia, + illegal_instruction_program_interrupt); + +# +# I.2.4.1 Branch Instructions +# +0.18,6.LI,30.AA,31.LK:I:::Branch +*601: PPC_UNIT_BPU, PPC_UNIT_BPU, 1, 1, 0 +*603: PPC_UNIT_BPU, PPC_UNIT_BPU, 1, 1, 0 +*603e:PPC_UNIT_BPU, PPC_UNIT_BPU, 1, 1, 0 +*604: PPC_UNIT_BPU, PPC_UNIT_BPU, 1, 1, 0 + /* WITH_OPTION_MPC860C0 + No problem here because this branch is predicted taken (unconditional). */ + if (AA) NIA = IEA(EXTS(LI_0b00)); + else NIA = IEA(CIA + EXTS(LI_0b00)); + if (LK) LR = (spreg)CIA+4; + if (CURRENT_MODEL_ISSUE > 0) + model_branches(cpu_model(processor), 1, -1); + +0.16,6.BO,11.BI,16.BD,30.AA,31.LK:B:::Branch Conditional +*601: PPC_UNIT_BPU, PPC_UNIT_BPU, 1, 1, 0 +*603: PPC_UNIT_BPU, PPC_UNIT_BPU, 1, 1, 0 +*603e:PPC_UNIT_BPU, PPC_UNIT_BPU, 1, 1, 0 +*604: PPC_UNIT_BPU, PPC_UNIT_BPU, 1, 1, 0 + int M, ctr_ok, cond_ok, succeed; + if (CURRENT_MODEL_ISSUE > 0 && ! BO{0}) + model_wait_for_cr(cpu_model(processor), BIT32_BI); + if (is_64bit_implementation && is_64bit_mode) M = 0; + else M = 32; + if (!BO{2}) CTR = CTR - 1; + ctr_ok = BO{2} || ((MASKED(CTR, M, 63) != 0) != (BO{3})); + cond_ok = BO{0} || ((CR{BI}) == (BO{1})); + if (ctr_ok && cond_ok) { + if (AA) NIA = IEA(EXTS(BD_0b00)); + else NIA = IEA(CIA + EXTS(BD_0b00)); + succeed = 1; + } + else + succeed = 0; + if (LK) LR = (spreg)IEA(CIA + 4); + #ifdef WITH_OPTION_MPC860C0 + if (option_mpc860c0 && (BO{0} && BO{2} || !BO{4})) { + /* This branch is predicted as not-taken. + If this is a forward branch and it is near the end of a page, + we've detected a problematic branch. */ + if (succeed && NIA > CIA) { + if (PAGE_SIZE - (CIA & (PAGE_SIZE-1)) <= option_mpc860c0) + program_interrupt(processor, cia, mpc860c0_instruction_program_interrupt); + } + } + #endif // WITH_OPTION_MPC860C0 + if (CURRENT_MODEL_ISSUE > 0) + model_branches(cpu_model(processor), succeed, BO); + if (! BO{0}) { + int reverse; + if (BO{4}) { /* branch prediction bit set, reverse sense of test */ + reverse = EXTS(BD_0b00) < 0; + } else { /* branch prediction bit not set */ + reverse = EXTS(BD_0b00) >= 0; + } + if (CURRENT_MODEL_ISSUE > 0) + model_branch_predict(cpu_model(processor), reverse ? !succeed : succeed); + } + +0.19,6.BO,11.BI,16./,21.16,31.LK:XL:::Branch Conditional to Link Register +*601: PPC_UNIT_BPU, PPC_UNIT_BPU, 1, 1, 0 +*603: PPC_UNIT_BPU, PPC_UNIT_BPU, 1, 1, 0 +*603e:PPC_UNIT_BPU, PPC_UNIT_BPU, 1, 1, 0 +*604: PPC_UNIT_BPU, PPC_UNIT_BPU, 1, 1, 0 + int M, ctr_ok, cond_ok, succeed; + if (is_64bit_implementation && is_64bit_mode) M = 0; + else M = 32; + if (CURRENT_MODEL_ISSUE > 0 && ! BO{0}) + model_wait_for_cr(cpu_model(processor), BIT32_BI); + if (!BO{2}) CTR = CTR - 1; + ctr_ok = BO{2} || ((MASKED(CTR, M, 63) != 0) != BO{3}); + cond_ok = BO{0} || (CR{BI} == BO{1}); + if (ctr_ok && cond_ok) { + NIA = IEA(LR_0b00); + succeed = 1; + } + else + succeed = 0; + if (LK) LR = (spreg)IEA(CIA + 4); + #ifdef WITH_OPTION_MPC860C0 + if (option_mpc860c0 && (BO{0} && BO{2} || !BO{4})) { + /* This branch is predicted as not-taken. + If this is a forward branch and it is near the end of a page, + we've detected a problematic branch. */ + if (succeed && NIA > CIA) { + if (PAGE_SIZE - (CIA & (PAGE_SIZE-1)) <= option_mpc860c0) + program_interrupt(processor, cia, mpc860c0_instruction_program_interrupt); + } + } + #endif // WITH_OPTION_MPC860C0 + if (CURRENT_MODEL_ISSUE > 0) { + model_branches(cpu_model(processor), succeed, BO); + if (! BO{0}) + model_branch_predict(cpu_model(processor), BO{4} ? !succeed : succeed); + } + +0.19,6.BO,11.BI,16./,21.528,31.LK:XL:::Branch Conditional to Count Register +*601: PPC_UNIT_BPU, PPC_UNIT_BPU, 1, 1, 0 +*603: PPC_UNIT_BPU, PPC_UNIT_BPU, 1, 1, 0 +*603e:PPC_UNIT_BPU, PPC_UNIT_BPU, 1, 1, 0 +*604: PPC_UNIT_BPU, PPC_UNIT_BPU, 1, 1, 0 + int cond_ok, succeed; + if (CURRENT_MODEL_ISSUE > 0 && ! BO{0}) + model_wait_for_cr(cpu_model(processor), BIT32_BI); + cond_ok = BO{0} || (CR{BI} == BO{1}); + if (cond_ok) { + NIA = IEA(CTR_0b00); + succeed = 1; + } + else + succeed = 0; + if (LK) LR = (spreg)IEA(CIA + 4); + #ifdef WITH_OPTION_MPC860C0 + if (option_mpc860c0 && (BO{0} && BO{2} || !BO{4})) { + /* This branch is predicted as not-taken. + If this is a forward branch and it is near the end of a page, + we've detected a problematic branch. */ + if (succeed && NIA > CIA) { + if (PAGE_SIZE - (CIA & (PAGE_SIZE-1)) <= option_mpc860c0) + program_interrupt(processor, cia, mpc860c0_instruction_program_interrupt); + } + } + #endif // WITH_OPTION_MPC860C0 + if (CURRENT_MODEL_ISSUE > 0) { + model_branches(cpu_model(processor), succeed, BO); + if (! BO{0}) + model_branch_predict(cpu_model(processor), BO{4} ? !succeed : succeed); + } + +# +# I.2.4.2 System Call Instruction +# +0.17,6./,11./,16./,30.1,31./:SC:::System Call +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_SRU, PPC_UNIT_SRU, 3, 3, 0 +*603e:PPC_UNIT_SRU, PPC_UNIT_SRU, 3, 3, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + if (CURRENT_MODEL_ISSUE > 0) + model_serialize(MY_INDEX, cpu_model(processor)); + system_call_interrupt(processor, cia); + +# +# I.2.4.3 Condition Register Logical Instructions +# +0.19,6.BT,11.BA,16.BB,21.257,31./:XL::crand:Condition Register AND +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_SRU, PPC_UNIT_SRU, 1, 1, 0 +*603e:PPC_UNIT_SRU, PPC_UNIT_SRU, 1, 1, 0 +*604: PPC_UNIT_BPU, PPC_UNIT_BPU, 1, 1, 0 + BLIT32(CR, BT, CR{BA} && CR{BB}); + PPC_INSN_CR(BT_BITMASK, BA_BITMASK | BB_BITMASK); + +0.19,6.BT,11.BA,16.BB,21.449,31./:XL::cror:Condition Register OR +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_SRU, PPC_UNIT_SRU, 1, 1, 0 +*603e:PPC_UNIT_SRU, PPC_UNIT_SRU, 1, 1, 0 +*604: PPC_UNIT_BPU, PPC_UNIT_BPU, 1, 1, 0 + BLIT32(CR, BT, CR{BA} || CR{BB}); + PPC_INSN_CR(BT_BITMASK, BA_BITMASK | BB_BITMASK); + +0.19,6.BT,11.BA,16.BB,21.193,31./:XL::crxor:Condition Register XOR +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_SRU, PPC_UNIT_SRU, 1, 1, 0 +*603e:PPC_UNIT_SRU, PPC_UNIT_SRU, 1, 1, 0 +*604: PPC_UNIT_BPU, PPC_UNIT_BPU, 1, 1, 0 + BLIT32(CR, BT, CR{BA} != CR{BB}); + PPC_INSN_CR(BT_BITMASK, BA_BITMASK | BB_BITMASK); + +0.19,6.BT,11.BA,16.BB,21.225,31./:XL::crnand:Condition Register NAND +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_SRU, PPC_UNIT_SRU, 1, 1, 0 +*603e:PPC_UNIT_SRU, PPC_UNIT_SRU, 1, 1, 0 +*604: PPC_UNIT_BPU, PPC_UNIT_BPU, 1, 1, 0 + BLIT32(CR, BT, !(CR{BA} && CR{BB})); + PPC_INSN_CR(BT_BITMASK, BA_BITMASK | BB_BITMASK); + +0.19,6.BT,11.BA,16.BB,21.33,31./:XL::crnor:Condition Register NOR +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_SRU, PPC_UNIT_SRU, 1, 1, 0 +*603e:PPC_UNIT_SRU, PPC_UNIT_SRU, 1, 1, 0 +*604: PPC_UNIT_BPU, PPC_UNIT_BPU, 1, 1, 0 + BLIT32(CR, BT, !(CR{BA} || CR{BB})); + PPC_INSN_CR(BT_BITMASK, BA_BITMASK | BB_BITMASK); + +0.19,6.BT,11.BA,16.BB,21.289,31./:XL::creqv:Condition Register Equivalent +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_SRU, PPC_UNIT_SRU, 1, 1, 0 +*603e:PPC_UNIT_SRU, PPC_UNIT_SRU, 1, 1, 0 +*604: PPC_UNIT_BPU, PPC_UNIT_BPU, 1, 1, 0 + BLIT32(CR, BT, CR{BA} == CR{BB}); + PPC_INSN_CR(BT_BITMASK, BA_BITMASK | BB_BITMASK); + +0.19,6.BT,11.BA,16.BB,21.129,31./:XL::crandc:Condition Register AND with Complement +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_SRU, PPC_UNIT_SRU, 1, 1, 0 +*603e:PPC_UNIT_SRU, PPC_UNIT_SRU, 1, 1, 0 +*604: PPC_UNIT_BPU, PPC_UNIT_BPU, 1, 1, 0 + BLIT32(CR, BT, CR{BA} && !CR{BB}); + PPC_INSN_CR(BT_BITMASK, BA_BITMASK | BB_BITMASK); + +0.19,6.BT,11.BA,16.BB,21.417,31./:XL::crorc:Condition Register OR with Complement +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_SRU, PPC_UNIT_SRU, 1, 1, 0 +*603e:PPC_UNIT_SRU, PPC_UNIT_SRU, 1, 1, 0 +*604: PPC_UNIT_BPU, PPC_UNIT_BPU, 1, 1, 0 + BLIT32(CR, BT, CR{BA} || !CR{BB}); + PPC_INSN_CR(BT_BITMASK, BA_BITMASK | BB_BITMASK); + +# +# I.2.4.4 Condition Register Field Instruction +# +0.19,6.BF,9./,11.BFA,14./,16./,21.0,31./:XL:::Move Condition Register Field +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_SRU, PPC_UNIT_SRU, 1, 1, 0 +*603e:PPC_UNIT_SRU, PPC_UNIT_SRU, 1, 1, 0 +*604: PPC_UNIT_BPU, PPC_UNIT_BPU, 1, 1, 0 + MBLIT32(CR, 4*BF, 4*BF+3, EXTRACTED32(CR, 4*BFA, 4*BFA+3)); + PPC_INSN_CR(BF_BITMASK, 1 << BFA); + + +# +# I.3.3.2 Fixed-Point Load Instructions +# + +0.34,6.RT,11.RA,16.D:D:::Load Byte and Zero +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 2, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 + unsigned_word b; + unsigned_word EA; + if (RA_is_0) b = 0; + else b = *rA; + EA = b + EXTS(D); + *rT = MEM(unsigned, EA, 1); + PPC_INSN_INT(RT_BITMASK, (RA_BITMASK & ~1), 0); + + +0.31,6.RT,11.RA,16.RB,21.87,31./:X:::Load Byte and Zero Indexed +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 2, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 + unsigned_word b; + unsigned_word EA; + if (RA_is_0) b = 0; + else b = *rA; + EA = b + *rB; + *rT = MEM(unsigned, EA, 1); + PPC_INSN_INT(RT_BITMASK, (RA_BITMASK & ~1) | RB_BITMASK, 0); + +0.35,6.RT,11.RA,16.D:D:::Load Byte and Zero with Update +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 2, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 + unsigned_word EA; + if (RA_is_0 || RA == RT) + program_interrupt(processor, cia, + illegal_instruction_program_interrupt); + EA = *rA + EXTS(D); + *rT = MEM(unsigned, EA, 1); + *rA = EA; + PPC_INSN_INT(RT_BITMASK | RA_BITMASK, RA_BITMASK, 0); + +0.31,6.RT,11.RA,16.RB,21.119,31./:X:::Load Byte and Zero with Update Indexed +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 2, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 + unsigned_word EA; + if (RA_is_0 || RA == RT) + program_interrupt(processor, cia, + illegal_instruction_program_interrupt); + EA = *rA + *rB; + *rT = MEM(unsigned, EA, 1); + *rA = EA; + PPC_INSN_INT(RT_BITMASK | RA_BITMASK, RA_BITMASK | RB_BITMASK, 0); + +0.40,6.RT,11.RA,16.D:D:::Load Halfword and Zero +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 2, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 + unsigned_word b; + unsigned_word EA; + if (RA_is_0) b = 0; + else b = *rA; + EA = b + EXTS(D); + *rT = MEM(unsigned, EA, 2); + PPC_INSN_INT(RT_BITMASK, (RA_BITMASK & ~1), 0); + +0.31,6.RT,11.RA,16.RB,21.279,31./:X:::Load Halfword and Zero Indexed +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 2, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 + unsigned_word b; + unsigned_word EA; + if (RA_is_0) b = 0; + else b = *rA; + EA = b + *rB; + *rT = MEM(unsigned, EA, 2); + PPC_INSN_INT(RT_BITMASK, (RA_BITMASK & ~1) | RB_BITMASK, 0); + +0.41,6.RT,11.RA,16.D:D:::Load Halfword and Zero with Update +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 2, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 + unsigned_word EA; + if (RA_is_0 || RA == RT) + program_interrupt(processor, cia, + illegal_instruction_program_interrupt); + EA = *rA + EXTS(D); + *rT = MEM(unsigned, EA, 2); + *rA = EA; + PPC_INSN_INT(RT_BITMASK | RA_BITMASK, RA_BITMASK, 0); + +0.31,6.RT,11.RA,16.RB,21.311,31./:X:::Load Halfword and Zero with Update Indexed +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 2, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 + unsigned_word EA; + if (RA_is_0 || RA == RT) + program_interrupt(processor, cia, + illegal_instruction_program_interrupt); + EA = *rA + *rB; + *rT = MEM(unsigned, EA, 2); + *rA = EA; + PPC_INSN_INT(RT_BITMASK | RA_BITMASK, RA_BITMASK | RB_BITMASK, 0); + +0.42,6.RT,11.RA,16.D:D:::Load Halfword Algebraic +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 2, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 + unsigned_word b; + unsigned_word EA; + if (RA_is_0) b = 0; + else b = *rA; + EA = b + EXTS(D); + *rT = MEM(signed, EA, 2); + PPC_INSN_INT(RT_BITMASK, (RA_BITMASK & ~1), 0); + +0.31,6.RT,11.RA,16.RB,21.343,31./:X:::Load Halfword Algebraic Indexed +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 2, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 + unsigned_word b; + unsigned_word EA; + if (RA_is_0) b = 0; + else b = *rA; + EA = b + *rB; + *rT = MEM(signed, EA, 2); + PPC_INSN_INT(RT_BITMASK, (RA_BITMASK & ~1) | RB_BITMASK, 0); + +0.43,6.RT,11.RA,16.D:D:::Load Halfword Algebraic with Update +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 2, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 + unsigned_word EA; + if (RA_is_0 || RA == RT) + program_interrupt(processor, cia, + illegal_instruction_program_interrupt); + EA = *rA + EXTS(D); + *rT = MEM(signed, EA, 2); + *rA = EA; + PPC_INSN_INT(RT_BITMASK | RA_BITMASK, RA_BITMASK, 0); + +0.31,6.RT,11.RA,16.RB,21.375,31./:X:::Load Halfword Algebraic with Update Indexed +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 2, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 + unsigned_word EA; + if (RA_is_0 || RA == RT) + program_interrupt(processor, cia, + illegal_instruction_program_interrupt); + EA = *rA + *rB; + *rT = MEM(signed, EA, 2); + *rA = EA; + PPC_INSN_INT(RT_BITMASK | RA_BITMASK, RA_BITMASK | RB_BITMASK, 0); + +0.32,6.RT,11.RA,16.D:D:::Load Word and Zero +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 2, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 + unsigned_word b; + unsigned_word EA; + if (RA_is_0) b = 0; + else b = *rA; + EA = b + EXTS(D); + *rT = MEM(unsigned, EA, 4); + PPC_INSN_INT(RT_BITMASK, (RA_BITMASK & ~1), 0); + +0.31,6.RT,11.RA,16.RB,21.23,31./:X:::Load Word and Zero Indexed +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 2, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 + unsigned_word b; + unsigned_word EA; + if (RA_is_0) b = 0; + else b = *rA; + EA = b + *rB; + *rT = MEM(unsigned, EA, 4); + PPC_INSN_INT(RT_BITMASK, (RA_BITMASK & ~1) | RB_BITMASK, 0); + +0.33,6.RT,11.RA,16.D:D:::Load Word and Zero with Update +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 2, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 + unsigned_word EA; + if (RA_is_0 || RA == RT) + program_interrupt(processor, cia, + illegal_instruction_program_interrupt); + EA = *rA + EXTS(D); + *rT = MEM(unsigned, EA, 4); + *rA = EA; + PPC_INSN_INT(RT_BITMASK | RA_BITMASK, RA_BITMASK, 0); + +0.31,6.RT,11.RA,16.RB,21.55,31./:X:::Load Word and Zero with Update Indexed +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 2, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 + unsigned_word EA; + if (RA_is_0 || RA == RT) + program_interrupt(processor, cia, + illegal_instruction_program_interrupt); + EA = *rA + *rB; + *rT = MEM(unsigned, EA, 4); + *rA = EA; + PPC_INSN_INT(RT_BITMASK | RA_BITMASK, RA_BITMASK | RB_BITMASK, 0); + +0.58,6.RT,11.RA,16.DS,30.2:DS:64::Load Word Algebraic +# unsigned_word b; +# unsigned_word EA; +# if (RA_is_0) b = 0; +# else b = *rA; +# EA = b + EXTS(DS_0b00); +# *rT = MEM(signed, EA, 4); + +0.31,6.RT,11.RA,16.RB,21.341,31./:X:64::Load Word Algebraic Indexed +# unsigned_word b; +# unsigned_word EA; +# if (RA_is_0) b = 0; +# else b = *rA; +# EA = b + *rB;; +# *rT = MEM(signed, EA, 4); + +0.31,6.RT,11.RA,16.RB,21.373,31./:X:64::Load Word Algebraic with Update Indexed +# unsigned_word EA; +# if (RA_is_0 || RA == RT) +# program_interrupt(processor, cia +# illegal_instruction_program_interrupt); +# EA = *rA + *rB; +# *rT = MEM(signed, EA, 4); +# *rA = EA; + +0.58,6.RT,11.RA,16.DS,30.0:DS:64::Load Doubleword +# unsigned_word b; +# unsigned_word EA; +# if (RA_is_0) b = 0; +# else b = *rA; +# EA = b + EXTS(DS_0b00); +# *rT = MEM(unsigned, EA, 8); + +0.31,6.RT,11.RA,16.RB,21.21,31./:X:64::Load Doubleword Indexed +# unsigned_word b; +# unsigned_word EA; +# if (RA_is_0) b = 0; +# else b = *rA; +# EA = b + *rB; +# *rT = MEM(unsigned, EA, 8); + +0.58,6.RT,11.RA,16.DS,30.1:DS:64::Load Doubleword with Update +# unsigned_word EA; +# if (RA_is_0 || RA == RT) +# program_interrupt(processor, cia +# illegal_instruction_program_interrupt); +# EA = *rA + EXTS(DS_0b00); +# *rT = MEM(unsigned, EA, 8); +# *rA = EA; + +0.31,6.RT,11.RA,16.RB,21.53,31./:DS:64::Load Doubleword with Update Indexed +# unsigned_word EA; +# if (RA_is_0 || RA == RT) +# program_interrupt(processor, cia +# illegal_instruction_program_interrupt); +# EA = *rA + *rB; +# *rT = MEM(unsigned, EA, 8); +# *rA = EA; + + + +# +# I.3.3.3 Fixed-Point Store Instructions +# + +0.38,6.RS,11.RA,16.D:D:::Store Byte +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 3, 0 + unsigned_word b; + unsigned_word EA; + if (RA_is_0) b = 0; + else b = *rA; + EA = b + EXTS(D); + STORE(EA, 1, *rS); + PPC_INSN_INT(0, (RA_BITMASK & ~1) | RS_BITMASK, 0); + +0.31,6.RS,11.RA,16.RB,21.215,31./:X:::Store Byte Indexed +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 3, 0 + unsigned_word b; + unsigned_word EA; + if (RA_is_0) b = 0; + else b = *rA; + EA = b + *rB; + STORE(EA, 1, *rS); + PPC_INSN_INT(0, (RA_BITMASK & ~1) | RB_BITMASK | RS_BITMASK, 0); + +0.39,6.RS,11.RA,16.D:D:::Store Byte with Update +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 3, 0 + unsigned_word EA; + if (RA_is_0) + program_interrupt(processor, cia, + illegal_instruction_program_interrupt); + EA = *rA + EXTS(D); + STORE(EA, 1, *rS); + *rA = EA; + PPC_INSN_INT(RA_BITMASK, RA_BITMASK | RS_BITMASK, 0); + +0.31,6.RS,11.RA,16.RB,21.247,31./:X:::Store Byte with Update Indexed +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 3, 0 + unsigned_word EA; + if (RA_is_0) + program_interrupt(processor, cia, + illegal_instruction_program_interrupt); + EA = *rA + *rB; + STORE(EA, 1, *rS); + *rA = EA; + PPC_INSN_INT(RA_BITMASK, RA_BITMASK | RB_BITMASK | RS_BITMASK, 0); + +0.44,6.RS,11.RA,16.D:D:::Store Half Word +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 3, 0 + unsigned_word b; + unsigned_word EA; + if (RA_is_0) b = 0; + else b = *rA; + EA = b + EXTS(D); + STORE(EA, 2, *rS); + PPC_INSN_INT(0, (RA_BITMASK & ~1) | RS_BITMASK, 0); + +0.31,6.RS,11.RA,16.RB,21.407,31./:X:::Store Half Word Indexed +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 3, 0 + unsigned_word b; + unsigned_word EA; + if (RA_is_0) b = 0; + else b = *rA; + EA = b + *rB; + STORE(EA, 2, *rS); + PPC_INSN_INT(0, (RA_BITMASK & ~1) | RB_BITMASK | RS_BITMASK, 0); + +0.45,6.RS,11.RA,16.D:D:::Store Half Word with Update +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 3, 0 + unsigned_word EA; + if (RA_is_0) + program_interrupt(processor, cia, + illegal_instruction_program_interrupt); + EA = *rA + EXTS(D); + STORE(EA, 2, *rS); + *rA = EA; + PPC_INSN_INT(RA_BITMASK, RA_BITMASK | RS_BITMASK, 0); + +0.31,6.RS,11.RA,16.RB,21.439,31./:X:::Store Half Word with Update Indexed +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 3, 0 + unsigned_word EA; + if (RA_is_0) + program_interrupt(processor, cia, + illegal_instruction_program_interrupt); + EA = *rA + *rB; + STORE(EA, 2, *rS); + *rA = EA; + PPC_INSN_INT(RA_BITMASK, RA_BITMASK | RB_BITMASK | RS_BITMASK, 0); + +0.36,6.RS,11.RA,16.D:D:::Store Word +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 3, 0 + unsigned_word b; + unsigned_word EA; + if (RA_is_0) b = 0; + else b = *rA; + EA = b + EXTS(D); + STORE(EA, 4, *rS); + PPC_INSN_INT(0, (RA_BITMASK & ~1) | RS_BITMASK, 0); + +0.31,6.RS,11.RA,16.RB,21.151,31./:X:::Store Word Indexed +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 3, 0 + unsigned_word b; + unsigned_word EA; + if (RA_is_0) b = 0; + else b = *rA; + EA = b + *rB; + STORE(EA, 4, *rS); + PPC_INSN_INT(0, (RA_BITMASK & ~1) | RB_BITMASK | RS_BITMASK, 0); + +0.37,6.RS,11.RA,16.D:D:::Store Word with Update +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 3, 0 + unsigned_word EA; + if (RA_is_0) + program_interrupt(processor, cia, + illegal_instruction_program_interrupt); + EA = *rA + EXTS(D); + STORE(EA, 4, *rS); + *rA = EA; + PPC_INSN_INT(RA_BITMASK, RA_BITMASK | RS_BITMASK, 0); + +0.31,6.RS,11.RA,16.RB,21.183,31./:X:::Store Word with Update Indexed +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 3, 0 + unsigned_word EA; + if (RA_is_0) + program_interrupt(processor, cia, + illegal_instruction_program_interrupt); + EA = *rA + *rB; + STORE(EA, 4, *rS); + *rA = EA; + PPC_INSN_INT(RA_BITMASK, RA_BITMASK | RB_BITMASK | RS_BITMASK, 0); + +0.62,6.RS,11.RA,16.DS,30.0:DS:64::Store Doubleword +# unsigned_word b; +# unsigned_word EA; +# if (RA_is_0) b = 0; +# else b = *rA; +# EA = b + EXTS(DS_0b00); +# STORE(EA, 8, *rS); +0.31,6.RS,11.RA,16.RB,21.149,31./:X:64::Store Doubleword Indexed +# unsigned_word b; +# unsigned_word EA; +# if (RA_is_0) b = 0; +# else b = *rA; +# EA = b + *rB; +# STORE(EA, 8, *rS); +0.62,6.RS,11.RA,16.DS,30.1:DS:64::Store Doubleword with Update +# unsigned_word EA; +# if (RA_is_0) +# program_interrupt(processor, cia +# illegal_instruction_program_interrupt); +# EA = *rA + EXTS(DS_0b00); +# STORE(EA, 8, *rS); +# *rA = EA; +0.31,6.RS,11.RA,16.RB,21.181,31./:X:64::Store Doubleword with Update Indexed +# unsigned_word EA; +# if (RA_is_0) +# program_interrupt(processor, cia +# illegal_instruction_program_interrupt); +# EA = *rA + *rB; +# STORE(EA, 8, *rS); +# *rA = EA; + + +# +# I.3.3.4 Fixed-Point Load and Store with Byte Reversal Instructions +# + +0.31,6.RT,11.RA,16.RB,21.790,31./:X:::Load Halfword Byte-Reverse Indexed +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 3, 0 + unsigned_word b; + unsigned_word EA; + if (RA_is_0) b = 0; + else b = *rA; + EA = b + *rB; + *rT = SWAP_2(MEM(unsigned, EA, 2)); + PPC_INSN_INT(RT_BITMASK, (RA_BITMASK & ~1) | RB_BITMASK, 0); + +0.31,6.RT,11.RA,16.RB,21.534,31./:X:::Load Word Byte-Reverse Indexed +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 3, 0 + unsigned_word b; + unsigned_word EA; + if (RA_is_0) b = 0; + else b = *rA; + EA = b + *rB; + *rT = SWAP_4(MEM(unsigned, EA, 4)); + PPC_INSN_INT(RT_BITMASK, (RA_BITMASK & ~1) | RB_BITMASK, 0); + +0.31,6.RS,11.RA,16.RB,21.918,31./:X:::Store Half Word Byte-Reversed Indexed +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 3, 0 + unsigned_word b; + unsigned_word EA; + if (RA_is_0) b = 0; + else b = *rA; + EA = b + *rB; + STORE(EA, 2, SWAP_2(*rS)); + PPC_INSN_INT(0, (RA_BITMASK & ~1) | RB_BITMASK | RS_BITMASK, 0); + +0.31,6.RS,11.RA,16.RB,21.662,31./:X:::Store Word Byte-Reversed Indexed +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 3, 0 + unsigned_word b; + unsigned_word EA; + if (RA_is_0) b = 0; + else b = *rA; + EA = b + *rB; + STORE(EA, 4, SWAP_4(*rS)); + PPC_INSN_INT(0, (RA_BITMASK & ~1) | RB_BITMASK | RS_BITMASK, 0); + + +# +# I.3.3.5 Fixed-Point Load and Store Multiple Instrctions +# + +0.46,6.RT,11.RA,16.D:D:::Load Multiple Word + unsigned_word EA; + unsigned_word b; + int r; + if (RA_is_0) b = 0; + else b = *rA; + EA = b + EXTS(D); + r = RT; + if (RA >= r) + program_interrupt(processor, cia, + illegal_instruction_program_interrupt); + if (CURRENT_ALIGNMENT == STRICT_ALIGNMENT || (EA % 4 != 0)) + alignment_interrupt(processor, cia, EA); + while (r <= 31) { + GPR(r) = MEM(unsigned, EA, 4); + r = r + 1; + EA = EA + 4; + } + +0.47,6.RS,11.RA,16.D:D:::Store Multiple Word + unsigned_word EA; + unsigned_word b; + int r; + if (RA_is_0) b = 0; + else b = *rA; + EA = b + EXTS(D); + if (CURRENT_ALIGNMENT == STRICT_ALIGNMENT + || (EA % 4 != 0)) + alignment_interrupt(processor, cia, EA); + r = RS; + while (r <= 31) { + STORE(EA, 4, GPR(r)); + r = r + 1; + EA = EA + 4; + } + + +# +# I.3.3.6 Fixed-Point Move Assist Instructions +# + +0.31,6.RT,11.RA,16.NB,21.597,31./:X:::Load String Word Immediate + unsigned_word EA; + int n; + int r; + int i; + int nr; + if (RA_is_0) EA = 0; + else EA = *rA; + if (NB == 0) n = 32; + else n = NB; + r = RT - 1; + i = 32; + nr = (n + 3) / 4; + if ((RT + nr >= 32) + ? (RA >= RT || RA < (RT + nr) % 32) + : (RA >= RT && RA < RT + nr)) + program_interrupt(processor, cia, + illegal_instruction_program_interrupt); + if (CURRENT_ALIGNMENT == STRICT_ALIGNMENT) + alignment_interrupt(processor, cia, EA); + while (n > 0) { + if (i == 32) { + r = (r + 1) % 32; + GPR(r) = 0; + } + GPR(r) |= INSERTED(MEM(unsigned, EA, 1), i, i+7); + if (i == 64) i = 32; + EA = EA + 1; + n = n - 1; + } + +0.31,6.RT,11.RA,16.RB,21.533,31./:X:::Load String Word Indexed + unsigned_word EA; + unsigned_word b; + int n; + int r; + int i; + int nr; + if (RA_is_0) b = 0; + else b = *rA; + EA = b + *rB; + n = EXTRACTED32(XER, 25, 31); + r = RT - 1; + i = 32; + nr = (n + 3) / 4; + if (((RT + n >= 32) + ? ((RA >= RT || RA < (RT + n) % 32) + || (RB >= RT || RB < (RT + n) % 32)) + : ((RA >= RT && RA < RT + n) + || (RB >= RT && RB < RT + n))) + || (RT == RA || RT == RB)) + program_interrupt(processor, cia, + illegal_instruction_program_interrupt); + if (CURRENT_ALIGNMENT == STRICT_ALIGNMENT) + alignment_interrupt(processor, cia, EA); + while (n > 0) { + if (i == 32) { + r = (r + 1) % 32; + GPR(i) = 0; + } + GPR(r) |= INSERTED(MEM(unsigned, EA, 1), i, i+7); + i = i + 8; + if (i == 64) i = 32; + EA = EA + 1; + n = n - 1; + } + +0.31,6.RS,11.RA,16.NB,21.725,31./:X:::Store String Word Immedate + unsigned_word EA; + int n; + int r; + int i; + if (RA_is_0) EA = 0; + else EA = *rA; + if (NB == 0) n = 32; + else n = NB; + r = RS - 1; + i = 32; + if (CURRENT_ALIGNMENT == STRICT_ALIGNMENT) + alignment_interrupt(processor, cia, EA); + while (n > 0) { + if (i == 32) r = (r + 1) % 32; + STORE(EA, 1, EXTRACTED(GPR(r), i, i+7)); + i = i + 8; + if (i == 64) i = 32; + EA = EA + 1; + n = n - 1; + } + +0.31,6.RS,11.RA,16.RB,21.661,31./:X:::Store String Word Indexed + unsigned_word EA; + unsigned_word b; + int n; + int r; + int i; + if (RA_is_0) b = 0; + else b = *rA; + EA = b + *rB; + if (CURRENT_ALIGNMENT == STRICT_ALIGNMENT) + alignment_interrupt(processor, cia, EA); + n = EXTRACTED32(XER, 25, 31); + r = RS - 1; + i = 32; + while (n > 0) { + if (i == 32) r = (r + 1) % 32; + STORE(EA, 1, EXTRACTED(GPR(r), i, i+7)); + i = i + 8; + if (i == 64) i = 32; + EA = EA + 1; + n = n - 1; + } + + +# +# I.3.3.7 Storage Synchronization Instructions +# +# HACK: Rather than monitor addresses looking for a reason +# to cancel a reservation. This code instead keeps +# a copy of the data read from memory. Before performing +# a store, the memory area is checked to see if it has +# been changed. +0.31,6.RT,11.RA,16.RB,21.20,31./:X:::Load Word And Reserve Indexed +*601: PPC_UNIT_IU, PPC_UNIT_IU, 2, 2, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_IU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_IU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 3, 0 + unsigned_word b; + unsigned_word EA; + if (RA_is_0) b = 0; + else b = *rA; + EA = b + *rB; + RESERVE = 1; + RESERVE_ADDR = real_addr(EA, 1/*is-read?*/); + RESERVE_DATA = MEM(unsigned, EA, 4); + *rT = RESERVE_DATA; + PPC_INSN_INT(RT_BITMASK, (RA_BITMASK & ~1) | RB_BITMASK, 0); + +0.31,6.RT,11.RA,16.RB,21.84,31./:X:64::Load Doubleword And Reserve Indexed + unsigned_word b; + unsigned_word EA; + if (RA_is_0) b = 0; + else b = *rA; + EA = b + *rB; + RESERVE = 1; + RESERVE_ADDR = real_addr(EA, 1/*is-read?*/); + RESERVE_DATA = MEM(unsigned, EA, 8); + *rT = RESERVE_DATA; + PPC_INSN_INT(RT_BITMASK, (RA_BITMASK & ~1) | RB_BITMASK, 0); + +0.31,6.RS,11.RA,16.RB,21.150,31.1:X:::Store Word Conditional Indexed +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_SRU, PPC_UNIT_SRU, 8, 8, 0 +*603e:PPC_UNIT_SRU, PPC_UNIT_SRU, 8, 8, 0 +*604: PPC_UNIT_BPU, PPC_UNIT_BPU, 1, 3, 0 + unsigned_word b; + unsigned_word EA; + if (RA_is_0) b = 0; + else b = *rA; + EA = b + *rB; + if (RESERVE) { + if (RESERVE_ADDR == real_addr(EA, 0/*is-read?*/) + && /*HACK*/ RESERVE_DATA == MEM(unsigned, EA, 4)) { + STORE(EA, 4, *rS); + CR_SET_XER_SO(0, cr_i_zero); + } + else { + /* ment to randomly to store, we never do! */ + CR_SET_XER_SO(0, 0); + } + RESERVE = 0; + } + else { + CR_SET_XER_SO(0, 0); + } + PPC_INSN_INT(0, (RA_BITMASK & ~1) | RB_BITMASK | RS_BITMASK, 1/*Rc*/); + +0.31,6.RS,11.RA,16.RB,21.214,31.1:X:64::Store Doubleword Conditional Indexed + unsigned_word b; + unsigned_word EA; + if (RA_is_0) b = 0; + else b = *rA; + EA = b + *rB; + if (RESERVE) { + if (RESERVE_ADDR == real_addr(EA, 0/*is-read?*/) + && /*HACK*/ RESERVE_DATA == MEM(unsigned, EA, 8)) { + STORE(EA, 8, *rS); + CR_SET_XER_SO(0, cr_i_zero); + } + else { + /* ment to randomly to store, we never do */ + CR_SET_XER_SO(0, 0); + } + RESERVE = 0; + } + else { + CR_SET_XER_SO(0, 0); + } + PPC_INSN_INT(0, (RA_BITMASK & ~1) | RB_BITMASK | RS_BITMASK, 1/*Rc*/); + +0.31,6./,11./,16./,21.598,31./:X::sync:Synchronize +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_SRU, PPC_UNIT_SRU, 1, 1, 0 +*603e:PPC_UNIT_SRU, PPC_UNIT_SRU, 1, 1, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 1, 0 + /* do nothing */ + + +# +# I.3.3.9 Fixed-Point Arithmetic Instructions +# + +0.14,6.RT,11.RA,16.SI:D:::Add Immediate +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_SRU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + if (RA_is_0) *rT = EXTS(SI); + else *rT = *rA + EXTS(SI); + ITRACE(trace_alu, (" Result = %ld (0x%lx)\n", (long)*rT, (long)*rT)); + PPC_INSN_INT(RT_BITMASK, (RA_BITMASK & ~1), 0); + +0.15,6.RT,11.RA,16.SI:D:::Add Immediate Shifted +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_SRU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + if (RA_is_0) *rT = EXTS(SI) << 16; + else *rT = *rA + (EXTS(SI) << 16); + ITRACE(trace_alu, (" Result = %ld (0x%lx)\n", (long)*rT, (long)*rT)); + PPC_INSN_INT(RT_BITMASK, (RA_BITMASK & ~1), 0); + +0.31,6.RT,11.RA,16.RB,21.OE,22.266,31.Rc:XO:::Add +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_SRU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + ALU_BEGIN(*rA); + ALU_ADD(*rB); + ALU_END(*rT, 0/*CA*/, OE, Rc); + PPC_INSN_INT(RT_BITMASK, RA_BITMASK | RB_BITMASK, Rc); + +0.31,6.RT,11.RA,16.RB,21.OE,22.40,31.Rc:XO:::Subtract From +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + ALU_BEGIN(*rA); + ALU_NOT; + ALU_ADD(*rB); + ALU_ADD(1); + ALU_END(*rT, 0/*CA*/, OE, Rc); + PPC_INSN_INT(RT_BITMASK, RA_BITMASK | RB_BITMASK, Rc); + +0.12,6.RT,11.RA,16.SI:D:::Add Immediate Carrying +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + ALU_BEGIN(*rA); + ALU_ADD(EXTS(SI)); + ALU_END(*rT, 1/*CA*/, 0/*OE*/, 0/*Rc*/); + PPC_INSN_INT(RT_BITMASK, RA_BITMASK, 0/*Rc*/); + +0.13,6.RT,11.RA,16.SI:D:::Add Immediate Carrying and Record +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + ALU_BEGIN(*rA); + ALU_ADD(EXTS(SI)); + ALU_END(*rT, 1/*CA*/, 0/*OE*/, 1/*Rc*/); + PPC_INSN_INT(RT_BITMASK, RA_BITMASK, 1/*Rc*/); + +0.8,6.RT,11.RA,16.SI:D:::Subtract From Immediate Carrying +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + ALU_BEGIN(*rA); + ALU_NOT; + ALU_ADD(EXTS(SI)); + ALU_ADD(1); + ALU_END(*rT, 1/*CA*/, 0/*OE*/, 0/*Rc*/); + PPC_INSN_INT(RT_BITMASK, RA_BITMASK, 0/*Rc*/); + +0.31,6.RT,11.RA,16.RB,21.OE,22.10,31.Rc:XO:::Add Carrying +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + ALU_BEGIN(*rA); + ALU_ADD(*rB); + ALU_END(*rT, 1/*CA*/, OE, Rc); + PPC_INSN_INT(RT_BITMASK, RA_BITMASK | RB_BITMASK, Rc); + +0.31,6.RT,11.RA,16.RB,21.OE,22.8,31.Rc:XO:::Subtract From Carrying +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + /* RT <- ~RA + RB + 1 === RT <- RB - RA */ + ALU_BEGIN(*rA); + ALU_NOT; + ALU_ADD(*rB); + ALU_ADD(1); + ALU_END(*rT, 1/*CA*/, OE, Rc); + PPC_INSN_INT(RT_BITMASK, RA_BITMASK | RB_BITMASK, Rc); + +0.31,6.RT,11.RA,16.RB,21.OE,22.138,31.Rc:XO:::Add Extended +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + ALU_BEGIN(*rA); + ALU_ADD(*rB); + ALU_ADD_CA; + ALU_END(*rT, 1/*CA*/, OE, Rc); + PPC_INSN_INT(RT_BITMASK, RA_BITMASK | RB_BITMASK, Rc); + +0.31,6.RT,11.RA,16.RB,21.OE,22.136,31.Rc:XO:::Subtract From Extended +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + ALU_BEGIN(*rA); + ALU_NOT; + ALU_ADD(*rB); + ALU_ADD_CA; + ALU_END(*rT, 1/*CA*/, OE, Rc); + PPC_INSN_INT(RT_BITMASK, RA_BITMASK | RB_BITMASK, Rc); + +0.31,6.RT,11.RA,16./,21.OE,22.234,31.Rc:XO:::Add to Minus One Extended +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + ALU_BEGIN(*rA); + ALU_ADD_CA; + ALU_ADD(-1); + ALU_END(*rT, 1/*CA*/, OE, Rc); + PPC_INSN_INT(RT_BITMASK, RA_BITMASK, Rc); + +0.31,6.RT,11.RA,16./,21.OE,22.232,31.Rc:XO:::Subtract From Minus One Extended +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + ALU_BEGIN(*rA); + ALU_NOT; + ALU_ADD_CA; + ALU_ADD(-1); + ALU_END(*rT, 1/*CA*/, OE, Rc); + PPC_INSN_INT(RT_BITMASK, RA_BITMASK, Rc); + +0.31,6.RT,11.RA,16./,21.OE,22.202,31.Rc:XO::addze:Add to Zero Extended +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + ALU_BEGIN(*rA); + ALU_ADD_CA; + ALU_END(*rT, 1/*CA*/, OE, Rc); + PPC_INSN_INT(RT_BITMASK, RA_BITMASK, Rc); + +0.31,6.RT,11.RA,16./,21.OE,22.200,31.Rc:XO:::Subtract from Zero Extended +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + ALU_BEGIN(*rA); + ALU_NOT; + ALU_ADD_CA; + ALU_END(*rT, 1/*CA*/, OE, Rc); + PPC_INSN_INT(RT_BITMASK, RA_BITMASK, Rc); + +0.31,6.RT,11.RA,16./,21.OE,22.104,31.Rc:XO:::Negate +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + ALU_BEGIN(*rA); + ALU_NOT; + ALU_ADD(1); + ALU_END(*rT,0/*CA*/,OE,Rc); + PPC_INSN_INT(RT_BITMASK, RA_BITMASK, Rc); + +0.7,6.RT,11.RA,16.SI:D::mulli:Multiply Low Immediate +*601: PPC_UNIT_IU, PPC_UNIT_IU, 5, 5, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 3, 3, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 3, 3, 0 +*604: PPC_UNIT_MCIU, PPC_UNIT_MCIU, 3, 3, 0 + signed_word prod = *rA * EXTS(SI); + *rT = prod; + PPC_INSN_INT(RT_BITMASK, RA_BITMASK, 0/*Rc*/); + +0.31,6.RT,11.RA,16.RB,21.OE,22.233,31.Rc:D:64::Multiply Low Doubleword + +0.31,6.RT,11.RA,16.RB,21.OE,22.235,31.Rc:XO::mullw:Multiply Low Word +*601: PPC_UNIT_IU, PPC_UNIT_IU, 5, 5, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 5, 5, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 5, 5, 0 +*604: PPC_UNIT_MCIU, PPC_UNIT_MCIU, 4, 4, 0 + signed64 a = (signed32)(*rA); + signed64 b = (signed32)(*rB); + signed64 prod = a * b; + signed_word t = prod; + *rT = *rA * *rB; + if (t != prod && OE) + XER |= (xer_overflow | xer_summary_overflow); + CR0_COMPARE(t, 0, Rc); + PPC_INSN_INT(RT_BITMASK, RA_BITMASK | RB_BITMASK, Rc); + +0.31,6.RT,11.RA,16.RB,21./,22.73,31.Rc:XO:64::Multiply High Doubleword + +0.31,6.RT,11.RA,16.RB,21./,22.75,31.Rc:XO::mulhw:Multiply High Word +*601: PPC_UNIT_IU, PPC_UNIT_IU, 5, 5, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 5, 5, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 5, 5, 0 +*604: PPC_UNIT_MCIU, PPC_UNIT_MCIU, 4, 4, 0 + signed64 a = (signed32)(*rA); + signed64 b = (signed32)(*rB); + signed64 prod = a * b; + signed_word t = EXTRACTED64(prod, 0, 31); + *rT = t; + CR0_COMPARE(t, 0, Rc); + PPC_INSN_INT(RT_BITMASK, RA_BITMASK | RB_BITMASK, Rc); + +0.31,6.RT,11.RA,16.RB,21./,22.9,31.Rc:XO:64::Multiply High Doubleword Unsigned + +0.31,6.RT,11.RA,16.RB,21./,22.11,31.Rc:XO::mulhwu:Multiply High Word Unsigned +*601: PPC_UNIT_IU, PPC_UNIT_IU, 10, 10, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 6, 6, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 6, 6, 0 +*604: PPC_UNIT_MCIU, PPC_UNIT_MCIU, 4, 4, 0 + unsigned64 a = (unsigned32)(*rA); + unsigned64 b = (unsigned32)(*rB); + unsigned64 prod = a * b; + signed_word t = EXTRACTED64(prod, 0, 31); + *rT = t; + CR0_COMPARE(t, 0, Rc); + PPC_INSN_INT(RT_BITMASK, RA_BITMASK | RB_BITMASK, Rc); + +0.31,6.RT,11.RA,16.RB,21.OE,22.489,31.Rc:XO:64::Divide Doubleword + +0.31,6.RT,11.RA,16.RB,21.OE,22.491,31.Rc:XO::divw:Divide Word +*601: PPC_UNIT_IU, PPC_UNIT_IU, 36, 36, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 37, 37, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 37, 37, 0 +*604: PPC_UNIT_MCIU, PPC_UNIT_MCIU, 20, 20, 0 + signed64 dividend = (signed32)(*rA); + signed64 divisor = (signed32)(*rB); + if (divisor == 0 /* nb 0x8000..0 is sign extended */ + || (dividend == 0x80000000 && divisor == -1)) { + if (OE) + XER |= (xer_overflow | xer_summary_overflow); + CR0_COMPARE(0, 0, Rc); + } + else { + signed64 quotent = dividend / divisor; + *rT = quotent; + CR0_COMPARE((signed_word)quotent, 0, Rc); + } + PPC_INSN_INT(RT_BITMASK, RA_BITMASK | RB_BITMASK, Rc); + +0.31,6.RT,11.RA,16.RB,21.OE,22.457,31.Rc:XO:64::Divide Doubleword Unsigned + +0.31,6.RT,11.RA,16.RB,21.OE,22.459,31.Rc:XO::divwu:Divide Word Unsigned +*601: PPC_UNIT_IU, PPC_UNIT_IU, 36, 36, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 37, 37, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 37, 37, 0 +*604: PPC_UNIT_MCIU, PPC_UNIT_MCIU, 20, 20, 0 + unsigned64 dividend = (unsigned32)(*rA); + unsigned64 divisor = (unsigned32)(*rB); + if (divisor == 0) { + if (OE) + XER |= (xer_overflow | xer_summary_overflow); + CR0_COMPARE(0, 0, Rc); + } + else { + unsigned64 quotent = dividend / divisor; + *rT = quotent; + CR0_COMPARE((signed_word)quotent, 0, Rc); + } + PPC_INSN_INT(RT_BITMASK, RA_BITMASK | RB_BITMASK, Rc); + + +# +# I.3.3.10 Fixed-Point Compare Instructions +# + +0.11,6.BF,9./,10.L,11.RA,16.SI:D:::Compare Immediate +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_SRU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + if (!is_64bit_mode && L) + program_interrupt(processor, cia, + illegal_instruction_program_interrupt); + else { + signed_word a; + signed_word b = EXTS(SI); + if (L == 0) + a = EXTENDED(*rA); + else + a = *rA; + CR_COMPARE(BF, a, b); + } + PPC_INSN_INT_CR(0, RA_BITMASK, BF_BITMASK); + +0.31,6.BF,9./,10.L,11.RA,16.RB,21.0,31./:X:::Compare +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_SRU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + if (!is_64bit_mode && L) + program_interrupt(processor, cia, + illegal_instruction_program_interrupt); + else { + signed_word a; + signed_word b; + if (L == 0) { + a = EXTENDED(*rA); + b = EXTENDED(*rB); + } + else { + a = *rA; + b = *rB; + } + CR_COMPARE(BF, a, b); + } + PPC_INSN_INT_CR(0, RA_BITMASK | RB_BITMASK, BF_BITMASK); + +0.10,6.BF,9./,10.L,11.RA,16.UI:D:::Compare Logical Immediate +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_SRU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + if (!is_64bit_mode && L) + program_interrupt(processor, cia, + illegal_instruction_program_interrupt); + else { + unsigned_word a; + unsigned_word b = UI; + if (L == 0) + a = MASKED(*rA, 32, 63); + else + a = *rA; + CR_COMPARE(BF, a, b); + } + PPC_INSN_INT_CR(0, RA_BITMASK, BF_BITMASK); + +0.31,6.BF,9./,10.L,11.RA,16.RB,21.32,31./:X:::Compare Logical +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_SRU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + if (!is_64bit_mode && L) + program_interrupt(processor, cia, + illegal_instruction_program_interrupt); + else { + unsigned_word a; + unsigned_word b; + if (L == 0) { + a = MASKED(*rA, 32, 63); + b = MASKED(*rB, 32, 63); + } + else { + a = *rA; + b = *rB; + } + CR_COMPARE(BF, a, b); + } + PPC_INSN_INT_CR(0, RA_BITMASK | RB_BITMASK, BF_BITMASK); + + +# +# I.3.3.11 Fixed-Point Trap Instructions +# + +0.2,6.TO,11.RA,16.SI:D:64::Trap Doubleword Immediate + if (!is_64bit_mode) + program_interrupt(processor, cia, + illegal_instruction_program_interrupt); + else { + signed_word a = *rA; + signed_word b = EXTS(SI); + if ((a < b && TO{0}) + || (a > b && TO{1}) + || (a == b && TO{2}) + || ((unsigned_word)a < (unsigned_word)b && TO{3}) + || ((unsigned_word)a > (unsigned_word)b && TO{4}) + ) + program_interrupt(processor, cia, + trap_program_interrupt); + } + +0.3,6.TO,11.RA,16.SI:D:::Trap Word Immediate +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 2, 2, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 2, 2, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + signed_word a = EXTENDED(*rA); + signed_word b = EXTS(SI); + if ((a < b && TO{0}) + || (a > b && TO{1}) + || (a == b && TO{2}) + || ((unsigned_word)a < (unsigned_word)b && TO{3}) + || ((unsigned_word)a > (unsigned_word)b && TO{4}) + ) + program_interrupt(processor, cia, + trap_program_interrupt); + +0.31,6.TO,11.RA,16.RB,21.68,31./:X:64::Trap Doubleword + if (!is_64bit_mode) + program_interrupt(processor, cia, + illegal_instruction_program_interrupt); + else { + signed_word a = *rA; + signed_word b = *rB; + if ((a < b && TO{0}) + || (a > b && TO{1}) + || (a == b && TO{2}) + || ((unsigned_word)a < (unsigned_word)b && TO{3}) + || ((unsigned_word)a > (unsigned_word)b && TO{4}) + ) + program_interrupt(processor, cia, + trap_program_interrupt); + } + +0.31,6.TO,11.RA,16.RB,21.4,31./:X:::Trap Word +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 2, 2, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 2, 2, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + signed_word a = EXTENDED(*rA); + signed_word b = EXTENDED(*rB); + if (TO == 12 && rA == rB) { + ITRACE(trace_breakpoint, ("breakpoint\n")); + cpu_halt(processor, cia, was_trap, 0); + } + else if ((a < b && TO{0}) + || (a > b && TO{1}) + || (a == b && TO{2}) + || ((unsigned_word)a < (unsigned_word)b && TO{3}) + || ((unsigned_word)a > (unsigned_word)b && TO{4}) + ) + program_interrupt(processor, cia, + trap_program_interrupt); + +# +# I.3.3.12 Fixed-Point Logical Instructions +# + +0.28,6.RS,11.RA,16.UI:D:::AND Immediate +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + *rA = *rS & UI; + CR0_COMPARE(*rA, 0, 1/*Rc*/); + ITRACE(trace_alu, (" Result = %ld (0x%lx)\n", (long)*rA, (long)*rA)); + PPC_INSN_INT(RA_BITMASK, RS_BITMASK, 1/*Rc*/); + +0.29,6.RS,11.RA,16.UI:D:::AND Immediate Shifted +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + *rA = *rS & (UI << 16); + CR0_COMPARE(*rA, 0, 1/*Rc*/); + ITRACE(trace_alu, (" Result = %ld (0x%lx)\n", (long)*rA, (long)*rA)); + PPC_INSN_INT(RA_BITMASK, RS_BITMASK, 1/*Rc*/); + +0.24,6.RS,11.RA,16.UI:D:::OR Immediate +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + *rA = *rS | UI; + ITRACE(trace_alu, (" Result = %ld (0x%lx)\n", (long)*rA, (long)*rA)); + PPC_INSN_INT(RA_BITMASK, RS_BITMASK, 0/*Rc*/); + +0.25,6.RS,11.RA,16.UI:D:::OR Immediate Shifted +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + *rA = *rS | (UI << 16); + ITRACE(trace_alu, (" Result = %ld (0x%lx)\n", (long)*rA, (long)*rA)); + PPC_INSN_INT(RA_BITMASK, RS_BITMASK, 0/*Rc*/); + +0.26,6.RS,11.RA,16.UI:D:::XOR Immediate +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + *rA = *rS ^ UI; + ITRACE(trace_alu, (" Result = %ld (0x%lx)\n", (long)*rA, (long)*rA)); + PPC_INSN_INT(RA_BITMASK, RS_BITMASK, 0/*Rc*/); + +0.27,6.RS,11.RA,16.UI:D:::XOR Immediate Shifted +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + *rA = *rS ^ (UI << 16); + ITRACE(trace_alu, (" Result = %ld (0x%lx)\n", (long)*rA, (long)*rA)); + PPC_INSN_INT(RA_BITMASK, RS_BITMASK, 0/*Rc*/); + +0.31,6.RS,11.RA,16.RB,21.28,31.Rc:X:::AND +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + *rA = *rS & *rB; + CR0_COMPARE(*rA, 0, Rc); + ITRACE(trace_alu, (" Result = %ld (0x%lx)\n", (long)*rA, (long)*rA)); + PPC_INSN_INT(RA_BITMASK, RS_BITMASK | RB_BITMASK, Rc); + +0.31,6.RS,11.RA,16.RB,21.444,31.Rc:X:::OR +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + *rA = *rS | *rB; + CR0_COMPARE(*rA, 0, Rc); + ITRACE(trace_alu, (" Result = %ld (0x%lx)\n", (long)*rA, (long)*rA)); + PPC_INSN_INT(RA_BITMASK, RS_BITMASK | RB_BITMASK, Rc); + +0.31,6.RS,11.RA,16.RB,21.316,31.Rc:X:::XOR +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + *rA = *rS ^ *rB; + CR0_COMPARE(*rA, 0, Rc); + ITRACE(trace_alu, (" Result = %ld (0x%lx)\n", (long)*rA, (long)*rA)); + PPC_INSN_INT(RA_BITMASK, RS_BITMASK | RB_BITMASK, Rc); + +0.31,6.RS,11.RA,16.RB,21.476,31.Rc:X:::NAND +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + *rA = ~(*rS & *rB); + CR0_COMPARE(*rA, 0, Rc); + ITRACE(trace_alu, (" Result = %ld (0x%lx)\n", (long)*rA, (long)*rA)); + PPC_INSN_INT(RA_BITMASK, RS_BITMASK | RB_BITMASK, Rc); + +0.31,6.RS,11.RA,16.RB,21.124,31.Rc:X:::NOR +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + *rA = ~(*rS | *rB); + CR0_COMPARE(*rA, 0, Rc); + ITRACE(trace_alu, (" Result = %ld (0x%lx)\n", (long)*rA, (long)*rA)); + PPC_INSN_INT(RA_BITMASK, RS_BITMASK | RB_BITMASK, Rc); + +0.31,6.RS,11.RA,16.RB,21.284,31.Rc:X:::Equivalent +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + *rA = ~(*rS ^ *rB); /* A === B */ + CR0_COMPARE(*rA, 0, Rc); + ITRACE(trace_alu, (" Result = %ld (0x%lx)\n", (long)*rA, (long)*rA)); + PPC_INSN_INT(RA_BITMASK, RS_BITMASK | RB_BITMASK, Rc); + +0.31,6.RS,11.RA,16.RB,21.60,31.Rc:X:::AND with Complement +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + *rA = *rS & ~*rB; + CR0_COMPARE(*rA, 0, Rc); + ITRACE(trace_alu, (" Result = %ld (0x%lx)\n", (long)*rA, (long)*rA)); + PPC_INSN_INT(RA_BITMASK, RS_BITMASK | RB_BITMASK, Rc); + +0.31,6.RS,11.RA,16.RB,21.412,31.Rc:X:::OR with Complement +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + *rA = *rS | ~*rB; + CR0_COMPARE(*rA, 0, Rc); + ITRACE(trace_alu, (" Result = %ld (0x%lx)\n", (long)*rA, (long)*rA)); + PPC_INSN_INT(RA_BITMASK, RS_BITMASK | RB_BITMASK, Rc); + +0.31,6.RS,11.RA,16./,21.954,31.Rc:X::extsb:Extend Sign Byte +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + *rA = (signed_word)(signed8)*rS; + CR0_COMPARE(*rA, 0, Rc); + ITRACE(trace_alu, (" Result = %ld (0x%lx)\n", (long)*rA, (long)*rA)); + PPC_INSN_INT(RA_BITMASK, RS_BITMASK, Rc); + +0.31,6.RS,11.RA,16./,21.922,31.Rc:X::extsh:Extend Sign Half Word +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + *rA = (signed_word)(signed16)*rS; + CR0_COMPARE(*rA, 0, Rc); + ITRACE(trace_alu, (" Result = %ld (0x%lx)\n", (long)*rA, (long)*rA)); + PPC_INSN_INT(RA_BITMASK, RS_BITMASK, Rc); + +0.31,6.RS,11.RA,16./,21.986,31.Rc:X:64::Extend Sign Word +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 +# *rA = (signed_word)(signed32)*rS; +# CR0_COMPARE(*rA, 0, Rc); + +0.31,6.RS,11.RA,16./,21.58,31.Rc:X:64::Count Leading Zeros Doubleword +# int count = 0; +# unsigned64 mask = BIT64(0); +# unsigned64 source = *rS; +# while (!(source & mask) && mask != 0) { +# mask >>= 1; +# count++; +# } +# *rA = count; +# CR0_COMPARE(count, 0, Rc); /* FIXME - is this correct */ + +0.31,6.RS,11.RA,16./,21.26,31.Rc:X:::Count Leading Zeros Word +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + int count = 0; + unsigned32 mask = BIT32(0); + unsigned32 source = *rS; + while (!(source & mask) && mask != 0) { + mask >>= 1; + count++; + } + *rA = count; + ITRACE(trace_alu, (" Result = %ld (0x%lx)\n", (long)*rA, (long)*rA)); + CR0_COMPARE(count, 0, Rc); /* FIXME - is this correct */ + + +# +# I.3.3.13 Fixed-Point Rotate and Shift Instructions +# + +0.30,6.RS,11.RA,16.sh_0_4,21.mb,27.0,30.sh_5,31.Rc:MD:64::Rotate Left Doubleword Immediate then Clear Left +# long n = (sh_5 << 4) | sh_0_4; +# unsigned_word r = ROTL64(*rS, n); +# long b = (mb_5 << 4) | mb_0_4; +# unsigned_word m = MASK(b, 63); +# signed_word result = r & m; +# *rA = result; +# ITRACE(trace_alu, (" Result = %ld (0x%lx)\n", (long)*rA, (long)*rA)); +# CR0_COMPARE(result, 0, Rc); /* FIXME - is this correct */ + +0.30,6.RS,11.RA,16.sh_0_4,21.me,27.1,30.sh_5,31.Rc:MD:64::Rotate Left Doubleword Immediate then Clear Right +# long n = (sh_5 << 4) | sh_0_4; +# unsigned_word r = ROTL64(*rS, n); +# long e = (me_5 << 4) | me_0_4; +# unsigned_word m = MASK(0, e); +# signed_word result = r & m; +# *rA = result; +# CR0_COMPARE(result, 0, Rc); /* FIXME - is this correct */ + +0.30,6.RS,11.RA,16.sh_0_4,21.mb,27.2,30.sh_5,31.Rc:MD:64::Rotate Left Doubleword Immediate then Clear +# long n = (sh_5 << 4) | sh_0_4; +# unsigned_word r = ROTL64(*rS, n); +# long b = (mb_5 << 4) | mb_0_4; +# unsigned_word m = MASK(0, (64-n)); +# signed_word result = r & m; +# *rA = result; +# CR0_COMPARE(result, 0, Rc); /* FIXME - is this correct */ + +0.21,6.RS,11.RA,16.SH,21.MB,26.ME,31.Rc:M:::Rotate Left Word Immediate then AND with Mask +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + long n = SH; + unsigned32 s = *rS; + unsigned32 r = ROTL32(s, n); + unsigned32 m = MASK(MB+32, ME+32); + signed_word result = r & m; + *rA = result; + CR0_COMPARE(result, 0, Rc); + ITRACE(trace_alu, + ("n=%ld, s=0x%lx, r=0x%lx, m=0x%lx, result=0x%lx, cr=0x%lx\n", + n, (unsigned long)s, (unsigned long)r, (unsigned long)m, + (unsigned long)result, (unsigned long)CR)); + PPC_INSN_INT(RA_BITMASK, RS_BITMASK, Rc); + +0.30,6.RS,11.RA,16.RB,21.mb,27.8,31.Rc:MDS:64::Rotate Left Doubleword then Clear Left +# long n = MASKED(*rB, 58, 63); +# unsigned_word r = ROTL64(*rS, n); +# long b = (mb_5 << 4) | mb_0_4; +# unsigned_word m = MASK(b, 63); +# signed_word result = r & m; +# *rA = result; +# CR0_COMPARE(result, 0, Rc); + +0.30,6.RS,11.RA,16.RB,21.me,27.9,31.Rc:MDS:64::Rotate Left Doubleword then Clear Right +# long n = MASKED(*rB, 58, 63); +# unsigned_word r = ROTL64(*rS, n); +# long e = (me_5 << 4) | me_0_4; +# unsigned_word m = MASK(0, e); +# signed_word result = r & m; +# *rA = result; +# CR0_COMPARE(result, 0, Rc); + +0.23,6.RS,11.RA,16.RB,21.MB,26.ME,31.Rc:M:::Rotate Left Word then AND with Mask + long n = MASKED(*rB, 59, 63); + unsigned32 r = ROTL32(*rS, n); + unsigned32 m = MASK(MB+32, ME+32); + signed_word result = r & m; + *rA = result; + CR0_COMPARE(result, 0, Rc); + +0.30,6.RS,11.RA,16.sh_0_4,21.mb,27.3,30.sh_5,31.Rc:MD:64::Rotate Left Doubleword Immediate then Mask Insert +# long n = (sh_5 << 4) | sh_0_4; +# unsigned_word r = ROTL64(*rS, n); +# long b = (mb_5 << 4) | mb_0_4; +# unsigned_word m = MASK(b, (64-n)); +# signed_word result = (r & m) | (*rA & ~m) +# *rA = result; +# CR0_COMPARE(result, 0, Rc); + +0.20,6.RS,11.RA,16.SH,21.MB,26.ME,31.Rc:M::rlwimi:Rotate Left Word Immediate then Mask Insert +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + long n = SH; + unsigned32 r = ROTL32(*rS, n); + unsigned32 m = MASK(MB+32, ME+32); + signed_word result = (r & m) | (*rA & ~m); + *rA = result; + ITRACE(trace_alu, (": n=%ld *rS=0x%lx r=0x%lx m=0x%lx result=0x%lx\n", + n, (unsigned long)*rS, (unsigned long)r, (unsigned long)m, + (unsigned long)result)); + CR0_COMPARE(result, 0, Rc); + PPC_INSN_INT(RA_BITMASK, RS_BITMASK, Rc); + + +0.31,6.RS,11.RA,16.RB,21.27,31.Rc:X:64::Shift Left Doubleword + +0.31,6.RS,11.RA,16.RB,21.24,31.Rc:X:::Shift Left Word +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + int n = MASKED(*rB, 58, 63); + unsigned32 source = *rS; + signed_word shifted; + if (n < 32) + shifted = (source << n); + else + shifted = 0; + *rA = shifted; + CR0_COMPARE(shifted, 0, Rc); + ITRACE(trace_alu, + ("n=%d, source=0x%lx, shifted=0x%lx\n", + n, (unsigned long)source, (unsigned long)shifted)); + PPC_INSN_INT(RA_BITMASK, RS_BITMASK, Rc); + +0.31,6.RS,11.RA,16.RB,21.539,31.Rc:X:64::Shift Right Doubleword + +0.31,6.RS,11.RA,16.RB,21.536,31.Rc:X:::Shift Right Word +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + int n = MASKED(*rB, 58, 63); + unsigned32 source = *rS; + signed_word shifted; + if (n < 32) + shifted = (source >> n); + else + shifted = 0; + *rA = shifted; + CR0_COMPARE(shifted, 0, Rc); + ITRACE(trace_alu, \ + ("n=%d, source=0x%lx, shifted=0x%lx\n", + n, (unsigned long)source, (unsigned long)shifted)); + PPC_INSN_INT(RA_BITMASK, RS_BITMASK, Rc); + +0.31,6.RS,11.RA,16.sh_0_4,21.413,30.sh_5,31.Rc:XS:64::Shift Right Algebraic Doubleword Immediate + +0.31,6.RS,11.RA,16.SH,21.824,31.Rc:X:::Shift Right Algebraic Word Immediate +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + int n = SH; + signed_word r = ROTL32(*rS, /*64*/32-n); + signed_word m = MASK(n+32, 63); + int S = MASKED(*rS, 32, 32); + signed_word shifted = (r & m) | (S ? ~m : 0); + *rA = shifted; + if (S && ((r & ~m) & MASK(32, 63)) != 0) + XER |= xer_carry; + else + XER &= ~xer_carry; + CR0_COMPARE(shifted, 0, Rc); + ITRACE(trace_alu, (" Result = %ld (0x%lx), XER = %ld\n", + (long)*rA, (long)*rA, (long)XER)); + PPC_INSN_INT(RA_BITMASK, RS_BITMASK, Rc); + +0.31,6.RS,11.RA,16.RB,21.794,31.Rc:X:64::Shift Right Algebraic Doubleword + +0.31,6.RS,11.RA,16.RB,21.792,31.Rc:X:::Shift Right Algebraic Word +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603e:PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*604: PPC_UNIT_SCIU1, PPC_UNIT_SCIU2, 1, 1, 0 + unsigned64 mask; + int n = MASKED(*rB, 59, 63); + signed32 source = (signed32)*rS; /* signed to keep sign bit */ + signed32 shifted = source >> n; + int S = (MASKED(*rS,32,32) != 0); + signed64 r = ((unsigned64) source); + r = ((unsigned64) source) << 32 | (unsigned32) source; + r = ROTL64(r,64-n); + if (MASKED(*rB,58,58) == 0) + mask = (unsigned64) MASK64(n+32,63); + else + mask = (unsigned64) 0; + *rA = (signed_word) (r & mask | ((signed64) -1*S) & ~mask); /* if 64bit will sign extend */ + if (S && (MASKED(r & ~mask,32,63)!=0)) + XER |= xer_carry; + else + XER &= ~xer_carry; + CR0_COMPARE(*rA, 0, Rc); + ITRACE(trace_alu, (" Result = %ld (0x%lx), XER = %ld\n", + (long)*rA, (long)*rA, (long)XER)); + PPC_INSN_INT(RA_BITMASK, RS_BITMASK, Rc); + +# +# I.3.3.14 Move to/from System Register Instructions +# + +0.31,6.RS,11.SPR,21.467,31./:XFX::mtspr %SPR, %RS:Move to Special Purpose Register +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_SRU, PPC_UNIT_SRU, 2, 2, 0 +*603e:PPC_UNIT_SRU, PPC_UNIT_SRU, 2, 2, 0 +*604: PPC_UNIT_MCIU, PPC_UNIT_MCIU, 1, 1, 0 + int n = (SPR{5:9} << 5) | SPR{0:4}; + if (SPR{0} && IS_PROBLEM_STATE(processor)) + program_interrupt(processor, cia, + privileged_instruction_program_interrupt); + else if (!spr_is_valid(n) + || spr_is_readonly(n)) + program_interrupt(processor, cia, + illegal_instruction_program_interrupt); + else { + spreg new_val = (spr_length(n) == 64 + ? *rS + : MASKED(*rS, 32, 63)); + /* HACK - time base registers need to be updated immediatly */ + if (WITH_TIME_BASE) { + switch (n) { + case spr_tbu: + cpu_set_time_base(processor, + (MASKED64(cpu_get_time_base(processor), 32, 63) + | INSERTED64(new_val, 0, 31))); + break; + case spr_tbl: + cpu_set_time_base(processor, + (MASKED64(cpu_get_time_base(processor), 0, 31) + | INSERTED64(new_val, 32, 63))); + break; + case spr_dec: + cpu_set_decrementer(processor, new_val); + break; + default: + SPREG(n) = new_val; + break; + } + } + else { + SPREG(n) = new_val; + } + } + PPC_INSN_TO_SPR(RS_BITMASK, n); + +0.31,6.RT,11.SPR,21.339,31./:XFX::mfspr %RT, %SPR:Move from Special Purpose Register +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_SRU, PPC_UNIT_SRU, 1, 1, 0 +*603e:PPC_UNIT_SRU, PPC_UNIT_SRU, 1, 1, 0 +*604: PPC_UNIT_MCIU, PPC_UNIT_MCIU, 3, 3, 0 + int n = (SPR{5:9} << 5) | SPR{0:4}; + if (SPR{0} && IS_PROBLEM_STATE(processor)) + program_interrupt(processor, cia, + privileged_instruction_program_interrupt); + else if (!spr_is_valid(n)) + program_interrupt(processor, cia, + illegal_instruction_program_interrupt); + else { + /* HACK - time base registers need to be calculated */ + if (WITH_TIME_BASE) { + switch (n) { + case spr_dec: + *rT = cpu_get_decrementer(processor); + break; + case spr_tbu: + case spr_tbl: + /* NOTE - these SPR's are not readable. Use mftb[ul] */ + default: + *rT = SPREG(n); + break; + } + } + else { + *rT = SPREG(n); + } + } + PPC_INSN_FROM_SPR(RT_BITMASK, n); + +0.31,6.RS,11./,12.FXM,20./,21.144,31./:XFX::mtfcr:Move to Condition Register Fields +*601: PPC_UNIT_IU, PPC_UNIT_IU, 2, 2, 0 +*603: PPC_UNIT_SRU, PPC_UNIT_SRU, 1, 1, 0 +*603e:PPC_UNIT_SRU, PPC_UNIT_SRU, 1, 1, 0 +*604: PPC_UNIT_MCIU, PPC_UNIT_MCIU, 1, 1, 0 + if (FXM == 0xff) { + CR = *rS; + } + else { + unsigned_word mask = 0; + unsigned_word f; + for (f = 0; f < 8; f++) { + if (FXM & (0x80 >> f)) + mask |= (0xf << 4*(7-f)); + } + CR = (MASKED(*rS, 32, 63) & mask) | (CR & ~mask); + } + PPC_INSN_MTCR(RS_BITMASK, FXM); + +0.31,6.BF,9./,11./,16./,21.512,31./:X:::Move to Condition Register from XER +# CR_SET(BF, EXTRACTED32(XER, 0, 3)); +# MBLIT32(XER, 0, 3, 0); + +0.31,6.RT,11./,16./,21.19,31./:X:::Move From Condition Register +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_SRU, PPC_UNIT_SRU, 1, 1, 0 +*603e:PPC_UNIT_SRU, PPC_UNIT_SRU, 1, 1, 0 +*604: PPC_UNIT_MCIU, PPC_UNIT_MCIU, 3, 3, 0 + *rT = (unsigned32)CR; + PPC_INSN_MFCR(RT_BITMASK); + +# +# I.4.6.2 Floating-Point Load Instructions +# + +0.48,6.FRT,11.RA,16.D:D:f:lfs:Load Floating-Point Single +*601: PPC_UNIT_IU, PPC_UNIT_IU, 3, 3, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 3, 0 + unsigned_word b; + unsigned_word EA; + if (RA_is_0) b = 0; + else b = *rA; + EA = b + EXTS(D); + *frT = DOUBLE(MEM(unsigned, EA, 4)); + PPC_INSN_INT_FLOAT(0, FRT_BITMASK, (RA_BITMASK & ~1), 0); + +0.31,6.FRT,11.RA,16.RB,21.535,31./:X:f::Load Floating-Point Single Indexed +*601: PPC_UNIT_IU, PPC_UNIT_IU, 3, 3, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 3, 0 + unsigned_word b; + unsigned_word EA; + if (RA_is_0) b = 0; + else b = *rA; + EA = b + *rB; + *frT = DOUBLE(MEM(unsigned, EA, 4)); + PPC_INSN_INT_FLOAT(0, FRT_BITMASK, (RA_BITMASK & ~1) | RB_BITMASK, 0); + +0.49,6.FRT,11.RA,16.D:D:f::Load Floating-Point Single with Update +*601: PPC_UNIT_IU, PPC_UNIT_IU, 3, 3, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 3, 0 + unsigned_word EA; + if (RA_is_0) + program_interrupt(processor, cia, + illegal_instruction_program_interrupt); + EA = *rA + EXTS(D); + *frT = DOUBLE(MEM(unsigned, EA, 4)); + *rA = EA; + PPC_INSN_INT_FLOAT(RA_BITMASK, FRT_BITMASK, (RA_BITMASK & ~1), 0); + +0.31,6.FRT,11.RA,16.RB,21.576,31./:X:f::Load Floating-Point Single with Update Indexed +*601: PPC_UNIT_IU, PPC_UNIT_IU, 3, 3, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 3, 0 + unsigned_word EA; + if (RA_is_0) + program_interrupt(processor, cia, + illegal_instruction_program_interrupt); + EA = *rA + *rB; + *frT = DOUBLE(MEM(unsigned, EA, 4)); + *rA = EA; + PPC_INSN_INT_FLOAT(RA_BITMASK, FRT_BITMASK, (RA_BITMASK & ~1) | RB_BITMASK, 0); + +0.50,6.FRT,11.RA,16.D:D:f::Load Floating-Point Double +*601: PPC_UNIT_IU, PPC_UNIT_IU, 3, 3, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 3, 0 + unsigned_word b; + unsigned_word EA; + if (RA_is_0) b = 0; + else b = *rA; + EA = b + EXTS(D); + *frT = MEM(unsigned, EA, 8); + PPC_INSN_INT_FLOAT(0, FRT_BITMASK, (RA_BITMASK & ~1), 0); + +0.31,6.FRT,11.RA,16.RB,21.599,31./:X:f::Load Floating-Point Double Indexed +*601: PPC_UNIT_IU, PPC_UNIT_IU, 3, 3, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 3, 0 + unsigned_word b; + unsigned_word EA; + if (RA_is_0) b = 0; + else b = *rA; + EA = b + *rB; + *frT = MEM(unsigned, EA, 8); + PPC_INSN_INT_FLOAT(0, FRT_BITMASK, (RA_BITMASK & ~1) | RB_BITMASK, 0); + +0.51,6.FRT,11.RA,16.D:D:f::Load Floating-Point Double with Update +*601: PPC_UNIT_IU, PPC_UNIT_IU, 3, 3, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 3, 0 + unsigned_word EA; + if (RA_is_0) + program_interrupt(processor, cia, + illegal_instruction_program_interrupt); + EA = *rA + EXTS(D); + *frT = MEM(unsigned, EA, 8); + *rA = EA; + PPC_INSN_INT_FLOAT(RA_BITMASK, FRT_BITMASK, (RA_BITMASK & ~1), 0); + +0.31,6.FRT,11.RA,16.RB,21.631,31./:X:f::Load Floating-Point Double with Update Indexed +*601: PPC_UNIT_IU, PPC_UNIT_IU, 3, 3, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 3, 0 + unsigned_word EA; + if (RA_is_0) + program_interrupt(processor, cia, + illegal_instruction_program_interrupt); + EA = *rA + *rB; + *frT = MEM(unsigned, EA, 8); + *rA = EA; + PPC_INSN_INT_FLOAT(RA_BITMASK, FRT_BITMASK, (RA_BITMASK & ~1) | RB_BITMASK, 0); + + +# +# I.4.6.3 Floating-Point Store Instructions +# + +0.52,6.FRS,11.RA,16.D:D:f::Store Floating-Point Single +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 3, 0 + unsigned_word b; + unsigned_word EA; + if (RA_is_0) b = 0; + else b = *rA; + EA = b + EXTS(D); + STORE(EA, 4, SINGLE(*frS)); + PPC_INSN_INT_FLOAT(0, 0, (RA_BITMASK & ~1), FRS_BITMASK); + +0.31,6.FRS,11.RA,16.RB,21.663,31./:X:f::Store Floating-Point Single Indexed +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 3, 0 + unsigned_word b; + unsigned_word EA; + if (RA_is_0) b = 0; + else b = *rA; + EA = b + *rB; + STORE(EA, 4, SINGLE(*frS)); + PPC_INSN_INT_FLOAT(0, 0, (RA_BITMASK & ~1) | RB_BITMASK, FRS_BITMASK); + +0.53,6.FRS,11.RA,16.D:D:f::Store Floating-Point Single with Update +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 3, 0 + unsigned_word EA; + if (RA_is_0) + program_interrupt(processor, cia, + illegal_instruction_program_interrupt); + EA = *rA + EXTS(D); + STORE(EA, 4, SINGLE(*frS)); + *rA = EA; + PPC_INSN_INT_FLOAT(RA_BITMASK, 0, (RA_BITMASK & ~1), FRS_BITMASK); + +0.31,6.FRS,11.RA,16.RB,21.695,31./:X:f::Store Floating-Point Single with Update Indexed +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 3, 0 + unsigned_word EA; + if (RA_is_0) + program_interrupt(processor, cia, + illegal_instruction_program_interrupt); + EA = *rA + *rB; + STORE(EA, 4, SINGLE(*frS)); + *rA = EA; + PPC_INSN_INT_FLOAT(RA_BITMASK, 0, (RA_BITMASK & ~1) | RB_BITMASK, FRS_BITMASK); + +0.54,6.FRS,11.RA,16.D:D:f::Store Floating-Point Double +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 3, 0 + unsigned_word b; + unsigned_word EA; + if (RA_is_0) b = 0; + else b = *rA; + EA = b + EXTS(D); + STORE(EA, 8, *frS); + PPC_INSN_INT_FLOAT(0, 0, (RA_BITMASK & ~1), FRS_BITMASK); + +0.31,6.FRS,11.RA,16.RB,21.727,31./:X:f::Store Floating-Point Double Indexed +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 3, 0 + unsigned_word b; + unsigned_word EA; + if (RA_is_0) b = 0; + else b = *rA; + EA = b + *rB; + STORE(EA, 8, *frS); + PPC_INSN_INT_FLOAT(0, 0, (RA_BITMASK & ~1) | RB_BITMASK, FRS_BITMASK); + +0.55,6.FRS,11.RA,16.D:D:f::Store Floating-Point Double with Update +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 3, 0 + unsigned_word EA; + if (RA_is_0) + program_interrupt(processor, cia, + illegal_instruction_program_interrupt); + EA = *rA + EXTS(D); + STORE(EA, 8, *frS); + *rA = EA; + PPC_INSN_INT_FLOAT(RA_BITMASK, 0, (RA_BITMASK & ~1), FRS_BITMASK); + +0.31,6.FRS,11.RA,16.RB,21.759,31./:X:f::Store Floating-Point Double with Update Indexed +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 3, 0 + unsigned_word EA; + if (RA_is_0) + program_interrupt(processor, cia, + illegal_instruction_program_interrupt); + EA = *rA + *rB; + STORE(EA, 8, *frS); + *rA = EA; + PPC_INSN_INT_FLOAT(RA_BITMASK, 0, (RA_BITMASK & ~1) | RB_BITMASK, FRS_BITMASK); + + +# +# I.4.6.4 Floating-Point Move Instructions +# + +0.63,6.FRT,11./,16.FRB,21.72,31.Rc:X:f::Floating Move Register +*601: PPC_UNIT_FPU, PPC_UNIT_FPU, 4, 4, 0 +*603: PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 +*603e:PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 +*604: PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 + *frT = *frB; + CR1_UPDATE(Rc); + PPC_INSN_FLOAT(FRT_BITMASK, FRB_BITMASK, Rc); + +0.63,6.FRT,11./,16.FRB,21.40,31.Rc:X:f::Floating Negate +*601: PPC_UNIT_FPU, PPC_UNIT_FPU, 4, 4, 0 +*603: PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 +*603e:PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 +*604: PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 + *frT = *frB ^ BIT64(0); + CR1_UPDATE(Rc); + PPC_INSN_FLOAT(FRT_BITMASK, FRB_BITMASK, Rc); + +0.63,6.FRT,11./,16.FRB,21.264,31.Rc:X:f::Floating Absolute Value +*601: PPC_UNIT_FPU, PPC_UNIT_FPU, 4, 4, 0 +*603: PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 +*603e:PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 +*604: PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 + *frT = *frB & ~BIT64(0); + CR1_UPDATE(Rc); + PPC_INSN_FLOAT(FRT_BITMASK, FRB_BITMASK, Rc); + +0.63,6.FRT,11./,16.FRB,21.136,31.Rc:X:f::Floating Negative Absolute Value +*601: PPC_UNIT_FPU, PPC_UNIT_FPU, 4, 4, 0 +*603: PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 +*603e:PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 +*604: PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 + *frT = *frB | BIT64(0); + CR1_UPDATE(Rc); + PPC_INSN_FLOAT(FRT_BITMASK, FRB_BITMASK, Rc); + + +# +# I.4.6.5 Floating-Point Arithmetic Instructions +# + +0.63,6.FRT,11.FRA,16.FRB,21./,26.21,31.Rc:A:f:fadd:Floating Add +*601: PPC_UNIT_FPU, PPC_UNIT_FPU, 4, 4, 0 +*603: PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 +*603e:PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 +*604: PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 + FPSCR_BEGIN; + if (is_invalid_operation(processor, cia, + *frA, *frB, + fpscr_vxsnan | fpscr_vxisi, + 0, /*single?*/ + 0) /*negate?*/) { + invalid_arithemetic_operation(processor, cia, + frT, *frA, *frB, 0, + 0, /*instruction_is_frsp*/ + 0, /*instruction_is_convert_to_64bit*/ + 0, /*instruction_is_convert_to_32bit*/ + 0); /*single-precision*/ + } + else { + /*HACK!*/ + double s = *(double*)frA + *(double*)frB; + *(double*)frT = s; + } + FPSCR_END(Rc); + PPC_INSN_FLOAT(FRT_BITMASK, FRA_BITMASK | FRB_BITMASK, Rc); + +0.59,6.FRT,11.FRA,16.FRB,21./,26.21,31.Rc:A:f:fadds:Floating Add Single +*601: PPC_UNIT_FPU, PPC_UNIT_FPU, 4, 4, 0 +*603: PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 +*603e:PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 +*604: PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 + FPSCR_BEGIN; + if (is_invalid_operation(processor, cia, + *frA, *frB, + fpscr_vxsnan | fpscr_vxisi, + 1, /*single?*/ + 0) /*negate?*/) { + invalid_arithemetic_operation(processor, cia, + frT, *frA, *frB, 0, + 0, /*instruction_is_frsp*/ + 0, /*instruction_is_convert_to_64bit*/ + 0, /*instruction_is_convert_to_32bit*/ + 1); /*single-precision*/ + } + else { + /*HACK!*/ + float s = *(double*)frA + *(double*)frB; + *(double*)frT = s; + } + FPSCR_END(Rc); + PPC_INSN_FLOAT(FRT_BITMASK, FRA_BITMASK | FRB_BITMASK, Rc); + +0.63,6.FRT,11.FRA,16.FRB,21./,26.20,31.Rc:A:f:fsub:Floating Subtract +*601: PPC_UNIT_FPU, PPC_UNIT_FPU, 4, 4, 0 +*603: PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 +*603e:PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 +*604: PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 + FPSCR_BEGIN; + if (is_invalid_operation(processor, cia, + *frA, *frB, + fpscr_vxsnan | fpscr_vxisi, + 0, /*single?*/ + 1) /*negate?*/) { + invalid_arithemetic_operation(processor, cia, + frT, *frA, *frB, 0, + 0, /*instruction_is_frsp*/ + 0, /*instruction_is_convert_to_64bit*/ + 0, /*instruction_is_convert_to_32bit*/ + 0); /*single-precision*/ + } + else { + /*HACK!*/ + double s = *(double*)frA - *(double*)frB; + *(double*)frT = s; + } + FPSCR_END(Rc); + PPC_INSN_FLOAT(FRT_BITMASK, FRA_BITMASK | FRB_BITMASK, Rc); + +0.59,6.FRT,11.FRA,16.FRB,21./,26.20,31.Rc:A:f:fsubs:Floating Subtract Single +*601: PPC_UNIT_FPU, PPC_UNIT_FPU, 4, 4, 0 +*603: PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 +*603e:PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 +*604: PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 + FPSCR_BEGIN; + if (is_invalid_operation(processor, cia, + *frA, *frB, + fpscr_vxsnan | fpscr_vxisi, + 1, /*single?*/ + 1) /*negate?*/) { + invalid_arithemetic_operation(processor, cia, + frT, *frA, *frB, 0, + 0, /*instruction_is_frsp*/ + 0, /*instruction_is_convert_to_64bit*/ + 0, /*instruction_is_convert_to_32bit*/ + 1); /*single-precision*/ + } + else { + /*HACK!*/ + float s = *(double*)frA - *(double*)frB; + *(double*)frT = s; + } + FPSCR_END(Rc); + PPC_INSN_FLOAT(FRT_BITMASK, FRA_BITMASK | FRB_BITMASK, Rc); + +0.63,6.FRT,11.FRA,16./,21.FRC,26.25,31.Rc:A:f:fmul:Floating Multiply +*601: PPC_UNIT_FPU, PPC_UNIT_FPU, 5, 5, 0 +*603: PPC_UNIT_FPU, PPC_UNIT_FPU, 2, 4, 0 +*603e:PPC_UNIT_FPU, PPC_UNIT_FPU, 2, 4, 0 +*604: PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 + FPSCR_BEGIN; + if (is_invalid_operation(processor, cia, + *frA, *frC, + fpscr_vxsnan | fpscr_vximz, + 0, /*single?*/ + 0) /*negate?*/) { + invalid_arithemetic_operation(processor, cia, + frT, *frA, 0, *frC, + 0, /*instruction_is_frsp*/ + 0, /*instruction_is_convert_to_64bit*/ + 0, /*instruction_is_convert_to_32bit*/ + 0); /*single-precision*/ + } + else { + /*HACK!*/ + double s = *(double*)frA * *(double*)frC; + *(double*)frT = s; + } + FPSCR_END(Rc); + PPC_INSN_FLOAT(FRT_BITMASK, FRA_BITMASK | FRC_BITMASK, Rc); + +0.59,6.FRT,11.FRA,16./,21.FRC,26.25,31.Rc:A:f:fmuls:Floating Multiply Single +*601: PPC_UNIT_FPU, PPC_UNIT_FPU, 4, 4, 0 +*603: PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 +*603e:PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 +*604: PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 + FPSCR_BEGIN; + if (is_invalid_operation(processor, cia, + *frA, *frC, + fpscr_vxsnan | fpscr_vximz, + 1, /*single?*/ + 0) /*negate?*/) { + invalid_arithemetic_operation(processor, cia, + frT, *frA, 0, *frC, + 0, /*instruction_is_frsp*/ + 0, /*instruction_is_convert_to_64bit*/ + 0, /*instruction_is_convert_to_32bit*/ + 1); /*single-precision*/ + } + else { + /*HACK!*/ + float s = *(double*)frA * *(double*)frC; + *(double*)frT = s; + } + FPSCR_END(Rc); + PPC_INSN_FLOAT(FRT_BITMASK, FRA_BITMASK | FRC_BITMASK, Rc); + +0.63,6.FRT,11.FRA,16.FRB,21./,26.18,31.Rc:A:f:fdiv:Floating Divide +*601: PPC_UNIT_FPU, PPC_UNIT_FPU, 31, 31, 0 +*603: PPC_UNIT_FPU, PPC_UNIT_FPU, 33, 33, 0 +*603e:PPC_UNIT_FPU, PPC_UNIT_FPU, 33, 33, 0 +*604: PPC_UNIT_FPU, PPC_UNIT_FPU, 32, 32, 0 + FPSCR_BEGIN; + if (is_invalid_operation(processor, cia, + *frA, *frB, + fpscr_vxsnan | fpscr_vxzdz, + 0, /*single?*/ + 0) /*negate?*/) { + invalid_arithemetic_operation(processor, cia, + frT, *frA, *frB, 0, + 0, /*instruction_is_frsp*/ + 0, /*instruction_is_convert_to_64bit*/ + 0, /*instruction_is_convert_to_32bit*/ + 0); /*single-precision*/ + } + else if (is_invalid_zero_divide (processor, cia, + *frA, *frB, + 0 /*single?*/)) { + invalid_zero_divide_operation (processor, cia, + frT, *frA, *frB, + 0 /*single?*/); + } + else { + /*HACK!*/ + double s = *(double*)frA / *(double*)frB; + *(double*)frT = s; + } + FPSCR_END(Rc); + PPC_INSN_FLOAT(FRT_BITMASK, FRA_BITMASK | FRB_BITMASK, Rc); + +0.59,6.FRT,11.FRA,16.FRB,21./,26.18,31.Rc:A:f:fdivs:Floating Divide Single +*601: PPC_UNIT_FPU, PPC_UNIT_FPU, 17, 17, 0 +*603: PPC_UNIT_FPU, PPC_UNIT_FPU, 18, 18, 0 +*603e:PPC_UNIT_FPU, PPC_UNIT_FPU, 18, 18, 0 +*604: PPC_UNIT_FPU, PPC_UNIT_FPU, 18, 18, 0 + FPSCR_BEGIN; + if (is_invalid_operation(processor, cia, + *frA, *frB, + fpscr_vxsnan | fpscr_vxzdz, + 1, /*single?*/ + 0) /*negate?*/) { + invalid_arithemetic_operation(processor, cia, + frT, *frA, *frB, 0, + 0, /*instruction_is_frsp*/ + 0, /*instruction_is_convert_to_64bit*/ + 0, /*instruction_is_convert_to_32bit*/ + 1); /*single-precision*/ + } + else if (is_invalid_zero_divide (processor, cia, + *frA, *frB, + 1 /*single?*/)) { + invalid_zero_divide_operation (processor, cia, + frT, *frA, *frB, + 1 /*single?*/); + } + else { + /*HACK!*/ + float s = *(double*)frA / *(double*)frB; + *(double*)frT = s; + } + FPSCR_END(Rc); + PPC_INSN_FLOAT(FRT_BITMASK, FRA_BITMASK | FRB_BITMASK, Rc); + +0.63,6.FRT,11.FRA,16.FRB,21.FRC,26.29,31.Rc:A:f:fmadd:Floating Multiply-Add +*601: PPC_UNIT_FPU, PPC_UNIT_FPU, 5, 5, 0 +*603: PPC_UNIT_FPU, PPC_UNIT_FPU, 2, 4, 0 +*603e:PPC_UNIT_FPU, PPC_UNIT_FPU, 2, 4, 0 +*604: PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 + FPSCR_BEGIN; + double product; /*HACK! - incorrectly loosing precision ... */ + /* compute the multiply */ + if (is_invalid_operation(processor, cia, + *frA, *frC, + fpscr_vxsnan | fpscr_vximz, + 0, /*single?*/ + 0) /*negate?*/) { + invalid_arithemetic_operation(processor, cia, + (unsigned64*)&product, *frA, 0, *frC, + 0, /*instruction_is_frsp*/ + 0, /*instruction_is_convert_to_64bit*/ + 0, /*instruction_is_convert_to_32bit*/ + 0); /*single-precision*/ + } + else { + /*HACK!*/ + product = *(double*)frA * *(double*)frC; + } + /* compute the add */ + if (is_invalid_operation(processor, cia, + product, *frB, + fpscr_vxsnan | fpscr_vxisi, + 0, /*single?*/ + 0) /*negate?*/) { + invalid_arithemetic_operation(processor, cia, + frT, product, *frB, 0, + 0, /*instruction_is_frsp*/ + 0, /*instruction_is_convert_to_64bit*/ + 0, /*instruction_is_convert_to_32bit*/ + 0); /*single-precision*/ + } + else { + /*HACK!*/ + double s = product + *(double*)frB; + *(double*)frT = s; + } + FPSCR_END(Rc); + PPC_INSN_FLOAT(FRT_BITMASK, FRA_BITMASK | FRB_BITMASK | FRC_BITMASK, Rc); + +0.59,6.FRT,11.FRA,16.FRB,21.FRC,26.29,31.Rc:A:f::Floating Multiply-Add Single +*601: PPC_UNIT_FPU, PPC_UNIT_FPU, 4, 4, 0 +*603: PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 +*603e:PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 +*604: PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 + FPSCR_BEGIN; + float product; /*HACK! - incorrectly loosing precision ... */ + /* compute the multiply */ + if (is_invalid_operation(processor, cia, + *frA, *frC, + fpscr_vxsnan | fpscr_vximz, + 1, /*single?*/ + 0) /*negate?*/) { + invalid_arithemetic_operation(processor, cia, + (unsigned64*)&product, *frA, 0, *frC, + 0, /*instruction_is_frsp*/ + 0, /*instruction_is_convert_to_64bit*/ + 0, /*instruction_is_convert_to_32bit*/ + 0); /*single-precision*/ + } + else { + /*HACK!*/ + product = *(double*)frA * *(double*)frC; + } + /* compute the add */ + if (is_invalid_operation(processor, cia, + product, *frB, + fpscr_vxsnan | fpscr_vxisi, + 1, /*single?*/ + 0) /*negate?*/) { + invalid_arithemetic_operation(processor, cia, + frT, product, *frB, 0, + 0, /*instruction_is_frsp*/ + 0, /*instruction_is_convert_to_64bit*/ + 0, /*instruction_is_convert_to_32bit*/ + 0); /*single-precision*/ + } + else { + /*HACK!*/ + float s = product + *(double*)frB; + *(double*)frT = (double)s; + } + FPSCR_END(Rc); + PPC_INSN_FLOAT(FRT_BITMASK, FRA_BITMASK | FRB_BITMASK | FRC_BITMASK, Rc); + +0.63,6.FRT,11.FRA,16.FRB,21.FRC,26.28,31.Rc:A:f::Floating Multiply-Subtract +*601: PPC_UNIT_FPU, PPC_UNIT_FPU, 5, 5, 0 +*603: PPC_UNIT_FPU, PPC_UNIT_FPU, 2, 4, 0 +*603e:PPC_UNIT_FPU, PPC_UNIT_FPU, 2, 4, 0 +*604: PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 + FPSCR_BEGIN; + double product; /*HACK! - incorrectly loosing precision ... */ + /* compute the multiply */ + if (is_invalid_operation(processor, cia, + *frA, *frC, + fpscr_vxsnan | fpscr_vximz, + 0, /*single?*/ + 0) /*negate?*/) { + invalid_arithemetic_operation(processor, cia, + (unsigned64*)&product, *frA, 0, *frC, + 0, /*instruction_is_frsp*/ + 0, /*instruction_is_convert_to_64bit*/ + 0, /*instruction_is_convert_to_32bit*/ + 0); /*single-precision*/ + } + else { + /*HACK!*/ + product = *(double*)frA * *(double*)frC; + } + /* compute the subtract */ + if (is_invalid_operation(processor, cia, + product, *frB, + fpscr_vxsnan | fpscr_vxisi, + 0, /*single?*/ + 0) /*negate?*/) { + invalid_arithemetic_operation(processor, cia, + frT, product, *frB, 0, + 0, /*instruction_is_frsp*/ + 0, /*instruction_is_convert_to_64bit*/ + 0, /*instruction_is_convert_to_32bit*/ + 0); /*single-precision*/ + } + else { + /*HACK!*/ + double s = product - *(double*)frB; + *(double*)frT = s; + } + FPSCR_END(Rc); + PPC_INSN_FLOAT(FRT_BITMASK, FRA_BITMASK | FRB_BITMASK | FRC_BITMASK, Rc); + +0.59,6.FRT,11.FRA,16.FRB,21.FRC,26.28,31.Rc:A:f::Floating Multiply-Subtract Single +*601: PPC_UNIT_FPU, PPC_UNIT_FPU, 4, 4, 0 +*603: PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 +*603e:PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 +*604: PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 + FPSCR_BEGIN; + float product; /*HACK! - incorrectly loosing precision ... */ + /* compute the multiply */ + if (is_invalid_operation(processor, cia, + *frA, *frC, + fpscr_vxsnan | fpscr_vximz, + 1, /*single?*/ + 0) /*negate?*/) { + invalid_arithemetic_operation(processor, cia, + (unsigned64*)&product, *frA, 0, *frC, + 0, /*instruction_is_frsp*/ + 0, /*instruction_is_convert_to_64bit*/ + 0, /*instruction_is_convert_to_32bit*/ + 0); /*single-precision*/ + } + else { + /*HACK!*/ + product = *(double*)frA * *(double*)frC; + } + /* compute the subtract */ + if (is_invalid_operation(processor, cia, + product, *frB, + fpscr_vxsnan | fpscr_vxisi, + 1, /*single?*/ + 0) /*negate?*/) { + invalid_arithemetic_operation(processor, cia, + frT, product, *frB, 0, + 0, /*instruction_is_frsp*/ + 0, /*instruction_is_convert_to_64bit*/ + 0, /*instruction_is_convert_to_32bit*/ + 0); /*single-precision*/ + } + else { + /*HACK!*/ + float s = product - *(double*)frB; + *(double*)frT = (double)s; + } + FPSCR_END(Rc); + PPC_INSN_FLOAT(FRT_BITMASK, FRA_BITMASK | FRB_BITMASK | FRC_BITMASK, Rc); + +0.63,6.FRT,11.FRA,16.FRB,21.FRC,26.31,31.Rc:A:f::Floating Negative Multiply-Add +*601: PPC_UNIT_FPU, PPC_UNIT_FPU, 5, 5, 0 +*603: PPC_UNIT_FPU, PPC_UNIT_FPU, 2, 4, 0 +*603e:PPC_UNIT_FPU, PPC_UNIT_FPU, 2, 4, 0 +*604: PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 + FPSCR_BEGIN; + double product; /*HACK! - incorrectly loosing precision ... */ + /* compute the multiply */ + if (is_invalid_operation(processor, cia, + *frA, *frC, + fpscr_vxsnan | fpscr_vximz, + 0, /*single?*/ + 0) /*negate?*/) { + invalid_arithemetic_operation(processor, cia, + (unsigned64*)&product, *frA, 0, *frC, + 0, /*instruction_is_frsp*/ + 0, /*instruction_is_convert_to_64bit*/ + 0, /*instruction_is_convert_to_32bit*/ + 0); /*single-precision*/ + } + else { + /*HACK!*/ + product = *(double*)frA * *(double*)frC; + } + /* compute the add */ + if (is_invalid_operation(processor, cia, + product, *frB, + fpscr_vxsnan | fpscr_vxisi, + 0, /*single?*/ + 0) /*negate?*/) { + invalid_arithemetic_operation(processor, cia, + frT, product, *frB, 0, + 0, /*instruction_is_frsp*/ + 0, /*instruction_is_convert_to_64bit*/ + 0, /*instruction_is_convert_to_32bit*/ + 0); /*single-precision*/ + } + else { + /*HACK!*/ + double s = -(product + *(double*)frB); + *(double*)frT = s; + } + FPSCR_END(Rc); + PPC_INSN_FLOAT(FRT_BITMASK, FRA_BITMASK | FRB_BITMASK | FRC_BITMASK, Rc); + +0.59,6.FRT,11.FRA,16.FRB,21.FRC,26.31,31.Rc:A:f::Floating Negative Multiply-Add Single +*601: PPC_UNIT_FPU, PPC_UNIT_FPU, 4, 4, 0 +*603: PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 +*603e:PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 +*604: PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 + FPSCR_BEGIN; + float product; /*HACK! - incorrectly loosing precision ... */ + /* compute the multiply */ + if (is_invalid_operation(processor, cia, + *frA, *frC, + fpscr_vxsnan | fpscr_vximz, + 1, /*single?*/ + 0) /*negate?*/) { + invalid_arithemetic_operation(processor, cia, + (unsigned64*)&product, *frA, 0, *frC, + 0, /*instruction_is_frsp*/ + 0, /*instruction_is_convert_to_64bit*/ + 0, /*instruction_is_convert_to_32bit*/ + 0); /*single-precision*/ + } + else { + /*HACK!*/ + product = *(double*)frA * *(double*)frC; + } + /* compute the add */ + if (is_invalid_operation(processor, cia, + product, *frB, + fpscr_vxsnan | fpscr_vxisi, + 1, /*single?*/ + 0) /*negate?*/) { + invalid_arithemetic_operation(processor, cia, + frT, product, *frB, 0, + 0, /*instruction_is_frsp*/ + 0, /*instruction_is_convert_to_64bit*/ + 0, /*instruction_is_convert_to_32bit*/ + 0); /*single-precision*/ + } + else { + /*HACK!*/ + float s = -(product + *(double*)frB); + *(double*)frT = (double)s; + } + FPSCR_END(Rc); + PPC_INSN_FLOAT(FRT_BITMASK, FRA_BITMASK | FRB_BITMASK | FRC_BITMASK, Rc); + +0.63,6.FRT,11.FRA,16.FRB,21.FRC,26.30,31.Rc:A:f::Floating Negative Multiply-Subtract +*601: PPC_UNIT_FPU, PPC_UNIT_FPU, 5, 5, 0 +*603: PPC_UNIT_FPU, PPC_UNIT_FPU, 2, 4, 0 +*603e:PPC_UNIT_FPU, PPC_UNIT_FPU, 2, 4, 0 +*604: PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 + FPSCR_BEGIN; + double product; /*HACK! - incorrectly loosing precision ... */ + /* compute the multiply */ + if (is_invalid_operation(processor, cia, + *frA, *frC, + fpscr_vxsnan | fpscr_vximz, + 0, /*single?*/ + 0) /*negate?*/) { + invalid_arithemetic_operation(processor, cia, + (unsigned64*)&product, *frA, 0, *frC, + 0, /*instruction_is_frsp*/ + 0, /*instruction_is_convert_to_64bit*/ + 0, /*instruction_is_convert_to_32bit*/ + 0); /*single-precision*/ + } + else { + /*HACK!*/ + product = *(double*)frA * *(double*)frC; + } + /* compute the subtract */ + if (is_invalid_operation(processor, cia, + product, *frB, + fpscr_vxsnan | fpscr_vxisi, + 0, /*single?*/ + 0) /*negate?*/) { + invalid_arithemetic_operation(processor, cia, + frT, product, *frB, 0, + 0, /*instruction_is_frsp*/ + 0, /*instruction_is_convert_to_64bit*/ + 0, /*instruction_is_convert_to_32bit*/ + 0); /*single-precision*/ + } + else { + /*HACK!*/ + double s = -(product - *(double*)frB); + *(double*)frT = s; + } + FPSCR_END(Rc); + PPC_INSN_FLOAT(FRT_BITMASK, FRA_BITMASK | FRB_BITMASK | FRC_BITMASK, Rc); + +0.59,6.FRT,11.FRA,16.FRB,21.FRC,26.30,31.Rc:A:f::Floating Negative Multiply-Subtract Single +*601: PPC_UNIT_FPU, PPC_UNIT_FPU, 4, 4, 0 +*603: PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 +*603e:PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 +*604: PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 + FPSCR_BEGIN; + float product; /*HACK! - incorrectly loosing precision ... */ + /* compute the multiply */ + if (is_invalid_operation(processor, cia, + *frA, *frC, + fpscr_vxsnan | fpscr_vximz, + 1, /*single?*/ + 0) /*negate?*/) { + invalid_arithemetic_operation(processor, cia, + (unsigned64*)&product, *frA, 0, *frC, + 0, /*instruction_is_frsp*/ + 0, /*instruction_is_convert_to_64bit*/ + 0, /*instruction_is_convert_to_32bit*/ + 0); /*single-precision*/ + } + else { + /*HACK!*/ + product = *(double*)frA * *(double*)frC; + } + /* compute the subtract */ + if (is_invalid_operation(processor, cia, + product, *frB, + fpscr_vxsnan | fpscr_vxisi, + 1, /*single?*/ + 0) /*negate?*/) { + invalid_arithemetic_operation(processor, cia, + frT, product, *frB, 0, + 0, /*instruction_is_frsp*/ + 0, /*instruction_is_convert_to_64bit*/ + 0, /*instruction_is_convert_to_32bit*/ + 0); /*single-precision*/ + } + else { + /*HACK!*/ + float s = -(product - *(double*)frB); + *(double*)frT = (double)s; + } + FPSCR_END(Rc); + PPC_INSN_FLOAT(FRT_BITMASK, FRA_BITMASK | FRB_BITMASK | FRC_BITMASK, Rc); + + +# +# I.4.6.6 Floating-Point Rounding and Conversion Instructions +# + +0.63,6.FRT,11./,16.FRB,21.12,31.Rc:X:f::Floating Round to Single-Precision +*601: PPC_UNIT_FPU, PPC_UNIT_FPU, 4, 4, 0 +*603: PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 +*603e:PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 +*604: PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 + int sign; + int exp; + unsigned64 frac_grx; + /***/ + /* split off cases for what to do */ + if (EXTRACTED64(*frB, 1, 11) < 897 + && EXTRACTED64(*frB, 1, 63) > 0) { + if ((FPSCR & fpscr_ue) == 0) GOTO(Disabled_Exponent_Underflow); + if ((FPSCR & fpscr_ue) != 0) GOTO(Enabled_Exponent_Underflow); + } + if (EXTRACTED64(*frB, 1, 11) > 1150 + && EXTRACTED64(*frB, 1, 11) < 2047) { + if ((FPSCR & fpscr_oe) == 0) GOTO(Disabled_Exponent_Overflow); + if ((FPSCR & fpscr_oe) != 0) GOTO(Enabled_Exponent_Overflow); + } + if (EXTRACTED64(*frB, 1, 11) > 896 + && EXTRACTED64(*frB, 1, 11) < 1151) GOTO(Normal_Operand); + if (EXTRACTED64(*frB, 1, 63) == 0) GOTO(Zero_Operand); + if (EXTRACTED64(*frB, 1, 11) == 2047) { + if (EXTRACTED64(*frB, 12, 63) == 0) GOTO(Infinity_Operand); + if (EXTRACTED64(*frB, 12, 12) == 1) GOTO(QNaN_Operand); + if (EXTRACTED64(*frB, 12, 12) == 0 + && EXTRACTED64(*frB, 13, 63) > 0) GOTO(SNaN_Operand); + } + /**/ + LABEL(Disabled_Exponent_Underflow): + sign = EXTRACTED64(*frB, 0, 0); + if (EXTRACTED64(*frB, 1, 11) == 0) { + exp = -1022; + frac_grx = INSERTED64(EXTRACTED64(*frB, 12, 63), 1, 52); + } + if (EXTRACTED64(*frB, 1, 11) > 0) { + exp = EXTRACTED64(*frB, 1, 11) - 1023; + frac_grx = BIT64(0) | INSERTED64(EXTRACTED64(*frB, 12, 63), 1, 52); + } + /* G|R|X == zero from above */ + while (exp < -126) { + exp = exp - 1; + frac_grx = (INSERTED64(EXTRACTED64(frac_grx, 0, 54), 1, 55) + | MASKED64(frac_grx, 55, 55)); + } + FPSCR_SET_UX(EXTRACTED64(frac_grx, 24, 55) > 0); + Round_Single(processor, sign, &exp, &frac_grx); + FPSCR_SET_XX(FPSCR & fpscr_fi); + if (EXTRACTED64(frac_grx, 0, 52) == 0) { + *frT = INSERTED64(sign, 0, 0); + if (sign == 0) FPSCR_SET_FPRF(fpscr_rf_pos_zero); + if (sign == 1) FPSCR_SET_FPRF(fpscr_rf_neg_zero); + } + if (EXTRACTED64(frac_grx, 0, 52) > 0) { + if (EXTRACTED64(frac_grx, 0, 0) == 1) { + if (sign == 0) FPSCR_SET_FPRF(fpscr_rf_pos_normal_number); + if (sign == 1) FPSCR_SET_FPRF(fpscr_rf_neg_normal_number); + } + if (EXTRACTED64(frac_grx, 0, 0) == 0) { + if (sign == 0) FPSCR_SET_FPRF(fpscr_rf_pos_denormalized_number); + if (sign == 1) FPSCR_SET_FPRF(fpscr_rf_neg_denormalized_number); + } + /*Normalize_Operand:*/ + while (EXTRACTED64(frac_grx, 0, 0) == 0) { + exp = exp - 1; + frac_grx = INSERTED64(EXTRACTED64(frac_grx, 1, 52), 0, 51); + } + *frT = (INSERTED64(sign, 0, 0) + | INSERTED64(exp + 1023, 1, 11) + | INSERTED64(EXTRACTED64(frac_grx, 1, 52), 12, 63)); + } + GOTO(Done); + /**/ + LABEL(Enabled_Exponent_Underflow): + FPSCR_SET_UX(1); + sign = EXTRACTED64(*frB, 0, 0); + if (EXTRACTED64(*frB, 1, 11) == 0) { + exp = -1022; + frac_grx = INSERTED64(EXTRACTED64(*frB, 12, 63), 1, 52); + } + if (EXTRACTED64(*frB, 1, 11) > 0) { + exp = EXTRACTED64(*frB, 1, 11) - 1023; + frac_grx = (BIT64(0) | + INSERTED64(EXTRACTED64(*frB, 12, 63), 1, 52)); + } + /*Normalize_Operand:*/ + while (EXTRACTED64(frac_grx, 0, 0) == 0) { + exp = exp - 1; + frac_grx = INSERTED64(EXTRACTED64(frac_grx, 1, 52), 0, 51); + } + Round_Single(processor, sign, &exp, &frac_grx); + FPSCR_SET_XX(FPSCR & fpscr_fi); + exp = exp + 192; + *frT = (INSERTED64(sign, 0, 0) + | INSERTED64(exp + 1023, 1, 11) + | INSERTED64(EXTRACTED64(frac_grx, 1, 52), 12, 63)); + if (sign == 0) FPSCR_SET_FPRF(fpscr_rf_pos_normal_number); + if (sign == 1) FPSCR_SET_FPRF(fpscr_rf_neg_normal_number); + GOTO(Done); + /**/ + LABEL(Disabled_Exponent_Overflow): + FPSCR_SET_OX(1); + if ((FPSCR & fpscr_rn) == fpscr_rn_round_to_nearest) { + if (EXTRACTED64(*frB, 0, 0) == 0) { + *frT = INSERTED64(0x7FF00000, 0, 31) | 0x00000000; + FPSCR_SET_FPRF(fpscr_rf_pos_infinity); + } + if (EXTRACTED64(*frB, 0, 0) == 1) { + *frT = INSERTED64(0xFFF00000, 0, 31) | 0x00000000; + FPSCR_SET_FPRF(fpscr_rf_neg_infinity); + } + } + if ((FPSCR & fpscr_rn) == fpscr_rn_round_towards_zero) { + if (EXTRACTED64(*frB, 0, 0) == 0) { + *frT = INSERTED64(0x47EFFFFF, 0, 31) | 0xE0000000; + FPSCR_SET_FPRF(fpscr_rf_pos_normal_number); + } + if (EXTRACTED64(*frB, 0, 0) == 1) { + *frT = INSERTED64(0xC7EFFFFF, 0, 31) | 0xE0000000; + FPSCR_SET_FPRF(fpscr_rf_neg_normal_number); + } + } + if ((FPSCR & fpscr_rn) == fpscr_rn_round_towards_pos_infinity) { + if (EXTRACTED64(*frB, 0, 0) == 0) { + *frT = INSERTED64(0x7FF00000, 0, 31) | 0x00000000; + FPSCR_SET_FPRF(fpscr_rf_pos_infinity); + } + if (EXTRACTED64(*frB, 0, 0) == 1) { + *frT = INSERTED64(0xC7EFFFFF, 0, 31) | 0xE0000000; + FPSCR_SET_FPRF(fpscr_rf_neg_normal_number); + } + } + if ((FPSCR & fpscr_rn) == fpscr_rn_round_towards_neg_infinity) { + if (EXTRACTED64(*frB, 0, 0) == 0) { + *frT = INSERTED64(0x47EFFFFF, 0, 31) | 0xE0000000; + FPSCR_SET_FPRF(fpscr_rf_pos_normal_number); + } + if (EXTRACTED64(*frB, 0, 0) == 1) { + *frT = INSERTED64(0xFFF00000, 0, 31) | 0x00000000; + FPSCR_SET_FPRF(fpscr_rf_neg_infinity); + } + } + /* FPSCR[FR] <- undefined */ + FPSCR_SET_FI(1); + FPSCR_SET_XX(1); + GOTO(Done); + /**/ + LABEL(Enabled_Exponent_Overflow): + sign = EXTRACTED64(*frB, 0, 0); + exp = EXTRACTED64(*frB, 1, 11) - 1023; + frac_grx = BIT64(0) | INSERTED64(EXTRACTED64(*frB, 12, 63), 1, 52); + Round_Single(processor, sign, &exp, &frac_grx); + FPSCR_SET_XX(FPSCR & fpscr_fi); + /**/ + LABEL(Enabled_Overflow): + FPSCR_SET_OX(1); + exp = exp - 192; + *frT = (INSERTED64(sign, 0, 0) + | INSERTED64(exp + 1023, 1, 11) + | INSERTED64(EXTRACTED64(frac_grx, 1, 52), 12, 63)); + if (sign == 0) FPSCR_SET_FPRF(fpscr_rf_pos_normal_number); + if (sign == 1) FPSCR_SET_FPRF(fpscr_rf_neg_normal_number); + GOTO(Done); + /**/ + LABEL(Zero_Operand): + *frT = *frB; + if (EXTRACTED64(*frB, 0, 0) == 0) FPSCR_SET_FPRF(fpscr_rf_pos_zero); + if (EXTRACTED64(*frB, 0, 0) == 1) FPSCR_SET_FPRF(fpscr_rf_neg_zero); + FPSCR_SET_FR(0); + FPSCR_SET_FI(0); + GOTO(Done); + /**/ + LABEL(Infinity_Operand): + *frT = *frB; + if (EXTRACTED64(*frB, 0, 0) == 0) FPSCR_SET_FPRF(fpscr_rf_pos_infinity); + if (EXTRACTED64(*frB, 0, 0) == 1) FPSCR_SET_FPRF(fpscr_rf_neg_infinity); + FPSCR_SET_FR(0); + FPSCR_SET_FI(0); + GOTO(Done); + /**/ + LABEL(QNaN_Operand): + *frT = INSERTED64(EXTRACTED64(*frB, 0, 34), 0, 34); + FPSCR_SET_FPRF(fpscr_rf_quiet_nan); + FPSCR_SET_FR(0); + FPSCR_SET_FI(0); + GOTO(Done); + /**/ + LABEL(SNaN_Operand): + FPSCR_OR_VX(fpscr_vxsnan); + if ((FPSCR & fpscr_ve) == 0) { + *frT = (MASKED64(*frB, 0, 11) + | BIT64(12) + | MASKED64(*frB, 13, 34)); + FPSCR_SET_FPRF(fpscr_rf_quiet_nan); + } + FPSCR_SET_FR(0); + FPSCR_SET_FI(0); + GOTO(Done); + /**/ + LABEL(Normal_Operand): + sign = EXTRACTED64(*frB, 0, 0); + exp = EXTRACTED64(*frB, 1, 11) - 1023; + frac_grx = BIT64(0) | INSERTED64(EXTRACTED64(*frB, 12, 63), 1, 52); + Round_Single(processor, sign, &exp, &frac_grx); + FPSCR_SET_XX(FPSCR & fpscr_fi); + if (exp > 127 && (FPSCR & fpscr_oe) == 0) GOTO(Disabled_Exponent_Overflow); + if (exp > 127 && (FPSCR & fpscr_oe) != 0) GOTO(Enabled_Overflow); + *frT = (INSERTED64(sign, 0, 0) + | INSERTED64(exp + 1023, 1, 11) + | INSERTED64(EXTRACTED64(frac_grx, 1, 52), 12, 63)); + if (sign == 0) FPSCR_SET_FPRF(fpscr_rf_pos_normal_number); + if (sign == 1) FPSCR_SET_FPRF(fpscr_rf_neg_normal_number); + GOTO(Done); + /**/ + LABEL(Done): + PPC_INSN_FLOAT(FRT_BITMASK, FRB_BITMASK, Rc); + + +0.63,6.FRT,11./,16.FRB,21.814,31.Rc:X:64,f::Floating Convert To Integer Doubleword + floating_point_assist_interrupt(processor, cia); + +0.63,6.FRT,11./,16.FRB,21.815,31.Rc:X:64,f::Floating Convert To Integer Doubleword with round towards Zero + floating_point_assist_interrupt(processor, cia); + +0.63,6.FRT,11./,16.FRB,21.14,31.Rc:X:f::Floating Convert To Integer Word + floating_point_assist_interrupt(processor, cia); + +0.63,6.FRT,11./,16.FRB,21.15,31.Rc:X:f:fctiwz:Floating Convert To Integer Word with round towards Zero +*601: PPC_UNIT_FPU, PPC_UNIT_FPU, 4, 4, 0 +*603: PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 +*603e:PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 +*604: PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 + FPSCR_BEGIN; + convert_to_integer(processor, cia, + frT, *frB, + fpscr_rn_round_towards_zero, 32); + FPSCR_END(Rc); + PPC_INSN_FLOAT(FRT_BITMASK, FRB_BITMASK, Rc); + +0.63,6.FRT,11./,16.FRB,21.846,31.Rc:X:64,f::Floating Convert from Integer Doubleword + int sign = EXTRACTED64(*frB, 0, 0); + int exp = 63; + unsigned64 frac = *frB; + /***/ + if (frac == 0) GOTO(Zero_Operand); + if (sign == 1) frac = ~frac + 1; + while (EXTRACTED64(frac, 0, 0) == 0) { + /*??? do the loop 0 times if (FRB) = max negative integer */ + frac = INSERTED64(EXTRACTED64(frac, 1, 63), 0, 62); + exp = exp - 1; + } + Round_Float(processor, sign, &exp, &frac, FPSCR & fpscr_rn); + if (sign == 0) FPSCR_SET_FPRF(fpscr_rf_pos_normal_number); + if (sign == 1) FPSCR_SET_FPRF(fpscr_rf_pos_normal_number); + *frT = (INSERTED64(sign, 0, 0) + | INSERTED64(exp + 1023, 1, 11) + | INSERTED64(EXTRACTED64(frac, 1, 52), 12, 63)); + GOTO(Done); + /**/ + LABEL(Zero_Operand): + FPSCR_SET_FR(0); + FPSCR_SET_FI(0); + FPSCR_SET_FPRF(fpscr_rf_pos_zero); + *frT = 0; + GOTO(Done); + /**/ + LABEL(Done): + PPC_INSN_FLOAT(FRT_BITMASK, FRB_BITMASK, Rc); + + +# +# I.4.6.7 Floating-Point Compare Instructions +# + +0.63,6.BF,9./,11.FRA,16.FRB,21.0,31./:X:f:fcmpu:Floating Compare Unordered +*601: PPC_UNIT_FPU, PPC_UNIT_FPU, 4, 4, 0 +*603: PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 +*603e:PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 +*604: PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 + FPSCR_BEGIN; + unsigned c; + if (is_NaN(*frA, 0) || is_NaN(*frB, 0)) + c = cr_i_summary_overflow; /* 0b0001 - (FRA) ? (FRB) */ + else if (is_less_than(frA, frB)) + c = cr_i_negative; /* 0b1000 - (FRA) < (FRB) */ + else if (is_greater_than(frA, frB)) + c = cr_i_positive; /* 0b0100 - (FRA) > (FRB) */ + else + c = cr_i_zero; /* 0b0010 - (FRA) = (FRB) */ + FPSCR_SET_FPCC(c); + CR_SET(BF, c); /* CR[4*BF..4*BF+3] = c */ + if (is_SNaN(*frA, 0) || is_SNaN(*frB, 0)) + FPSCR_OR_VX(fpscr_vxsnan); + FPSCR_END(0); + PPC_INSN_FLOAT_CR(0, FRA_BITMASK | FRB_BITMASK, BF_BITMASK); + +0.63,6.BF,9./,11.FRA,16.FRB,21.32,31./:X:f:fcmpo:Floating Compare Ordered +*601: PPC_UNIT_FPU, PPC_UNIT_FPU, 4, 4, 0 +*603: PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 +*603e:PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 +*604: PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 + FPSCR_BEGIN; + unsigned c; + if (is_NaN(*frA, 0) || is_NaN(*frB, 0)) + c = cr_i_summary_overflow; /* 0b0001 - (FRA) ? (FRB) */ + else if (is_less_than(frA, frB)) + c = cr_i_negative; /* 0b1000 - (FRA) < (FRB) */ + else if (is_greater_than(frA, frB)) + c = cr_i_positive; /* 0b0100 - (FRA) > (FRB) */ + else + c = cr_i_zero; /* 0b0010 - (FRA) = (FRB) */ + FPSCR_SET_FPCC(c); + CR_SET(BF, c); /* CR[4*BF..4*BF+3] = c */ + if (is_SNaN(*frA, 0) || is_SNaN(*frB, 0)) { + FPSCR_OR_VX(fpscr_vxsnan); + if ((FPSCR & fpscr_ve) == 0) + FPSCR_OR_VX(fpscr_vxvc); + } + else if (is_QNaN(*frA, 0) || is_QNaN(*frB, 0)) { + FPSCR_OR_VX(fpscr_vxvc); + } + FPSCR_END(0); + PPC_INSN_FLOAT_CR(0, FRA_BITMASK | FRB_BITMASK, BF_BITMASK); + + +# +# I.4.6.8 Floating-Point Status and Control Register Instructions +# + +0.63,6.FRT,11./,16./,21.583,31.Rc:X:f::Move From FPSCR + FPSCR_BEGIN; + *frT = FPSCR; + FPSCR_END(Rc); + +0.63,6.BF,9./,11.BFA,14./,16./,21.64,31./:X:f::Move to Condition Register from FPSCR + FPSCR_BEGIN; + unsigned field = FPSCR_FIELD(BFA); + CR_SET(BF, field); + FPSCR_SET(BFA, 0); /* FPSCR_END fixes up FEX/VX */ + FPSCR_END(0); + +0.64,6.BF,9./,11./,16.U,20./,21.134,31.Rc:X:f::Move To FPSCR Field Immediate + FPSCR_BEGIN; + FPSCR_SET(BF, U); + FPSCR_END(Rc); + +0.63,6./,7.FLM,15./,16.FRB,21.711,31.Rc:XFL:f::Move To FPSCR Fields + FPSCR_BEGIN; + int i; + for (i = 0; i < 8; i++) { + if ((FLM & BIT8(i))) { + FPSCR &= ~MASK32(i*4, i*4+3); + FPSCR |= MASKED32(*frB, i*4, i*4+3); + } + } + FPSCR_END(Rc); + +0.63,6.BT,11./,16./,21.70,31.Rc:X:f::Move To FPSCR Bit 0 + FPSCR_BEGIN; + unsigned32 bit = BIT32(BT); + FPSCR &= ~bit; + FPSCR_END(Rc); + +0.63,6.BT,11./,16./,21.38,31.Rc:X:f::Move To FPSCR Bit 1 + FPSCR_BEGIN; + unsigned32 bit = BIT32(BT); + if (bit & fpscr_fi) + bit |= fpscr_xx; + if ((bit & fpscr_vx_bits)) + bit |= fpscr_fx; + /* note - omit vx bit */ + if ((bit & (fpscr_ox | fpscr_ux | fpscr_zx | fpscr_xx))) + bit |= fpscr_fx; + FPSCR |= bit; + FPSCR_END(Rc); + + +# +# I.A.1.1 Floating-Point Store Instruction +# +0.31,6.FRS,11.RA,16.RB,21.983,31./:X:f,o::Store Floating-Point as Integer Word Indexed + program_interrupt(processor, cia, optional_instruction_program_interrupt); + +# +# I.A.1.2 Floating-Point Arithmetic Instructions +# + +0.63,6.FRT,11./,16.FRB,21./,26.22,31.Rc:A:f,o::Floating Square Root + program_interrupt(processor, cia, optional_instruction_program_interrupt); + +0.59,6.FRT,11./,16.FRB,21./,26.22,31.Rc:A:f,o::Floating Square Root Single + program_interrupt(processor, cia, optional_instruction_program_interrupt); + +0.59,6.FRT,11./,16.FRB,21./,26.24,31.Rc:A:f,o::Floating Reciprocal Estimate Single + program_interrupt(processor, cia, optional_instruction_program_interrupt); + +0.63,6.FRT,11./,16.FRB,21./,26.26,31.Rc:A:f,o::Floating Reciprocal Square Root Estimate + program_interrupt(processor, cia, optional_instruction_program_interrupt); + +# +# I.A.1.3 Floating-Point Select Instruction +# + +0.63,6.FRT,11.FRA,16.FRB,21.FRC,26.23,31.Rc:A:f,o::Floating Select +*601: PPC_UNIT_BAD, PPC_UNIT_BAD, 0, 0, 0 +*603: PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 +*603e:PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 +*604: PPC_UNIT_FPU, PPC_UNIT_FPU, 1, 3, 0 + if (CURRENT_MODEL == MODEL_ppc601) { + program_interrupt(processor, cia, optional_instruction_program_interrupt); + } else { + unsigned64 zero = 0; + FPSCR_BEGIN; + if (is_NaN(*frA, 0) || is_less_than (frA, &zero)) *frT = *frB; + else *frT = *frC; + FPSCR_END(Rc); + PPC_INSN_FLOAT(FRT_BITMASK, FRA_BITMASK | FRB_BITMASK | FRC_BITMASK, Rc); + } + +# +# II.3.2 Cache Management Instructions +# + +0.31,6./,11.RA,16.RB,21.982,31./:X::icbi:Instruction Cache Block Invalidate +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 3, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 3, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 1, 0 + /* blindly flush all instruction cache entries */ + #if WITH_IDECODE_CACHE_SIZE + cpu_flush_icache(processor); + #endif + PPC_INSN_INT(0, (RA_BITMASK & ~1) | RB_BITMASK, 0); + +0.19,6./,11./,16./,21.150,31./:XL::isync:Instruction Synchronize +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 3, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 3, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 1, 0 + cpu_synchronize_context(processor, cia); + PPC_INSN_INT(0, 0, 0); + + +# +# II.3.2.2 Data Cache Instructions +# + +0.31,6./,11.RA,16.RB,21.278,31./:X:::Data Cache Block Touch +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 2, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 2, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 1, 0 + TRACE(trace_tbd,("Data Cache Block Touch\n")); + PPC_INSN_INT(0, (RA_BITMASK & ~1) | RB_BITMASK, 0/*Rc*/); + +0.31,6./,11.RA,16.RB,21.246,31./:X:::Data Cache Block Touch for Store +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 2, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 2, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 3, 0 + TRACE(trace_tbd,("Data Cache Block Touch for Store\n")); + PPC_INSN_INT(0, (RA_BITMASK & ~1) | RB_BITMASK, 0/*Rc*/); + +0.31,6./,11.RA,16.RB,21.1014,31./:X:::Data Cache Block set to Zero +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 10, 10, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 10, 10, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 3, 0 + TRACE(trace_tbd,("Data Cache Block set to Zero\n")); + PPC_INSN_INT(0, (RA_BITMASK & ~1) | RB_BITMASK, 0/*Rc*/); + +0.31,6./,11.RA,16.RB,21.54,31./:X:::Data Cache Block Store +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 5, 5, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 5, 5, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 1, 0 + TRACE(trace_tbd,("Data Cache Block Store\n")); + PPC_INSN_INT(0, (RA_BITMASK & ~1) | RB_BITMASK, 0/*Rc*/); + +0.31,6./,11.RA,16.RB,21.86,31./:X:::Data Cache Block Flush +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 5, 5, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 5, 5, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 1, 0 + TRACE(trace_tbd,("Data Cache Block Flush\n")); + PPC_INSN_INT(0, (RA_BITMASK & ~1) | RB_BITMASK, 0/*Rc*/); + +# +# II.3.3 Enforce In-order Execution of I/O Instruction +# + +0.31,6./,11./,16./,21.854,31./:X::eieio:Enforce In-order Execution of I/O + /* Since this model has no instruction overlap + this instruction need do nothing */ + +# +# II.4.1 Time Base Instructions +# + +0.31,6.RT,11.tbr,21.371,31./:XFX::mftb:Move From Time Base +*603: PPC_UNIT_SRU, PPC_UNIT_SRU, 1, 1, 0 +*603e:PPC_UNIT_SRU, PPC_UNIT_SRU, 1, 1, 0 +*604: PPC_UNIT_MCIU, PPC_UNIT_MCIU, 3, 3, 0 + int n = (tbr{5:9} << 5) | tbr{0:4}; + if (n == 268) { + if (is_64bit_implementation) *rT = TB; + else *rT = EXTRACTED64(TB, 32, 63); + } + else if (n == 269) { + if (is_64bit_implementation) *rT = EXTRACTED64(TB, 0, 31); + else *rT = EXTRACTED64(TB, 0, 31); + } + else + program_interrupt(processor, cia, + illegal_instruction_program_interrupt); + + +# +# III.2.3.1 System Linkage Instructions +# + +0.19,6./,11./,16./,21.50,31./:XL::rfi:Return From Interrupt +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_SRU, PPC_UNIT_SRU, 3, 3, 0 +*603e:PPC_UNIT_SRU, PPC_UNIT_SRU, 3, 3, 0 +*604: PPC_UNIT_MCIU, PPC_UNIT_MCIU, 3, 3, 0 + if (IS_PROBLEM_STATE(processor)) { + program_interrupt(processor, cia, + privileged_instruction_program_interrupt); + } + else { + MSR = (MASKED(SRR1, 0, 32) + | MASKED(SRR1, 37, 41) + | MASKED(SRR1, 48, 63)); + NIA = MASKED(SRR0, 0, 61); + cpu_synchronize_context(processor, cia); + check_masked_interrupts(processor); + } + +# +# III.3.4.1 Move to/from System Register Instructions +# + +#0.31,6.RS,11.SPR,21.467,31./:XFX:::Move To Special Purpose Register +#0.31,6.RT,11.SPR,21.339,31./:XFX:::Move From Special Purpose Register +0.31,6.RS,11./,16./,21.146,31./:X:::Move To Machine State Register +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_SRU, PPC_UNIT_SRU, 2, 2, 0 +*603e:PPC_UNIT_SRU, PPC_UNIT_SRU, 2, 2, 0 +*604: PPC_UNIT_MCIU, PPC_UNIT_MCIU, 1, 1, 0 + if (IS_PROBLEM_STATE(processor)) + program_interrupt(processor, cia, + privileged_instruction_program_interrupt); + else { + MSR = *rS; + check_masked_interrupts(processor); + } + +0.31,6.RT,11./,16./,21.83,31./:X:::Move From Machine State Register +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_SRU, PPC_UNIT_SRU, 1, 1, 0 +*603e:PPC_UNIT_SRU, PPC_UNIT_SRU, 1, 1, 0 +*604: PPC_UNIT_MCIU, PPC_UNIT_MCIU, 3, 3, 0 + if (IS_PROBLEM_STATE(processor)) + program_interrupt(processor, cia, + privileged_instruction_program_interrupt); + else { + *rT = MSR; + check_masked_interrupts(processor); + } + + +# +# III.4.11.1 Cache Management Instructions +# + +0.31,6./,11.RA,16.RB,21.470,31./:X::dcbi:Data Cache Block Invalidate +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_LSU, PPC_UNIT_LSU, 2, 2, 0 +*603e:PPC_UNIT_LSU, PPC_UNIT_LSU, 2, 2, 0 +*604: PPC_UNIT_LSU, PPC_UNIT_LSU, 1, 3, 0 + if (IS_PROBLEM_STATE(processor)) + program_interrupt(processor, cia, + privileged_instruction_program_interrupt); + else + TRACE(trace_tbd,("Data Cache Block Invalidate\n")); + +# +# III.4.11.2 Segment Register Manipulation Instructions +# + +0.31,6.RS,11./,12.SR,16./,21.210,31./:X:32:mtsr %SR,%RS:Move To Segment Register +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_SRU, PPC_UNIT_SRU, 2, 2, 0 +*603e:PPC_UNIT_SRU, PPC_UNIT_SRU, 2, 2, 0 +*604: PPC_UNIT_MCIU, PPC_UNIT_MCIU, 1, 1, 0 + if (IS_PROBLEM_STATE(processor)) + program_interrupt(processor, cia, + privileged_instruction_program_interrupt); + else + SEGREG(SR) = *rS; + +0.31,6.RS,11./,16.RB,21.242,31./:X:32:mtsrin %RS,%RB:Move To Segment Register Indirect +*601: PPC_UNIT_IU, PPC_UNIT_IU, 1, 1, 0 +*603: PPC_UNIT_SRU, PPC_UNIT_SRU, 2, 2, 0 +*603e:PPC_UNIT_SRU, PPC_UNIT_SRU, 2, 2, 0 +*604: PPC_UNIT_MCIU, PPC_UNIT_MCIU, 1, 1, 0 + if (IS_PROBLEM_STATE(processor)) + program_interrupt(processor, cia, + privileged_instruction_program_interrupt); + else + SEGREG(EXTRACTED32(*rB, 0, 3)) = *rS; + +0.31,6.RT,11./,12.SR,16./,21.595,31./:X:32:mfsr %RT,%RS:Move From Segment Register +*601: PPC_UNIT_IU, PPC_UNIT_IU, 2, 2, 0 +*603: PPC_UNIT_SRU, PPC_UNIT_SRU, 3, 3, 0 +*603e:PPC_UNIT_SRU, PPC_UNIT_SRU, 3, 3, 0 +*604: PPC_UNIT_MCIU, PPC_UNIT_MCIU, 1, 1, 0 + if (IS_PROBLEM_STATE(processor)) + program_interrupt(processor, cia, + privileged_instruction_program_interrupt); + else + *rT = SEGREG(SR); + +0.31,6.RT,11./,16.RB,21.659,31./:X:32:mfsrin %RT,%RB:Move From Segment Register Indirect +*601: PPC_UNIT_IU, PPC_UNIT_IU, 2, 2, 0 +*603: PPC_UNIT_SRU, PPC_UNIT_SRU, 3, 3, 0 +*603e:PPC_UNIT_SRU, PPC_UNIT_SRU, 3, 3, 0 +*604: PPC_UNIT_MCIU, PPC_UNIT_MCIU, 1, 1, 0 + if (IS_PROBLEM_STATE(processor)) + program_interrupt(processor, cia, + privileged_instruction_program_interrupt); + else + *rT = SEGREG(EXTRACTED32(*rB, 0, 3)); + + +# +# III.4.11.3 Lookaside Buffer Management Instructions (Optional) +# + +0.31,6./,11./,16.RB,21.434,31./:X:64::SLB Invalidate Entry + +0.31,6./,11./,16./,21.498,31./:X:64::SLB Invalidate All + +0.31,6./,11./,16.RB,21.306,31./:X:::TLB Invalidate Entry + if (IS_PROBLEM_STATE(processor)) + program_interrupt(processor, cia, + privileged_instruction_program_interrupt); + else { + int nr = 0; + cpu *proc; + while (1) { + proc = psim_cpu(cpu_system(processor), nr); + if (proc == NULL) break; + cpu_page_tlb_invalidate_entry(proc, *rB); + nr++; + } + } + +0.31,6./,11./,16./,21.370,31./:X:::TLB Invalidate All + if (IS_PROBLEM_STATE(processor)) + program_interrupt(processor, cia, + privileged_instruction_program_interrupt); + else { + int nr = 0; + cpu *proc; + while (1) { + proc = psim_cpu(cpu_system(processor), nr); + if (proc == NULL) break; + cpu_page_tlb_invalidate_all(proc); + nr++; + } + } + +0.31,6./,11./,16./,21.566,31./:X:::TLB Synchronize + /* nothing happens here - always in sync */ + +# +# III.A.1.2 External Access Instructions +# + +0.31,6.RT,11.RA,16.RB,21.310,31./:X:earwax::External Control In Word Indexed + +0.31,6.RS,11.RA,16.RB,21.438,31./:X:earwax::External Control Out Word Indexed diff --git a/sim/ppc/ppc-spr-table b/sim/ppc/ppc-spr-table new file mode 100644 index 00000000000..93e65325239 --- /dev/null +++ b/sim/ppc/ppc-spr-table @@ -0,0 +1,88 @@ +# +# 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. +# + +# Name:reg-nr:read-only:length +MQ:0:0:0 +XER:1:0:0 +RTCU:4:0:0 +RTCL:5:0:0 +LR:8:0:0 +CTR:9:0:0 +DSISR:18:0:0 +DAR:19:0:0 +DEC:22:0:0 +SDR1:25:0:0 +SRR0:26:0:0 +SRR1:27:0:0 +SPRG0:272:0:0 +SPRG1:273:0:0 +SPRG2:274:0:0 +SPRG3:275:0:0 +EAR:282:0:0 +TBL:284:0:0 +TBU:285:0:0 +PVR:287:0:0 +IBAT0U:528:0:0 +IBAT0L:529:0:0 +IBAT1U:530:0:0 +IBAT1L:531:0:0 +IBAT2U:532:0:0 +IBAT2L:533:0:0 +IBAT3U:534:0:0 +IBAT3L:535:0:0 +DBAT0U:536:0:0 +DBAT0L:537:0:0 +DBAT1U:538:0:0 +DBAT1L:539:0:0 +DBAT2U:540:0:0 +DBAT2L:541:0:0 +DBAT3U:542:0:0 +DBAT3L:543:0:0 +UMMCR0:936:0:0 +UMMCR1:940:0:0 +UPMC1:937:0:0 +UPMC2:938:0:0 +USIA:939:0:0 +UPMC3:941:0:0 +UPMC4:942:0:0 +MMCR0:952:0:0 +PMC1:953:0:0 +PMC2:954:0:0 +SIA:955:0:0 +MMCR1:956:0:0 +PMC3:957:0:0 +PMC4:958:0:0 +DMISS:976:0:0 +DCMP:977:0:0 +HASH1:978:0:0 +HASH2:979:0:0 +IMISS:980:0:0 +ICMP:981:0:0 +RPA:982:0:0 +HID0:1008:0:0 +HID1:1009:0:0 +IABR:1010:0:0 +DABR:1013:0:0 +L2CR:1017:0:0 +ICTC:1019:0:0 +THRM1:1020:0:0 +THRM2:1021:0:0 +THRM3:1022:0:0 +HID15:1023:0:0 diff --git a/sim/ppc/ppc.mt b/sim/ppc/ppc.mt new file mode 100644 index 00000000000..ef3ea683cd2 --- /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/psim.c b/sim/ppc/psim.c new file mode 100644 index 00000000000..81e54b0adf9 --- /dev/null +++ b/sim/ppc/psim.c @@ -0,0 +1,1100 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1997, 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 _PSIM_C_ +#define _PSIM_C_ + +#include "cpu.h" /* includes psim.h */ +#include "idecode.h" +#include "options.h" + +#include "tree.h" + +#include <signal.h> + +#include <stdio.h> +#include <ctype.h> + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#include <setjmp.h> + +#ifdef HAVE_STRING_H +#include <string.h> +#else +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif +#endif + + +#include "bfd.h" + + +/* system structure, actual size of processor array determined at + runtime */ + +struct _psim { + event_queue *events; + device *devices; + mon *monitor; + os_emul *os_emulation; + core *memory; + + /* escape routine for inner functions */ + void *path_to_halt; + void *path_to_restart; + + /* status from last halt */ + psim_status halt_status; + + /* the processors proper */ + int nr_cpus; + int last_cpu; /* CPU that last (tried to) execute an instruction */ + cpu *processors[MAX_NR_PROCESSORS]; +}; + + +int current_target_byte_order; +int current_host_byte_order; +int current_environment; +int current_alignment; +int current_floating_point; +int current_model_issue = MODEL_ISSUE_IGNORE; +int current_stdio = DO_USE_STDIO; +model_enum current_model = WITH_DEFAULT_MODEL; + + +/* create the device tree */ + +INLINE_PSIM\ +(device *) +psim_tree(void) +{ + device *root = tree_parse(NULL, "core"); + tree_parse(root, "/aliases"); + tree_parse(root, "/options"); + tree_parse(root, "/chosen"); + tree_parse(root, "/packages"); + tree_parse(root, "/cpus"); + tree_parse(root, "/openprom"); + tree_parse(root, "/openprom/init"); + tree_parse(root, "/openprom/trace"); + tree_parse(root, "/openprom/options"); + return root; +} + +STATIC_INLINE_PSIM\ +(char *) +find_arg(char *err_msg, + int *ptr_to_argp, + char **argv) +{ + *ptr_to_argp += 1; + if (argv[*ptr_to_argp] == NULL) + error(err_msg); + return argv[*ptr_to_argp]; +} + +INLINE_PSIM\ +(void) +psim_usage(int verbose) +{ + printf_filtered("Usage:\n"); + printf_filtered("\n"); + printf_filtered("\tpsim [ <psim-option> ... ] <image> [ <image-arg> ... ]\n"); + printf_filtered("\n"); + printf_filtered("Where\n"); + printf_filtered("\n"); + printf_filtered("\t<image> Name of the PowerPC program to run.\n"); + if (verbose) { + printf_filtered("\t This can either be a PowerPC binary or\n"); + printf_filtered("\t a text file containing a device tree\n"); + printf_filtered("\t specification.\n"); + printf_filtered("\t PSIM will attempt to determine from the\n"); + printf_filtered("\t specified <image> the intended emulation\n"); + printf_filtered("\t environment.\n"); + printf_filtered("\t If PSIM gets it wrong, the emulation\n"); + printf_filtered("\t environment can be specified using the\n"); + printf_filtered("\t `-e' option (described below).\n"); + printf_filtered("\n"); } + printf_filtered("\t<image-arg> Argument to be passed to <image>\n"); + if (verbose) { + printf_filtered("\t These arguments will be passed to\n"); + printf_filtered("\t <image> (as standard C argv, argc)\n"); + printf_filtered("\t when <image> is started.\n"); + printf_filtered("\n"); } + printf_filtered("\t<psim-option> See below\n"); + printf_filtered("\n"); + printf_filtered("The following are valid <psim-option>s:\n"); + printf_filtered("\n"); + + printf_filtered("\t-c <count> Limit the simulation to <count> iterations\n"); + if (verbose) { + printf_filtered("\n"); + } + + printf_filtered("\t-i or -i2 Print instruction counting statistics\n"); + if (verbose) { + printf_filtered("\t Specify -i2 for a more detailed display\n"); + printf_filtered("\n"); + } + + printf_filtered("\t-I Print execution unit statistics\n"); + if (verbose) { printf_filtered("\n"); } + + printf_filtered("\t-e <os-emul> specify an OS or platform to model\n"); + if (verbose) { + printf_filtered("\t Can be any of the following:\n"); + printf_filtered("\t bug - OEA + MOTO BUG ROM calls\n"); + printf_filtered("\t netbsd - UEA + NetBSD system calls\n"); + printf_filtered("\t solaris - UEA + Solaris system calls\n"); + printf_filtered("\t linux - UEA + Linux system calls\n"); + printf_filtered("\t chirp - OEA + a few OpenBoot calls\n"); + printf_filtered("\n"); } + + printf_filtered("\t-E <endian> Specify the endianness of the target\n"); + if (verbose) { + printf_filtered("\t Can be any of the following:\n"); + printf_filtered("\t big - big endian target\n"); + printf_filtered("\t little - little endian target\n"); + printf_filtered("\n"); } + + printf_filtered("\t-f <file> Merge <file> into the device tree\n"); + if (verbose) { printf_filtered("\n"); } + + printf_filtered("\t-h -? -H give more detailed usage\n"); + if (verbose) { printf_filtered("\n"); } + + printf_filtered("\t-m <model> Specify the processor to model (604)\n"); + if (verbose) { + printf_filtered("\t Selects the processor to use when\n"); + printf_filtered("\t modeling execution units. Includes:\n"); + printf_filtered("\t 604, 603 and 603e\n"); + printf_filtered("\n"); } + + printf_filtered("\t-n <nr-smp> Specify the number of processors in SMP simulations\n"); + if (verbose) { + printf_filtered("\t Specifies the number of processors that are\n"); + printf_filtered("\t to be modeled in a symetric multi-processor (SMP)\n"); + printf_filtered("\t simulation\n"); + printf_filtered("\n"); } + + printf_filtered("\t-o <dev-spec> Add device <dev-spec> to the device tree\n"); + if (verbose) { printf_filtered("\n"); } + + printf_filtered("\t-r <ram-size> Set RAM size in bytes (OEA environments)\n"); + if (verbose) { printf_filtered("\n"); } + + printf_filtered("\t-t [!]<trace> Enable (disable) <trace> option\n"); + if (verbose) { printf_filtered("\n"); } + + printf_filtered("\n"); + trace_usage(verbose); + device_usage(verbose); + if (verbose > 1) { + printf_filtered("\n"); + print_options(); + } + error(""); +} + +/* Test "string" for containing a string of digits that form a number +between "min" and "max". The return value is the number of "err". */ +static +int is_num( char *string, int min, int max, int err) +{ + int result = 0; + + for ( ; *string; ++string) + { + if (!isdigit(*string)) + { + result = err; + break; + } + result = result * 10 + (*string - '0'); + } + if (result < min || result > max) + result = err; + + return result; +} + +INLINE_PSIM\ +(char **) +psim_options(device *root, + char **argv) +{ + device *current = root; + int argp; + if (argv == NULL) + return NULL; + argp = 0; + while (argv[argp] != NULL && argv[argp][0] == '-') { + char *p = argv[argp] + 1; + char *param; + while (*p != '\0') { + switch (*p) { + default: + psim_usage(0); + error (""); + break; + case 'c': + param = find_arg("Missing <count> option for -c (max-iterations)\n", &argp, argv); + tree_parse(root, "/openprom/options/max-iterations %s", param); + break; + case 'e': + param = find_arg("Missing <emul> option for -e (os-emul)\n", &argp, argv); + tree_parse(root, "/openprom/options/os-emul %s", param); + break; + case 'E': + /* endian spec, ignored for now */ + param = find_arg("Missing <endian> option for -E (target-endian)\n", &argp, argv); + if (strcmp (param, "big") == 0) + tree_parse (root, "/options/little-endian? false"); + else if (strcmp (param, "little") == 0) + tree_parse (root, "/options/little-endian? true"); + else + { + printf_filtered ("Invalid <endian> option for -E (target-endian)\n"); + psim_usage (0); + } + break; + case 'f': + param = find_arg("Missing <file> option for -f\n", &argp, argv); + psim_merge_device_file(root, param); + break; + case 'h': + case '?': + psim_usage(1); + break; + case 'H': + psim_usage(2); + break; + case 'i': + if (isdigit(p[1])) { + tree_parse(root, "/openprom/trace/print-info %c", p[1]); + p++; + } + else { + tree_parse(root, "/openprom/trace/print-info 1"); + } + break; + case 'I': + tree_parse(root, "/openprom/trace/print-info 2"); + tree_parse(root, "/openprom/options/model-issue %d", + MODEL_ISSUE_PROCESS); + break; + case 'm': + param = find_arg("Missing <model> option for -m (model)\n", &argp, argv); + tree_parse(root, "/openprom/options/model \"%s", param); + break; + case 'n': + param = find_arg("Missing <nr-smp> option for -n (smp)\n", &argp, argv); + tree_parse(root, "/openprom/options/smp %s", param); + break; + case 'o': + param = find_arg("Missing <dev-spec> option for -o\n", &argp, argv); +#ifdef WITH_OPTION_MPC860C0 + if (memcmp(param, "mpc860c0", 8) == 0) + { + if (param[8] == '\0') + tree_parse(root, "/options/mpc860c0 5"); + else if (param[8] == '=' && is_num(param+9, 1, 10, 0)) + { + tree_parse(root, "/options/mpc860c0 %s", param+9); + } + else error("Invalid mpc860c0 option for -o\n"); + } + else +#endif // WITH_OPTION_MPC860C0 + current = tree_parse(current, "%s", param); + break; + case 'r': + param = find_arg("Missing <ram-size> option for -r (oea-memory-size)\n", &argp, argv); + tree_parse(root, "/openprom/options/oea-memory-size %s", + param); + break; + case 't': + param = find_arg("Missing <trace> option for -t (trace/*)\n", &argp, argv); + if (param[0] == '!') + tree_parse(root, "/openprom/trace/%s 0", param+1); + else + tree_parse(root, "/openprom/trace/%s 1", param); + break; + } + p += 1; + } + argp += 1; + } + /* force the trace node to process its options now *before* the tree + initialization occures */ + device_ioctl(tree_find_device(root, "/openprom/trace"), + NULL, 0, + device_ioctl_set_trace); + +#ifdef WITH_OPTION_MPC860C0 + semantic_init(root); +#endif // WITH_OPTION_MPC860C0 + + /* return where the options end */ + return argv + argp; +} + +INLINE_PSIM\ +(void) +psim_command(device *root, + char **argv) +{ + int argp = 0; + if (argv[argp] == NULL) { + return; + } + else if (strcmp(argv[argp], "trace") == 0) { + const char *opt = find_arg("Missing <trace> option", &argp, argv); + if (opt[0] == '!') + trace_option(opt + 1, 0); + else + trace_option(opt, 1); + } + else if (strcmp(*argv, "change-media") == 0) { + char *device = find_arg("Missing device name", &argp, argv); + char *media = argv[++argp]; + device_ioctl(tree_find_device(root, device), NULL, 0, + device_ioctl_change_media, media); + } + else { + printf_filtered("Unknown PSIM command %s, try\n", argv[argp]); + printf_filtered(" trace <trace-option>\n"); + printf_filtered(" change-media <device> [ <new-image> ]\n"); + } +} + + +/* create the simulator proper from the device tree and executable */ + +INLINE_PSIM\ +(psim *) +psim_create(const char *file_name, + device *root) +{ + int cpu_nr; + const char *env; + psim *system; + os_emul *os_emulation; + int nr_cpus; + + /* given this partially populated device tree, os_emul_create() uses + it and file_name to determine the selected emulation and hence + further populate the tree with any other required nodes. */ + + os_emulation = os_emul_create(file_name, root); + if (os_emulation == NULL) + error("psim: either file %s was not reconized or unreconized or unknown os-emulation type\n", file_name); + + /* fill in the missing real number of CPU's */ + nr_cpus = tree_find_integer_property(root, "/openprom/options/smp"); + if (MAX_NR_PROCESSORS < nr_cpus) + error("target and configured number of cpus conflict\n"); + + /* fill in the missing TARGET BYTE ORDER information */ + current_target_byte_order + = (tree_find_boolean_property(root, "/options/little-endian?") + ? LITTLE_ENDIAN + : BIG_ENDIAN); + if (CURRENT_TARGET_BYTE_ORDER != current_target_byte_order) + error("target and configured byte order conflict\n"); + + /* fill in the missing HOST BYTE ORDER information */ + current_host_byte_order = (current_host_byte_order = 1, + (*(char*)(¤t_host_byte_order) + ? LITTLE_ENDIAN + : BIG_ENDIAN)); + if (CURRENT_HOST_BYTE_ORDER != current_host_byte_order) + error("host and configured byte order conflict\n"); + + /* fill in the missing OEA/VEA information */ + env = tree_find_string_property(root, "/openprom/options/env"); + current_environment = ((strcmp(env, "user") == 0 + || strcmp(env, "uea") == 0) + ? USER_ENVIRONMENT + : (strcmp(env, "virtual") == 0 + || strcmp(env, "vea") == 0) + ? VIRTUAL_ENVIRONMENT + : (strcmp(env, "operating") == 0 + || strcmp(env, "oea") == 0) + ? OPERATING_ENVIRONMENT + : 0); + if (current_environment == 0) + error("unreconized /options env property\n"); + if (CURRENT_ENVIRONMENT != current_environment) + error("target and configured environment conflict\n"); + + /* fill in the missing ALLIGNMENT information */ + current_alignment + = (tree_find_boolean_property(root, "/openprom/options/strict-alignment?") + ? STRICT_ALIGNMENT + : NONSTRICT_ALIGNMENT); + if (CURRENT_ALIGNMENT != current_alignment) + error("target and configured alignment conflict\n"); + + /* fill in the missing FLOATING POINT information */ + current_floating_point + = (tree_find_boolean_property(root, "/openprom/options/floating-point?") + ? HARD_FLOATING_POINT + : SOFT_FLOATING_POINT); + if (CURRENT_FLOATING_POINT != current_floating_point) + error("target and configured floating-point conflict\n"); + + /* fill in the missing STDIO information */ + current_stdio + = (tree_find_boolean_property(root, "/openprom/options/use-stdio?") + ? DO_USE_STDIO + : DONT_USE_STDIO); + if (CURRENT_STDIO != current_stdio) + error("target and configured stdio interface conflict\n"); + + /* sort out the level of detail for issue modeling */ + current_model_issue + = tree_find_integer_property(root, "/openprom/options/model-issue"); + if (CURRENT_MODEL_ISSUE != current_model_issue) + error("target and configured model-issue conflict\n"); + + /* sort out our model architecture - wrong. + + FIXME: this should be obtaining the required information from the + device tree via the "/chosen" property "cpu" which is an instance + (ihandle) for the only executing processor. By converting that + ihandle into the corresponding cpu's phandle and then querying + the "name" property, the cpu type can be determined. Ok? */ + + model_set(tree_find_string_property(root, "/openprom/options/model")); + + /* create things */ + system = ZALLOC(psim); + system->events = event_queue_create(); + system->memory = core_from_device(root); + system->monitor = mon_create(); + system->nr_cpus = nr_cpus; + system->os_emulation = os_emulation; + system->devices = root; + + /* now all the processors attaching to each their per-cpu information */ + for (cpu_nr = 0; cpu_nr < MAX_NR_PROCESSORS; cpu_nr++) { + system->processors[cpu_nr] = cpu_create(system, + system->memory, + mon_cpu(system->monitor, + cpu_nr), + system->os_emulation, + cpu_nr); + } + + /* dump out the contents of the device tree */ + if (ppc_trace[trace_print_device_tree] || ppc_trace[trace_dump_device_tree]) + tree_print(root); + if (ppc_trace[trace_dump_device_tree]) + error(""); + + return system; +} + + +/* allow the simulation to stop/restart abnormaly */ + +INLINE_PSIM\ +(void) +psim_set_halt_and_restart(psim *system, + void *halt_jmp_buf, + void *restart_jmp_buf) +{ + system->path_to_halt = halt_jmp_buf; + system->path_to_restart = restart_jmp_buf; +} + +INLINE_PSIM\ +(void) +psim_clear_halt_and_restart(psim *system) +{ + system->path_to_halt = NULL; + system->path_to_restart = NULL; +} + +INLINE_PSIM\ +(void) +psim_restart(psim *system, + int current_cpu) +{ + ASSERT(current_cpu >= 0 && current_cpu < system->nr_cpus); + ASSERT(system->path_to_restart != NULL); + system->last_cpu = current_cpu; + longjmp(*(jmp_buf*)(system->path_to_restart), current_cpu + 1); +} + + +static void +cntrl_c_simulation(void *data) +{ + psim *system = data; + psim_halt(system, + psim_nr_cpus(system), + was_continuing, + SIGINT); +} + +INLINE_PSIM\ +(void) +psim_stop(psim *system) +{ + event_queue_schedule_after_signal(psim_event_queue(system), + 0 /*NOW*/, + cntrl_c_simulation, + system); +} + +INLINE_PSIM\ +(void) +psim_halt(psim *system, + int current_cpu, + stop_reason reason, + int signal) +{ + ASSERT(current_cpu >= 0 && current_cpu <= system->nr_cpus); + ASSERT(system->path_to_halt != NULL); + system->last_cpu = current_cpu; + system->halt_status.reason = reason; + system->halt_status.signal = signal; + if (current_cpu == system->nr_cpus) { + system->halt_status.cpu_nr = 0; + system->halt_status.program_counter = + cpu_get_program_counter(system->processors[0]); + } + else { + system->halt_status.cpu_nr = current_cpu; + system->halt_status.program_counter = + cpu_get_program_counter(system->processors[current_cpu]); + } + longjmp(*(jmp_buf*)(system->path_to_halt), current_cpu + 1); +} + + +INLINE_PSIM\ +(int) +psim_last_cpu(psim *system) +{ + return system->last_cpu; +} + +INLINE_PSIM\ +(int) +psim_nr_cpus(psim *system) +{ + return system->nr_cpus; +} + +INLINE_PSIM\ +(psim_status) +psim_get_status(psim *system) +{ + return system->halt_status; +} + + +INLINE_PSIM\ +(cpu *) +psim_cpu(psim *system, + int cpu_nr) +{ + if (cpu_nr < 0 || cpu_nr >= system->nr_cpus) + return NULL; + else + return system->processors[cpu_nr]; +} + + +INLINE_PSIM\ +(device *) +psim_device(psim *system, + const char *path) +{ + return tree_find_device(system->devices, path); +} + +INLINE_PSIM\ +(event_queue *) +psim_event_queue(psim *system) +{ + return system->events; +} + + + +STATIC_INLINE_PSIM\ +(void) +psim_max_iterations_exceeded(void *data) +{ + psim *system = data; + psim_halt(system, + system->nr_cpus, /* halted during an event */ + was_signalled, + -1); +} + + +INLINE_PSIM\ +(void) +psim_init(psim *system) +{ + int cpu_nr; + + /* scrub the monitor */ + mon_init(system->monitor, system->nr_cpus); + + /* trash any pending events */ + event_queue_init(system->events); + + /* if needed, schedule a halt event. FIXME - In the future this + will be replaced by a more generic change to psim_command(). A + new command `schedule NNN halt' being added. */ + if (tree_find_property(system->devices, "/openprom/options/max-iterations")) { + event_queue_schedule(system->events, + tree_find_integer_property(system->devices, + "/openprom/options/max-iterations") - 2, + psim_max_iterations_exceeded, + system); + } + + /* scrub all the cpus */ + for (cpu_nr = 0; cpu_nr < system->nr_cpus; cpu_nr++) + cpu_init(system->processors[cpu_nr]); + + /* init all the devices (which updates the cpus) */ + tree_init(system->devices, system); + + /* and the emulation (which needs an initialized device tree) */ + os_emul_init(system->os_emulation, system->nr_cpus); + + /* now sync each cpu against the initialized state of its registers */ + for (cpu_nr = 0; cpu_nr < system->nr_cpus; cpu_nr++) { + cpu *processor = system->processors[cpu_nr]; + cpu_synchronize_context(processor, cpu_get_program_counter(processor)); + cpu_page_tlb_invalidate_all(processor); + } + + /* force loop to start with first cpu */ + system->last_cpu = -1; +} + +INLINE_PSIM\ +(void) +psim_stack(psim *system, + char **argv, + char **envp) +{ + /* pass the stack device the argv/envp and let it work out what to + do with it */ + device *stack_device = tree_find_device(system->devices, + "/openprom/init/stack"); + if (stack_device != (device*)0) { + unsigned_word stack_pointer; + psim_read_register(system, 0, &stack_pointer, "sp", cooked_transfer); + device_ioctl(stack_device, + NULL, /*cpu*/ + 0, /*cia*/ + device_ioctl_create_stack, + stack_pointer, + argv, + envp); + } +} + + + +/* SIMULATE INSTRUCTIONS, various different ways of achieving the same + thing */ + +INLINE_PSIM\ +(void) +psim_step(psim *system) +{ + volatile int keep_running = 0; + idecode_run_until_stop(system, &keep_running, + system->events, system->processors, system->nr_cpus); +} + +INLINE_PSIM\ +(void) +psim_run(psim *system) +{ + idecode_run(system, + system->events, system->processors, system->nr_cpus); +} + + +/* storage manipulation functions */ + +INLINE_PSIM\ +(void) +psim_read_register(psim *system, + int which_cpu, + void *buf, + const char reg[], + transfer_mode mode) +{ + register_descriptions description; + char cooked_buf[sizeof(unsigned_8)]; + cpu *processor; + + /* find our processor */ + if (which_cpu == MAX_NR_PROCESSORS) { + if (system->last_cpu == system->nr_cpus + || system->last_cpu == -1) + which_cpu = 0; + else + which_cpu = system->last_cpu; + } + ASSERT(which_cpu >= 0 && which_cpu < system->nr_cpus); + + processor = system->processors[which_cpu]; + + /* find the register description */ + description = register_description(reg); + if (description.type == reg_invalid) + error("psim_read_register() invalid register name `%s'\n", reg); + + /* get the cooked value */ + switch (description.type) { + + case reg_gpr: + *(gpreg*)cooked_buf = cpu_registers(processor)->gpr[description.index]; + break; + + case reg_spr: + *(spreg*)cooked_buf = cpu_registers(processor)->spr[description.index]; + break; + + case reg_sr: + *(sreg*)cooked_buf = cpu_registers(processor)->sr[description.index]; + break; + + case reg_fpr: + *(fpreg*)cooked_buf = cpu_registers(processor)->fpr[description.index]; + break; + + case reg_pc: + *(unsigned_word*)cooked_buf = cpu_get_program_counter(processor); + break; + + case reg_cr: + *(creg*)cooked_buf = cpu_registers(processor)->cr; + break; + + case reg_msr: + *(msreg*)cooked_buf = cpu_registers(processor)->msr; + break; + + case reg_fpscr: + *(fpscreg*)cooked_buf = cpu_registers(processor)->fpscr; + break; + + case reg_insns: + *(unsigned_word*)cooked_buf = mon_get_number_of_insns(system->monitor, + which_cpu); + break; + + case reg_stalls: + if (cpu_model(processor) == NULL) + error("$stalls only valid if processor unit model enabled (-I)\n"); + *(unsigned_word*)cooked_buf = model_get_number_of_stalls(cpu_model(processor)); + break; + + case reg_cycles: + if (cpu_model(processor) == NULL) + error("$cycles only valid if processor unit model enabled (-I)\n"); + *(unsigned_word*)cooked_buf = model_get_number_of_cycles(cpu_model(processor)); + break; + + default: + printf_filtered("psim_read_register(processor=0x%lx,buf=0x%lx,reg=%s) %s\n", + (unsigned long)processor, (unsigned long)buf, reg, + "read of this register unimplemented"); + break; + + } + + /* the PSIM internal values are in host order. To fetch raw data, + they need to be converted into target order and then returned */ + if (mode == raw_transfer) { + /* FIXME - assumes that all registers are simple integers */ + switch (description.size) { + case 1: + *(unsigned_1*)buf = H2T_1(*(unsigned_1*)cooked_buf); + break; + case 2: + *(unsigned_2*)buf = H2T_2(*(unsigned_2*)cooked_buf); + break; + case 4: + *(unsigned_4*)buf = H2T_4(*(unsigned_4*)cooked_buf); + break; + case 8: + *(unsigned_8*)buf = H2T_8(*(unsigned_8*)cooked_buf); + break; + } + } + else { + memcpy(buf/*dest*/, cooked_buf/*src*/, description.size); + } + +} + + + +INLINE_PSIM\ +(void) +psim_write_register(psim *system, + int which_cpu, + const void *buf, + const char reg[], + transfer_mode mode) +{ + cpu *processor; + register_descriptions description; + char cooked_buf[sizeof(unsigned_8)]; + + /* find our processor */ + if (which_cpu == MAX_NR_PROCESSORS) { + if (system->last_cpu == system->nr_cpus + || system->last_cpu == -1) + which_cpu = 0; + else + which_cpu = system->last_cpu; + } + if (which_cpu == -1) { + int i; + for (i = 0; i < system->nr_cpus; i++) + psim_write_register(system, i, buf, reg, mode); + return; + } + ASSERT(which_cpu >= 0 && which_cpu < system->nr_cpus); + + processor = system->processors[which_cpu]; + + /* find the description of the register */ + description = register_description(reg); + if (description.type == reg_invalid) + error("psim_write_register() invalid register name %s\n", reg); + + /* If the data is comming in raw (target order), need to cook it + into host order before putting it into PSIM's internal structures */ + if (mode == raw_transfer) { + switch (description.size) { + case 1: + *(unsigned_1*)cooked_buf = T2H_1(*(unsigned_1*)buf); + break; + case 2: + *(unsigned_2*)cooked_buf = T2H_2(*(unsigned_2*)buf); + break; + case 4: + *(unsigned_4*)cooked_buf = T2H_4(*(unsigned_4*)buf); + break; + case 8: + *(unsigned_8*)cooked_buf = T2H_8(*(unsigned_8*)buf); + break; + } + } + else { + memcpy(cooked_buf/*dest*/, buf/*src*/, description.size); + } + + /* put the cooked value into the register */ + switch (description.type) { + + case reg_gpr: + cpu_registers(processor)->gpr[description.index] = *(gpreg*)cooked_buf; + break; + + case reg_fpr: + cpu_registers(processor)->fpr[description.index] = *(fpreg*)cooked_buf; + break; + + case reg_pc: + cpu_set_program_counter(processor, *(unsigned_word*)cooked_buf); + break; + + case reg_spr: + cpu_registers(processor)->spr[description.index] = *(spreg*)cooked_buf; + break; + + case reg_sr: + cpu_registers(processor)->sr[description.index] = *(sreg*)cooked_buf; + break; + + case reg_cr: + cpu_registers(processor)->cr = *(creg*)cooked_buf; + break; + + case reg_msr: + cpu_registers(processor)->msr = *(msreg*)cooked_buf; + break; + + case reg_fpscr: + cpu_registers(processor)->fpscr = *(fpscreg*)cooked_buf; + break; + + default: + printf_filtered("psim_write_register(processor=0x%lx,cooked_buf=0x%lx,reg=%s) %s\n", + (unsigned long)processor, (unsigned long)cooked_buf, reg, + "read of this register unimplemented"); + break; + + } + +} + + + +INLINE_PSIM\ +(unsigned) +psim_read_memory(psim *system, + int which_cpu, + void *buffer, + unsigned_word vaddr, + unsigned nr_bytes) +{ + cpu *processor; + if (which_cpu == MAX_NR_PROCESSORS) { + if (system->last_cpu == system->nr_cpus + || system->last_cpu == -1) + which_cpu = 0; + else + which_cpu = system->last_cpu; + } + processor = system->processors[which_cpu]; + return vm_data_map_read_buffer(cpu_data_map(processor), + buffer, vaddr, nr_bytes, + NULL, -1); +} + + +INLINE_PSIM\ +(unsigned) +psim_write_memory(psim *system, + int which_cpu, + const void *buffer, + unsigned_word vaddr, + unsigned nr_bytes, + int violate_read_only_section) +{ + cpu *processor; + if (which_cpu == MAX_NR_PROCESSORS) { + if (system->last_cpu == system->nr_cpus + || system->last_cpu == -1) + which_cpu = 0; + else + which_cpu = system->last_cpu; + } + ASSERT(which_cpu >= 0 && which_cpu < system->nr_cpus); + processor = system->processors[which_cpu]; + return vm_data_map_write_buffer(cpu_data_map(processor), + buffer, vaddr, nr_bytes, 1/*violate-read-only*/, + NULL, -1); +} + + +INLINE_PSIM\ +(void) +psim_print_info(psim *system, + int verbose) +{ + mon_print_info(system, system->monitor, verbose); +} + + +/* Merge a device tree and a device file. */ + +INLINE_PSIM\ +(void) +psim_merge_device_file(device *root, + const char *file_name) +{ + FILE *description; + int line_nr; + char device_path[1000]; + device *current; + + /* try opening the file */ + description = fopen(file_name, "r"); + if (description == NULL) { + perror(file_name); + error("Invalid file %s specified", file_name); + } + + line_nr = 0; + current = root; + while (fgets(device_path, sizeof(device_path), description)) { + char *device; + /* check that the full line was read */ + if (strchr(device_path, '\n') == NULL) { + fclose(description); + error("%s:%d: line to long - %s", + file_name, line_nr, device_path); + } + else + *strchr(device_path, '\n') = '\0'; + line_nr++; + /* skip comments ("#" or ";") and blank lines lines */ + for (device = device_path; + *device != '\0' && isspace(*device); + device++); + if (device[0] == '#' + || device[0] == ';' + || device[0] == '\0') + continue; + /* merge any appended lines */ + while (device_path[strlen(device_path) - 1] == '\\') { + int curlen = strlen(device_path) - 1; + /* zap \ */ + device_path[curlen] = '\0'; + /* append the next line */ + if (!fgets(device_path + curlen, sizeof(device_path) - curlen, description)) { + fclose(description); + error("%s:%s: unexpected eof in line continuation - %s", + file_name, line_nr, device_path); + } + if (strchr(device_path, '\n') == NULL) { + fclose(description); + error("%s:%d: line to long - %s", + file_name, line_nr, device_path); + } + else + *strchr(device_path, '\n') = '\0'; + line_nr++; + } + /* parse this line */ + current = tree_parse(current, "%s", device); + } + fclose(description); +} + + +#endif /* _PSIM_C_ */ diff --git a/sim/ppc/psim.h b/sim/ppc/psim.h new file mode 100644 index 00000000000..f0cf3a5d8b4 --- /dev/null +++ b/sim/ppc/psim.h @@ -0,0 +1,181 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1996, 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 _PSIM_H_ +#define _PSIM_H_ + +#include "basics.h" + + +/* the system object */ +/* typedef struct _psim psim; */ +/* typedef struct _device device; */ + +/* when the `system' stops, find out why. FIXME - at this point this + is really a bit puzzling. After all, how can there be a status + when there several processors involved */ + +typedef struct _psim_status { + int cpu_nr; + stop_reason reason; + int signal; + unsigned_word program_counter; +} psim_status; + + +/* create an initial device tree and then populate it using + information obtained from either the command line or a file */ + +extern device *psim_tree +(void); + +extern char **psim_options +(device *root, + char **argv); + +extern void psim_command +(device *root, + char **argv); + + +extern void psim_merge_device_file +(device *root, + const char *file_name); + +extern void psim_usage +(int verbose); + + +/* create a new simulator from the device tree */ + +extern psim *psim_create +(const char *file_name, + device *root); + + +/* Given the created simulator (re) initialize it */ + +extern void psim_init +(psim *system); + +extern void psim_stack +(psim *system, + char **argv, + char **envp); + + +/* Run/stop the system */ + +extern void psim_step +(psim *system); + +extern void psim_run +(psim *system); + +extern void psim_restart +(psim *system, + int cpu_nr); + +extern void psim_set_halt_and_restart +(psim *system, + void *halt_jmp_buf, + void *restart_jmp_buf); + +extern void psim_clear_halt_and_restart +(psim *system); + +extern void psim_stop +(psim *system); + +extern void psim_halt +(psim *system, + int cpu_nr, + stop_reason reason, + int signal); + +extern int psim_last_cpu +(psim *system); + +extern int psim_nr_cpus +(psim *system); + + +extern psim_status psim_get_status +(psim *system); + + +/* reveal the internals of the simulation. Grant access to the + processor (cpu) device tree (device) and events (event_queue). */ + +extern cpu *psim_cpu +(psim *system, + int cpu_nr); + +extern device *psim_device +(psim *system, + const char *path); + +extern event_queue *psim_event_queue +(psim *system); + + + +/* manipulate the state (registers or memory) of a processor within + the system. In the case of memory, the read/write is performed + using the specified processors address translation tables. + + Where applicable, WHICH_CPU == -1 indicates all processors and + WHICH_CPU == <nr_cpus> indicates the `current' processor. */ + +extern void psim_read_register +(psim *system, + int which_cpu, + void *host_ordered_buf, + const char reg[], + transfer_mode mode); + +extern void psim_write_register +(psim *system, + int which_cpu, + const void *buf, + const char reg[], + transfer_mode mode); + +extern unsigned psim_read_memory +(psim *system, + int which_cpu, + void *buf, + unsigned_word vaddr, + unsigned len); + +extern unsigned psim_write_memory +(psim *system, + int which_cpu, + const void *buf, + unsigned_word vaddr, + unsigned len, + int violate_read_only_section); + +extern void psim_print_info +(psim *system, + int verbose); + +#endif /* _PSIM_H_ */ diff --git a/sim/ppc/psim.texinfo b/sim/ppc/psim.texinfo new file mode 100644 index 00000000000..f81e46099c4 --- /dev/null +++ b/sim/ppc/psim.texinfo @@ -0,0 +1,1105 @@ +\input texinfo.tex @c -*-texinfo-*- +@comment %**start of header +@setfilename texinfo +@settitle Texinfo @value{edition} +@syncodeindex vr fn +@footnotestyle separate +@paragraphindent 2 +@smallbook +@comment %**end of header + +@c Set smallbook if printing in smallbook format so the example of the +@c smallbook font is actually written using smallbook; in bigbook, a kludge +@c is used for TeX output. +@set smallbook +@c @@clear smallbook + +@ignore +@ifinfo +@format +START-INFO-DIR-ENTRY +* Texinfo: (texinfo). The documentation format for the GNU Project. +END-INFO-DIR-ENTRY +@end format +@end ifinfo +@end ignore + +@set edition 2.21 +@set update-date 7 June 1995 +@set update-month June 1995 + +@c Experiment with smaller amounts of whitespace between chapters +@c and sections. +@tex +\global\chapheadingskip = 15pt plus 4pt minus 2pt +\global\secheadingskip = 12pt plus 3pt minus 2pt +\global\subsecheadingskip = 9pt plus 2pt minus 2pt +@end tex + +@c Experiment with smaller amounts of whitespace between paragraphs in +@c the 8.5 by 11 inch format. +@ifclear smallbook +@tex +\global\parskip 6pt plus 1pt +@end tex +@end ifclear + +@finalout + +@c Currently undocumented command, 5 December 1993: +@c +@c nwnode (Same as node, but no warnings; for `makeinfo'.) + +@ifinfo +This file documents Texinfo, a documentation system that uses a single +source file to produce both on-line information and a printed manual. + +Copyright (C) 1988, 1990, 1991, 1992, 1993, 1995 Free Software Foundation, Inc. + +This is the second edition of the Texinfo documentation,@* +and is consistent with version 2 of @file{texinfo.tex}. + +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +are preserved on all copies. + +@ignore +Permission is granted to process this file through TeX and print the +results, provided the printed document carries copying permission +notice identical to this one except for the removal of this paragraph +(this paragraph not being relevant to the printed manual). + +@end ignore +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the entire +resulting derived work is distributed under the terms of a permission +notice identical to this one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions, +except that this permission notice may be stated in a translation approved +by the Free Software Foundation. +@end ifinfo + +@setchapternewpage odd + +@shorttitlepage Texinfo + +@titlepage +@c use the new format for titles +@title Texinfo +@subtitle The GNU Documentation Format +@subtitle Edition @value{edition}, for Texinfo Version Three +@subtitle @value{update-month} + +@author by Robert J. Chassell and Richard M. Stallman + +@comment Include the Distribution inside the titlepage so +@c that headings are turned off. + +@page +@vskip 0pt plus 1filll +Copyright @copyright{} 1988, 1990, 1991, 1992, 1993, 1995 Free Software Foundation, Inc. + +@sp 2 +This is the second edition of the Texinfo documentation,@* +and is consistent with version 2 of @file{texinfo.tex}. +@sp 2 + +Published by the Free Software Foundation @* +59 Temple Place Suite 330, @* +Boston, MA 02111-1307 USA @* +Printed copies are available for $15 each.@* +ISBN 1-882114-63-9 +@c ISBN number 1-882114-63-9 is for edition 2.20 of 28 February 1995 + +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +are preserved on all copies. + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the entire +resulting derived work is distributed under the terms of a permission +notice identical to this one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions, +except that this permission notice may be stated in a translation approved +by the Free Software Foundation. +@sp 2 +Cover art by Etienne Suvasa. +@end titlepage + +@ifinfo +@node Top, Copying, (dir), (dir) +@top Texinfo + +Texinfo is a documentation system that uses a single source file to +produce both on-line information and printed output.@refill + +The first part of this master menu lists the major nodes in this Info +document, including the @@-command and concept indices. The rest of +the menu lists all the lower level nodes in the document.@refill + +This is Edition @value{edition} of the Texinfo documentation, +@w{@value{update-date},} for Texinfo Version Three. +@end ifinfo + +@c Here is a spare copy of the chapter menu entry descriptions, +@c in case they are accidently deleted +@ignore +Your rights. +Texinfo in brief. +How to use Texinfo mode. +What is at the beginning of a Texinfo file? +What is at the end of a Texinfo file? +How to create chapters, sections, subsections, + appendices, and other parts. +How to provide structure for a document. +How to write nodes. +How to write menus. +How to write cross references. +How to mark words and phrases as code, + keyboard input, meta-syntactic + variables, and the like. +How to write quotations, examples, etc. +How to write lists and tables. +How to create indices. +How to insert @@-signs, braces, etc. +How to indicate results of evaluation, + expansion of macros, errors, etc. +How to force and prevent line and page breaks. +How to describe functions and the like in a uniform manner. +How to write footnotes. +How to specify text for either @TeX{} or Info. +How to print hardcopy. +How to create an Info file. +How to install an Info file +A list of all the Texinfo @@-commands. +Hints on how to write a Texinfo document. +A sample Texinfo file to look at. +Tell readers they have the right to copy + and distribute. +How to incorporate other Texinfo files. +How to write page headings and footings. +How to find formatting mistakes. +All about paragraph refilling. +A description of @@-Command syntax. +Texinfo second edition features. +A menu containing commands and variables. +A menu covering many topics. +@end ignore + +@menu +* Copying:: Your rights. +* Overview:: Texinfo in brief. +* Texinfo Mode:: How to use Texinfo mode. +* Beginning a File:: What is at the beginning of a Texinfo file? +* Ending a File:: What is at the end of a Texinfo file? +* Structuring:: How to create chapters, sections, subsections, + appendices, and other parts. +* Nodes:: How to write nodes. +* Menus:: How to write menus. +* Cross References:: How to write cross references. +* Marking Text:: How to mark words and phrases as code, + keyboard input, meta-syntactic + variables, and the like. +* Quotations and Examples:: How to write quotations, examples, etc. +* Lists and Tables:: How to write lists and tables. +* Indices:: How to create indices. +* Insertions:: How to insert @@-signs, braces, etc. +* Glyphs:: How to indicate results of evaluation, + expansion of macros, errors, etc. +* Breaks:: How to force and prevent line and page breaks. +* Definition Commands:: How to describe functions and the like + in a uniform manner. +* Footnotes:: How to write footnotes. +* Conditionals:: How to specify text for either @TeX{} or Info. +* Format/Print Hardcopy:: How to convert a Texinfo file to a file + for printing and how to print that file. +* Create an Info File:: Convert a Texinfo file into an Info file. +* Install an Info File:: Make an Info file accessible to users. +* Command List:: All the Texinfo @@-commands. +* Tips:: Hints on how to write a Texinfo document. +* Sample Texinfo File:: A sample Texinfo file to look at. +* Sample Permissions:: Tell readers they have the right to copy + and distribute. +* Include Files:: How to incorporate other Texinfo files. +* Headings:: How to write page headings and footings. +* Catching Mistakes:: How to find formatting mistakes. +* Refilling Paragraphs:: All about paragraph refilling. +* Command Syntax:: A description of @@-Command syntax. +* Obtaining TeX:: How to Obtain @TeX{}. +* New Features:: Texinfo second edition features. +* Command and Variable Index:: A menu containing commands and variables. +* Concept Index:: A menu covering many topics. + + --- The Detailed Node Listing --- + +Overview of Texinfo + +* Using Texinfo:: Create a conventional printed book + or an Info file. +* Info Files:: What is an Info file? +* Printed Books:: Characteristics of a printed book or manual. +* Formatting Commands:: @@-commands are used for formatting. +* Conventions:: General rules for writing a Texinfo file. +* Comments:: How to write comments and mark regions that + the formatting commands will ignore. +* Minimum:: What a Texinfo file must have. +* Six Parts:: Usually, a Texinfo file has six parts. +* Short Sample:: A short sample Texinfo file. +* Acknowledgements:: + +Using Texinfo Mode + +* Texinfo Mode Overview:: How Texinfo mode can help you. +* Emacs Editing:: Texinfo mode adds to GNU Emacs' general + purpose editing features. +* Inserting:: How to insert frequently used @@-commands. +* Showing the Structure:: How to show the structure of a file. +* Updating Nodes and Menus:: How to update or create new nodes and menus. +* Info Formatting:: How to format for Info. +* Printing:: How to format and print part or all of a file. +* Texinfo Mode Summary:: Summary of all the Texinfo mode commands. + +Updating Nodes and Menus + +* Updating Commands:: Five major updating commands. +* Updating Requirements:: How to structure a Texinfo file for + using the updating command. +* Other Updating Commands:: How to indent descriptions, insert + missing nodes lines, and update + nodes in sequence. + +Beginning a Texinfo File + +* Four Parts:: Four parts begin a Texinfo file. +* Sample Beginning:: Here is a sample beginning for a Texinfo file. +* Header:: The very beginning of a Texinfo file. +* Info Summary and Permissions:: Summary and copying permissions for Info. +* Titlepage & Copyright Page:: Creating the title and copyright pages. +* The Top Node:: Creating the `Top' node and master menu. +* Software Copying Permissions:: Ensure that you and others continue to + have the right to use and share software. + +The Texinfo File Header + +* First Line:: The first line of a Texinfo file. +* Start of Header:: Formatting a region requires this. +* setfilename:: Tell Info the name of the Info file. +* settitle:: Create a title for the printed work. +* setchapternewpage:: Start chapters on right-hand pages. +* paragraphindent:: An option to specify paragraph indentation. +* End of Header:: Formatting a region requires this. + +The Title and Copyright Pages + +* titlepage:: Create a title for the printed document. +* titlefont center sp:: The @code{@@titlefont}, @code{@@center}, + and @code{@@sp} commands. +* title subtitle author:: The @code{@@title}, @code{@@subtitle}, + and @code{@@author} commands. +* Copyright & Permissions:: How to write the copyright notice and + include copying permissions. +* end titlepage:: Turn on page headings after the title and + copyright pages. +* headings on off:: An option for turning headings on and off + and double or single sided printing. + +The `Top' Node and Master Menu + +* Title of Top Node:: Sketch what the file is about. +* Master Menu Parts:: A master menu has three or more parts. + +Ending a Texinfo File + +* Printing Indices & Menus:: How to print an index in hardcopy and + generate index menus in Info. +* Contents:: How to create a table of contents. +* File End:: How to mark the end of a file. + +Chapter Structuring + +* Tree Structuring:: A manual is like an upside down tree @dots{} +* Structuring Command Types:: How to divide a manual into parts. +* makeinfo top:: The @code{@@top} command, part of the `Top' node. +* chapter:: +* unnumbered & appendix:: +* majorheading & chapheading:: +* section:: +* unnumberedsec appendixsec heading:: +* subsection:: +* unnumberedsubsec appendixsubsec subheading:: +* subsubsection:: Commands for the lowest level sections. +* Raise/lower sections:: How to change commands' hierarchical level. + +Nodes + +* Two Paths:: Different commands to structure + Info output and printed output. +* Node Menu Illustration:: A diagram, and sample nodes and menus. +* node:: How to write a node, in detail. +* makeinfo Pointer Creation:: How to create node pointers with @code{makeinfo}. + +The @code{@@node} Command + +* Node Names:: How to choose node and pointer names. +* Writing a Node:: How to write an @code{@@node} line. +* Node Line Tips:: Keep names short. +* Node Line Requirements:: Keep names unique, without @@-commands. +* First Node:: How to write a `Top' node. +* makeinfo top command:: How to use the @code{@@top} command. +* Top Node Summary:: Write a brief description for readers. + +Menus + +* Menu Location:: Put a menu in a short node. +* Writing a Menu:: What is a menu? +* Menu Parts:: A menu entry has three parts. +* Less Cluttered Menu Entry:: Two part menu entry. +* Menu Example:: Two and three part menu entries. +* Other Info Files:: How to refer to a different Info file. + +Cross References + +* References:: What cross references are for. +* Cross Reference Commands:: A summary of the different commands. +* Cross Reference Parts:: A cross reference has several parts. +* xref:: Begin a reference with `See' @dots{} +* Top Node Naming:: How to refer to the beginning of another file. +* ref:: A reference for the last part of a sentence. +* pxref:: How to write a parenthetical cross reference. +* inforef:: How to refer to an Info-only file. + +@code{@@xref} + +* Reference Syntax:: What a reference looks like and requires. +* One Argument:: @code{@@xref} with one argument. +* Two Arguments:: @code{@@xref} with two arguments. +* Three Arguments:: @code{@@xref} with three arguments. +* Four and Five Arguments:: @code{@@xref} with four and five arguments. + +Marking Words and Phrases + +* Indicating:: How to indicate definitions, files, etc. +* Emphasis:: How to emphasize text. + +Indicating Definitions, Commands, etc. + +* Useful Highlighting:: Highlighting provides useful information. +* code:: How to indicate code. +* kbd:: How to show keyboard input. +* key:: How to specify keys. +* samp:: How to show a literal sequence of characters. +* var:: How to indicate a metasyntactic variable. +* file:: How to indicate the name of a file. +* dfn:: How to specify a definition. +* cite:: How to refer to a book that is not in Info. + +Emphasizing Text + +* emph & strong:: How to emphasize text in Texinfo. +* Smallcaps:: How to use the small caps font. +* Fonts:: Various font commands for printed output. +* Customized Highlighting:: How to define highlighting commands. + +Quotations and Examples + +* Block Enclosing Commands:: Use different constructs for + different purposes. +* quotation:: How to write a quotation. +* example:: How to write an example in a fixed-width font. +* noindent:: How to prevent paragraph indentation. +* Lisp Example:: How to illustrate Lisp code. +* smallexample & smalllisp:: Forms for the @code{@@smallbook} option. +* display:: How to write an example in the current font. +* format:: How to write an example that does not narrow + the margins. +* exdent:: How to undo the indentation of a line. +* flushleft & flushright:: How to push text flushleft or flushright. +* cartouche:: How to draw cartouches around examples. + +Making Lists and Tables + +* Introducing Lists:: Texinfo formats lists for you. +* itemize:: How to construct a simple list. +* enumerate:: How to construct a numbered list. +* Two-column Tables:: How to construct a two-column table. + +Making a Two-column Table + +* table:: How to construct a two-column table. +* ftable vtable:: How to construct a two-column table + with automatic indexing. +* itemx:: How to put more entries in the first column. + +Creating Indices + +* Index Entries:: Choose different words for index entries. +* Predefined Indices:: Use different indices for different kinds + of entry. +* Indexing Commands:: How to make an index entry. +* Combining Indices:: How to combine indices. +* New Indices:: How to define your own indices. + +Combining Indices + +* syncodeindex:: How to merge two indices, using @code{@@code} + font for the merged-from index. +* synindex:: How to merge two indices, using the + default font of the merged-to index. + +Special Insertions + +* Braces Atsigns Periods:: How to insert braces, @samp{@@} and periods. +* dmn:: How to format a dimension. +* Dots Bullets:: How to insert dots and bullets. +* TeX and copyright:: How to insert the @TeX{} logo + and the copyright symbol. +* minus:: How to insert a minus sign. +* math:: How to format a mathematical expression. + +Inserting @samp{@@}, Braces, and Periods + +* Inserting An Atsign:: +* Inserting Braces:: How to insert @samp{@{} and @samp{@}} +* Controlling Spacing:: How to insert the right amount of space + after punctuation within a sentence. + +Inserting Ellipsis, Dots, and Bullets + +* dots:: How to insert dots @dots{} +* bullet:: How to insert a bullet. + +Inserting @TeX{} and the Copyright Symbol + +* tex:: How to insert the @TeX{} logo. +* copyright symbol:: How to use @code{@@copyright}@{@}. + +Glyphs for Examples + +* Glyphs Summary:: +* result:: How to show the result of expression. +* expansion:: How to indicate an expansion. +* Print Glyph:: How to indicate printed output. +* Error Glyph:: How to indicate an error message. +* Equivalence:: How to indicate equivalence. +* Point Glyph:: How to indicate the location of point. + +Making and Preventing Breaks + +* Break Commands:: Cause and prevent splits. +* Line Breaks:: How to force a single line to use two lines. +* w:: How to prevent unwanted line breaks. +* sp:: How to insert blank lines. +* page:: How to force the start of a new page. +* group:: How to prevent unwanted page breaks. +* need:: Another way to prevent unwanted page breaks. + +Definition Commands + +* Def Cmd Template:: How to structure a description using a + definition command. +* Optional Arguments:: How to handle optional and repeated arguments. +* deffnx:: How to group two or more `first' lines. +* Def Cmds in Detail:: All the definition commands. +* Def Cmd Conventions:: Conventions for writing definitions. +* Sample Function Definition:: + +The Definition Commands + +* Functions Commands:: Commands for functions and similar entities. +* Variables Commands:: Commands for variables and similar entities. +* Typed Functions:: Commands for functions in typed languages. +* Typed Variables:: Commands for variables in typed languages. +* Abstract Objects:: Commands for object-oriented programming. +* Data Types:: The definition command for data types. + +Footnotes + +* Footnote Commands:: How to write a footnote in Texinfo. +* Footnote Styles:: Controlling how footnotes appear in Info. + +Conditionally Visible Text + +* Conditional Commands:: How to specify text for Info or @TeX{}. +* Using Ordinary TeX Commands:: You can use any and all @TeX{} commands. +* set clear value:: How to designate which text to format (for + both Info and @TeX{}); and how to set a + flag to a string that you can insert. + +@code{@@set}, @code{@@clear}, and @code{@@value} + +* ifset ifclear:: Format a region if a flag is set. +* value:: Replace a flag with a string. +* value Example:: An easy way to update edition information. + +Format and Print Hardcopy + +* Use TeX:: Use @TeX{} to format for hardcopy. +* Format with tex/texindex:: How to format in a shell. +* Format with texi2dvi:: A simpler way to use the shell. +* Print with lpr:: How to print. +* Within Emacs:: How to format and print from an Emacs shell. +* Texinfo Mode Printing:: How to format and print in Texinfo mode. +* Compile-Command:: How to print using Emacs's compile command. +* Requirements Summary:: @TeX{} formatting requirements summary. +* Preparing for TeX:: What you need to do to use @TeX{}. +* Overfull hboxes:: What are and what to do with overfull hboxes. +* smallbook:: How to print small format books and manuals. +* A4 Paper:: How to print on European A4 paper. +* Cropmarks and Magnification:: How to print marks to indicate the size + of pages and how to print scaled up output. + +Creating an Info File + +* makeinfo advantages:: @code{makeinfo} provides better error checking. +* Invoking makeinfo:: How to run @code{makeinfo} from a shell. +* makeinfo options:: Specify fill-column and other options. +* Pointer Validation:: How to check that pointers point somewhere. +* makeinfo in Emacs:: How to run @code{makeinfo} from Emacs. +* texinfo-format commands:: Two Info formatting commands written + in Emacs Lisp are an alternative + to @code{makeinfo}. +* Batch Formatting:: How to format for Info in Emacs Batch mode. +* Tag and Split Files:: How tagged and split files help Info + to run better. + +Installing an Info File + +* Directory file:: The top level menu for all Info files. +* New Info File:: Listing a new info file. +* Other Info Directories:: How to specify Info files that are + located in other directories. + +Sample Permissions + +* Inserting Permissions:: How to put permissions in your document. +* ifinfo Permissions:: Sample @samp{ifinfo} copying permissions. +* Titlepage Permissions:: Sample Titlepage copying permissions. + +Include Files + +* Using Include Files:: How to use the @code{@@include} command. +* texinfo-multiple-files-update:: How to create and update nodes and + menus when using included files. +* Include File Requirements:: What @code{texinfo-multiple-files-update} expects. +* Sample Include File:: A sample outer file with included files + within it; and a sample included file. +* Include Files Evolution:: How use of the @code{@@include} command + has changed over time. + +Page Headings + +* Headings Introduced:: Conventions for using page headings. +* Heading Format:: Standard page heading formats. +* Heading Choice:: How to specify the type of page heading. +* Custom Headings:: How to create your own headings and footings. + +Formatting Mistakes + +* makeinfo preferred:: @code{makeinfo} finds errors. +* Debugging with Info:: How to catch errors with Info formatting. +* Debugging with TeX:: How to catch errors with @TeX{} formatting. +* Using texinfo-show-structure:: How to use @code{texinfo-show-structure}. +* Using occur:: How to list all lines containing a pattern. +* Running Info-Validate:: How to find badly referenced nodes. + +Finding Badly Referenced Nodes + +* Using Info-validate:: How to run @code{Info-validate}. +* Unsplit:: How to create an unsplit file. +* Tagifying:: How to tagify a file. +* Splitting:: How to split a file manually. + +Second Edition Features + +* New Texinfo Mode Commands:: The updating commands are especially useful. +* New Commands:: Many newly described @@-commands. +@end menu + +@node Copying, Overview, Top, Top +@comment node-name, next, previous, up +@unnumbered Texinfo Copying Conditions +@cindex Copying conditions +@cindex Conditions for copying Texinfo + +The programs currently being distributed that relate to Texinfo include +portions of GNU Emacs, plus other separate programs (including +@code{makeinfo}, @code{info}, @code{texindex}, and @file{texinfo.tex}). +These programs are @dfn{free}; this means that everyone is free to use +them and free to redistribute them on a free basis. The Texinfo-related +programs are not in the public domain; they are copyrighted and there +are restrictions on their distribution, but these restrictions are +designed to permit everything that a good cooperating citizen would want +to do. What is not allowed is to try to prevent others from further +sharing any version of these programs that they might get from +you.@refill + + Specifically, we want to make sure that you have the right to give +away copies of the programs that relate to Texinfo, that you receive +source code or else can get it if you want it, that you can change these +programs or use pieces of them in new free programs, and that you know +you can do these things.@refill + + To make sure that everyone has such rights, we have to forbid you to +deprive anyone else of these rights. For example, if you distribute +copies of the Texinfo related programs, 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 tell them their rights.@refill + + Also, for our own protection, we must make certain that everyone finds +out that there is no warranty for the programs that relate to Texinfo. +If these programs are modified by someone else and passed on, we want +their recipients to know that what they have is not what we distributed, +so that any problems introduced by others will not reflect on our +reputation.@refill + + The precise conditions of the licenses for the programs currently +being distributed that relate to Texinfo are found in the General Public +Licenses that accompany them.@refill + +@node Overview, Texinfo Mode, Copying, Top +@comment node-name, next, previous, up +@chapter Overview of Texinfo +@cindex Overview of Texinfo +@cindex Texinfo overview + +@dfn{Texinfo}@footnote{Note that the first syllable of ``Texinfo'' is +pronounced like ``speck'', not ``hex''. This odd pronunciation is +derived from, but is not the same as, the pronunciation of @TeX{}. In +the word @TeX{}, the @samp{X} is actually the Greek letter ``chi'' +rather than the English letter ``ex''. Pronounce @TeX{} as if the +@samp{X} were the last sound in the name `Bach'; but pronounce Texinfo +as if the @samp{x} were a `k'. Spell ``Texinfo'' with a capital ``T'' +and write the other letters in lower case.} +is a documentation system that uses a single source file to produce both +on-line information and printed output. This means that instead of +writing two different documents, one for the on-line help or other on-line +information and the other for a typeset manual or other printed work, you +need write only one document. When the work is revised, you need revise +only one document. (You can read the on-line information, known as an +@dfn{Info file}, with an Info documentation-reading program.)@refill + +@menu +* Using Texinfo:: Create a conventional printed book + or an Info file. +* Info Files:: What is an Info file? +* Printed Books:: Characteristics of a printed book or manual. +* Formatting Commands:: @@-commands are used for formatting. +* Conventions:: General rules for writing a Texinfo file. +* Comments:: How to write comments and mark regions that + the formatting commands will ignore. +* Minimum:: What a Texinfo file must have. +* Six Parts:: Usually, a Texinfo file has six parts. +* Short Sample:: A short sample Texinfo file. +* Acknowledgements:: +@end menu + +@c ************************************************************************ + + + +\input texinfo @c -*-texinfo-*- +@c %**start of header +@setfilename psim.info +@settitle PSIM +@setchapternewpage odd +@c %**end of header + + + +@ifinfo +This file documents the program PSIM. + +Copyright (C) 1994-1996, Andrew Cagney. + +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +are preserved on all copies. + +@ignore +Permission is granted to process this file through Tex and print the +results, provided the printed document carries copying permission +notice identical to this one except for the removal of this paragraph +(this paragraph not being relevant to the printed manual). + +@end ignore +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, subject to the terms +of the GNU General Public License, which includes the provision that the +entire resulting derived work is distributed under the terms of a +permission notice identical to this one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions. +@end ifinfo + + +@titlepage +@title PSIM +@subtitle Model of the PowerPC Environments +@author Andrew Cagney + +@page +@vskip Opt plus ifill +Copyright @copyright{} 1994-1996, Andrew Cagney + +This is the first edition of the PSIM manual and is consistent with PSIM +version 1.0. + +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +are preserved on all copies. + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, subject to the terms +of the GNU General Public License, which includes the provision that the +entire resulting derived work is distributed under the terms of a +permission notice identical to this one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions. +@end titlepage + + + +@menu + +* Copying:: Your rights and freedoms. +* First Chappeter:: Getting started .... +* Second Chapter:: Getting finished .... + + +@end menu + + +PSIM is a program written in extended ANSI-C that implements an +instruction level simulation of the PowerPC environment. It is freely +available in source code form under the terms of the GNU General +Public License (version 2 or later). + +The PowerPC Architecture is described as having three levels of +compliance: + + UEA - User Environment Architecture + VEA - Virtual Environment Architecture + OEA - Operating Environment Architecture + +PSIM both implements all three levels of the PowerPC and includes (for +each level) a corresponding simulated run-time environment. + +In addition, PSIM, to the execution unit level, models the performance +of most of the current PowerPC implementations (contributed by Michael +Meissner). This detailed performance monitoring (unlike many other +simulators) resulting in only a relatively marginal reduction in the +simulators performance. + + +A description of how to build PSIM is contained in the file: + + ftp://ftp.ci.com.au/pub/psim/INSTALL + or ftp://cambridge.cygnus.com/pub/psim/INSTALL + +while an overview of how to use PSIM is in: + + ftp://ftp.ci.com.au/pub/psim/RUN +or ftp://cambridge.cygnus.com/pub/psim/RUN + +This file is found in: + + ftp://ftp.ci.com.au/pub/psim/README +or ftp://cambridge.cygnus.com/pub/psim/README + + +Thanks goes firstly to: + + Corinthian Engineering Pty Ltd + Cygnus Support + Highland Logic Pty Ltd + +who provided the resources needed for making this software available +on the Internet. + +More importantly I'd like to thank the following individuals who each +contributed in their own unique way: + + Allen Briggs, Bett Koch, David Edelsohn, Gordon Irlam, + Michael Meissner, Bob Mercier, Richard Perini, Dale Rahn, + Richard Stallman, Mitchele Walker + + + Andrew Cagney + Feb, 1995 + + + ---------------------------------------------------------------------- + + + What features does PSIM include? + + Monitoring and modeling + + PSIM includes (thanks to Michael Meissner) + a detailed model of most of the PowerPC + implementations to the functional unit level. + + + SMP + + The PowerPC ISA defines SMP synchronizing instructions. + This simulator implements a limited, but functional, + subset of the PowerPC synchronization instructions + behaviour. Programs that restrict their synchronization + primitives to those that work with this functional + sub-set (eg P() and V()) are able to run on the SMP + version of PSIM. + + People intending to use this system should study + the code implementing the lwarx instruction. + + ENDIAN SUPPORT + + PSIM implements the PowerPC's big and little (xor + endian) modes and correctly simulates code that + switches between these two modes. + + In addition, psim can model a true little-endian + machine. + + ISA (Instruction Set Architecture) models + + PSIM includes a model of the UEA, VEA and OEA. This + includes the time base registers (VEA) and HTAB + and BATS (OEA). + + In addition, a preliminary model of the 64 bit + PowerPC architecture is implemented. + + IO Hardware + + PSIM's internals are based around the concept + of a Device Tree. This tree intentionally + resembles that of the Device Tree found in + OpenBoot firmware. PSIM is flexible enough + to allow the user to fully configure this device + tree (and consequently the hardware model) at + run time. + + Run-time environments: + + PSIM's UEA model includes emulation for BSD + based UNIX system calls. + + PSIM's OEA model includes emulation of either: + + o OpenBoot client interface + + o MOTO's BUG interface. + + + Floating point + + Preliminary support for floating point is included. + + + Who would be interested in PSIM? + + o the curious + + Using psim, gdb, gcc and binutils the curious + user can construct an environment that allows + them to play with PowerPC Environment without + the need for real hardware. + + + o the analyst + + PSIM includes many (contributed) monitoring + features which (unlike many other simulators) + do not come with a great penalty in performance. + + Thus the performance analyst is able to use + this simulator to analyse the performance of + the system under test. + + If PSIM doesn't monitor a components of interest, + the source code is freely available, and hence + there is no hinderance to changing things + to meet a specific analysts needs. + + + o the serious SW developer + + PSIM models all three levels of the PowerPC + Architecture: UEA, VEA and OEA. Further, + the internal design is such that PSIM can + be extended to support additional requirements. + + + What performance analysis measurements can PSIM perform? + + Below is the output from a recent analysis run + (contributed by Michael Meissner): + + For the following program: + + long + simple_rand () + { + static unsigned long seed = 47114711; + unsigned long this = seed * 1103515245 + 12345; + seed = this; + /* cut-cut-cut - see the file RUN.psim */ + } + + Here is the current output generated with the -I switch on a P90 + (the compiler used is the development version of GCC with a new + scheduler replacing the old one): + + CPU #1 executed 41,994 AND instructions. + CPU #1 executed 519,785 AND Immediate instructions. + . + . + . + CPU #1 executed 1 System Call instruction. + CPU #1 executed 207,746 XOR instructions. + + CPU #1 executed 23,740,856 cycles. + CPU #1 executed 10,242,780 stalls waiting for data. + CPU #1 executed 1 stall waiting for a function unit. + . + . + . + CPU #1 executed 3,136,229 branch functional unit instructions. + CPU #1 executed 16,949,396 instructions that were accounted for in timing info. + CPU #1 executed 871,920 data reads. + CPU #1 executed 971,926 data writes. + CPU #1 executed 221 icache misses. + CPU #1 executed 16,949,396 instructions in total. + + Simulator speed was 250,731 instructions/second + + + What motivated PSIM? + + As an idea, psim was first discussed seriously during mid + 1994. At that time its main objectives were: + + + o good performance + + Many simulators loose out by only providing + a binary interface to the internals. This + interface eventually becomes a bottle neck + in the simulators performance. + + It was intended that PSIM would avoid this + problem by giving the user access to the + full source code. + + Further, by exploiting the power of modern + compilers it was hoped that PSIM would achieve + good performance with out having to compromise + its internal design. + + + o practical portability + + Rather than try to be portable to every + C compiler on every platform, it was decided + that PSIM would restrict its self to supporting + ANSI compilers that included the extension + of a long long type. + + GCC is one such compiler, consequently PSIM + should be portable to any machine running GCC. + + + o flexibility in its design + + PSIM should allow the user to select the + features required and customise the build + accordingly. By having the source code, + the compiler is able to eliminate any un + used features of the simulator. + + After all, let the compiler do the work. + + + o SMP + + A model that allowed the simulation of + SMP platforms with out the large overhead + often encountered with such models. + + + PSIM achieves each of these objectives. + + + Is PSIM PowerPC Platform (PPCP) (nee CHRP) Compliant? + + No. + + Among other things it does not have an Apple ROM socket. + + + Could PSIM be extended so that it models a CHRP machine? + + Yes. + + PSIM has been designed with the CHRP spec in mind. To model + a CHRP desktop the following would need to be added: + + o An apple ROM socket :-) + + o Model of each of the desktop IO devices + + o An OpenPIC device. + + o RTAS (Run Time Abstraction Services). + + o A fully populated device tree. + + + Is the source code available? + + Yes. + + The source code to PSIM is available under the terms of + the GNU Public Licence. This allows you to distribute + the source code for free but with certain conditions. + + See the file: + + ftp://archie.au/gnu/COPYING + + For details of the terms and conditions. + + + Where do I send bugs or report problems? + + There is a mailing list (subscribe through majordomo@ci.com.au) at: + + powerpc-psim@ci.com.au + + If I get the ftp archive updated I post a note to that mailing list. + In addition your welcome to send bugs or problems either to me or to + that e-mail list. + + This list currently averages zero articles a day. + + + Does PSIM have any limitations or problems? + + PSIM can't run rs6000/AIX binaries - At present PSIM can only + simulate static executables. Since an AIX executable is + never static, PSIM is unable to simulate its execution. + + PSIM is still under development - consequently there are going + to be bugs. + + See the file BUGS (included in the distribution) for any + other outstanding issues. + diff --git a/sim/ppc/registers.c b/sim/ppc/registers.c new file mode 100644 index 00000000000..2181cedd94a --- /dev/null +++ b/sim/ppc/registers.c @@ -0,0 +1,169 @@ +/* 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 _REGISTERS_C_ +#define _REGISTERS_C_ + +#include <ctype.h> + +#include "basics.h" +#include "registers.h" + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#ifdef HAVE_STRING_H +#include <string.h> +#else +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif +#endif + + +INLINE_REGISTERS\ +(void) +registers_dump(registers *registers) +{ + int i; + int j; + for (i = 0; i < 8; i++) { + printf_filtered("GPR %2d:", i*4); + for (j = 0; j < 4; j++) { + printf_filtered(" 0x%08lx", (long)registers->gpr[i*4 + j]); + } + printf_filtered("\n"); + } +} + +STATIC_INLINE_REGISTERS\ +(sprs) +find_spr(const char name[]) +{ + sprs spr; + for (spr = 0; spr < nr_of_sprs; spr++) + if (spr_is_valid(spr) + && !strcmp(name, spr_name(spr)) + && spr_index(spr) == spr) + return spr; + return nr_of_sprs; +} + +STATIC_INLINE_REGISTERS\ +(int) +are_digits(const char *digits) +{ + while (isdigit(*digits)) + digits++; + return *digits == '\0'; +} + + +INLINE_REGISTERS\ +(register_descriptions) +register_description(const char reg[]) +{ + register_descriptions description; + + /* try for a general-purpose integer or floating point register */ + if (reg[0] == 'r' && are_digits(reg + 1)) { + description.type = reg_gpr; + description.index = atoi(reg+1); + description.size = sizeof(gpreg); + } + else if (reg[0] == 'f' && are_digits(reg + 1)) { + description.type = reg_fpr; + description.index = atoi(reg+1); + description.size = sizeof(fpreg); + } + else if (!strcmp(reg, "pc") || !strcmp(reg, "nia")) { + description.type = reg_pc; + description.index = 0; + description.size = sizeof(unsigned_word); + } + else if (!strcmp(reg, "sp")) { + description.type = reg_gpr; + description.index = 1; + description.size = sizeof(gpreg); + } + else if (!strcmp(reg, "toc")) { + description.type = reg_gpr; + description.index = 2; + description.size = sizeof(gpreg); + } + else if (!strcmp(reg, "cr") || !strcmp(reg, "cnd")) { + description.type = reg_cr; + description.index = 0; + description.size = sizeof(creg); /* FIXME */ + } + else if (!strcmp(reg, "msr") || !strcmp(reg, "ps")) { + description.type = reg_msr; + description.index = 0; + description.size = sizeof(msreg); + } + else if (!strcmp(reg, "fpscr")) { + description.type = reg_fpscr; + description.index = 0; + description.size = sizeof(fpscreg); + } + else if (!strncmp(reg, "sr", 2) && are_digits(reg + 2)) { + description.type = reg_sr; + description.index = atoi(reg+2); + description.size = sizeof(sreg); + } + else if (!strcmp(reg, "cnt")) { + description.type = reg_spr; + description.index = spr_ctr; + description.size = sizeof(spreg); + } + else if (!strcmp(reg, "insns")) { + description.type = reg_insns; + description.index = spr_ctr; + description.size = sizeof(unsigned_word); + } + else if (!strcmp(reg, "stalls")) { + description.type = reg_stalls; + description.index = spr_ctr; + description.size = sizeof(unsigned_word); + } + else if (!strcmp(reg, "cycles")) { + description.type = reg_cycles; + description.index = spr_ctr; + description.size = sizeof(unsigned_word); + } + else { + sprs spr = find_spr(reg); + if (spr != nr_of_sprs) { + description.type = reg_spr; + description.index = spr; + description.size = sizeof(spreg); + } + else { + description.type = reg_invalid; + description.index = 0; + description.size = 0; + } + } + return description; +} + +#endif /* _REGISTERS_C_ */ diff --git a/sim/ppc/registers.h b/sim/ppc/registers.h new file mode 100644 index 00000000000..4da6ea4bbe6 --- /dev/null +++ b/sim/ppc/registers.h @@ -0,0 +1,324 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1997, 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 _REGISTERS_H_ +#define _REGISTERS_H_ + + +/* + * The PowerPC registers + * + */ + + +/** + ** General Purpose Registers + **/ + +typedef signed_word gpreg; + + + +/** + ** Floating Point Registers + **/ + +typedef unsigned64 fpreg; + + + +/** + ** The condition register + ** + **/ + +typedef unsigned32 creg; + +/* The following sub bits are defined for the condition register */ +enum { + cr_i_negative = BIT4(0), + cr_i_positive = BIT4(1), + cr_i_zero = BIT4(2), + cr_i_summary_overflow = BIT4(3), +#if 0 + /* cr0 - integer status */ + cr0_i_summary_overflow_bit = 3, + cr0_i_negative = BIT32(0), + cr0_i_positive = BIT32(1), + cr0_i_zero = BIT32(2), + cr0_i_summary_overflow = BIT32(3), + cr0_i_mask = MASK32(0,3), +#endif + /* cr1 - floating-point status */ + cr1_i_floating_point_exception_summary_bit = 4, + cr1_i_floating_point_enabled_exception_summary_bit = 5, + cr1_i_floating_point_invalid_operation_exception_summary_bit = 6, + cr1_i_floating_point_overflow_exception_bit = 7, + cr1_i_floating_point_exception_summary = BIT32(4), + cr1_i_floating_point_enabled_exception_summary = BIT32(5), + cr1_i_floating_point_invalid_operation_exception_summary = BIT32(6), + cr1_i_floating_point_overflow_exception = BIT32(7), + cr1_i_mask = MASK32(4,7), +}; + + +/* Condition register 1 contains the result of floating point arithmetic */ +enum { + cr_fp_exception = BIT4(0), + cr_fp_enabled_exception = BIT4(1), + cr_fp_invalid_exception = BIT4(2), + cr_fp_overflow_exception = BIT4(3), +}; + + + +/** + ** Floating-Point Status and Control Register + **/ + +typedef unsigned32 fpscreg; +enum { + fpscr_fx_bit = 0, + fpscr_fx = BIT32(0), + fpscr_fex_bit = 1, + fpscr_fex = BIT32(1), + fpscr_vx_bit = 2, + fpscr_vx = BIT32(2), + fpscr_ox_bit = 3, + fpscr_ox = BIT32(3), + fpscr_ux = BIT32(4), + fpscr_zx = BIT32(5), + fpscr_xx = BIT32(6), + fpscr_vxsnan = BIT32(7), /* SNAN */ + fpscr_vxisi = BIT32(8), /* INF - INF */ + fpscr_vxidi = BIT32(9), /* INF / INF */ + fpscr_vxzdz = BIT32(10), /* 0 / 0 */ + fpscr_vximz = BIT32(11), /* INF * 0 */ + fpscr_vxvc = BIT32(12), + fpscr_fr = BIT32(13), + fpscr_fi = BIT32(14), + fpscr_fprf = MASK32(15, 19), + fpscr_c = BIT32(15), + fpscr_fpcc_bit = 16, /* well sort of */ + fpscr_fpcc = MASK32(16, 19), + fpscr_fl = BIT32(16), + fpscr_fg = BIT32(17), + fpscr_fe = BIT32(18), + fpscr_fu = BIT32(19), + fpscr_rf_quiet_nan = fpscr_c | fpscr_fu, + fpscr_rf_neg_infinity = fpscr_fl | fpscr_fu, + fpscr_rf_neg_normal_number = fpscr_fl, + fpscr_rf_neg_denormalized_number = fpscr_c | fpscr_fl, + fpscr_rf_neg_zero = fpscr_c | fpscr_fe, + fpscr_rf_pos_zero = fpscr_fe, + fpscr_rf_pos_denormalized_number = fpscr_c | fpscr_fg, + fpscr_rf_pos_normal_number = fpscr_fg, + fpscr_rf_pos_infinity = fpscr_fg | fpscr_fu, + fpscr_reserved_20 = BIT32(20), + fpscr_vxsoft = BIT32(21), + fpscr_vxsqrt = BIT32(22), + fpscr_vxcvi = BIT32(23), + fpscr_ve = BIT32(24), + fpscr_oe = BIT32(25), + fpscr_ue = BIT32(26), + fpscr_ze = BIT32(27), + fpscr_xe = BIT32(28), + fpscr_ni = BIT32(29), + fpscr_rn = MASK32(30, 31), + fpscr_rn_round_to_nearest = 0, + fpscr_rn_round_towards_zero = MASK32(31,31), + fpscr_rn_round_towards_pos_infinity = MASK32(30,30), + fpscr_rn_round_towards_neg_infinity = MASK32(30,31), + fpscr_vx_bits = (fpscr_vxsnan | fpscr_vxisi | fpscr_vxidi + | fpscr_vxzdz | fpscr_vximz | fpscr_vxvc + | fpscr_vxsoft | fpscr_vxsqrt | fpscr_vxcvi), +}; + + + +/** + ** XER Register + **/ + +typedef unsigned32 xereg; + +enum { + xer_summary_overflow = BIT32(0), xer_summary_overflow_bit = 0, + xer_carry = BIT32(2), xer_carry_bit = 2, + xer_overflow = BIT32(1), + xer_reserved_3_24 = MASK32(3,24), + xer_byte_count_mask = MASK32(25,31) +}; + + +/** + ** SPR's + **/ + +#include "spreg.h" + + +/** + ** Segment Registers + **/ + +typedef unsigned32 sreg; +enum { + nr_of_srs = 16 +}; + + +/** + ** Machine state register + **/ +typedef unsigned_word msreg; /* 32 or 64 bits */ + +enum { +#if (WITH_TARGET_WORD_BITSIZE == 64) + msr_64bit_mode = BIT(0), +#endif +#if (WITH_TARGET_WORD_BITSIZE == 32) + msr_64bit_mode = 0, +#endif + msr_power_management_enable = BIT(45), + msr_tempoary_gpr_remapping = BIT(46), /* 603 specific */ + msr_interrupt_little_endian_mode = BIT(47), + msr_external_interrupt_enable = BIT(48), + msr_problem_state = BIT(49), + msr_floating_point_available = BIT(50), + msr_machine_check_enable = BIT(51), + msr_floating_point_exception_mode_0 = BIT(52), + msr_single_step_trace_enable = BIT(53), + msr_branch_trace_enable = BIT(54), + msr_floating_point_exception_mode_1 = BIT(55), + msr_interrupt_prefix = BIT(57), + msr_instruction_relocate = BIT(58), + msr_data_relocate = BIT(59), + msr_recoverable_interrupt = BIT(62), + msr_little_endian_mode = BIT(63) +}; + +enum { + srr1_hash_table_or_ibat_miss = BIT(33), + srr1_direct_store_error_exception = BIT(35), + srr1_protection_violation = BIT(36), + srr1_segment_table_miss = BIT(42), + srr1_floating_point_enabled = BIT(43), + srr1_illegal_instruction = BIT(44), + srr1_priviliged_instruction = BIT(45), + srr1_trap = BIT(46), + srr1_subsequent_instruction = BIT(47) +}; + + +/** + ** storage interrupt registers + **/ + +typedef enum { + dsisr_direct_store_error_exception = BIT32(0), + dsisr_hash_table_or_dbat_miss = BIT32(1), + dsisr_protection_violation = BIT32(4), + dsisr_earwax_violation = BIT32(5), + dsisr_store_operation = BIT32(6), + dsisr_segment_table_miss = BIT32(10), + dsisr_earwax_disabled = BIT32(11) +} dsisr_status; + + + +/** + ** And the registers proper + **/ +typedef struct _registers { + + gpreg gpr[32]; + fpreg fpr[32]; + creg cr; + fpscreg fpscr; + + /* Machine state register */ + msreg msr; + + /* Spr's */ + spreg spr[nr_of_sprs]; + + /* Segment Registers */ + sreg sr[nr_of_srs]; + +} registers; + + +/* dump out all the registers */ + +INLINE_REGISTERS\ +(void) registers_dump +(registers *regs); + + +/* return information on a register based on name */ + +typedef enum { + reg_invalid, + reg_gpr, reg_fpr, reg_spr, reg_msr, + reg_cr, reg_fpscr, reg_pc, reg_sr, + reg_insns, reg_stalls, reg_cycles, + nr_register_types +} register_types; + +typedef struct { + register_types type; + int index; + int size; +} register_descriptions; + +INLINE_REGISTERS\ +(register_descriptions) register_description +(const char reg[]); + + +/* Special purpose registers by their more common names */ + +#define SPREG(N) cpu_registers(processor)->spr[N] +#define XER SPREG(spr_xer) +#define LR SPREG(spr_lr) +#define CTR SPREG(spr_ctr) +#define SRR0 SPREG(spr_srr0) +#define SRR1 SPREG(spr_srr1) +#define DAR SPREG(spr_dar) +#define DSISR SPREG(spr_dsisr) + +/* general purpose registers - indexed access */ +#define GPR(N) cpu_registers(processor)->gpr[N] + +/* segment registers */ +#define SEGREG(N) cpu_registers(processor)->sr[N] + +/* condition register */ +#define CR cpu_registers(processor)->cr + +/* machine status register */ +#define MSR cpu_registers(processor)->msr + +/* floating-point status condition register */ +#define FPSCR cpu_registers(processor)->fpscr + +#endif /* _REGISTERS_H_ */ diff --git a/sim/ppc/sim-endian-n.h b/sim/ppc/sim-endian-n.h new file mode 100644 index 00000000000..4d0410a92b2 --- /dev/null +++ b/sim/ppc/sim-endian-n.h @@ -0,0 +1,134 @@ +/* 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 N +#error "N must be #defined" +#endif + +/* NOTE: See end of file for #undef */ +#define unsigned_N XCONCAT2(unsigned_,N) +#define endian_t2h_N XCONCAT2(endian_t2h_,N) +#define endian_h2t_N XCONCAT2(endian_h2t_,N) +#define _SWAP_N XCONCAT2(_SWAP_,N) +#define swap_N XCONCAT2(swap_,N) +#define endian_h2be_N XCONCAT2(endian_h2be_,N) +#define endian_be2h_N XCONCAT2(endian_be2h_,N) +#define endian_h2le_N XCONCAT2(endian_h2le_,N) +#define endian_le2h_N XCONCAT2(endian_le2h_,N) + + +INLINE_SIM_ENDIAN\ +(unsigned_N) +endian_t2h_N(unsigned_N raw_in) +{ + if (CURRENT_TARGET_BYTE_ORDER == CURRENT_HOST_BYTE_ORDER) { + return raw_in; + } + else { + _SWAP_N(return,raw_in); + } +} + + +INLINE_SIM_ENDIAN\ +(unsigned_N) +endian_h2t_N(unsigned_N raw_in) +{ + if (CURRENT_TARGET_BYTE_ORDER == CURRENT_HOST_BYTE_ORDER) { + return raw_in; + } + else { + _SWAP_N(return,raw_in); + } +} + + +INLINE_SIM_ENDIAN\ +(unsigned_N) +swap_N(unsigned_N raw_in) +{ + _SWAP_N(return,raw_in); +} + + + +INLINE_SIM_ENDIAN\ +(unsigned_N) +endian_h2be_N(unsigned_N raw_in) +{ + if (CURRENT_HOST_BYTE_ORDER == BIG_ENDIAN) { + return raw_in; + } + else { + _SWAP_N(return,raw_in); + } +} + + +INLINE_SIM_ENDIAN\ +(unsigned_N) +endian_be2h_N(unsigned_N raw_in) +{ + if (CURRENT_HOST_BYTE_ORDER == BIG_ENDIAN) { + return raw_in; + } + else { + _SWAP_N(return,raw_in); + } +} + + +INLINE_SIM_ENDIAN\ +(unsigned_N) +endian_h2le_N(unsigned_N raw_in) +{ + if (CURRENT_HOST_BYTE_ORDER == LITTLE_ENDIAN) { + return raw_in; + } + else { + _SWAP_N(return,raw_in); + } +} + + +INLINE_SIM_ENDIAN\ +(unsigned_N) +endian_le2h_N(unsigned_N raw_in) +{ + if (CURRENT_HOST_BYTE_ORDER == LITTLE_ENDIAN) { + return raw_in; + } + else { + _SWAP_N(return,raw_in); + } +} + + +/* NOTE: See start of file for #define */ +#undef unsigned_N +#undef endian_t2h_N +#undef endian_h2t_N +#undef _SWAP_N +#undef swap_N +#undef endian_h2be_N +#undef endian_be2h_N +#undef endian_h2le_N +#undef endian_le2h_N diff --git a/sim/ppc/sim-endian.c b/sim/ppc/sim-endian.c new file mode 100644 index 00000000000..9fe36692fd0 --- /dev/null +++ b/sim/ppc/sim-endian.c @@ -0,0 +1,74 @@ +/* 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_ENDIAN_C_ +#define _SIM_ENDIAN_C_ + +#include "config.h" +#include "basics.h" + + +#if !defined(_SWAP_1) +#define _SWAP_1(SET,RAW) SET (RAW) +#endif + +#if !defined(_SWAP_2) && (WITH_HOST_BYTE_ORDER == LITTLE_ENDIAN) && defined(htons) +#define _SWAP_2(SET,RAW) SET htons (RAW) +#endif + +#ifndef _SWAP_2 +#define _SWAP_2(SET,RAW) SET (((RAW) >> 8) | ((RAW) << 8)) +#endif + +#if !defined(_SWAP_4) && (WITH_HOST_BYTE_ORDER == LITTLE_ENDIAN) && defined(htonl) +#define _SWAP_4(SET,RAW) SET htonl (RAW) +#endif + +#ifndef _SWAP_4 +#define _SWAP_4(SET,RAW) SET (((RAW) << 24) | (((RAW) & 0xff00) << 8) | (((RAW) & 0xff0000) >> 8) | ((RAW) >> 24)) +#endif + +#ifndef _SWAP_8 +#define _SWAP_8(SET,RAW) \ + union { unsigned_8 dword; unsigned_4 words[2]; } in, out; \ + in.dword = RAW; \ + _SWAP_4 (out.words[0] =, in.words[1]); \ + _SWAP_4 (out.words[1] =, in.words[0]); \ + SET out.dword; +#endif + +#define N 1 +#include "sim-endian-n.h" +#undef N + +#define N 2 +#include "sim-endian-n.h" +#undef N + +#define N 4 +#include "sim-endian-n.h" +#undef N + +#define N 8 +#include "sim-endian-n.h" +#undef N + +#endif /* _SIM_ENDIAN_C_ */ diff --git a/sim/ppc/sim-endian.h b/sim/ppc/sim-endian.h new file mode 100644 index 00000000000..ceca97024d3 --- /dev/null +++ b/sim/ppc/sim-endian.h @@ -0,0 +1,409 @@ +/* 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_ENDIAN_H_ +#define _SIM_ENDIAN_H_ + + +/* C byte conversion functions */ + +INLINE_SIM_ENDIAN(unsigned_1) endian_h2t_1(unsigned_1 x); +INLINE_SIM_ENDIAN(unsigned_2) endian_h2t_2(unsigned_2 x); +INLINE_SIM_ENDIAN(unsigned_4) endian_h2t_4(unsigned_4 x); +INLINE_SIM_ENDIAN(unsigned_8) endian_h2t_8(unsigned_8 x); + +INLINE_SIM_ENDIAN(unsigned_1) endian_t2h_1(unsigned_1 x); +INLINE_SIM_ENDIAN(unsigned_2) endian_t2h_2(unsigned_2 x); +INLINE_SIM_ENDIAN(unsigned_4) endian_t2h_4(unsigned_4 x); +INLINE_SIM_ENDIAN(unsigned_8) endian_t2h_8(unsigned_8 x); + +INLINE_SIM_ENDIAN(unsigned_1) swap_1(unsigned_1 x); +INLINE_SIM_ENDIAN(unsigned_2) swap_2(unsigned_2 x); +INLINE_SIM_ENDIAN(unsigned_4) swap_4(unsigned_4 x); +INLINE_SIM_ENDIAN(unsigned_8) swap_8(unsigned_8 x); + +INLINE_SIM_ENDIAN(unsigned_1) endian_h2be_1(unsigned_1 x); +INLINE_SIM_ENDIAN(unsigned_2) endian_h2be_2(unsigned_2 x); +INLINE_SIM_ENDIAN(unsigned_4) endian_h2be_4(unsigned_4 x); +INLINE_SIM_ENDIAN(unsigned_8) endian_h2be_8(unsigned_8 x); + +INLINE_SIM_ENDIAN(unsigned_1) endian_be2h_1(unsigned_1 x); +INLINE_SIM_ENDIAN(unsigned_2) endian_be2h_2(unsigned_2 x); +INLINE_SIM_ENDIAN(unsigned_4) endian_be2h_4(unsigned_4 x); +INLINE_SIM_ENDIAN(unsigned_8) endian_be2h_8(unsigned_8 x); + +INLINE_SIM_ENDIAN(unsigned_1) endian_h2le_1(unsigned_1 x); +INLINE_SIM_ENDIAN(unsigned_2) endian_h2le_2(unsigned_2 x); +INLINE_SIM_ENDIAN(unsigned_4) endian_h2le_4(unsigned_4 x); +INLINE_SIM_ENDIAN(unsigned_8) endian_h2le_8(unsigned_8 x); + +INLINE_SIM_ENDIAN(unsigned_1) endian_le2h_1(unsigned_1 x); +INLINE_SIM_ENDIAN(unsigned_2) endian_le2h_2(unsigned_2 x); +INLINE_SIM_ENDIAN(unsigned_4) endian_le2h_4(unsigned_4 x); +INLINE_SIM_ENDIAN(unsigned_8) endian_le2h_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. + + */ + + +/* NetBSD: + + NetBSD is easy, everything you could ever want is in a header file + (well almost :-) */ + +#if defined(__NetBSD__) +# include <machine/endian.h> +# 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 (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 (i686) || defined(__i386__) || defined(__i486__) || defined(__i586__) || defined (__i686__) +# 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 + +#if (defined (__i486__) || defined (__i586__) || defined (__i686__)) && defined(__GNUC__) && WITH_BSWAP +#undef htonl +#undef ntohl +#define htonl(IN) __extension__ ({ int _out; __asm__ ("bswap %0" : "=r" (_out) : "0" (IN)); _out; }) +#define ntohl(IN) __extension__ ({ int _out; __asm__ ("bswap %0" : "=r" (_out) : "0" (IN)); _out; }) +#endif + +/* Power or PowerPC running AIX */ +#if defined(_POWER) && defined(_AIX) +# 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 "Power/PowerPC AIX was big endian last time I looked ..." +# endif +#endif + +/* Solaris running PowerPC */ +#if defined(__PPC) && defined(__sun__) +# 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 "Solaris on PowerPCs was little endian last time I looked ..." +# endif +#endif + +/* HP/PA */ +#if defined(__hppa__) +# 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 "HP/PA was big endian last time I looked ..." +# endif +#endif + +/* Big endian MIPS */ +#if defined(__MIPSEB__) +# 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 "MIPSEB was big endian last time I looked ..." +# endif +#endif + +/* Little endian MIPS */ +#if defined(__MIPSEL__) +# 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 "MIPSEL was little endian last time I looked ..." +# endif +#endif + +/* Windows NT */ +#if defined(__WIN32__) +# 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 "Windows NT was little endian last time I looked ..." +# endif +#endif + +/* Alpha running DEC unix */ +#if defined(__osf__) && defined(__alpha__) +# 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 "AXP running DEC unix was little endian last time I looked ..." +# endif +#endif + + +/* INSERT HERE - additional hosts that do not have LITTLE_ENDIAN and + BIG_ENDIAN definitions available. */ + +/* SWAP */ + +#define SWAP_1(X) swap_1(X) +#define SWAP_2(X) swap_2(X) +#define SWAP_4(X) swap_4(X) +#define SWAP_8(X) swap_8(X) + + +/* HOST to BE */ + +#define H2BE_1(X) endian_h2be_1(X) +#define H2BE_2(X) endian_h2be_2(X) +#define H2BE_4(X) endian_h2be_4(X) +#define H2BE_8(X) endian_h2be_8(X) +#define BE2H_1(X) endian_be2h_1(X) +#define BE2H_2(X) endian_be2h_2(X) +#define BE2H_4(X) endian_be2h_4(X) +#define BE2H_8(X) endian_be2h_8(X) + + +/* HOST to LE */ + +#define H2LE_1(X) endian_h2le_1(X) +#define H2LE_2(X) endian_h2le_2(X) +#define H2LE_4(X) endian_h2le_4(X) +#define H2LE_8(X) endian_h2le_8(X) +#define LE2H_1(X) endian_le2h_1(X) +#define LE2H_2(X) endian_le2h_2(X) +#define LE2H_4(X) endian_le2h_4(X) +#define LE2H_8(X) endian_le2h_8(X) + + +/* HOST to TARGET */ + +#define H2T_1(X) endian_h2t_1(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) endian_t2h_1(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) + + +/* CONVERT IN PLACE + + These macros, given an argument of unknown size, swap its value in + place if a host/target conversion is 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) + +#define SWAP(VARIABLE) \ +do { \ + switch (sizeof(VARIABLE)) { \ + case 1: VARIABLE = SWAP_1(VARIABLE); break; \ + case 2: VARIABLE = SWAP_2(VARIABLE); break; \ + case 4: VARIABLE = SWAP_4(VARIABLE); break; \ + case 8: VARIABLE = SWAP_8(VARIABLE); break; \ + } \ +} while (0) + +#define H2BE(VARIABLE) \ +do { \ + switch (sizeof(VARIABLE)) { \ + case 1: VARIABLE = H2BE_1(VARIABLE); break; \ + case 2: VARIABLE = H2BE_2(VARIABLE); break; \ + case 4: VARIABLE = H2BE_4(VARIABLE); break; \ + case 8: VARIABLE = H2BE_8(VARIABLE); break; \ + } \ +} while (0) + +#define BE2H(VARIABLE) \ +do { \ + switch (sizeof(VARIABLE)) { \ + case 1: VARIABLE = BE2H_1(VARIABLE); break; \ + case 2: VARIABLE = BE2H_2(VARIABLE); break; \ + case 4: VARIABLE = BE2H_4(VARIABLE); break; \ + case 8: VARIABLE = BE2H_8(VARIABLE); break; \ + } \ +} while (0) + +#define H2LE(VARIABLE) \ +do { \ + switch (sizeof(VARIABLE)) { \ + case 1: VARIABLE = H2LE_1(VARIABLE); break; \ + case 2: VARIABLE = H2LE_2(VARIABLE); break; \ + case 4: VARIABLE = H2LE_4(VARIABLE); break; \ + case 8: VARIABLE = H2LE_8(VARIABLE); break; \ + } \ +} while (0) + +#define LE2H(VARIABLE) \ +do { \ + switch (sizeof(VARIABLE)) { \ + case 1: VARIABLE = LE2H_1(VARIABLE); break; \ + case 2: VARIABLE = LE2H_2(VARIABLE); break; \ + case 4: VARIABLE = LE2H_4(VARIABLE); break; \ + case 8: VARIABLE = LE2H_8(VARIABLE); break; \ + } \ +} while (0) + + + +/* TARGET WORD: + + Byte swap a quantity the size of the targets word */ + +#if (WITH_TARGET_WORD_BITSIZE == 64) +#define H2T_word(X) H2T_8(X) +#define T2H_word(X) T2H_8(X) +#define H2BE_word(X) H2BE_8(X) +#define BE2H_word(X) BE2H_8(X) +#define H2LE_word(X) H2LE_8(X) +#define LE2H_word(X) LE2H_8(X) +#define SWAP_word(X) SWAP_8(X) +#endif +#if (WITH_TARGET_WORD_BITSIZE == 32) +#define H2T_word(X) H2T_4(X) +#define T2H_word(X) T2H_4(X) +#define H2BE_word(X) H2BE_4(X) +#define BE2H_word(X) BE2H_4(X) +#define H2LE_word(X) H2LE_4(X) +#define LE2H_word(X) LE2H_4(X) +#define SWAP_word(X) SWAP_4(X) +#endif + + +/* TARGET CELL: + + Byte swap a quantity the size of the targets IEEE 1275 memory cell */ + +#define H2T_cell(X) H2T_4(X) +#define T2H_cell(X) T2H_4(X) +#define H2BE_cell(X) H2BE_4(X) +#define BE2H_cell(X) BE2H_4(X) +#define H2LE_cell(X) H2LE_4(X) +#define LE2H_cell(X) LE2H_4(X) +#define SWAP_cell(X) SWAP_4(X) + + +#if (SIM_ENDIAN_INLINE & INCLUDE_MODULE) +# include "sim-endian.c" +#endif + +#endif /* _SIM_ENDIAN_H_ */ diff --git a/sim/ppc/sim_callbacks.h b/sim/ppc/sim_callbacks.h new file mode 100644 index 00000000000..322af9de8a1 --- /dev/null +++ b/sim/ppc/sim_callbacks.h @@ -0,0 +1,115 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1995,1998, 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_ + +/* Simulator output: + + Functions to report diagnostic information to the user. */ + +#define printf_filtered sim_io_printf_filtered +void sim_io_printf_filtered +(const char *msg, ...) __attribute__ ((format (printf, 1, 2))); + +void NORETURN error +(const char *msg, ...); + + +/* External environment: + + Some HOST OS's require regular polling so that external events such + as GUI io can be handled. */ + +void sim_io_poll_quit +(void); + + + +/* Model I/O: + + These functions provide the interface between the modeled targets + standard input and output streams and the hosts external + environment. + + The underlying model is responsible for co-ordinating I/O + interactions such as: + + main() + { + const char mess[] = "Hello World\r\n"; + int out; + out = dup(1); + close(1); + write(out, mess, sizeof(mess)) + } + + That is to say, the underlying model would, in implementing dup() + recorded the fact that any output directed at fid-OUT should be + displayed using sim_io_write_stdout(). While for the code stub: + + main() + { + const char mess[] = "Hello World\r\n"; + int out; + close(1); + out = open("output", 0x0001|0x0200, 0); // write+create + out = dup(1); + write(out, mess, sizeof(mess)) + } + + would result in output to fid-1 being directed towards the file + "output". */ + + +int +sim_io_write_stdout +(const char *buf, + int sizeof_buf); + +int +sim_io_write_stderr +(const char *buf, + int sizeof_buf); + +/* read results */ +enum { sim_io_eof = -1, sim_io_not_ready = -2, }; /* See: IEEE 1275 */ + +int +sim_io_read_stdin +(char *buf, + int sizeof_buf); + +#define flush_stdoutput sim_io_flush_stdoutput +void sim_io_flush_stdoutput +(void); + + +/* Memory management with an allocator that clears memory before use. */ + +void *zalloc +(long size); + +#define ZALLOC(TYPE) (TYPE*)zalloc(sizeof (TYPE)) + +void zfree(void*); + +#endif diff --git a/sim/ppc/sim_calls.c b/sim/ppc/sim_calls.c new file mode 100644 index 00000000000..bb845397152 --- /dev/null +++ b/sim/ppc/sim_calls.c @@ -0,0 +1,445 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1996,1998, 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. + + */ + + +#include <signal.h> /* FIXME - should be machine dependant version */ +#include <stdarg.h> +#include <ctype.h> + +#include "psim.h" +#include "options.h" + +#undef printf_filtered /* blow away the mapping */ + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#ifdef HAVE_STRING_H +#include <string.h> +#else +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif +#endif + +#include "defs.h" +#include "bfd.h" +#include "callback.h" +#include "remote-sim.h" + + +/* Structures used by the simulator, for gdb just have static structures */ + +static psim *simulator; +static device *root_device; +static host_callback *callbacks; + +/* We use GDB's reg_names array to map GDB register numbers onto + names, which we can then look up in the register table. + + We used to just use the REGISTER_NAMES macro, from GDB's + target-dependent header files. That was kind of nice, because it + meant that libsim.a had only a compile-time dependency on GDB; + using reg_names directly means that there are now link-time and + run-time dependencies too. + + However, the GDB PPC back-end now modifies the reg_names array when + the user runs the `set processor' command, which affects the + meanings of the register numbers. So the sim needs to see the + register names GDB is actually using. + + Perhaps the host_callback structure could contain a pointer to the + register name table; that would be cleaner. */ + +SIM_DESC +sim_open (SIM_OPEN_KIND kind, + host_callback *callback, + struct _bfd *abfd, + char **argv) +{ + callbacks = callback; + + /* Note: The simulation is not created by sim_open() because + complete information is not yet available */ + /* trace the call */ + TRACE(trace_gdb, ("sim_open called\n")); + + if (root_device != NULL) + sim_io_printf_filtered("Warning - re-open of simulator leaks memory\n"); + root_device = psim_tree(); + simulator = NULL; + + psim_options(root_device, argv + 1); + + if (ppc_trace[trace_opts]) + print_options (); + + /* fudge our descriptor for now */ + return (SIM_DESC) 1; +} + + +void +sim_close (SIM_DESC sd, int quitting) +{ + TRACE(trace_gdb, ("sim_close(quitting=%d) called\n", quitting)); + if (ppc_trace[trace_print_info] && simulator != NULL) + psim_print_info (simulator, ppc_trace[trace_print_info]); +} + + +SIM_RC +sim_load (SIM_DESC sd, char *prog, bfd *abfd, int from_tty) +{ + TRACE(trace_gdb, ("sim_load(prog=%s, from_tty=%d) called\n", + prog, from_tty)); + ASSERT(prog != NULL); + + /* create the simulator */ + TRACE(trace_gdb, ("sim_load() - first time, create the simulator\n")); + simulator = psim_create(prog, root_device); + + /* bring in all the data section */ + psim_init(simulator); + + /* get the start address */ + if (abfd == NULL) + { + abfd = bfd_openr (prog, 0); + if (abfd == NULL) + error ("psim: can't open \"%s\": %s\n", + prog, bfd_errmsg (bfd_get_error ())); + if (!bfd_check_format (abfd, bfd_object)) + { + const char *errmsg = bfd_errmsg (bfd_get_error ()); + bfd_close (abfd); + error ("psim: \"%s\" is not an object file: %s\n", + prog, errmsg); + } + bfd_close (abfd); + } + + return SIM_RC_OK; +} + + +int +sim_read (SIM_DESC sd, SIM_ADDR mem, unsigned char *buf, int length) +{ + int result = psim_read_memory(simulator, MAX_NR_PROCESSORS, + buf, mem, length); + TRACE(trace_gdb, ("sim_read(mem=0x%lx, buf=0x%lx, length=%d) = %d\n", + (long)mem, (long)buf, length, result)); + return result; +} + + +int +sim_write (SIM_DESC sd, SIM_ADDR mem, unsigned char *buf, int length) +{ + int result = psim_write_memory(simulator, MAX_NR_PROCESSORS, + buf, mem, length, + 1/*violate_ro*/); + TRACE(trace_gdb, ("sim_write(mem=0x%lx, buf=0x%lx, length=%d) = %d\n", + (long)mem, (long)buf, length, result)); + return result; +} + + +int +sim_fetch_register (SIM_DESC sd, int regno, unsigned char *buf, int length) +{ + char *regname; + + if (simulator == NULL) { + return 0; + } + + /* GDB will sometimes ask for the contents of a register named ""; + we ignore such requests, and leave garbage in *BUF. In + REG_NAMES, the empty string means "the register with this + number is not present in the currently selected architecture + variant." That's following the kludge we're using for the MIPS + processors. But there are loops that just walk through the + entire list of names and try to get everything. */ + regname = REGISTER_NAME (regno); + if (! regname || regname[0] == '\0') + return -1; + + TRACE(trace_gdb, ("sim_fetch_register(regno=%d(%s), buf=0x%lx)\n", + regno, regname, (long)buf)); + psim_read_register(simulator, MAX_NR_PROCESSORS, + buf, regname, raw_transfer); + return -1; +} + + +int +sim_store_register (SIM_DESC sd, int regno, unsigned char *buf, int length) +{ + char *regname; + + if (simulator == NULL) + return 0; + + /* See comments in sim_fetch_register, above. */ + regname = REGISTER_NAME (regno); + if (! regname || regname[0] == '\0') + return -1; + + TRACE(trace_gdb, ("sim_store_register(regno=%d(%s), buf=0x%lx)\n", + regno, regname, (long)buf)); + psim_write_register(simulator, MAX_NR_PROCESSORS, + buf, regname, raw_transfer); + return -1; +} + + +void +sim_info (SIM_DESC sd, int verbose) +{ + TRACE(trace_gdb, ("sim_info(verbose=%d) called\n", verbose)); + psim_print_info (simulator, verbose); +} + + +SIM_RC +sim_create_inferior (SIM_DESC sd, + struct _bfd *abfd, + char **argv, + char **envp) +{ + unsigned_word entry_point; + TRACE(trace_gdb, ("sim_create_inferior(start_address=0x%x, ...)\n", + entry_point)); + + if (simulator == NULL) + error ("No program loaded"); + + if (abfd != NULL) + entry_point = bfd_get_start_address (abfd); + else + entry_point = 0xfff00000; /* ??? */ + + psim_init(simulator); + psim_stack(simulator, argv, envp); + + psim_write_register(simulator, -1 /* all start at same PC */, + &entry_point, "pc", cooked_transfer); + return SIM_RC_OK; +} + + +void +sim_stop_reason (SIM_DESC sd, enum sim_stop *reason, int *sigrc) +{ + psim_status status = psim_get_status(simulator); + + switch (status.reason) { + case was_continuing: + *reason = sim_stopped; + if (status.signal == 0) + *sigrc = SIGTRAP; + else + *sigrc = status.signal; + break; + case was_trap: + *reason = sim_stopped; + *sigrc = SIGTRAP; + break; + case was_exited: + *reason = sim_exited; + *sigrc = status.signal; + break; + case was_signalled: + *reason = sim_signalled; + *sigrc = status.signal; + break; + } + + TRACE(trace_gdb, ("sim_stop_reason(reason=0x%lx(%ld), sigrc=0x%lx(%ld))\n", + (long)reason, (long)*reason, (long)sigrc, (long)*sigrc)); +} + + + +/* Run (or resume) the program. */ + +int +sim_stop (SIM_DESC sd) +{ + psim_stop (simulator); + return 1; +} + +void +sim_resume (SIM_DESC sd, int step, int siggnal) +{ + TRACE(trace_gdb, ("sim_resume(step=%d, siggnal=%d)\n", + step, siggnal)); + + if (step) + { + psim_step (simulator); + } + else + { + psim_run (simulator); + } +} + +void +sim_do_command (SIM_DESC sd, char *cmd) +{ + TRACE(trace_gdb, ("sim_do_commands(cmd=%s) called\n", + cmd ? cmd : "(null)")); + if (cmd != NULL) { + char **argv = buildargv(cmd); + psim_command(root_device, argv); + freeargv(argv); + } +} + + +/* Polling, if required */ + +void +sim_io_poll_quit (void) +{ + if (callbacks->poll_quit != NULL) + { + if (callbacks->poll_quit (callbacks)) + psim_stop (simulator); + } +} + + + +/* Map simulator IO operations onto the corresponding GDB I/O + functions. + + NB: Only a limited subset of operations are mapped across. More + advanced operations (such as dup or write) must either be mapped to + one of the below calls or handled internally */ + +int +sim_io_read_stdin(char *buf, + int sizeof_buf) +{ + switch (CURRENT_STDIO) { + case DO_USE_STDIO: + return callbacks->read_stdin(callbacks, buf, sizeof_buf); + break; + case DONT_USE_STDIO: + return callbacks->read(callbacks, 0, buf, sizeof_buf); + break; + default: + error("sim_io_read_stdin: unaccounted switch\n"); + break; + } + return 0; +} + +int +sim_io_write_stdout(const char *buf, + int sizeof_buf) +{ + switch (CURRENT_STDIO) { + case DO_USE_STDIO: + return callbacks->write_stdout(callbacks, buf, sizeof_buf); + break; + case DONT_USE_STDIO: + return callbacks->write(callbacks, 1, buf, sizeof_buf); + break; + default: + error("sim_io_write_stdout: unaccounted switch\n"); + break; + } + return 0; +} + +int +sim_io_write_stderr(const char *buf, + int sizeof_buf) +{ + switch (CURRENT_STDIO) { + case DO_USE_STDIO: + /* NB: I think there should be an explicit write_stderr callback */ + return callbacks->write(callbacks, 3, buf, sizeof_buf); + break; + case DONT_USE_STDIO: + return callbacks->write(callbacks, 3, buf, sizeof_buf); + break; + default: + error("sim_io_write_stderr: unaccounted switch\n"); + break; + } + return 0; +} + + +void +sim_io_printf_filtered(const char *fmt, + ...) +{ + char message[1024]; + va_list ap; + /* format the message */ + va_start(ap, fmt); + vsprintf(message, fmt, ap); + va_end(ap); + /* sanity check */ + if (strlen(message) >= sizeof(message)) + error("sim_io_printf_filtered: buffer overflow\n"); + callbacks->printf_filtered(callbacks, "%s", message); +} + +void +sim_io_flush_stdoutput(void) +{ + switch (CURRENT_STDIO) { + case DO_USE_STDIO: + callbacks->flush_stdout (callbacks); + break; + case DONT_USE_STDIO: + break; + default: + error("sim_io_read_stdin: unaccounted switch\n"); + break; + } +} + +/****/ + +void * +zalloc(long size) +{ + void *memory = (void*)xmalloc(size); + if (memory == NULL) + error("xmalloc failed\n"); + memset(memory, 0, size); + return memory; +} + +void zfree(void *data) +{ + free(data); +} diff --git a/sim/ppc/std-config.h b/sim/ppc/std-config.h new file mode 100644 index 00000000000..eaf35582346 --- /dev/null +++ b/sim/ppc/std-config.h @@ -0,0 +1,672 @@ +/* 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 _PSIM_CONFIG_H_ +#define _PSIM_CONFIG_H_ + + +/* endianness of the host/target: + + If the build process is aware (at compile time) of the endianness + of the host/target it is able to eliminate slower generic endian + handling code. + + Possible values are 0 (unknown), LITTLE_ENDIAN, BIG_ENDIAN */ + +#ifndef WITH_HOST_BYTE_ORDER +#define WITH_HOST_BYTE_ORDER 0 /*unknown*/ +#endif + +#ifndef WITH_TARGET_BYTE_ORDER +#define WITH_TARGET_BYTE_ORDER 0 /*unknown*/ +#endif + +extern int current_host_byte_order; +#define CURRENT_HOST_BYTE_ORDER (WITH_HOST_BYTE_ORDER \ + ? WITH_HOST_BYTE_ORDER \ + : current_host_byte_order) +extern int current_target_byte_order; +#define CURRENT_TARGET_BYTE_ORDER (WITH_TARGET_BYTE_ORDER \ + ? WITH_TARGET_BYTE_ORDER \ + : current_target_byte_order) + + +/* PowerPC XOR endian. + + In addition to the above, the simulator can support the PowerPC's + horrible XOR endian mode. This feature makes it possible to + control the endian mode of a processor using the MSR. */ + +#ifndef WITH_XOR_ENDIAN +#define WITH_XOR_ENDIAN 8 +#endif + + +/* Intel host BSWAP support: + + Whether to use bswap on the 486 and pentiums rather than the 386 + sequence that uses xchgb/rorl/xchgb */ +#ifndef WITH_BSWAP +#define WITH_BSWAP 0 +#endif + + +/* SMP support: + + Sets a limit on the number of processors that can be simulated. If + WITH_SMP is set to zero (0), the simulator is restricted to + suporting only on processor (and as a consequence leaves the SMP + code out of the build process). + + The actual number of processors is taken from the device + /options/smp@<nr-cpu> */ + +#ifndef WITH_SMP +#define WITH_SMP 5 +#endif +#if WITH_SMP +#define MAX_NR_PROCESSORS WITH_SMP +#else +#define MAX_NR_PROCESSORS 1 +#endif + + +/* Word size of host/target: + + Set these according to your host and target requirements. At this + point in time, I've only compiled (not run) for a 64bit and never + built for a 64bit host. This will always remain a compile time + option */ + +#ifndef WITH_TARGET_WORD_BITSIZE +#define WITH_TARGET_WORD_BITSIZE 32 /* compiled only */ +#endif + +#ifndef WITH_HOST_WORD_BITSIZE +#define WITH_HOST_WORD_BITSIZE 32 /* 64bit ready? */ +#endif + + +/* Program environment: + + Three environments are available - UEA (user), VEA (virtual) and + OEA (perating). The former two are environment that users would + expect to see (VEA includes things like coherency and the time + base) while OEA is what an operating system expects to see. By + setting these to specific values, the build process is able to + eliminate non relevent environment code + + CURRENT_ENVIRONMENT specifies which of vea or oea is required for + the current runtime. */ + +#define USER_ENVIRONMENT 1 +#define VIRTUAL_ENVIRONMENT 2 +#define OPERATING_ENVIRONMENT 3 + +#ifndef WITH_ENVIRONMENT +#define WITH_ENVIRONMENT 0 +#endif + +extern int current_environment; +#define CURRENT_ENVIRONMENT (WITH_ENVIRONMENT \ + ? WITH_ENVIRONMENT \ + : current_environment) + + +/* Optional VEA/OEA code: + + The below, required for the OEA model may also be included in the + VEA model however, as far as I can tell only make things + slower... */ + + +/* Events. Devices modeling real H/W need to be able to efficiently + schedule things to do at known times in the future. The event + queue implements this. Unfortunatly this adds the need to check + for any events once each full instruction cycle. */ + +#define WITH_EVENTS (WITH_ENVIRONMENT != USER_ENVIRONMENT) + + +/* Time base: + + The PowerPC architecture includes the addition of both a time base + register and a decrement timer. Like events adds to the overhead + of of some instruction cycles. */ + +#ifndef WITH_TIME_BASE +#define WITH_TIME_BASE (WITH_ENVIRONMENT != USER_ENVIRONMENT) +#endif + + +/* Callback/Default Memory. + + Core includes a builtin memory type (raw_memory) that is + implemented using an array. raw_memory does not require any + additional functions etc. + + Callback memory is where the core calls a core device for the data + it requires. + + Default memory is an extenstion of this where for addresses that do + not map into either a callback or core memory range a default map + can be used. + + The OEA model uses callback memory for devices and default memory + for buses. + + The VEA model uses callback memory to capture `page faults'. + + While it may be possible to eliminate callback/default memory (and + hence also eliminate an additional test per memory fetch) it + probably is not worth the effort. + + BTW, while raw_memory could have been implemented as a callback, + profiling has shown that there is a biger win (at least for the + x86) in eliminating a function call for the most common + (raw_memory) case. */ + +#define WITH_CALLBACK_MEMORY 1 + + +/* Alignment: + + The PowerPC may or may not handle miss aligned transfers. An + implementation normally handles miss aligned transfers in big + endian mode but generates an exception in little endian mode. + + This model. Instead allows both little and big endian modes to + either take exceptions or handle miss aligned transfers. + + If 0 is specified then for big-endian mode miss alligned accesses + are permitted (NONSTRICT_ALIGNMENT) while in little-endian mode the + processor will fault on them (STRICT_ALIGNMENT). */ + +#define NONSTRICT_ALIGNMENT 1 +#define STRICT_ALIGNMENT 2 + +#ifndef WITH_ALIGNMENT +#define WITH_ALIGNMENT 0 +#endif + +extern int current_alignment; +#define CURRENT_ALIGNMENT (WITH_ALIGNMENT \ + ? WITH_ALIGNMENT \ + : current_alignment) + + +/* Floating point suport: + + Still under development. */ + +#define SOFT_FLOATING_POINT 1 +#define HARD_FLOATING_POINT 2 + +#ifndef WITH_FLOATING_POINT +#define WITH_FLOATING_POINT HARD_FLOATING_POINT +#endif +extern int current_floating_point; +#define CURRENT_FLOATING_POINT (WITH_FLOATING_POINT \ + ? WITH_FLOATING_POINT \ + : current_floating_point) + + +/* Debugging: + + Control the inclusion of debugging code. */ + +/* Include the tracing code. Disabling this eliminates all tracing + code */ + +#ifndef WITH_TRACE +#define WITH_TRACE 1 +#endif + +/* include code that checks assertions scattered through out the + program */ + +#ifndef WITH_ASSERT +#define WITH_ASSERT 1 +#endif + +/* Whether to check instructions for reserved bits being set */ + +#ifndef WITH_RESERVED_BITS +#define WITH_RESERVED_BITS 1 +#endif + +/* include monitoring code */ + +#define MONITOR_INSTRUCTION_ISSUE 1 +#define MONITOR_LOAD_STORE_UNIT 2 +#ifndef WITH_MON +#define WITH_MON (MONITOR_LOAD_STORE_UNIT \ + | MONITOR_INSTRUCTION_ISSUE) +#endif + +/* Current CPU model (models are in the generated models.h include file) */ +#ifndef WITH_MODEL +#define WITH_MODEL 0 +#endif + +#define CURRENT_MODEL (WITH_MODEL \ + ? WITH_MODEL \ + : current_model) + +#ifndef WITH_DEFAULT_MODEL +#define WITH_DEFAULT_MODEL DEFAULT_MODEL +#endif + +#define MODEL_ISSUE_IGNORE (-1) +#define MODEL_ISSUE_PROCESS 1 + +#ifndef WITH_MODEL_ISSUE +#define WITH_MODEL_ISSUE 0 +#endif + +extern int current_model_issue; +#define CURRENT_MODEL_ISSUE (WITH_MODEL_ISSUE \ + ? WITH_MODEL_ISSUE \ + : current_model_issue) + +/* Whether or not input/output just uses stdio, or uses printf_filtered for + output, and polling input for input. */ + +#define DONT_USE_STDIO 2 +#define DO_USE_STDIO 1 + +#ifndef WITH_STDIO +#define WITH_STDIO 0 +#endif + +extern int current_stdio; +#define CURRENT_STDIO (WITH_STDIO \ + ? WITH_STDIO \ + : current_stdio) + + + +/* INLINE CODE SELECTION: + + GCC -O3 attempts to inline any function or procedure in scope. The + options below facilitate fine grained control over what is and what + isn't made inline. For instance it can control things down to a + specific modules static routines. Doing this allows the compiler + to both eliminate the overhead of function calls and (as a + consequence) also eliminate further dead code. + + On a CISC (x86) I've found that I can achieve an order of magintude + speed improvement (x3-x5). In the case of RISC (sparc) while the + performance gain isn't as great it is still significant. + + Each module is controled by the macro <module>_INLINE which can + have the values described below + + 0 Do not inline any thing for the given module + + The following additional values are `bit fields' and can be + combined. + + REVEAL_MODULE: + + Include the C file for the module into the file being compiled + but do not make the functions within the module inline. + + While of no apparent benefit, this makes it possible for the + included module, when compiled to inline its calls to what + would otherwize be external functions. + + INLINE_MODULE: + + Make external functions within the module `inline'. Thus if + the module is included into a file being compiled, calls to + its funtions can be eliminated. 2 implies 1. + + INLINE_LOCALS: + + Make internal (static) functions within the module `inline'. + + The following abreviations are available: + + INCLUDE_MODULE == (REVEAL_MODULE | INLINE_MODULE) + + ALL_INLINE == (REVEAL_MODULE | INLINE_MODULE | INLINE_LOCALS) + + In addition to this, modules have been put into two categories. + + Simple modules - eg sim-endian.h bits.h + + Because these modules are small and simple and do not have + any complex interpendencies they are configured, if + <module>_INLINE is so enabled, to inline themselves in all + modules that include those files. + + For the default build, this is a real win as all byte + conversion and bit manipulation functions are inlined. + + Complex modules - the rest + + These are all handled using the files inline.h and inline.c. + psim.c includes the above which in turn include any remaining + code. + + IMPLEMENTATION: + + The inline ability is enabled by prefixing every data / function + declaration and definition with one of the following: + + + INLINE_<module> + + Prefix to any global function that is a candidate for being + inline. + + values - `', `static', `static INLINE' + + + EXTERN_<module> + + Prefix to any global data structures for the module. Global + functions that are not to be inlined shall also be prefixed + with this. + + values - `', `static', `static' + + + STATIC_INLINE_<module> + + Prefix to any local (static) function that is a candidate for + being made inline. + + values - `static', `static INLINE' + + + static + + Prefix all local data structures. Local functions that are not + to be inlined shall also be prefixed with this. + + values - `static', `static' + + nb: will not work for modules that are being inlined for every + use (white lie). + + + extern + #ifndef _INLINE_C_ + #endif + + Prefix to any declaration of a global object (function or + variable) that should not be inlined and should have only one + definition. The #ifndef wrapper goes around the definition + propper to ensure that only one copy is generated. + + nb: this will not work when a module is being inlined for every + use. + + + STATIC_<module> + + Replaced by either `static' or `EXTERN_MODULE'. + + + REALITY CHECK: + + This is not for the faint hearted. I've seen GCC get up to 500mb + trying to compile what this can create. + + Some of the modules do not yet implement the WITH_INLINE_STATIC + option. Instead they use the macro STATIC_INLINE to control their + local function. + + Because of the way that GCC parses __attribute__(), the macro's + need to be adjacent to the functioin name rather then at the start + of the line vis: + + int STATIC_INLINE_MODULE f(void); + void INLINE_MODULE *g(void); + + */ + +#define REVEAL_MODULE 1 +#define INLINE_MODULE 2 +#define INCLUDE_MODULE (INLINE_MODULE | REVEAL_MODULE) +#define INLINE_LOCALS 4 +#define ALL_INLINE 7 + +/* Your compilers inline reserved word */ + +#ifndef INLINE +#if defined(__GNUC__) && defined(__OPTIMIZE__) +#define INLINE __inline__ +#else +#define INLINE /*inline*/ +#endif +#endif + + +/* Your compilers pass parameters in registers reserved word */ + +#ifndef WITH_REGPARM +#define WITH_REGPARM 0 +#endif + +/* Your compilers use an alternative calling sequence reserved word */ + +#ifndef WITH_STDCALL +#define WITH_STDCALL 0 +#endif + +#if !defined REGPARM +#if defined(__GNUC__) && (defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__)) +#if (WITH_REGPARM && WITH_STDCALL) +#define REGPARM __attribute__((__regparm__(WITH_REGPARM),__stdcall__)) +#else +#if (WITH_REGPARM && !WITH_STDCALL) +#define REGPARM __attribute__((__regparm__(WITH_REGPARM))) +#else +#if (!WITH_REGPARM && WITH_STDCALL) +#define REGPARM __attribute__((__stdcall__)) +#endif +#endif +#endif +#endif +#endif + +#if !defined REGPARM +#define REGPARM +#endif + + + +/* Default prefix for static functions */ + +#ifndef STATIC_INLINE +#define STATIC_INLINE static INLINE +#endif + +/* Default macro to simplify control several of key the inlines */ + +#ifndef DEFAULT_INLINE +#define DEFAULT_INLINE INLINE_LOCALS +#endif + +/* Code that converts between hosts and target byte order. Used on + every memory access (instruction and data). See sim-endian.h for + additional byte swapping configuration information. This module + can inline for all callers */ + +#ifndef SIM_ENDIAN_INLINE +#define SIM_ENDIAN_INLINE (DEFAULT_INLINE ? ALL_INLINE : 0) +#endif + +/* Low level bit manipulation routines. This module can inline for all + callers */ + +#ifndef BITS_INLINE +#define BITS_INLINE (DEFAULT_INLINE ? ALL_INLINE : 0) +#endif + +/* Code that gives access to various CPU internals such as registers. + Used every time an instruction is executed */ + +#ifndef CPU_INLINE +#define CPU_INLINE (DEFAULT_INLINE ? ALL_INLINE : 0) +#endif + +/* Code that translates between an effective and real address. Used + by every load or store. */ + +#ifndef VM_INLINE +#define VM_INLINE DEFAULT_INLINE +#endif + +/* Code that loads/stores data to/from the memory data structure. + Used by every load or store */ + +#ifndef CORE_INLINE +#define CORE_INLINE DEFAULT_INLINE +#endif + +/* Code to check for and process any events scheduled in the future. + Called once per instruction cycle */ + +#ifndef EVENTS_INLINE +#define EVENTS_INLINE (DEFAULT_INLINE ? ALL_INLINE : 0) +#endif + +/* Code monotoring the processors performance. It counts events on + every instruction cycle */ + +#ifndef MON_INLINE +#define MON_INLINE (DEFAULT_INLINE ? ALL_INLINE : 0) +#endif + +/* Code called on the rare occasions that an interrupt occures. */ + +#ifndef INTERRUPTS_INLINE +#define INTERRUPTS_INLINE DEFAULT_INLINE +#endif + +/* Code called on the rare occasion that either gdb or the device tree + need to manipulate a register within a processor */ + +#ifndef REGISTERS_INLINE +#define REGISTERS_INLINE DEFAULT_INLINE +#endif + +/* Code called on the rare occasion that a processor is manipulating + real hardware instead of RAM. + + Also, most of the functions in devices.c are always called through + a jump table. */ + +#ifndef DEVICE_INLINE +#define DEVICE_INLINE (DEFAULT_INLINE ? INLINE_LOCALS : 0) +#endif + +/* Code called used while the device tree is being built. + + Inlining this is of no benefit */ + +#ifndef TREE_INLINE +#define TREE_INLINE (DEFAULT_INLINE ? INLINE_LOCALS : 0) +#endif + +/* Code called whenever information on a Special Purpose Register is + required. Called by the mflr/mtlr pseudo instructions */ + +#ifndef SPREG_INLINE +#define SPREG_INLINE DEFAULT_INLINE +#endif + +/* Functions modeling the semantics of each instruction. Two cases to + consider, firstly of idecode is implemented with a switch then this + allows the idecode function to inline each semantic function + (avoiding a call). The second case is when idecode is using a + table, even then while the semantic functions can't be inlined, + setting it to one still enables each semantic function to inline + anything they call (if that code is marked for being inlined). + + WARNING: you need lots (like 200mb of swap) of swap. Setting this + to 1 is useful when using a table as it enables the sematic code to + inline all of their called functions */ + +#ifndef SEMANTICS_INLINE +#define SEMANTICS_INLINE (DEFAULT_INLINE & ~INLINE_MODULE) +#endif + +/* When using the instruction cache, code to decode an instruction and + install it into the cache. Normally called when ever there is a + miss in the instruction cache. */ + +#ifndef ICACHE_INLINE +#define ICACHE_INLINE (DEFAULT_INLINE & ~INLINE_MODULE) +#endif + +/* General functions called by semantics functions but part of the + instruction table. Although called by the semantic functions the + frequency of calls is low. Consequently the need to inline this + code is reduced. */ + +#ifndef SUPPORT_INLINE +#define SUPPORT_INLINE INLINE_LOCALS +#endif + +/* Model specific code used in simulating functional units. Note, it actaully + pays NOT to inline the PowerPC model functions (at least on the x86). This + is because if it is inlined, each PowerPC instruction gets a separate copy + of the code, which is not friendly to the cache. */ + +#ifndef MODEL_INLINE +#define MODEL_INLINE (DEFAULT_INLINE & ~INLINE_MODULE) +#endif + +/* Code to print out what options we were compiled with. Because this + is called at process startup, it doesn't have to be inlined, but + if it isn't brought in and the model routines are inline, the model + routines will be pulled in twice. */ + +#ifndef OPTIONS_INLINE +#define OPTIONS_INLINE MODEL_INLINE +#endif + +/* idecode acts as the hub of the system, everything else is imported + into this file */ + +#ifndef IDECOCE_INLINE +#define IDECODE_INLINE INLINE_LOCALS +#endif + +/* psim, isn't actually inlined */ + +#ifndef PSIM_INLINE +#define PSIM_INLINE INLINE_LOCALS +#endif + +/* Code to emulate os or rom compatibility. This code is called via a + table and hence there is little benefit in making it inline */ + +#ifndef OS_EMUL_INLINE +#define OS_EMUL_INLINE 0 +#endif + +#endif /* _PSIM_CONFIG_H */ diff --git a/sim/ppc/table.c b/sim/ppc/table.c new file mode 100644 index 00000000000..1994d0d3ce0 --- /dev/null +++ b/sim/ppc/table.c @@ -0,0 +1,279 @@ +/* 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. + + */ + + +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <fcntl.h> +#include <ctype.h> + +#include "config.h" +#include "misc.h" +#include "lf.h" +#include "table.h" + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +struct _table { + size_t size; + char *buffer; + char *pos; + int line_nr; + int nr_fields; + int nr_model_fields; + char *file_name; +}; + +extern table * +table_open(const char *file_name, + int nr_fields, + int nr_model_fields) +{ + int fd; + struct stat stat_buf; + table *file; + int nr; + + /* create a file descriptor */ + file = ZALLOC(table); + ASSERT(file != NULL); + file->nr_fields = nr_fields; + file->nr_model_fields = nr_model_fields; + + /* save the file name */ + file->file_name = (char*)zalloc(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("table_open.fstat"); + exit(1); + } + file->size = stat_buf.st_size; + + /* allocate this much memory */ + file->buffer = (char*)zalloc(file->size+1); + if(file->buffer == NULL) { + perror("table_open.calloc.file->size+1"); + exit(1); + } + file->pos = file->buffer; + + /* read it in */ +#ifdef __CYGWIN32__ + if ((file->size) && ((nr = read(fd, file->buffer, file->size)) <= 0)) { +#else + if ((nr = read(fd, file->buffer, file->size)) < file->size) { +#endif + perror("table_open.read"); + exit(1); + } + file->size = nr; + file->buffer[file->size] = '\0'; + + /* done */ + close(fd); + return file; +} + + +extern table_entry * +table_entry_read(table *file) +{ + int field; + 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 = (table_entry*)zalloc(sizeof(table_entry) + + (file->nr_fields + 1) * sizeof(char*)); + ASSERT(entry != NULL); + entry->file_name = file->file_name; + entry->nr_fields = file->nr_fields; + + /* break the line into its colon delimitered fields */ + for (field = 0; field < file->nr_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->nr_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++; + + /* if following lines begin with a star, add them to the model + section. */ + while ((file->nr_model_fields > 0) && (*file->pos == '*')) { + table_model_entry *model = (table_model_entry*)zalloc(sizeof(table_model_entry) + + (file->nr_model_fields + 1) * sizeof(char*)); + if (entry->model_last) + entry->model_last->next = model; + else + entry->model_first = model; + entry->model_last = model; + + /* break the line into its colon delimitered fields */ + file->pos++; + for (field = 0; field < file->nr_model_fields-1; field++) { + model->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->nr_model_fields-1); + model->fields[field] = file->pos; + while (*file->pos && *file->pos != '\n') { + file->pos++; + } + if (*file->pos == '\n') { + *file->pos = '\0'; + file->pos++; + } + + file->line_nr++; + model->line_nr = file->line_nr; + } + + entry->line_nr = file->line_nr; + + /* if following lines are 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') { + char *save_pos = ++file->pos; + int extra_lines = 0; + file->line_nr++; + /* Allow tab indented to have blank lines */ + while (*save_pos == '\n') { + save_pos++; + extra_lines++; + } + if (*save_pos == '\t') { + file->pos = save_pos; + file->line_nr += extra_lines; + } + } + } while (*file->pos != '\0' && *file->pos == '\t'); + if (file->pos[-1] == '\n') + file->pos[-1] = '\0'; + } + else + entry->annex = NULL; + + /* return it */ + return entry; + +} + + +extern void +dump_table_entry(table_entry *entry, + int indent) +{ + printf("(table_entry*)%p\n", entry); + + if (entry != NULL) { + int field; + char sep; + + sep = ' '; + dumpf(indent, "(fields"); + for (field = 0; field < entry->nr_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"); + + } +} + + +extern void +table_entry_print_cpp_line_nr(lf *file, + table_entry *entry) +{ + lf_print__external_reference(file, entry->line_nr, entry->file_name); +} + + diff --git a/sim/ppc/table.h b/sim/ppc/table.h new file mode 100644 index 00000000000..509fb22870e --- /dev/null +++ b/sim/ppc/table.h @@ -0,0 +1,60 @@ +/* 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. + + */ + + +/* load a table into memory */ + +typedef struct _table table; + +typedef struct _table_model_entry table_model_entry; +struct _table_model_entry { + table_model_entry *next; + int line_nr; + int nr_fields; + char *fields[0]; /* User defined */ +}; + +typedef struct _table_entry table_entry; +struct _table_entry { + int line_nr; + int nr_fields; + char *file_name; + table_model_entry *model_first; + table_model_entry *model_last; + char *annex; + char *fields[0]; /* User defined */ +}; + + +extern table *table_open +(const char *file_name, + int max_nr_fields, + int max_nr_model_fields); + +extern table_entry *table_entry_read +(table *file); + +extern void dump_table_entry +(table_entry *entry, + int indent); + +extern void table_entry_print_cpp_line_nr +(lf *file, + table_entry *entry); diff --git a/sim/ppc/tree.c b/sim/ppc/tree.c new file mode 100644 index 00000000000..360059353ce --- /dev/null +++ b/sim/ppc/tree.c @@ -0,0 +1,1256 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1997, 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 _PARSE_C_ +#define _PARSE_C_ + +#include <stdio.h> +#include <stdarg.h> + +#include "basics.h" + +#include "device.h" +#include "tree.h" + + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#ifdef HAVE_STRING_H +#include <string.h> +#else +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif +#endif + +#include <ctype.h> + + +/* manipulate/lookup device names */ + +typedef struct _name_specifier { + /* components in the full length name */ + char *path; + char *property; + char *value; + /* current device */ + char *name; + char *base; + char *unit; + char *args; + /* previous device */ + char *last_name; + char *last_base; + char *last_unit; + char *last_args; + /* work area */ + char buf[1024]; +} name_specifier; + + + +/* Given a device specifier, break it up into its main components: + path (and if present) property name and property value. */ + +STATIC_INLINE_TREE\ +(int) +split_device_specifier(device *current, + const char *device_specifier, + name_specifier *spec) +{ + char *chp = NULL; + + /* expand any leading alias if present */ + if (current != NULL + && *device_specifier != '\0' + && *device_specifier != '.' + && *device_specifier != '/') { + device *aliases = tree_find_device(current, "/aliases"); + char alias[32]; + int len = 0; + while (device_specifier[len] != '\0' + && device_specifier[len] != '/' + && device_specifier[len] != ':' + && !isspace(device_specifier[len])) { + alias[len] = device_specifier[len]; + len++; + if (len >= sizeof(alias)) + error("split_device_specifier: buffer overflow"); + } + alias[len] = '\0'; + if (aliases != NULL + && device_find_property(aliases, alias)) { + strcpy(spec->buf, device_find_string_property(aliases, alias)); + strcat(spec->buf, device_specifier + len); + } + else { + strcpy(spec->buf, device_specifier); + } + } + else { + strcpy(spec->buf, device_specifier); + } + + /* check no overflow */ + if (strlen(spec->buf) >= sizeof(spec->buf)) + error("split_device_specifier: buffer overflow\n"); + + /* strip leading spaces */ + chp = spec->buf; + while (*chp != '\0' && isspace(*chp)) + chp++; + if (*chp == '\0') + return 0; + + /* find the path and terminate it with null */ + spec->path = chp; + while (*chp != '\0' && !isspace(*chp)) + chp++; + if (*chp != '\0') { + *chp = '\0'; + chp++; + } + + /* and any value */ + while (*chp != '\0' && isspace(*chp)) + chp++; + spec->value = chp; + + /* now go back and chop the property off of the path */ + if (spec->value[0] == '\0') { + spec->property = NULL; /*not a property*/ + spec->value = NULL; + } + else if (spec->value[0] == '>' + || spec->value[0] == '<') { + /* an interrupt spec */ + spec->property = NULL; + } + else { + chp = strrchr(spec->path, '/'); + if (chp == NULL) { + spec->property = spec->path; + spec->path = strchr(spec->property, '\0'); + } + else { + *chp = '\0'; + spec->property = chp+1; + } + } + + /* and mark the rest as invalid */ + spec->name = NULL; + spec->base = NULL; + spec->unit = NULL; + spec->args = NULL; + spec->last_name = NULL; + spec->last_base = NULL; + spec->last_unit = NULL; + spec->last_args = NULL; + + return 1; +} + + +/* given a device specifier break it up into its main components - + path and property name - assuming that the last `device' is a + property name. */ + +STATIC_INLINE_DEVICE\ +(int) +split_property_specifier(device *current, + const char *property_specifier, + name_specifier *spec) +{ + if (split_device_specifier(current, property_specifier, spec)) { + if (spec->property == NULL) { + /* force the last name to be a property name */ + char *chp = strrchr(spec->path, '/'); + if (chp == NULL) { + spec->property = spec->path; + spec->path = strrchr(spec->property, '\0');; + } + else { + *chp = '\0'; + spec->property = chp+1; + } + } + return 1; + } + else + return 0; +} + + +/* device the next device name and split it up, return 0 when no more + names to device */ + +STATIC_INLINE_TREE\ +(int) +split_device_name(name_specifier *spec) +{ + char *chp; + /* remember what came before */ + spec->last_name = spec->name; + spec->last_base = spec->base; + spec->last_unit = spec->unit; + spec->last_args = spec->args; + /* finished? */ + if (spec->path[0] == '\0') { + spec->name = NULL; + spec->base = NULL; + spec->unit = NULL; + spec->args = NULL; + return 0; + } + /* break the current device spec from the path */ + spec->name = spec->path; + chp = strchr(spec->name, '/'); + if (chp == NULL) + spec->path = strchr(spec->name, '\0'); + else { + spec->path = chp+1; + *chp = '\0'; + } + /* break out the base */ + if (spec->name[0] == '(') { + chp = strchr(spec->name, ')'); + if (chp == NULL) { + spec->base = spec->name; + } + else { + *chp = '\0'; + spec->base = spec->name + 1; + spec->name = chp + 1; + } + } + else { + spec->base = spec->name; + } + /* now break out the unit */ + chp = strchr(spec->name, '@'); + if (chp == NULL) { + spec->unit = NULL; + chp = spec->name; + } + else { + *chp = '\0'; + chp += 1; + spec->unit = chp; + } + /* finally any args */ + chp = strchr(chp, ':'); + if (chp == NULL) + spec->args = NULL; + else { + *chp = '\0'; + spec->args = chp+1; + } + return 1; +} + + +/* device the value, returning the next non-space token */ + +STATIC_INLINE_TREE\ +(char *) +split_value(name_specifier *spec) +{ + char *token; + if (spec->value == NULL) + return NULL; + /* skip leading white space */ + while (isspace(spec->value[0])) + spec->value++; + if (spec->value[0] == '\0') { + spec->value = NULL; + return NULL; + } + token = spec->value; + /* find trailing space */ + while (spec->value[0] != '\0' && !isspace(spec->value[0])) + spec->value++; + /* chop this value out */ + if (spec->value[0] != '\0') { + spec->value[0] = '\0'; + spec->value++; + } + return token; +} + + + +/* traverse the path specified by spec starting at current */ + +STATIC_INLINE_TREE\ +(device *) +split_find_device(device *current, + name_specifier *spec) +{ + /* strip off (and process) any leading ., .., ./ and / */ + while (1) { + if (strncmp(spec->path, "/", strlen("/")) == 0) { + /* cd /... */ + while (current != NULL && device_parent(current) != NULL) + current = device_parent(current); + spec->path += strlen("/"); + } + else if (strncmp(spec->path, "./", strlen("./")) == 0) { + /* cd ./... */ + current = current; + spec->path += strlen("./"); + } + else if (strncmp(spec->path, "../", strlen("../")) == 0) { + /* cd ../... */ + if (current != NULL && device_parent(current) != NULL) + current = device_parent(current); + spec->path += strlen("../"); + } + else if (strcmp(spec->path, ".") == 0) { + /* cd . */ + current = current; + spec->path += strlen("."); + } + else if (strcmp(spec->path, "..") == 0) { + /* cd . */ + if (current != NULL && device_parent(current) != NULL) + current = device_parent(current); + spec->path += strlen(".."); + } + else + break; + } + + /* now go through the path proper */ + + if (current == NULL) { + split_device_name(spec); + return NULL; + } + + while (split_device_name(spec)) { + device *child; + for (child = device_child(current); + child != NULL; child = device_sibling(child)) { + if (strcmp(spec->name, device_name(child)) == 0) { + if (spec->unit == NULL) + break; + else { + device_unit phys; + device_decode_unit(current, spec->unit, &phys); + if (memcmp(&phys, device_unit_address(child), + sizeof(device_unit)) == 0) + break; + } + } + } + if (child == NULL) + return current; /* search failed */ + current = child; + } + + return current; +} + + +STATIC_INLINE_TREE\ +(device *) +split_fill_path(device *current, + const char *device_specifier, + name_specifier *spec) +{ + /* break it up */ + if (!split_device_specifier(current, device_specifier, spec)) + device_error(current, "error parsing %s\n", device_specifier); + + /* fill our tree with its contents */ + current = split_find_device(current, spec); + + /* add any additional devices as needed */ + if (spec->name != NULL) { + do { + current = device_create(current, spec->base, spec->name, + spec->unit, spec->args); + } while (split_device_name(spec)); + } + + return current; +} + + +INLINE_TREE\ +(void) +tree_init(device *root, + psim *system) +{ + TRACE(trace_device_tree, ("tree_init(root=0x%lx, system=0x%lx)\n", + (long)root, + (long)system)); + /* remove the old, rebuild the new */ + tree_traverse(root, device_clean, NULL, system); + tree_traverse(root, device_init_static_properties, NULL, system); + tree_traverse(root, device_init_address, NULL, system); + tree_traverse(root, device_init_runtime_properties, NULL, system); + tree_traverse(root, device_init_data, NULL, system); +} + + + +/* <non-white-space> */ + +STATIC_INLINE_TREE\ +(const char *) +skip_token(const char *chp) +{ + while (!isspace(*chp) && *chp != '\0') + chp++; + while (isspace(*chp) && *chp != '\0') + chp++; + return chp; +} + + +/* count the number of entries */ + +STATIC_INLINE_TREE\ +(int) +count_entries(device *current, + const char *property_name, + const char *property_value, + int modulo) +{ + const char *chp = property_value; + int nr_entries = 0; + while (*chp != '\0') { + nr_entries += 1; + chp = skip_token(chp); + } + if ((nr_entries % modulo) != 0) { + device_error(current, "incorrect number of entries for %s property %s, should be multiple of %d", + property_name, property_value, modulo); + } + return nr_entries / modulo; +} + + + +/* parse: <address> ::= <token> ; device dependant */ + +STATIC_INLINE_TREE\ +(const char *) +parse_address(device *current, + device *bus, + const char *chp, + device_unit *address) +{ + if (device_decode_unit(bus, chp, address) < 0) + device_error(current, "invalid unit address in %s", chp); + return skip_token(chp); +} + + +/* parse: <size> ::= <number> { "," <number> } ; */ + +STATIC_INLINE_TREE\ +(const char *) +parse_size(device *current, + device *bus, + const char *chp, + device_unit *size) +{ + int i; + int nr; + const char *curr = chp; + memset(size, 0, sizeof(*size)); + /* parse the numeric list */ + size->nr_cells = device_nr_size_cells(bus); + nr = 0; + while (1) { + char *next; + size->cells[nr] = strtoul(curr, &next, 0); + if (curr == next) + device_error(current, "Problem parsing <size> %s", chp); + nr += 1; + if (next[0] != ',') + break; + if (nr == size->nr_cells) + device_error(current, "Too many values in <size> %s", chp); + curr = next + 1; + } + ASSERT(nr > 0 && nr <= size->nr_cells); + /* right align the numbers */ + for (i = 1; i <= size->nr_cells; i++) { + if (i <= nr) + size->cells[size->nr_cells - i] = size->cells[nr - i]; + else + size->cells[size->nr_cells - i] = 0; + } + return skip_token(chp); +} + + +/* parse: <reg> ::= { <address> <size> } ; */ + +STATIC_INLINE_TREE\ +(void) +parse_reg_property(device *current, + const char *property_name, + const char *property_value) +{ + int nr_regs; + int reg_nr; + reg_property_spec *regs; + const char *chp; + + /* determine the number of reg entries by counting tokens */ + nr_regs = count_entries(current, property_name, property_value, 2); + + /* create working space */ + regs = zalloc(nr_regs * sizeof(*regs)); + + /* fill it in */ + chp = property_value; + for (reg_nr = 0; reg_nr < nr_regs; reg_nr++) { + chp = parse_address(current, device_parent(current), + chp, ®s[reg_nr].address); + chp = parse_size(current, device_parent(current), + chp, ®s[reg_nr].size); + } + + /* create it */ + device_add_reg_array_property(current, property_name, + regs, nr_regs); + + zfree(regs); +} + + +/* { <child-address> <parent-address> <child-size> }* */ + +STATIC_INLINE_TREE\ +(void) +parse_ranges_property(device *current, + const char *property_name, + const char *property_value) +{ + int nr_ranges; + int range_nr; + range_property_spec *ranges; + const char *chp; + + /* determine the number of ranges specified */ + nr_ranges = count_entries(current, property_name, property_value, 3); + + /* create a property of that size */ + ranges = zalloc(nr_ranges * sizeof(*ranges)); + + /* fill it in */ + chp = property_value; + for (range_nr = 0; range_nr < nr_ranges; range_nr++) { + chp = parse_address(current, current, + chp, &ranges[range_nr].child_address); + chp = parse_address(current, device_parent(current), + chp, &ranges[range_nr].parent_address); + chp = parse_size(current, current, + chp, &ranges[range_nr].size); + } + + /* create it */ + device_add_range_array_property(current, property_name, ranges, nr_ranges); + + zfree(ranges); +} + + +/* <integer> ... */ + +STATIC_INLINE_TREE\ +(void) +parse_integer_property(device *current, + const char *property_name, + const char *property_value) +{ + int nr_entries; + unsigned_cell words[1024]; + /* integer or integer array? */ + nr_entries = 0; + while (1) { + char *end; + words[nr_entries] = strtoul(property_value, &end, 0); + if (property_value == end) + break; + nr_entries += 1; + if (nr_entries * sizeof(words[0]) >= sizeof(words)) + device_error(current, "buffer overflow"); + property_value = end; + } + if (nr_entries == 0) + device_error(current, "error parsing integer property %s (%s)", + property_name, property_value); + else if (nr_entries == 1) + device_add_integer_property(current, property_name, words[0]); + else { + int i; + for (i = 0; i < nr_entries; i++) { + H2BE(words[i]); + } + /* perhaphs integer array property is better */ + device_add_array_property(current, property_name, words, + sizeof(words[0]) * nr_entries); + } +} + + +/* <string> ... */ + +STATIC_INLINE_TREE\ +(void) +parse_string_property(device *current, + const char *property_name, + const char *property_value) +{ + char **strings; + const char *chp; + int nr_strings; + int approx_nr_strings; + + /* get an estimate as to the number of strings by counting double + quotes */ + approx_nr_strings = 2; + for (chp = property_value; *chp; chp++) { + if (*chp == '"') + approx_nr_strings++; + } + approx_nr_strings = (approx_nr_strings) / 2; + + /* create a string buffer for that many (plus a null) */ + strings = (char**)zalloc((approx_nr_strings + 1) * sizeof(char*)); + + /* now find all the strings */ + chp = property_value; + nr_strings = 0; + while (1) { + + /* skip leading space */ + while (*chp != '\0' && isspace(*chp)) + chp += 1; + if (*chp == '\0') + break; + + /* copy it in */ + if (*chp == '"') { + /* a quoted string - watch for '\' et.al. */ + /* estimate the size and allocate space for it */ + int pos; + chp++; + pos = 0; + while (chp[pos] != '\0' && chp[pos] != '"') { + if (chp[pos] == '\\' && chp[pos+1] != '\0') + pos += 2; + else + pos += 1; + } + strings[nr_strings] = zalloc(pos + 1); + /* copy the string over */ + pos = 0; + while (*chp != '\0' && *chp != '"') { + if (*chp == '\\' && *(chp+1) != '\0') { + strings[nr_strings][pos] = *(chp+1); + chp += 2; + pos++; + } + else { + strings[nr_strings][pos] = *chp; + chp += 1; + pos++; + } + } + if (*chp != '\0') + chp++; + strings[nr_strings][pos] = '\0'; + } + else { + /* copy over a single unquoted token */ + int len = 0; + while (chp[len] != '\0' && !isspace(chp[len])) + len++; + strings[nr_strings] = zalloc(len + 1); + strncpy(strings[nr_strings], chp, len); + strings[nr_strings][len] = '\0'; + chp += len; + } + nr_strings++; + if (nr_strings > approx_nr_strings) + device_error(current, "String property %s badly formatted", + property_name); + } + ASSERT(strings[nr_strings] == NULL); /* from zalloc */ + + /* install it */ + if (nr_strings == 0) + device_add_string_property(current, property_name, ""); + else if (nr_strings == 1) + device_add_string_property(current, property_name, strings[0]); + else { + const char **specs = (const char**)strings; /* stop a bogus error */ + device_add_string_array_property(current, property_name, + specs, nr_strings); + } + + /* flush the created string */ + while (nr_strings > 0) { + nr_strings--; + zfree(strings[nr_strings]); + } + zfree(strings); +} + + +/* <path-to-ihandle-device> */ + +STATIC_INLINE_TREE\ +(void) +parse_ihandle_property(device *current, + const char *property, + const char *value) +{ + ihandle_runtime_property_spec ihandle; + + /* pass the full path */ + ihandle.full_path = value; + + /* save this ready for the ihandle create */ + device_add_ihandle_runtime_property(current, property, + &ihandle); +} + + + +EXTERN_TREE\ +(device *) +tree_parse(device *current, + const char *fmt, + ...) +{ + char device_specifier[1024]; + name_specifier spec; + + /* format the path */ + { + va_list ap; + va_start(ap, fmt); + vsprintf(device_specifier, fmt, ap); + va_end(ap); + if (strlen(device_specifier) >= sizeof(device_specifier)) + error("device_tree_add_deviced: buffer overflow\n"); + } + + /* construct the tree down to the final device */ + current = split_fill_path(current, device_specifier, &spec); + + /* is there an interrupt spec */ + if (spec.property == NULL + && spec.value != NULL) { + char *op = split_value(&spec); + switch (op[0]) { + case '>': + { + char *my_port_name = split_value(&spec); + int my_port; + char *dest_port_name = split_value(&spec); + int dest_port; + name_specifier dest_spec; + char *dest_device_name = split_value(&spec); + device *dest; + /* find my name */ + my_port = device_interrupt_decode(current, my_port_name, + output_port); + /* find the dest device and port */ + dest = split_fill_path(current, dest_device_name, &dest_spec); + dest_port = device_interrupt_decode(dest, dest_port_name, + input_port); + /* connect the two */ + device_interrupt_attach(current, + my_port, + dest, + dest_port, + permenant_object); + } + break; + default: + device_error(current, "unreconised interrupt spec %s\n", spec.value); + break; + } + } + + /* is there a property */ + if (spec.property != NULL) { + if (strcmp(spec.value, "true") == 0) + device_add_boolean_property(current, spec.property, 1); + else if (strcmp(spec.value, "false") == 0) + device_add_boolean_property(current, spec.property, 0); + else { + const device_property *property; + switch (spec.value[0]) { + case '*': + parse_ihandle_property(current, spec.property, spec.value + 1); + break; + case '[': + { + unsigned8 words[1024]; + char *curr = spec.value + 1; + int nr_words = 0; + while (1) { + char *next; + words[nr_words] = H2BE_1(strtoul(curr, &next, 0)); + if (curr == next) + break; + curr = next; + nr_words += 1; + } + device_add_array_property(current, spec.property, + words, sizeof(words[0]) * nr_words); + } + break; + case '"': + parse_string_property(current, spec.property, spec.value); + break; + case '!': + spec.value++; + property = tree_find_property(current, spec.value); + if (property == NULL) + device_error(current, "property %s not found\n", spec.value); + device_add_duplicate_property(current, + spec.property, + property); + break; + default: + if (strcmp(spec.property, "reg") == 0 + || strcmp(spec.property, "assigned-addresses") == 0 + || strcmp(spec.property, "alternate-reg") == 0){ + parse_reg_property(current, spec.property, spec.value); + } + else if (strcmp(spec.property, "ranges") == 0) { + parse_ranges_property(current, spec.property, spec.value); + } + else if (isdigit(spec.value[0]) + || (spec.value[0] == '-' && isdigit(spec.value[1])) + || (spec.value[0] == '+' && isdigit(spec.value[1]))) { + parse_integer_property(current, spec.property, spec.value); + } + else + parse_string_property(current, spec.property, spec.value); + break; + } + } + } + return current; +} + + +INLINE_TREE\ +(void) +tree_traverse(device *root, + tree_traverse_function *prefix, + tree_traverse_function *postfix, + void *data) +{ + device *child; + if (prefix != NULL) + prefix(root, data); + for (child = device_child(root); + child != NULL; + child = device_sibling(child)) { + tree_traverse(child, prefix, postfix, data); + } + if (postfix != NULL) + postfix(root, data); +} + + +STATIC_INLINE_TREE\ +(void) +print_address(device *bus, + const device_unit *phys) +{ + char unit[32]; + device_encode_unit(bus, phys, unit, sizeof(unit)); + printf_filtered(" %s", unit); +} + +STATIC_INLINE_TREE\ +(void) +print_size(device *bus, + const device_unit *size) +{ + int i; + for (i = 0; i < size->nr_cells; i++) + if (size->cells[i] != 0) + break; + if (i < size->nr_cells) { + printf_filtered(" 0x%lx", (unsigned long)size->cells[i]); + i++; + for (; i < size->nr_cells; i++) + printf_filtered(",0x%lx", (unsigned long)size->cells[i]); + } + else + printf_filtered(" 0"); +} + +STATIC_INLINE_TREE\ +(void) +print_reg_property(device *me, + const device_property *property) +{ + int reg_nr; + reg_property_spec reg; + for (reg_nr = 0; + device_find_reg_array_property(me, property->name, reg_nr, ®); + reg_nr++) { + print_address(device_parent(me), ®.address); + print_size(me, ®.size); + } +} + +STATIC_INLINE_TREE\ +(void) +print_ranges_property(device *me, + const device_property *property) +{ + int range_nr; + range_property_spec range; + for (range_nr = 0; + device_find_range_array_property(me, property->name, range_nr, &range); + range_nr++) { + print_address(me, &range.child_address); + print_address(device_parent(me), &range.parent_address); + print_size(me, &range.size); + } +} + +STATIC_INLINE_TREE\ +(void) +print_string(const char *string) +{ + printf_filtered(" \""); + while (*string != '\0') { + switch (*string) { + case '"': + printf_filtered("\\\""); + break; + case '\\': + printf_filtered("\\\\"); + break; + default: + printf_filtered("%c", *string); + break; + } + string++; + } + printf_filtered("\""); +} + +STATIC_INLINE_TREE\ +(void) +print_string_array_property(device *me, + const device_property *property) +{ + int nr; + string_property_spec string; + for (nr = 0; + device_find_string_array_property(me, property->name, nr, &string); + nr++) { + print_string(string); + } +} + +STATIC_INLINE_TREE\ +(void) +print_properties(device *me) +{ + const device_property *property; + for (property = device_find_property(me, NULL); + property != NULL; + property = device_next_property(property)) { + printf_filtered("%s/%s", device_path(me), property->name); + if (property->original != NULL) { + printf_filtered(" !"); + printf_filtered("%s/%s", + device_path(property->original->owner), + property->original->name); + } + else { + switch (property->type) { + case array_property: + if ((property->sizeof_array % sizeof(signed_cell)) == 0) { + unsigned_cell *w = (unsigned_cell*)property->array; + int cell_nr; + for (cell_nr = 0; + cell_nr < (property->sizeof_array / sizeof(unsigned_cell)); + cell_nr++) { + printf_filtered(" 0x%lx", (unsigned long)BE2H_cell(w[cell_nr])); + } + } + else { + unsigned8 *w = (unsigned8*)property->array; + printf_filtered(" ["); + while ((char*)w - (char*)property->array < property->sizeof_array) { + printf_filtered(" 0x%2x", BE2H_1(*w)); + w++; + } + } + break; + case boolean_property: + { + int b = device_find_boolean_property(me, property->name); + printf_filtered(" %s", b ? "true" : "false"); + } + break; + case ihandle_property: + { + if (property->array != NULL) { + device_instance *instance = device_find_ihandle_property(me, property->name); + printf_filtered(" *%s", device_instance_path(instance)); + } + else { + /* not yet initialized, ask the device for the path */ + ihandle_runtime_property_spec spec; + device_find_ihandle_runtime_property(me, property->name, &spec); + printf_filtered(" *%s", spec.full_path); + } + } + break; + case integer_property: + { + unsigned_word w = device_find_integer_property(me, property->name); + printf_filtered(" 0x%lx", (unsigned long)w); + } + break; + case range_array_property: + print_ranges_property(me, property); + break; + case reg_array_property: + print_reg_property(me, property); + break; + case string_property: + { + const char *s = device_find_string_property(me, property->name); + print_string(s); + } + break; + case string_array_property: + print_string_array_property(me, property); + break; + } + } + printf_filtered("\n"); + } +} + +STATIC_INLINE_TREE\ +(void) +print_interrupts(device *me, + int my_port, + device *dest, + int dest_port, + void *ignore_or_null) +{ + char src[32]; + char dst[32]; + device_interrupt_encode(me, my_port, src, sizeof(src), output_port); + device_interrupt_encode(dest, dest_port, dst, sizeof(dst), input_port); + printf_filtered("%s > %s %s %s\n", + device_path(me), + src, dst, + device_path(dest)); +} + +STATIC_INLINE_TREE\ +(void) +print_device(device *me, + void *ignore_or_null) +{ + printf_filtered("%s\n", device_path(me)); + print_properties(me); + device_interrupt_traverse(me, print_interrupts, NULL); +} + +INLINE_TREE\ +(void) +tree_print(device *root) +{ + tree_traverse(root, + print_device, NULL, + NULL); +} + + +INLINE_TREE\ +(void) +tree_usage(int verbose) +{ + if (verbose == 1) { + printf_filtered("\n"); + printf_filtered("A device/property specifier has the form:\n"); + printf_filtered("\n"); + printf_filtered(" /path/to/a/device [ property-value ]\n"); + printf_filtered("\n"); + printf_filtered("and a possible device is\n"); + printf_filtered("\n"); + } + if (verbose > 1) { + printf_filtered("\n"); + printf_filtered("A device/property specifier (<spec>) has the format:\n"); + printf_filtered("\n"); + printf_filtered(" <spec> ::= <path> [ <value> ] ;\n"); + printf_filtered(" <path> ::= { <prefix> } { <node> \"/\" } <node> ;\n"); + printf_filtered(" <prefix> ::= ( | \"/\" | \"../\" | \"./\" ) ;\n"); + printf_filtered(" <node> ::= <name> [ \"@\" <unit> ] [ \":\" <args> ] ;\n"); + printf_filtered(" <unit> ::= <number> { \",\" <number> } ;\n"); + printf_filtered("\n"); + printf_filtered("Where:\n"); + printf_filtered("\n"); + printf_filtered(" <name> is the name of a device (list below)\n"); + printf_filtered(" <unit> is the unit-address relative to the parent bus\n"); + printf_filtered(" <args> additional arguments used when creating the device\n"); + printf_filtered(" <value> ::= ( <number> # integer property\n"); + printf_filtered(" | \"[\" { <number> } # array property (byte)\n"); + printf_filtered(" | \"{\" { <number> } # array property (cell)\n"); + printf_filtered(" | [ \"true\" | \"false\" ] # boolean property\n"); + printf_filtered(" | \"*\" <path> # ihandle property\n"); + printf_filtered(" | \"!\" <path> # copy property\n"); + printf_filtered(" | \">\" [ <number> ] <path> # attach interrupt\n"); + printf_filtered(" | \"<\" <path> # attach child interrupt\n"); + printf_filtered(" | \"\\\"\" <text> # string property\n"); + printf_filtered(" | <text> # string property\n"); + printf_filtered(" ) ;\n"); + printf_filtered("\n"); + printf_filtered("And the following are valid device names:\n"); + printf_filtered("\n"); + } +} + + + +INLINE_TREE\ +(device_instance *) +tree_instance(device *root, + const char *device_specifier) +{ + /* find the device node */ + device *me; + name_specifier spec; + if (!split_device_specifier(root, device_specifier, &spec)) + return NULL; + me = split_find_device(root, &spec); + if (spec.name != NULL) + return NULL; + /* create the instance */ + return device_create_instance(me, device_specifier, spec.last_args); +} + + +INLINE_TREE\ +(device *) +tree_find_device(device *root, + const char *path_to_device) +{ + device *node; + name_specifier spec; + + /* parse the path */ + split_device_specifier(root, path_to_device, &spec); + if (spec.value != NULL) + return NULL; /* something wierd */ + + /* now find it */ + node = split_find_device(root, &spec); + if (spec.name != NULL) + return NULL; /* not a leaf */ + + return node; +} + + +INLINE_TREE\ +(const device_property *) +tree_find_property(device *root, + const char *path_to_property) +{ + name_specifier spec; + if (!split_property_specifier(root, path_to_property, &spec)) + device_error(root, "Invalid property path %s", path_to_property); + root = split_find_device(root, &spec); + return device_find_property(root, spec.property); +} + +INLINE_TREE\ +(int) +tree_find_boolean_property(device *root, + const char *path_to_property) +{ + name_specifier spec; + if (!split_property_specifier(root, path_to_property, &spec)) + device_error(root, "Invalid property path %s", path_to_property); + root = split_find_device(root, &spec); + return device_find_boolean_property(root, spec.property); +} + +INLINE_TREE\ +(signed_cell) +tree_find_integer_property(device *root, + const char *path_to_property) +{ + name_specifier spec; + if (!split_property_specifier(root, path_to_property, &spec)) + device_error(root, "Invalid property path %s", path_to_property); + root = split_find_device(root, &spec); + return device_find_integer_property(root, spec.property); +} + +INLINE_TREE\ +(device_instance *) +tree_find_ihandle_property(device *root, + const char *path_to_property) +{ + name_specifier spec; + if (!split_property_specifier(root, path_to_property, &spec)) + device_error(root, "Invalid property path %s", path_to_property); + root = split_find_device(root, &spec); + return device_find_ihandle_property(root, spec.property); +} + +INLINE_TREE\ +(const char *) +tree_find_string_property(device *root, + const char *path_to_property) +{ + name_specifier spec; + if (!split_property_specifier(root, path_to_property, &spec)) + device_error(root, "Invalid property path %s", path_to_property); + root = split_find_device(root, &spec); + return device_find_string_property(root, spec.property); +} + + +#endif /* _PARSE_C_ */ diff --git a/sim/ppc/tree.h b/sim/ppc/tree.h new file mode 100644 index 00000000000..46590cb6266 --- /dev/null +++ b/sim/ppc/tree.h @@ -0,0 +1,139 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1996, 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 _TREE_H_ +#define _TREE_H_ + +#ifndef INLINE_TREE +#define INLINE_TREE +#endif + +/* Constructing the device tree: + + The initial device tree populated with devices and basic properties + is created using the function <<device_tree_add_parsed()>>. This + function parses a PSIM device specification and uses it to populate + the tree accordingly. + + This function accepts a printf style formatted string as the + argument that describes the entry. Any properties or interrupt + connections added to a device tree using this function are marked + as having a permenant disposition. When the tree is (re) + initialized they will be restored to their initial value. + + */ + +EXTERN_TREE\ +(device *) tree_parse +(device *root, + const char *fmt, + ...) __attribute__ ((format (printf, 2, 3))); + + +INLINE_TREE\ +(void) tree_usage +(int verbose); + +INLINE_TREE\ +(void) tree_print +(device *root); + +INLINE_TREE\ +(device_instance*) tree_instance +(device *root, + const char *device_specifier); + + +/* Tree traversal:: + + The entire device tree can be traversed using the + <<device_tree_traverse()>> function. The traversal can be in + either pre- or postfix order. + + */ + +typedef void (tree_traverse_function) + (device *device, + void *data); + +INLINE_DEVICE\ +(void) tree_traverse +(device *root, + tree_traverse_function *prefix, + tree_traverse_function *postfix, + void *data); + + +/* Tree lookup:: + + The function <<tree_find_device()>> will attempt to locate + the specified device within the tree. If the device is not found a + NULL device is returned. + + */ + +INLINE_TREE\ +(device *) tree_find_device +(device *root, + const char *path); + + +INLINE_TREE\ +(const device_property *) tree_find_property +(device *root, + const char *path_to_property); + +INLINE_TREE\ +(int) tree_find_boolean_property +(device *root, + const char *path_to_property); + +INLINE_TREE\ +(signed_cell) tree_find_integer_property +(device *root, + const char *path_to_property); + +INLINE_TREE\ +(device_instance *) tree_find_ihandle_property +(device *root, + const char *path_to_property); + +INLINE_TREE\ +(const char *) tree_find_string_property +(device *root, + const char *path_to_property); + + +/* Initializing the created tree: + + Once a device tree has been created the <<device_tree_init()>> + function is used to initialize it. The exact sequence of events + that occure during initialization are described separatly. + + */ + +INLINE_TREE\ +(void) tree_init +(device *root, + psim *system); + + +#endif /* _TREE_H_ */ diff --git a/sim/ppc/vm.c b/sim/ppc/vm.c new file mode 100644 index 00000000000..bff962cc31e --- /dev/null +++ b/sim/ppc/vm.c @@ -0,0 +1,1198 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1997, 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_C_ +#define _VM_C_ + +#if 0 +#include "basics.h" +#include "registers.h" +#include "device.h" +#include "corefile.h" +#include "vm.h" +#include "interrupts.h" +#include "mon.h" +#endif + +#include "cpu.h" + +/* OEA vs VEA + + For the VEA model, the VM layer is almost transparent. It's only + purpose is to maintain separate core_map's for the instruction + and data address spaces. This being so that writes to instruction + space or execution of a data space is prevented. + + For the OEA model things are more complex. The reason for separate + instruction and data models becomes crucial. The OEA model is + built out of three parts. An instruction map, a data map and an + underlying structure that provides access to the VM data kept in + main memory. */ + + +/* OEA data structures: + + The OEA model maintains internal data structures that shadow the + semantics of the various OEA VM registers (BAT, SR, etc). This + allows a simple efficient model of the VM to be implemented. + + Consistency between OEA registers and this model's internal data + structures is maintained by updating the structures at + `synchronization' points. Of particular note is that (at the time + of writing) the memory data types for BAT registers are rebuilt + when ever the processor moves between problem and system states. + + Unpacked values are stored in the OEA so that they correctly align + to where they will be needed by the PTE address. */ + + +/* Protection table: + + Matrix of processor state, type of access and validity */ + +typedef enum { + om_supervisor_state, + om_problem_state, + nr_om_modes +} om_processor_modes; + +typedef enum { + om_data_read, om_data_write, + om_instruction_read, om_access_any, + nr_om_access_types +} om_access_types; + +static int om_valid_access[2][4][nr_om_access_types] = { + /* read, write, instruction, any */ + /* K bit == 0 */ + { /*r w i a pp */ + { 1, 1, 1, 1 }, /* 00 */ + { 1, 1, 1, 1 }, /* 01 */ + { 1, 1, 1, 1 }, /* 10 */ + { 1, 0, 1, 1 }, /* 11 */ + }, + /* K bit == 1 or P bit valid */ + { /*r w i a pp */ + { 0, 0, 0, 0 }, /* 00 */ + { 1, 0, 1, 1 }, /* 01 */ + { 1, 1, 1, 1 }, /* 10 */ + { 1, 0, 1, 1 }, /* 11 */ + } +}; + + +/* Bat translation: + + The bat data structure only contains information on valid BAT + translations for the current processor mode and type of access. */ + +typedef struct _om_bat { + unsigned_word block_effective_page_index; + unsigned_word block_effective_page_index_mask; + unsigned_word block_length_mask; + unsigned_word block_real_page_number; + int protection_bits; +} om_bat; + +enum _nr_om_bat_registers { + nr_om_bat_registers = 4 +}; + +typedef struct _om_bats { + int nr_valid_bat_registers; + om_bat bat[nr_om_bat_registers]; +} om_bats; + + +/* Segment TLB: + + In this model the 32 and 64 bit segment tables are treated in very + similar ways. The 32bit segment registers are treated as a + simplification of the 64bit segment tlb */ + +enum _om_segment_tlb_constants { +#if (WITH_TARGET_WORD_BITSIZE == 64) + sizeof_segment_table_entry_group = 128, + sizeof_segment_table_entry = 16, +#endif + om_segment_tlb_index_start_bit = 32, + om_segment_tlb_index_stop_bit = 35, + nr_om_segment_tlb_entries = 16, + nr_om_segment_tlb_constants +}; + +typedef struct _om_segment_tlb_entry { + int key[nr_om_modes]; + om_access_types invalid_access; /* set to instruction if no_execute bit */ + unsigned_word masked_virtual_segment_id; /* aligned ready for pte group addr */ +#if (WITH_TARGET_WORD_BITSIZE == 64) + int is_valid; + unsigned_word masked_effective_segment_id; +#endif +} om_segment_tlb_entry; + +typedef struct _om_segment_tlb { + om_segment_tlb_entry entry[nr_om_segment_tlb_entries]; +} om_segment_tlb; + + +/* Page TLB: + + This OEA model includes a small direct map Page TLB. The tlb is to + cut down on the need for the OEA to perform walks of the page hash + table. */ + +enum _om_page_tlb_constants { + om_page_tlb_index_start_bit = 46, + om_page_tlb_index_stop_bit = 51, + nr_om_page_tlb_entries = 64, +#if (WITH_TARGET_WORD_BITSIZE == 64) + sizeof_pte_group = 128, + sizeof_pte = 16, +#endif +#if (WITH_TARGET_WORD_BITSIZE == 32) + sizeof_pte_group = 64, + sizeof_pte = 8, +#endif + nr_om_page_tlb_constants +}; + +typedef struct _om_page_tlb_entry { + int protection; + int changed; + unsigned_word real_address_of_pte_1; + unsigned_word masked_virtual_segment_id; + unsigned_word masked_page; + unsigned_word masked_real_page_number; +} om_page_tlb_entry; + +typedef struct _om_page_tlb { + om_page_tlb_entry entry[nr_om_page_tlb_entries]; +} om_page_tlb; + + +/* memory translation: + + OEA memory translation possibly involves BAT, SR, TLB and HTAB + information*/ + +typedef struct _om_map { + + /* local cache of register values */ + int is_relocate; + int is_problem_state; + + /* block address translation */ + om_bats *bat_registers; + + /* failing that, translate ea to va using segment tlb */ +#if (WITH_TARGET_WORD_BITSIZE == 64) + unsigned_word real_address_of_segment_table; +#endif + om_segment_tlb *segment_tlb; + + /* then va to ra using hashed page table and tlb */ + unsigned_word real_address_of_page_table; + unsigned_word page_table_hash_mask; + om_page_tlb *page_tlb; + + /* physical memory for fetching page table entries */ + core_map *physical; + + /* address xor for PPC endian */ + unsigned xor[WITH_XOR_ENDIAN]; + +} om_map; + + +/* VM objects: + + External objects defined by vm.h */ + +struct _vm_instruction_map { + /* real memory for last part */ + core_map *code; + /* translate effective to real */ + om_map translation; +}; + +struct _vm_data_map { + /* translate effective to real */ + om_map translation; + /* real memory for translated address */ + core_map *read; + core_map *write; +}; + + +/* VM: + + Underlying memory object. For the VEA this is just the + core_map. For OEA it is the instruction and data memory + translation's */ + +struct _vm { + + /* OEA: base address registers */ + om_bats ibats; + om_bats dbats; + + /* OEA: segment registers */ + om_segment_tlb segment_tlb; + + /* OEA: translation lookaside buffers */ + om_page_tlb instruction_tlb; + om_page_tlb data_tlb; + + /* real memory */ + core *physical; + + /* memory maps */ + vm_instruction_map instruction_map; + vm_data_map data_map; + +}; + + +/* OEA Support procedures */ + + +STATIC_INLINE_VM\ +(unsigned_word) +om_segment_tlb_index(unsigned_word ea) +{ + unsigned_word index = EXTRACTED(ea, + om_segment_tlb_index_start_bit, + om_segment_tlb_index_stop_bit); + return index; +} + +STATIC_INLINE_VM\ +(unsigned_word) +om_page_tlb_index(unsigned_word ea) +{ + unsigned_word index = EXTRACTED(ea, + om_page_tlb_index_start_bit, + om_page_tlb_index_stop_bit); + return index; +} + +STATIC_INLINE_VM\ +(unsigned_word) +om_hash_page(unsigned_word masked_vsid, + unsigned_word ea) +{ + unsigned_word extracted_ea = EXTRACTED(ea, 36, 51); +#if (WITH_TARGET_WORD_BITSIZE == 32) + unsigned_word masked_ea = INSERTED32(extracted_ea, 7, 31-6); + unsigned_word hash = masked_vsid ^ masked_ea; +#endif +#if (WITH_TARGET_WORD_BITSIZE == 64) + unsigned_word masked_ea = INSERTED64(extracted_ea, 18, 63-7); + unsigned_word hash = masked_vsid ^ masked_ea; +#endif + TRACE(trace_vm, ("ea=0x%lx - masked-vsid=0x%lx masked-ea=0x%lx hash=0x%lx\n", + (unsigned long)ea, + (unsigned long)masked_vsid, + (unsigned long)masked_ea, + (unsigned long)hash)); + return hash; +} + +STATIC_INLINE_VM\ +(unsigned_word) +om_pte_0_api(unsigned_word pte_0) +{ +#if (WITH_TARGET_WORD_BITSIZE == 32) + return EXTRACTED32(pte_0, 26, 31); +#endif +#if (WITH_TARGET_WORD_BITSIZE == 64) + return EXTRACTED64(pte_0, 52, 56); +#endif +} + +STATIC_INLINE_VM\ +(unsigned_word) +om_pte_0_hash(unsigned_word pte_0) +{ +#if (WITH_TARGET_WORD_BITSIZE == 32) + return EXTRACTED32(pte_0, 25, 25); +#endif +#if (WITH_TARGET_WORD_BITSIZE == 64) + return EXTRACTED64(pte_0, 62, 62); +#endif +} + +STATIC_INLINE_VM\ +(int) +om_pte_0_valid(unsigned_word pte_0) +{ +#if (WITH_TARGET_WORD_BITSIZE == 32) + return MASKED32(pte_0, 0, 0) != 0; +#endif +#if (WITH_TARGET_WORD_BITSIZE == 64) + return MASKED64(pte_0, 63, 63) != 0; +#endif +} + +STATIC_INLINE_VM\ +(unsigned_word) +om_ea_masked_page(unsigned_word ea) +{ + return MASKED(ea, 36, 51); +} + +STATIC_INLINE_VM\ +(unsigned_word) +om_ea_masked_byte(unsigned_word ea) +{ + return MASKED(ea, 52, 63); +} + +/* return the VSID aligned for pte group addr */ +STATIC_INLINE_VM\ +(unsigned_word) +om_pte_0_masked_vsid(unsigned_word pte_0) +{ +#if (WITH_TARGET_WORD_BITSIZE == 32) + return INSERTED32(EXTRACTED32(pte_0, 1, 24), 31-6-24+1, 31-6); +#endif +#if (WITH_TARGET_WORD_BITSIZE == 64) + return INSERTED64(EXTRACTED64(pte_0, 0, 51), 63-7-52+1, 63-7); +#endif +} + +STATIC_INLINE_VM\ +(unsigned_word) +om_pte_1_pp(unsigned_word pte_1) +{ + return MASKED(pte_1, 62, 63); /*PP*/ +} + +STATIC_INLINE_VM\ +(int) +om_pte_1_referenced(unsigned_word pte_1) +{ + return EXTRACTED(pte_1, 55, 55); +} + +STATIC_INLINE_VM\ +(int) +om_pte_1_changed(unsigned_word pte_1) +{ + return EXTRACTED(pte_1, 56, 56); +} + +STATIC_INLINE_VM\ +(int) +om_pte_1_masked_rpn(unsigned_word pte_1) +{ + return MASKED(pte_1, 0, 51); /*RPN*/ +} + +STATIC_INLINE_VM\ +(unsigned_word) +om_ea_api(unsigned_word ea) +{ + return EXTRACTED(ea, 36, 41); +} + + +/* Page and Segment table read/write operators, these need to still + account for the PPC's XOR operation */ + +STATIC_INLINE_VM\ +(unsigned_word) +om_read_word(om_map *map, + unsigned_word ra, + cpu *processor, + unsigned_word cia) +{ + if (WITH_XOR_ENDIAN) + ra ^= map->xor[sizeof(instruction_word) - 1]; + return core_map_read_word(map->physical, ra, processor, cia); +} + +STATIC_INLINE_VM\ +(void) +om_write_word(om_map *map, + unsigned_word ra, + unsigned_word val, + cpu *processor, + unsigned_word cia) +{ + if (WITH_XOR_ENDIAN) + ra ^= map->xor[sizeof(instruction_word) - 1]; + core_map_write_word(map->physical, ra, val, processor, cia); +} + + +/* Bring things into existance */ + +INLINE_VM\ +(vm *) +vm_create(core *physical) +{ + vm *virtual; + + /* internal checks */ + if (nr_om_segment_tlb_entries + != (1 << (om_segment_tlb_index_stop_bit + - om_segment_tlb_index_start_bit + 1))) + error("internal error - vm_create - problem with om_segment constants\n"); + if (nr_om_page_tlb_entries + != (1 << (om_page_tlb_index_stop_bit + - om_page_tlb_index_start_bit + 1))) + error("internal error - vm_create - problem with om_page constants\n"); + + /* create the new vm register file */ + virtual = ZALLOC(vm); + + /* set up core */ + virtual->physical = physical; + + /* set up the address decoders */ + virtual->instruction_map.translation.bat_registers = &virtual->ibats; + virtual->instruction_map.translation.segment_tlb = &virtual->segment_tlb; + virtual->instruction_map.translation.page_tlb = &virtual->instruction_tlb; + virtual->instruction_map.translation.is_relocate = 0; + virtual->instruction_map.translation.is_problem_state = 0; + virtual->instruction_map.translation.physical = core_readable(physical); + virtual->instruction_map.code = core_readable(physical); + + virtual->data_map.translation.bat_registers = &virtual->dbats; + virtual->data_map.translation.segment_tlb = &virtual->segment_tlb; + virtual->data_map.translation.page_tlb = &virtual->data_tlb; + virtual->data_map.translation.is_relocate = 0; + virtual->data_map.translation.is_problem_state = 0; + virtual->data_map.translation.physical = core_readable(physical); + virtual->data_map.read = core_readable(physical); + virtual->data_map.write = core_writeable(physical); + + return virtual; +} + + +STATIC_INLINE_VM\ +(om_bat *) +om_effective_to_bat(om_map *map, + unsigned_word ea) +{ + int curr_bat = 0; + om_bats *bats = map->bat_registers; + int nr_bats = bats->nr_valid_bat_registers; + + for (curr_bat = 0; curr_bat < nr_bats; curr_bat++) { + om_bat *bat = bats->bat + curr_bat; + if ((ea & bat->block_effective_page_index_mask) + != bat->block_effective_page_index) + continue; + return bat; + } + + return NULL; +} + + +STATIC_INLINE_VM\ +(om_segment_tlb_entry *) +om_effective_to_virtual(om_map *map, + unsigned_word ea, + cpu *processor, + unsigned_word cia) +{ + /* first try the segment tlb */ + om_segment_tlb_entry *segment_tlb_entry = (map->segment_tlb->entry + + om_segment_tlb_index(ea)); + +#if (WITH_TARGET_WORD_BITSIZE == 32) + TRACE(trace_vm, ("ea=0x%lx - sr[%ld] - masked-vsid=0x%lx va=0x%lx%07lx\n", + (unsigned long)ea, + (long)om_segment_tlb_index(ea), + (unsigned long)segment_tlb_entry->masked_virtual_segment_id, + (unsigned long)EXTRACTED32(segment_tlb_entry->masked_virtual_segment_id, 31-6-24+1, 31-6), + (unsigned long)EXTRACTED32(ea, 4, 31))); + return segment_tlb_entry; +#endif + +#if (WITH_TARGET_WORD_BITSIZE == 64) + if (segment_tlb_entry->is_valid + && (segment_tlb_entry->masked_effective_segment_id == MASKED(ea, 0, 35))) { + error("fixme - is there a need to update any bits\n"); + return segment_tlb_entry; + } + + /* drats, segment tlb missed */ + { + unsigned_word segment_id_hash = ea; + int current_hash = 0; + for (current_hash = 0; current_hash < 2; current_hash += 1) { + unsigned_word segment_table_entry_group = + (map->real_address_of_segment_table + | (MASKED64(segment_id_hash, 31, 35) >> (56-35))); + unsigned_word segment_table_entry; + for (segment_table_entry = segment_table_entry_group; + segment_table_entry < (segment_table_entry_group + + sizeof_segment_table_entry_group); + segment_table_entry += sizeof_segment_table_entry) { + /* byte order? */ + unsigned_word segment_table_entry_dword_0 = + om_read_word(map->physical, segment_table_entry, processor, cia); + unsigned_word segment_table_entry_dword_1 = + om_read_word(map->physical, segment_table_entry + 8, + processor, cia); + int is_valid = MASKED64(segment_table_entry_dword_0, 56, 56) != 0; + unsigned_word masked_effective_segment_id = + MASKED64(segment_table_entry_dword_0, 0, 35); + if (is_valid && masked_effective_segment_id == MASKED64(ea, 0, 35)) { + /* don't permit some things */ + if (MASKED64(segment_table_entry_dword_0, 57, 57)) + error("om_effective_to_virtual() - T=1 in STE not supported\n"); + /* update segment tlb */ + segment_tlb_entry->is_valid = is_valid; + segment_tlb_entry->masked_effective_segment_id = + masked_effective_segment_id; + segment_tlb_entry->key[om_supervisor_state] = + EXTRACTED64(segment_table_entry_dword_0, 58, 58); + segment_tlb_entry->key[om_problem_state] = + EXTRACTED64(segment_table_entry_dword_0, 59, 59); + segment_tlb_entry->invalid_access = + (MASKED64(segment_table_entry_dword_0, 60, 60) + ? om_instruction_read + : om_access_any); + segment_tlb_entry->masked_virtual_segment_id = + INSERTED64(EXTRACTED64(segment_table_entry_dword_1, 0, 51), + 18-13, 63-7); /* aligned ready for pte group addr */ + return segment_tlb_entry; + } + } + segment_id_hash = ~segment_id_hash; + } + } + return NULL; +#endif +} + + + +STATIC_INLINE_VM\ +(om_page_tlb_entry *) +om_virtual_to_real(om_map *map, + unsigned_word ea, + om_segment_tlb_entry *segment_tlb_entry, + om_access_types access, + cpu *processor, + unsigned_word cia) +{ + om_page_tlb_entry *page_tlb_entry = (map->page_tlb->entry + + om_page_tlb_index(ea)); + + /* is it a tlb hit? */ + if ((page_tlb_entry->masked_virtual_segment_id + == segment_tlb_entry->masked_virtual_segment_id) + && (page_tlb_entry->masked_page + == om_ea_masked_page(ea))) { + TRACE(trace_vm, ("ea=0x%lx - tlb hit - tlb=0x%lx\n", + (long)ea, (long)page_tlb_entry)); + return page_tlb_entry; + } + + /* drats, it is a tlb miss */ + { + unsigned_word page_hash = + om_hash_page(segment_tlb_entry->masked_virtual_segment_id, ea); + int current_hash; + for (current_hash = 0; current_hash < 2; current_hash += 1) { + unsigned_word real_address_of_pte_group = + (map->real_address_of_page_table + | (page_hash & map->page_table_hash_mask)); + unsigned_word real_address_of_pte_0; + TRACE(trace_vm, + ("ea=0x%lx - htab search %d - htab=0x%lx hash=0x%lx mask=0x%lx pteg=0x%lx\n", + (long)ea, current_hash, + map->real_address_of_page_table, + page_hash, + map->page_table_hash_mask, + (long)real_address_of_pte_group)); + for (real_address_of_pte_0 = real_address_of_pte_group; + real_address_of_pte_0 < (real_address_of_pte_group + + sizeof_pte_group); + real_address_of_pte_0 += sizeof_pte) { + unsigned_word pte_0 = om_read_word(map, + real_address_of_pte_0, + processor, cia); + /* did we hit? */ + if (om_pte_0_valid(pte_0) + && (current_hash == om_pte_0_hash(pte_0)) + && (segment_tlb_entry->masked_virtual_segment_id + == om_pte_0_masked_vsid(pte_0)) + && (om_ea_api(ea) == om_pte_0_api(pte_0))) { + unsigned_word real_address_of_pte_1 = (real_address_of_pte_0 + + sizeof_pte / 2); + unsigned_word pte_1 = om_read_word(map, + real_address_of_pte_1, + processor, cia); + page_tlb_entry->protection = om_pte_1_pp(pte_1); + page_tlb_entry->changed = om_pte_1_changed(pte_1); + page_tlb_entry->masked_virtual_segment_id = segment_tlb_entry->masked_virtual_segment_id; + page_tlb_entry->masked_page = om_ea_masked_page(ea); + page_tlb_entry->masked_real_page_number = om_pte_1_masked_rpn(pte_1); + page_tlb_entry->real_address_of_pte_1 = real_address_of_pte_1; + if (!om_pte_1_referenced(pte_1)) { + om_write_word(map, + real_address_of_pte_1, + pte_1 | BIT(55), + processor, cia); + TRACE(trace_vm, + ("ea=0x%lx - htab hit - set ref - tlb=0x%lx &pte1=0x%lx\n", + (long)ea, (long)page_tlb_entry, (long)real_address_of_pte_1)); + } + else { + TRACE(trace_vm, + ("ea=0x%lx - htab hit - tlb=0x%lx &pte1=0x%lx\n", + (long)ea, (long)page_tlb_entry, (long)real_address_of_pte_1)); + } + return page_tlb_entry; + } + } + page_hash = ~page_hash; /*???*/ + } + } + return NULL; +} + + +STATIC_INLINE_VM\ +(void) +om_interrupt(cpu *processor, + unsigned_word cia, + unsigned_word ea, + om_access_types access, + storage_interrupt_reasons reason) +{ + switch (access) { + case om_data_read: + data_storage_interrupt(processor, cia, ea, reason, 0/*!is_store*/); + break; + case om_data_write: + data_storage_interrupt(processor, cia, ea, reason, 1/*is_store*/); + break; + case om_instruction_read: + instruction_storage_interrupt(processor, cia, reason); + break; + default: + error("internal error - om_interrupt - unexpected access type %d", access); + } +} + + +STATIC_INLINE_VM\ +(unsigned_word) +om_translate_effective_to_real(om_map *map, + unsigned_word ea, + om_access_types access, + cpu *processor, + unsigned_word cia, + int abort) +{ + om_bat *bat = NULL; + om_segment_tlb_entry *segment_tlb_entry = NULL; + om_page_tlb_entry *page_tlb_entry = NULL; + unsigned_word ra; + + if (!map->is_relocate) { + ra = ea; + TRACE(trace_vm, ("ea=0x%lx - direct map - ra=0x%lx\n", + (long)ea, (long)ra)); + return ra; + } + + /* match with BAT? */ + bat = om_effective_to_bat(map, ea); + if (bat != NULL) { + if (!om_valid_access[1][bat->protection_bits][access]) { + TRACE(trace_vm, ("ea=0x%lx - bat access violation\n", (long)ea)); + if (abort) + om_interrupt(processor, cia, ea, access, + protection_violation_storage_interrupt); + else + return MASK(0, 63); + } + + ra = ((ea & bat->block_length_mask) | bat->block_real_page_number); + TRACE(trace_vm, ("ea=0x%lx - bat translation - ra=0x%lx\n", + (long)ea, (long)ra)); + return ra; + } + + /* translate ea to va using segment map */ + segment_tlb_entry = om_effective_to_virtual(map, ea, processor, cia); +#if (WITH_TARGET_WORD_BITSIZE == 64) + if (segment_tlb_entry == NULL) { + TRACE(trace_vm, ("ea=0x%lx - segment tlb miss\n", (long)ea)); + if (abort) + om_interrupt(processor, cia, ea, access, + segment_table_miss_storage_interrupt); + else + return MASK(0, 63); + } +#endif + /* check for invalid segment access type */ + if (segment_tlb_entry->invalid_access == access) { + TRACE(trace_vm, ("ea=0x%lx - segment access invalid\n", (long)ea)); + if (abort) + om_interrupt(processor, cia, ea, access, + protection_violation_storage_interrupt); + else + return MASK(0, 63); + } + + /* lookup in PTE */ + page_tlb_entry = om_virtual_to_real(map, ea, segment_tlb_entry, + access, + processor, cia); + if (page_tlb_entry == NULL) { + TRACE(trace_vm, ("ea=0x%lx - page tlb miss\n", (long)ea)); + if (abort) + om_interrupt(processor, cia, ea, access, + hash_table_miss_storage_interrupt); + else + return MASK(0, 63); + } + if (!(om_valid_access + [segment_tlb_entry->key[map->is_problem_state]] + [page_tlb_entry->protection] + [access])) { + TRACE(trace_vm, ("ea=0x%lx - page tlb access violation\n", (long)ea)); + if (abort) + om_interrupt(processor, cia, ea, access, + protection_violation_storage_interrupt); + else + return MASK(0, 63); + } + + /* update change bit as needed */ + if (access == om_data_write &&!page_tlb_entry->changed) { + unsigned_word pte_1 = om_read_word(map, + page_tlb_entry->real_address_of_pte_1, + processor, cia); + om_write_word(map, + page_tlb_entry->real_address_of_pte_1, + pte_1 | BIT(56), + processor, cia); + TRACE(trace_vm, ("ea=0x%lx - set change bit - tlb=0x%lx &pte1=0x%lx\n", + (long)ea, (long)page_tlb_entry, + (long)page_tlb_entry->real_address_of_pte_1)); + } + + ra = (page_tlb_entry->masked_real_page_number | om_ea_masked_byte(ea)); + TRACE(trace_vm, ("ea=0x%lx - page translation - ra=0x%lx\n", + (long)ea, (long)ra)); + return ra; +} + + +/* + * Definition of operations for memory management + */ + + +/* rebuild all the relevant bat information */ +STATIC_INLINE_VM\ +(void) +om_unpack_bat(om_bat *bat, + spreg ubat, + spreg lbat) +{ + /* for extracting out the offset within a page */ + bat->block_length_mask = ((MASKED(ubat, 51, 61) << (17-2)) + | MASK(63-17+1, 63)); + + /* for checking the effective page index */ + bat->block_effective_page_index = MASKED(ubat, 0, 46); + bat->block_effective_page_index_mask = ~bat->block_length_mask; + + /* protection information */ + bat->protection_bits = EXTRACTED(lbat, 62, 63); + bat->block_real_page_number = MASKED(lbat, 0, 46); +} + + +/* rebuild the given bat table */ +STATIC_INLINE_VM\ +(void) +om_unpack_bats(om_bats *bats, + spreg *raw_bats, + msreg msr) +{ + int i; + bats->nr_valid_bat_registers = 0; + for (i = 0; i < nr_om_bat_registers*2; i += 2) { + spreg ubat = raw_bats[i]; + spreg lbat = raw_bats[i+1]; + if ((msr & msr_problem_state) + ? EXTRACTED(ubat, 63, 63) + : EXTRACTED(ubat, 62, 62)) { + om_unpack_bat(&bats->bat[bats->nr_valid_bat_registers], + ubat, lbat); + bats->nr_valid_bat_registers += 1; + } + } +} + + +#if (WITH_TARGET_WORD_BITSIZE == 32) +STATIC_INLINE_VM\ +(void) +om_unpack_sr(vm *virtual, + sreg *srs, + int which_sr, + cpu *processor, + unsigned_word cia) +{ + om_segment_tlb_entry *segment_tlb_entry = 0; + sreg new_sr_value = 0; + + /* check register in range */ + ASSERT(which_sr >= 0 && which_sr < nr_om_segment_tlb_entries); + + /* get the working values */ + segment_tlb_entry = &virtual->segment_tlb.entry[which_sr]; + new_sr_value = srs[which_sr]; + + /* do we support this */ + if (MASKED32(new_sr_value, 0, 0)) + cpu_error(processor, cia, "unsupported value of T in segment register %d", + which_sr); + + /* update info */ + segment_tlb_entry->key[om_supervisor_state] = EXTRACTED32(new_sr_value, 1, 1); + segment_tlb_entry->key[om_problem_state] = EXTRACTED32(new_sr_value, 2, 2); + segment_tlb_entry->invalid_access = (MASKED32(new_sr_value, 3, 3) + ? om_instruction_read + : om_access_any); + segment_tlb_entry->masked_virtual_segment_id = + INSERTED32(EXTRACTED32(new_sr_value, 8, 31), + 31-6-24+1, 31-6); /* aligned ready for pte group addr */ +} +#endif + + +#if (WITH_TARGET_WORD_BITSIZE == 32) +STATIC_INLINE_VM\ +(void) +om_unpack_srs(vm *virtual, + sreg *srs, + cpu *processor, + unsigned_word cia) +{ + int which_sr; + for (which_sr = 0; which_sr < nr_om_segment_tlb_entries; which_sr++) { + om_unpack_sr(virtual, srs, which_sr, + processor, cia); + } +} +#endif + + +/* Rebuild all the data structures for the new context as specifed by + the passed registers */ +INLINE_VM\ +(void) +vm_synchronize_context(vm *virtual, + spreg *sprs, + sreg *srs, + msreg msr, + /**/ + cpu *processor, + unsigned_word cia) +{ + + /* enable/disable translation */ + int problem_state = (msr & msr_problem_state) != 0; + int data_relocate = (msr & msr_data_relocate) != 0; + int instruction_relocate = (msr & msr_instruction_relocate) != 0; + int little_endian = (msr & msr_little_endian_mode) != 0; + + unsigned_word page_table_hash_mask; + unsigned_word real_address_of_page_table; + + /* update current processor mode */ + virtual->instruction_map.translation.is_relocate = instruction_relocate; + virtual->instruction_map.translation.is_problem_state = problem_state; + virtual->data_map.translation.is_relocate = data_relocate; + virtual->data_map.translation.is_problem_state = problem_state; + + /* update bat registers for the new context */ + om_unpack_bats(&virtual->ibats, &sprs[spr_ibat0u], msr); + om_unpack_bats(&virtual->dbats, &sprs[spr_dbat0u], msr); + + /* unpack SDR1 - the storage description register 1 */ +#if (WITH_TARGET_WORD_BITSIZE == 64) + real_address_of_page_table = MASKED64(sprs[spr_sdr1], 0, 45); + page_table_hash_mask = MASK64(18+28-EXTRACTED64(sprs[spr_sdr1], 59, 63), + 63-7); +#endif +#if (WITH_TARGET_WORD_BITSIZE == 32) + real_address_of_page_table = MASKED32(sprs[spr_sdr1], 0, 15); + page_table_hash_mask = (INSERTED32(EXTRACTED32(sprs[spr_sdr1], 23, 31), + 7, 7+9-1) + | MASK32(7+9, 31-6)); +#endif + virtual->instruction_map.translation.real_address_of_page_table = real_address_of_page_table; + virtual->instruction_map.translation.page_table_hash_mask = page_table_hash_mask; + virtual->data_map.translation.real_address_of_page_table = real_address_of_page_table; + virtual->data_map.translation.page_table_hash_mask = page_table_hash_mask; + + + /* unpack the segment tlb registers */ +#if (WITH_TARGET_WORD_BITSIZE == 32) + om_unpack_srs(virtual, srs, + processor, cia); +#endif + + /* set up the XOR registers if the current endian mode conflicts + with what is in the MSR */ + if (WITH_XOR_ENDIAN) { + int i = 1; + unsigned mask; + if ((little_endian && CURRENT_TARGET_BYTE_ORDER == LITTLE_ENDIAN) + || (!little_endian && CURRENT_TARGET_BYTE_ORDER == BIG_ENDIAN)) + mask = 0; + else + mask = WITH_XOR_ENDIAN - 1; + while (i - 1 < WITH_XOR_ENDIAN) { + virtual->instruction_map.translation.xor[i-1] = mask; + virtual->data_map.translation.xor[i-1] = mask; + mask = (mask << 1) & (WITH_XOR_ENDIAN - 1); + i = i * 2; + } + } + else { + /* don't allow the processor to change endian modes */ + if ((little_endian && CURRENT_TARGET_BYTE_ORDER != LITTLE_ENDIAN) + || (!little_endian && CURRENT_TARGET_BYTE_ORDER != BIG_ENDIAN)) + cpu_error(processor, cia, "attempt to change hardwired byte order"); + } +} + +/* update vm data structures due to a TLB operation */ + +INLINE_VM\ +(void) +vm_page_tlb_invalidate_entry(vm *memory, + unsigned_word ea) +{ + int i = om_page_tlb_index(ea); + memory->instruction_tlb.entry[i].masked_virtual_segment_id = MASK(0, 63); + memory->data_tlb.entry[i].masked_virtual_segment_id = MASK(0, 63); + TRACE(trace_vm, ("ea=0x%lx - tlb invalidate entry\n", (long)ea)); +} + +INLINE_VM\ +(void) +vm_page_tlb_invalidate_all(vm *memory) +{ + int i; + for (i = 0; i < nr_om_page_tlb_entries; i++) { + memory->instruction_tlb.entry[i].masked_virtual_segment_id = MASK(0, 63); + memory->data_tlb.entry[i].masked_virtual_segment_id = MASK(0, 63); + } + TRACE(trace_vm, ("tlb invalidate all\n")); +} + + + +INLINE_VM\ +(vm_data_map *) +vm_create_data_map(vm *memory) +{ + return &memory->data_map; +} + + +INLINE_VM\ +(vm_instruction_map *) +vm_create_instruction_map(vm *memory) +{ + return &memory->instruction_map; +} + + +STATIC_INLINE_VM\ +(unsigned_word) +vm_translate(om_map *map, + unsigned_word ea, + om_access_types access, + cpu *processor, + unsigned_word cia, + int abort) +{ + switch (CURRENT_ENVIRONMENT) { + case USER_ENVIRONMENT: + case VIRTUAL_ENVIRONMENT: + return ea; + case OPERATING_ENVIRONMENT: + return om_translate_effective_to_real(map, ea, access, + processor, cia, + abort); + default: + error("internal error - vm_translate - bad switch"); + return 0; + } +} + + +INLINE_VM\ +(unsigned_word) +vm_real_data_addr(vm_data_map *map, + unsigned_word ea, + int is_read, + cpu *processor, + unsigned_word cia) +{ + return vm_translate(&map->translation, + ea, + is_read ? om_data_read : om_data_write, + processor, + cia, + 1); /*abort*/ +} + + +INLINE_VM\ +(unsigned_word) +vm_real_instruction_addr(vm_instruction_map *map, + cpu *processor, + unsigned_word cia) +{ + return vm_translate(&map->translation, + cia, + om_instruction_read, + processor, + cia, + 1); /*abort*/ +} + +INLINE_VM\ +(instruction_word) +vm_instruction_map_read(vm_instruction_map *map, + cpu *processor, + unsigned_word cia) +{ + unsigned_word ra = vm_real_instruction_addr(map, processor, cia); + ASSERT((cia & 0x3) == 0); /* always aligned */ + if (WITH_XOR_ENDIAN) + ra ^= map->translation.xor[sizeof(instruction_word) - 1]; + return core_map_read_4(map->code, ra, processor, cia); +} + + +INLINE_VM\ +(int) +vm_data_map_read_buffer(vm_data_map *map, + void *target, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + unsigned count; + for (count = 0; count < nr_bytes; count++) { + unsigned_1 byte; + unsigned_word ea = addr + count; + unsigned_word ra = vm_translate(&map->translation, + ea, om_data_read, + processor, /*processor*/ + cia, /*cia*/ + processor != NULL); /*abort?*/ + if (ra == MASK(0, 63)) + break; + if (WITH_XOR_ENDIAN) + ra ^= map->translation.xor[0]; + if (core_map_read_buffer(map->read, &byte, ra, sizeof(byte)) + != sizeof(byte)) + break; + ((unsigned_1*)target)[count] = T2H_1(byte); + } + return count; +} + + +INLINE_VM\ +(int) +vm_data_map_write_buffer(vm_data_map *map, + const void *source, + unsigned_word addr, + unsigned nr_bytes, + int violate_read_only_section, + cpu *processor, + unsigned_word cia) +{ + unsigned count; + unsigned_1 byte; + for (count = 0; count < nr_bytes; count++) { + unsigned_word ea = addr + count; + unsigned_word ra = vm_translate(&map->translation, + ea, om_data_write, + processor, + cia, + processor != NULL); /*abort?*/ + if (ra == MASK(0, 63)) + break; + if (WITH_XOR_ENDIAN) + ra ^= map->translation.xor[0]; + byte = T2H_1(((unsigned_1*)source)[count]); + if (core_map_write_buffer((violate_read_only_section + ? map->read + : map->write), + &byte, ra, sizeof(byte)) != sizeof(byte)) + break; + } + return count; +} + + +/* define the read/write 1/2/4/8/word functions */ + +#define N 1 +#include "vm_n.h" +#undef N + +#define N 2 +#include "vm_n.h" +#undef N + +#define N 4 +#include "vm_n.h" +#undef N + +#define N 8 +#include "vm_n.h" +#undef N + +#define N word +#include "vm_n.h" +#undef N + + + +#endif /* _VM_C_ */ diff --git a/sim/ppc/vm.h b/sim/ppc/vm.h new file mode 100644 index 00000000000..b8c4c8c234b --- /dev/null +++ b/sim/ppc/vm.h @@ -0,0 +1,155 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1997, 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_ + +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); + + +/* generic block transfers. Dependant on the presence of the + PROCESSOR arg, either returns the number of bytes transfered or (if + PROCESSOR is non NULL) aborts the simulation */ + +INLINE_VM\ +(int) vm_data_map_read_buffer +(vm_data_map *map, + void *target, + unsigned_word addr, + unsigned len, + cpu *processor, + unsigned_word cia); + +INLINE_VM\ +(int) vm_data_map_write_buffer +(vm_data_map *map, + const void *source, + unsigned_word addr, + unsigned len, + int violate_read_only_section, + cpu *processor, + unsigned_word cia); + + +/* 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) +DECLARE_VM_DATA_MAP_READ_N(word) + + +/* 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) +DECLARE_VM_DATA_MAP_WRITE_N(word) + + +/* update vm data structures due to a synchronization point */ + +INLINE_VM\ +(void) vm_synchronize_context +(vm *memory, + spreg *sprs, + sreg *srs, + msreg msr, + /**/ + cpu *processor, + unsigned_word cia); + + +/* update vm data structures due to a TLB operation */ + +INLINE_VM\ +(void) vm_page_tlb_invalidate_entry +(vm *memory, + unsigned_word ea); + +INLINE_VM\ +(void) vm_page_tlb_invalidate_all +(vm *memory); + +#endif diff --git a/sim/ppc/vm_n.h b/sim/ppc/vm_n.h new file mode 100644 index 00000000000..9222ffa2cc0 --- /dev/null +++ b/sim/ppc/vm_n.h @@ -0,0 +1,134 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1997, 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 N +#error "N must be #defined" +#endif + +/* NOTE: See end of file for #undef */ +#define unsigned_N XCONCAT2(unsigned_,N) +#define T2H_N XCONCAT2(T2H_,N) +#define H2T_N XCONCAT2(H2T_,N) +#define vm_data_map_read_N XCONCAT2(vm_data_map_read_,N) +#define vm_data_map_write_N XCONCAT2(vm_data_map_write_,N) + + +INLINE_VM\ +(unsigned_N) +vm_data_map_read_N(vm_data_map *map, + unsigned_word ea, + cpu *processor, + unsigned_word cia) +{ + if ((ea & (sizeof(unsigned_N)-1)) == 0) { + unsigned ra = vm_real_data_addr(map, ea, 1/*is-read*/, processor, cia); + unsigned_N val; + if (WITH_XOR_ENDIAN) + ra ^= map->translation.xor[sizeof(unsigned_N) - 1]; + val = XCONCAT2(core_map_read_,N)(map->read, ra, processor, cia); + if (WITH_MON & MONITOR_LOAD_STORE_UNIT) + mon_read(ea, ra, sizeof(unsigned_N), processor, cia); + TRACE(trace_load_store, ("load cia=0x%lx ea=0x%lx N=%ld val=0x%lx\n", + (long)cia, (long)ea, (long)sizeof(unsigned_N), (long)val)); + return val; + } + else { + switch (CURRENT_ALIGNMENT) { + case STRICT_ALIGNMENT: + alignment_interrupt(processor, cia, ea); + return 0; + case NONSTRICT_ALIGNMENT: + { + unsigned_N val; + if (vm_data_map_read_buffer(map, &val, ea, sizeof(unsigned_N), processor, cia) + != sizeof(unsigned_N)) { + cpu_error(processor, cia, "misaligned %d byte read to 0x%lx failed", + sizeof(unsigned_N), (unsigned long)ea); + } + val = T2H_N(val); + if (WITH_MON & MONITOR_LOAD_STORE_UNIT) { + /* YUCK */ + unsigned ra = vm_real_data_addr(map, ea, 1, processor, cia); + mon_read(ea, ra, sizeof(unsigned_N), processor, cia); + } + TRACE(trace_load_store, ("load cia=0x%lx ea=0x%lx N=%ld data=0x%lx\n", + (long)cia, (long)ea, (long)sizeof(unsigned_N), (long)val)); + return val; + } + default: + error("internal error - vm_data_map_read_N - bad switch"); + return 0; + } + } +} + +INLINE_VM\ +(void) +vm_data_map_write_N(vm_data_map *map, + unsigned_word ea, + unsigned_N val, + cpu *processor, + unsigned_word cia) +{ + if ((ea & (sizeof(unsigned_N)-1)) == 0) { + unsigned ra = vm_real_data_addr(map, ea, 0/*is-read?*/, processor, cia); + if (WITH_XOR_ENDIAN) + ra ^= map->translation.xor[sizeof(unsigned_N) - 1]; + XCONCAT2(core_map_write_,N)(map->write, ra, val, processor, cia); + if (WITH_MON & MONITOR_LOAD_STORE_UNIT) + mon_write(ea, ra, sizeof(unsigned_N), processor, cia); + TRACE(trace_load_store, ("store cia=0x%lx ea=0x%lx N=%ld val=0x%lx\n", + (long)cia, (long)ea, (long)sizeof(unsigned_N), (long)val)); + } + else { + switch (CURRENT_ALIGNMENT) { + case STRICT_ALIGNMENT: + alignment_interrupt(processor, cia, ea); + break; + case NONSTRICT_ALIGNMENT: + { + unsigned_N data = H2T_N(val); + if (vm_data_map_write_buffer(map, &data, ea, sizeof(unsigned_N), 0, processor, cia) + != sizeof(unsigned_N)) { + cpu_error(processor, cia, "misaligned %d byte write to 0x%lx failed", + sizeof(unsigned_N), (unsigned long)ea); + } + if (WITH_MON & MONITOR_LOAD_STORE_UNIT) { + /* YUCK */ + unsigned ra = vm_real_data_addr(map, ea, 1, processor, cia); + mon_write(ea, ra, sizeof(unsigned_N), processor, cia); + } + TRACE(trace_load_store, ("store cia=0x%lx ea=0x%lx N=%ld val=0x%lx\n", + (long)cia, (long)ea, (long)sizeof(unsigned_N), (long)val)); + } + break; + default: + error("internal error - vm_data_map_write_N - bad switch"); + } + } +} + +/* NOTE: see start of file for #define */ +#undef unsigned_N +#undef T2H_N +#undef H2T_N +#undef vm_data_map_read_N +#undef vm_data_map_write_N diff --git a/sim/ppc/words.h b/sim/ppc/words.h new file mode 100644 index 00000000000..1c18df42ebf --- /dev/null +++ b/sim/ppc/words.h @@ -0,0 +1,116 @@ +/* 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) + *_cell Size based on the target architecture's + IEEE 1275 cell size (almost always 32 bits) + +*/ + + +/* bit based */ +typedef char natural8; +typedef short natural16; +typedef long natural32; + +typedef signed char signed8; +typedef signed short signed16; +typedef signed long signed32; + +typedef unsigned char unsigned8; +typedef unsigned short unsigned16; +typedef unsigned long unsigned32; + +#ifdef __GNUC__ +typedef long long natural64; +typedef signed long long signed64; +typedef unsigned long long unsigned64; +#endif + +#ifdef _MSC_VER +typedef __int64 natural64; +typedef signed __int64 signed64; +typedef unsigned __int64 unsigned64; +#endif + + +/* 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_TARGET_WORD_BITSIZE == 64) +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; + +/* IEEE 1275 cell size - only support 32bit mode at present */ +typedef natural32 natural_cell; +typedef unsigned32 unsigned_cell; +typedef signed32 signed_cell; + +#endif /* _WORDS_H_ */ |