pax_global_header00006660000000000000000000000064143513232160014512gustar00rootroot0000000000000052 comment=a1136ef4f01554fba073d96dac96f29424d5728b umps3-3.0.5/000077500000000000000000000000001435132321600125665ustar00rootroot00000000000000umps3-3.0.5/.gitignore000066400000000000000000000002561435132321600145610ustar00rootroot00000000000000CMakeLists.txt.user CMakeCache.txt CMakeFiles CMakeScripts Testing Makefile cmake_install.cmake install_manifest.txt compile_commands.json CTestTestfile.cmake _deps config.h umps3-3.0.5/CMakeLists.txt000066400000000000000000000036471435132321600153400ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5) project(umps VERSION 3.0.0) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) add_compile_options(-D_FORTIFY_SOURCE=2 -O2 -Wall -pedantic) include(GNUInstallDirs) include(TestBigEndian) TEST_BIG_ENDIAN(WORDS_BIGENDIAN) configure_file(config.h.in config.h) # Check for MIPS cross toolchain set(XT_PREFIXES ${MIPS_TOOL_PREFIX}) if(NOT XT_PREFIXES) set(XT_PREFIXES mips-sde-elf- mips64-linux-gnu-) if(WORDS_BIGENDIAN) set(XT_PREFIXES mips-linux-gnu- mips-linux- mips-elf- ${XT_PREFIXES}) else() set(XT_PREFIXES mipsel-linux-gnu- mipsel-linux- mipsel-elf- ${XT_PREFIXES}) endif() endif() foreach(PREF ${XT_PREFIXES}) find_program(XCGCC ${PREF}gcc) if(XCGCC) set(XT_PREFIX ${PREF}) break() endif() endforeach() if(NOT XCGCC) message(FATAL_ERROR "MIPS toolchain (gcc) not found.") endif() find_program(XCAS ${PREF}as) if(NOT XCAS) message(FATAL_ERROR "MIPS toolchain (as) not found.") endif() find_program(XCLD ${PREF}ld) if(NOT XCLD) message(FATAL_ERROR "MIPS toolchain (ld) not found.") endif() # Checks for libelf find_library(LIBELF elf) if(NOT LIBELF) message(FATAL_ERROR "libelf not found.") endif() # Checks for libdl find_library(LIBDL dl) if(NOT LIBDL) message(FATAL_ERROR "libdl not found.") endif() include(FindPkgConfig) pkg_check_modules(SIGCPP REQUIRED sigc++-2.0) find_package(Boost 1.34 REQUIRED) find_package(Qt5 COMPONENTS Widgets REQUIRED) if(${Qt5_VERSION_MINOR} LESS 11) execute_process(COMMAND git apply ${CMAKE_SOURCE_DIR}/patch/Qt5.patch WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) endif() add_subdirectory(src) add_subdirectory(man) add_custom_target(uninstall ${CMAKE_COMMAND} -P "${PROJECT_SOURCE_DIR}/Uninstall.cmake") umps3-3.0.5/LICENSE000066400000000000000000001045151435132321600136010ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. 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 them 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 prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. 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. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey 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; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If 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 convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU 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 that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. 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. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 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. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. 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 state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program 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, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU 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 Lesser General Public License instead of this License. But first, please read . umps3-3.0.5/README.md000066400000000000000000000177301435132321600140550ustar00rootroot00000000000000

µMPS3

---

latest packaged version(s) License

# µMPS3 A complete virtual machine simulator based around the MIPS R2/3000 microprocessor. Please report any bugs you find by [creating an issue ticket](https://github.com/virtualsquare/umps3/issues/new) here on GitHub. Make sure you include steps on how to reproduce it. ## Table of Contents * [Introduction](#introduction) * [Screenshots](#screenshots) * [How to install](#how-to-install) * [Ubuntu](#ubuntu) * [Debian](#debian) * [Arch Linux](#arch-linux) * [Building from source](#building-from-source) * [Getting started](#getting-started) * [License](#license) ## Introduction µMPS is an educational computer system architecture and an accompanying emulator designed from the ground up to achieve the right trade-off between simplicity and elegance on one side, and realism on the other. This makes µMPS ideally suited for use in education, such as hands-on operating systems or computer architecture university courses. The µMPS processor implements the MIPS I instruction set, and can therefore be supported out of the box by existing MIPS compilers. The architecture details a complete set of I/O devices (terminals, disks, flash devices, printers, and network adapters) that feature a clean, consistent, programming interface. The emulator comes with built-in debugging features and an easy to use graphical user interface. Apart from the emulator itself, several support utilities are provided that can get you quickly started in developing programs for µMPS. µMPS is now in its third iteration: µMPS3. Due to the pedagogically driven changes implemented in µMPS3, this latest version is **NOT** backward compatible with either µMPS2 or µMPS(1). [Learn more about µMPS](https://wiki.virtualsquare.org/#!education/umps.md) ## Screenshots screenshot 1 screenshot 2 screenshot 3 ## How to install Packaging status µMPS3 was already packaged for the distros listed below. If you can't find your distro here, you will have to [build from source](#building-from-source). If you create a package for any other distribution, please consider contributing the template. ### Ubuntu If you are using **Ubuntu 21.04 Hirsute Hippo**, **21.10 Impish Indri** or **22.04 Jammy Jellyfish**, you can install the [official µMPS3 package](https://packages.ubuntu.com/impish/umps3) using: ```bash $ sudo apt install umps3 ``` If you are using **Ubuntu 20.04 Focal Fossa**, **18.04 Bionic Beaver** or **16.04 Xenial Xerus** ([Checking your Ubuntu Version](https://help.ubuntu.com/community/CheckingYourUbuntuVersion)) or [derivatives](https://wiki.ubuntu.com/DerivativeTeam/Derivatives) (e.g. **Linux Mint**), you need to: 1. enable [Universe](https://help.ubuntu.com/community/Repositories/Ubuntu) ```bash $ sudo add-apt-repository universe $ sudo apt update ``` 2. add the [virtualsquare/umps PPA](https://launchpad.net/~virtualsquare/+archive/ubuntu/umps) ```bash $ sudo add-apt-repository ppa:virtualsquare/umps $ sudo apt update ``` 3. install ```bash $ sudo apt install umps3 ``` ### Debian If you are using **Debian 11 Stable ("bullseye")**, **Debian 12 Testing ("bookworm")** or **Debian Unstable ("sid")**, you can install the [official µMPS3 package](https://packages.debian.org/bullseye/umps3) using: ```bash $ sudo apt install umps3 ``` If you are using **Debian 10 Oldstable ("buster")**, you need to: 1. add the [virtualsquare/umps PPA](https://launchpad.net/~virtualsquare/+archive/ubuntu/umps) for the [Ubuntu 18.04 (Bionic Beaver)](https://releases.ubuntu.com/18.04/) version to your [Apt sources configuration file](https://wiki.debian.org/SourcesList): ```bash $ echo 'deb http://ppa.launchpad.net/virtualsquare/umps/ubuntu bionic main' | sudo tee /etc/apt/sources.list.d/virtualsquare-ubuntu-umps-bionic.list ``` 2. import the [signing key](https://keyserver.ubuntu.com/pks/lookup?fingerprint=on&op=index&search=0xBB8957296BD01F6CA96B5C88046AB1F65C49333A): ```bash $ sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 046AB1F65C49333A ``` 3. re-synchronize the package index files: ```bash $ sudo apt update ``` 4. install ```bash $ sudo apt install umps3 ``` ### Arch Linux If you are using **Arch Linux** or [derivatives](https://wiki.archlinux.org/index.php/Arch-based_distributions) (e.g. **Manjaro**), you can install the AUR package [umps3-git](https://aur.archlinux.org/packages/umps3-git/) to get the latest version, or [umps3](https://aur.archlinux.org/packages/umps3/) for the latest stable release. - [AUR - Installing and upgrading packages](https://wiki.archlinux.org/index.php/Arch_User_Repository#Installing_and_upgrading_packages) - [AUR helpers](https://wiki.archlinux.org/index.php/AUR_helpers) ### Building from source Please [report any problems](https://github.com/virtualsquare/umps3/issues/new) you run into when building the project. #### Dependencies A compiler with C++11 support ([clang-3.3+](https://llvm.org/releases/download.html), [gcc-4.8+](https://gcc.gnu.org/releases.html)), [cmake 3.5+](https://cmake.org/download/), [git](https://git-scm.com/downloads) - `Qt 5.5+` - `libelf` - `boost 1.34+` (headers) - `libsigc++ 2.0` Apart from the dependencies listed above, you will also need a MIPS cross toolchain in your PATH. CMakelists.txt will do its best to detect one; if that fails, you can pass the toolchain tool prefix to `cmake` using `-DMIPS_TOOL_PREFIX=PREFIX`. Known prefixes are: - `mips-sde-elf-` - `mips64-linux-gnu-` - `mips(el)-elf-` - `mips(el)-linux-` - `mips(el)-linux-gnu-` For example, on **Debian** and [derivatives](https://www.debian.org/derivatives/) (e.g. **Ubuntu**, **Pop!_OS**): ```sh $ sudo apt install git build-essential cmake qtbase5-dev libelf-dev libboost-dev libsigc++-2.0-dev gcc-mipsel-linux-gnu ``` On **Fedora**: ```sh $ sudo dnf install git make gcc-c++ cmake qt5-qtbase-devel qt5-qtsvg elfutils-libelf-devel boost-devel libsigc++20-devel gcc-mips64-linux-gnu ``` #### Get the source code ```sh $ git clone https://github.com/virtualsquare/umps3 ``` #### Build and Install ```sh $ cd umps3 $ mkdir build $ cd build $ cmake .. $ make $ sudo make install ``` #### Launch You should now be able to launch µMPS3 via the application menu of your desktop environment, or by typing `umps3` at the command line. #### Uninstallation From the root of the binary tree run: ```sh $ sudo make uninstall ``` N.B.: "install_manifest.txt" is generated during the installation process. ## Getting started [How to get started using µMPS3](https://wiki.virtualsquare.org/#!education/tutorials/umps/getting_started.md) ## License µMPS3 is licensed under the [GPL-3.0](https://www.gnu.org/licenses/gpl-3.0.en.html) license. [See LICENSE for more information](https://github.com/virtualsquare/umps3/blob/master/LICENSE). [Papirus Icons](https://git.io/papirus-icon-theme) by [Papirus Development Team](https://github.com/PapirusDevelopmentTeam) is licensed under [GPL-3.0](https://www.gnu.org/licenses/gpl-3.0.en.html) Logo and all other icons derived by [Mattia Biondi](https://github.com/mattiabiondi) from [Papirus Icons](https://git.io/papirus-icon-theme) umps3-3.0.5/Uninstall.cmake000066400000000000000000000013451435132321600155440ustar00rootroot00000000000000set(MANIFEST "${CMAKE_CURRENT_BINARY_DIR}/install_manifest.txt") if(NOT EXISTS ${MANIFEST}) message(FATAL_ERROR "Cannot find install manifest: '${MANIFEST}'") endif() file(STRINGS ${MANIFEST} files) foreach(file ${files}) if(EXISTS ${file} OR IS_SYMLINK ${file}) message(STATUS "Removing file: '${file}'") execute_process(COMMAND rm "${file}" OUTPUT_VARIABLE remove_file) if(${remove_file}) message(FATAL_ERROR "Failed to remove file: '${file}'.") endif() else() MESSAGE(STATUS "File '${file}' does not exist.") endif() endforeach(file) umps3-3.0.5/config.h.in000066400000000000000000000003031435132321600146050ustar00rootroot00000000000000/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel). */ #cmakedefine WORDS_BIGENDIAN "@WORDS_BIGENDIAN@" umps3-3.0.5/man/000077500000000000000000000000001435132321600133415ustar00rootroot00000000000000umps3-3.0.5/man/CMakeLists.txt000066400000000000000000000017421435132321600161050ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.7) set(PANDOC_ORG "VirtualSquare") # ### pandoc pages file(GLOB VU_PANDOC_PAGES ${CMAKE_CURRENT_SOURCE_DIR}/*.[1-8].md) set(VU_MAN_FILES) foreach(VU_PANDOC_PATH IN LISTS VU_PANDOC_PAGES) # VU_PANDOCPAGE: basename of VU_PANDOC_PATH get_filename_component(VU_PANDOCPAGE ${VU_PANDOC_PATH} NAME) # VU_MANPAGE: VU_PANDOCPAGE without the suffix string(REGEX REPLACE "\.md$" "" VU_MANPAGE ${VU_PANDOCPAGE}) list(APPEND VU_MAN_FILES ${VU_MANPAGE}) endforeach(VU_PANDOC_PATH) add_custom_target(${PROJECT_NAME}_manpages ALL make PANDOC_ORG="${PANDOC_ORG}" ${VU_MAN_FILES} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) ### man pages file(GLOB VU_MAN_PAGES ${CMAKE_CURRENT_SOURCE_DIR}/*.[1-8]) foreach(VU_MAN_PATH IN LISTS VU_MAN_PAGES) get_filename_component(VU_MANPAGE ${VU_MAN_PATH} NAME) string(REGEX REPLACE ".*\\." "" MAN_CHAPTER ${VU_MANPAGE}) install(FILES ${VU_MAN_PATH} DESTINATION ${CMAKE_INSTALL_MANDIR}/man${MAN_CHAPTER}) endforeach(VU_MAN_PATH) umps3-3.0.5/man/Makefile000066400000000000000000000013341435132321600150020ustar00rootroot00000000000000PANDOC=pandoc PANDOCOK := $(shell command -v ${PANDOC} 2> /dev/null) none: % : %.md ifdef PANDOCOK # copy copyright notice grep "^\.\\\\\"" $< > $@ || true # run pandoc $(eval SECTION := $(subst .,,$(suffix $@))) $(eval BASENAME := $(basename $@)) $(eval TITLE := $(shell echo "${BASENAME}\(${SECTION}\)" | tr [:lower:] [:upper:])) $(eval HEADER := "$(shell man ${SECTION} intro | head -1 | sed -e 's/^[^[:blank:]]*[[:blank:]]*//' -e 's/[[:blank:]]*[^[:blank:]]*$$//' )") $(PANDOC) -standalone -M title=${TITLE} -M section=${SECTION} -M header=${HEADER} -M footer=${PANDOC_ORG} -M "date=`date +\"%B %Y\"`" --to man $< >> $@ else echo "${PANDOC} is not available. Manpage $@ cannot be updated" >/dev/stderr >&2 endif umps3-3.0.5/man/umps3-elf2umps.1000066400000000000000000000106101435132321600162230ustar00rootroot00000000000000.\" Copyright (C) 2020 Mattia Biondi, Mikey Goldweber, Renzo Davoli .\" .\" This is free documentation; you can redistribute it and/or .\" modify it under the terms of the GNU General Public License, .\" as published by the Free Software Foundation, either version 3 .\" of the License, or (at your option) any later version. .\" .\" The GNU General Public License's references to "object code" .\" and "executables" are to be interpreted as the output of any .\" document formatting or typesetting system, including .\" intermediate and printed output. .\" .\" This manual is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public .\" License along with this manual; if not, write to the Free .\" Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, .\" MA 02110-1301 USA. .\" .\" Automatically generated by Pandoc 2.17.1.1 .\" .\" Define V font for inline verbatim, using C font in formats .\" that render this, and otherwise B font. .ie "\f[CB]x\f[]"x" \{\ . ftr V B . ftr VI BI . ftr VB B . ftr VBI BI .\} .el \{\ . ftr V CR . ftr VI CI . ftr VB CB . ftr VBI CBI .\} .TH "UMPS3-ELF2UMPS" "1" "December 2022" "" "General Commands Manual" .hy .SH NAME .PP \f[V]umps3-elf2umps\f[R] \[en] The umps3-elf2umps object file conversion utility .SH SYNOPSIS .PP \f[V]umps3-elf2umps\f[R] [\f[I]OPTIONS\f[R]] -k \f[I]FILE\f[R] .PD 0 .P .PD \f[V]umps3-elf2umps\f[R] [\f[I]OPTIONS\f[R]] -b \f[I]FILE\f[R] .PD 0 .P .PD \f[V]umps3-elf2umps\f[R] [\f[I]OPTIONS\f[R]] -a \f[I]FILE\f[R] .SH DESCRIPTION .PP The command-line \f[V]umps3-elf2umps\f[R] utility is used to convert the ELF formatted executable and object files produced by the gcc cross-platform development tools into the \f[V].aout\f[R], \f[V].core\f[R], and \f[V].rom\f[R] formatted files required by \f[V]uMPS3\f[R]. .PP A successful conversion will produce a file by the name of \f[V]file.core.umps\f[R], \f[V]file.rom.umps\f[R], or \f[V]file.aout.umps\f[R] accordingly. .PP A \f[V].stab\f[R] file is a text file containing a one-line uMPS3-specific header and the contents of the symbol table from the ELF-formatted input file. It is used by the uMPS3 simulator to map \f[V].text\f[R] and \f[V].data\f[R] locations to their symbolic, i.e.\ kernel/OS source code, names. Hence the automatic generation of the \f[V].stab\f[R] file whenever a \f[V].core\f[R] file is produced. Since \f[V].stab\f[R] files are text files one can also examine/modify them using traditional text-processing tools. .PP In addition to its utility in tracking down errors in the \f[V]umps3-elf2umps\f[R] program (which hopefully no longer exist), the \f[I]-v\f[R] flag is of general interest since it illustrates which ELF sections were found and produced and the resulting header data for \f[V].core\f[R] and \f[V].aout\f[R] files. For \f[V].rom\f[R] files, the \f[I]-v\f[R] flag also displays the BIOS code size obtained during file conversion. .SH OPTIONS .TP \f[V]-v\f[R] Optional flag to produce verbose output during the conversion process. .TP \f[V]-m\f[R] Optional flag to generate the \f[V].stab\f[R] symbol table map file associated with \f[I]FILE\f[R]. .TP \f[V]-k\f[R] Flag to produce a \f[V].core\f[R] formatted file. This flag can only be used with an executable file. A \f[V].stab\f[R] file is automatically produced with this option. .TP \f[V]-b\f[R] Flag to produce a \f[V].rom\f[R] formatted file. This flag can only be used with an object file that does not contain relocations. .TP \f[V]-a\f[R] Flag to produce a \f[V].aout\f[R] formatted file. .PD 0 .P .PD This flag can only be used with an executable file. .SH FILES .PP \f[I]FILE\f[R] is the executable or object file to be converted. .SH AUTHOR .PP Mauro Morsiani .PD 0 .P .PD Tomislav Jonjic \f[I]tjonjic\[at]gmail.com\f[R] .PD 0 .P .PD Contributors can be listed on GitHub. .SH BUGS .PP Report issues on GitHub: \f[I]https://github.com/virtualsquare/umps3\f[R] .SH SEE ALSO .PP \f[B]umps3\f[R](1), \f[B]umps3-mkdev\f[R](1), \f[B]umps3-objdump\f[R](1) .PP Full documentation at: \f[I]https://github.com/virtualsquare/umps3\f[R] .PD 0 .P .PD Project wiki: \f[I]https://wiki.virtualsquare.org/#!umps/umps.md\f[R] .SH COPYRIGHT .PP 2004, Mauro Morsiani .PD 0 .P .PD 2010-2011, Tomislav Jonjic \f[I]tjonjic\[at]gmail.com\f[R] .PD 0 .P .PD umps3-3.0.5/man/umps3-elf2umps.1.md000066400000000000000000000070241435132321600166270ustar00rootroot00000000000000 # NAME `umps3-elf2umps` -- The umps3-elf2umps object file conversion utility # SYNOPSIS `umps3-elf2umps` [*OPTIONS*] -k *FILE*\ `umps3-elf2umps` [*OPTIONS*] -b *FILE*\ `umps3-elf2umps` [*OPTIONS*] -a *FILE* # DESCRIPTION The command-line `umps3-elf2umps` utility is used to convert the ELF formatted executable and object files produced by the gcc cross-platform development tools into the `.aout`, `.core`, and `.rom` formatted files required by `uMPS3`. A successful conversion will produce a file by the name of `file.core.umps`, `file.rom.umps`, or `file.aout.umps` accordingly. A `.stab` file is a text file containing a one-line uMPS3-specific header and the contents of the symbol table from the ELF-formatted input file. It is used by the uMPS3 simulator to map `.text` and `.data` locations to their symbolic, i.e. kernel/OS source code, names. Hence the automatic generation of the `.stab` file whenever a `.core` file is produced. Since `.stab` files are text files one can also examine/modify them using traditional text-processing tools. In addition to its utility in tracking down errors in the `umps3-elf2umps` program (which hopefully no longer exist), the *-v* flag is of general interest since it illustrates which ELF sections were found and produced and the resulting header data for `.core` and `.aout` files. For `.rom` files, the *-v* flag also displays the BIOS code size obtained during file conversion. # OPTIONS `-v` : Optional flag to produce verbose output during the conversion process. `-m` : Optional flag to generate the `.stab` symbol table map file associated with *FILE*. `-k` : Flag to produce a `.core` formatted file. : This flag can only be used with an executable file. : A `.stab` file is automatically produced with this option. `-b` : Flag to produce a `.rom` formatted file. : This flag can only be used with an object file that does not contain relocations. `-a` : Flag to produce a `.aout` formatted file.\ : This flag can only be used with an executable file. # FILES *FILE* is the executable or object file to be converted. # AUTHOR Mauro Morsiani\ Tomislav Jonjic *tjonjic@gmail.com*\ Contributors can be listed on GitHub. # BUGS Report issues on GitHub: *https://github.com/virtualsquare/umps3* # SEE ALSO **umps3**(1), **umps3-mkdev**(1), **umps3-objdump**(1) Full documentation at: *https://github.com/virtualsquare/umps3*\ Project wiki: *https://wiki.virtualsquare.org/#!umps/umps.md* # COPYRIGHT 2004, Mauro Morsiani\ 2010-2011, Tomislav Jonjic *tjonjic@gmail.com*\ umps3-3.0.5/man/umps3-mkdev.1000066400000000000000000000137271435132321600156100ustar00rootroot00000000000000.\" Copyright (C) 2020 Mattia Biondi, Mikey Goldweber, Renzo Davoli .\" .\" This is free documentation; you can redistribute it and/or .\" modify it under the terms of the GNU General Public License, .\" as published by the Free Software Foundation, either version 3 .\" of the License, or (at your option) any later version. .\" .\" The GNU General Public License's references to "object code" .\" and "executables" are to be interpreted as the output of any .\" document formatting or typesetting system, including .\" intermediate and printed output. .\" .\" This manual is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public .\" License along with this manual; if not, write to the Free .\" Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, .\" MA 02110-1301 USA. .\" .\" Automatically generated by Pandoc 2.17.1.1 .\" .\" Define V font for inline verbatim, using C font in formats .\" that render this, and otherwise B font. .ie "\f[CB]x\f[]"x" \{\ . ftr V B . ftr VI BI . ftr VB B . ftr VBI BI .\} .el \{\ . ftr V CR . ftr VI CI . ftr VB CB . ftr VBI CBI .\} .TH "UMPS3-MKDEV" "1" "December 2022" "" "General Commands Manual" .hy .SH NAME .PP \f[V]umps3-mkdev\f[R] \[en] The umps3-mkdev device creation utility .SH SYNOPSIS .PP \f[V]umps3-mkdev\f[R] -d \f[I]DISKFILE\f[R] [\f[I]DISKOPTIONS\f[R]] .PD 0 .P .PD \f[V]umps3-mkdev\f[R] -f \f[I]FLASHFILE\f[R] \f[I]FILE\f[R] [\f[I]FLASHOPTIONS\f[R]] .SH DESCRIPTION .PP The command-line \f[V]umps3-mkdev\f[R] utility is used to create the files that represent disk and flash devices. .TP \f[V]DISKS\f[R]: Disks in uMPS3 are \[lq]direct access\[rq] nonvolatile read/write devices. The \f[V]umps3-mkdev\f[R] utility allows one to create an empty disk only; this way an OS developer may elect any desired disk data organization. .TP \f[V]\f[R] The created \f[I]DISKFILE\f[R] represents the entire disk contents, even when empty. Hence this file may be very large. It is recommended to create small disks which can be used to represent a little portion of an otherwise very large disk unit. .TP \f[V]\f[R] As with real disks, differing performance statistics result in differing simulated drive performance. E.g. a faster rotation speed results in less latency delay and a smaller sector data occupancy percentage results in shorter read/write times. .TP \f[V]\f[R] The default values for all these parameters are shown when entering the \f[V]umps3-mkdev\f[R] alone without any parameters. .TP \f[V]FLASH DEVICES\f[R]: Flash devices in uMPS3 are \[lq]random access\[rq] nonvolatile read/write devices. A uMPS3 flash device is essentially equivalent to a seek-free one-dimensional disk drive. The \f[V]umps3-mkdev\f[R] utility allows one to create both slow flash devices (e.g.\ USB stick) or fast flash devices (e.g.\ SSDs). Furthermore, the utility allows one to create both empty flash devices as well as ones preloaded with a specific file. .TP \f[V]\f[R] The created \f[I]FLASHFILE\f[R] represents the entire device contents, even when empty. Hence this file may be very large. It is recommended to create small flash devices which can be used to represent a little portion of an otherwise very large device. .TP \f[V]\f[R] uMPS3 caps the maximum block size for flash devices at 2\[ha]24. This translates to a maximum device size of 64GB. .TP \f[V]\f[R] As with real flash devices, read operations are faster than write operations. The read speed for uMPS3 flash devices is fixed at 75% of the device write time in microseconds. .TP \f[V]\f[R] The default values for all these parameters are shown when entering the \f[V]umps3-mkdev\f[R] alone without any parameters. .SH OPTIONS .TP \f[V]-d\f[R] instructs the utility to build a disk file image. .TP \f[V]-f\f[R] instructs the utility to build a flash device file image. .SH FILES .TP \f[V]DISKFILE\f[R] is the name of the disk file image to be created. .TP \f[V]FLASHFILE\f[R] is the name of the flash device file image to be created. .TP \f[V]FILE\f[R] is the name of the file to be preloaded onto the device beginning with block 0. If one wishes to create an empty flash device but still specify some of the additional parameters, use \f[V]/dev/null\f[R] as the \f[I]FILE\f[R] argument. To load a flash device with a collection of files, it is recommended to initially create a single \f[V].tar\f[R] file from the collection and then use this single \f[V].tar\f[R] file for this parameter. We recommend the \f[V].tar\f[R] file format due to its simple structure. .SH DISKOPTIONS .PP [\f[I]CYL\f[R] [\f[I]HEAD\f[R] [\f[I]SECT\f[R] [\f[I]RPM\f[R] [\f[I]SEEKT\f[R] [\f[I]DATAS\f[R]]]]]]] .TP \f[V]CYL\f[R]: Number of cylinders: [1..65535], default = 32 .TP \f[V]HEAD\f[R]: Number of heads/surfaces: [1..255], default = 2 .TP \f[V]SECT\f[R]: Number of 4KB sectors/tracks: [1..255], default = 8 .TP \f[V]RPM\f[R]: Disk rotations per minute: [360..10800], default = 3600 .TP \f[V]SEEKT\f[R]: Average cylinder to cylinder seek time in microseconds: [1..10000], default = 100 .TP \f[V]DATAS\f[R]: Sector data occupation %: [10%..90%], default = 80% .SH FLASHOPTIONS .PP [\f[I]BLOCKS\f[R] [\f[I]WT\f[R]]] .TP \f[V]BLOCKS\f[R]: Number of blocks: [1..0xFFFFFF], default = 512 .TP \f[V]WT\f[R]: Average write time in microseconds: [1..10000], default = 1000 .SH AUTHOR .PP Mauro Morsiani .PD 0 .P .PD Mattia Biondi \f[I]mattiabiondi1\[at]gmail.com\f[R] .PD 0 .P .PD Contributors can be listed on GitHub. .SH BUGS .PP Report issues on GitHub: \f[I]https://github.com/virtualsquare/umps3\f[R] .SH SEE ALSO .PP \f[V]umps3\f[R](1), \f[V]umps3-elf2umps\f[R](1), \f[V]umps3-objdump\f[R](1) .PP Full documentation at: \f[I]https://github.com/virtualsquare/umps3\f[R] .PD 0 .P .PD Project wiki: \f[I]https://wiki.virtualsquare.org/#!umps/umps.md\f[R] .SH COPYRIGHT .PP 2004, Mauro Morsiani .PD 0 .P .PD 2020, Mattia Biondi \f[I]mattiabiondi1\[at]gmail.com\f[R] umps3-3.0.5/man/umps3-mkdev.1.md000066400000000000000000000121151435132321600161750ustar00rootroot00000000000000 # NAME `umps3-mkdev` -- The umps3-mkdev device creation utility # SYNOPSIS `umps3-mkdev` -d *DISKFILE* [*DISKOPTIONS*]\ `umps3-mkdev` -f *FLASHFILE* *FILE* [*FLASHOPTIONS*] # DESCRIPTION The command-line `umps3-mkdev` utility is used to create the files that represent disk and flash devices. `DISKS`: : Disks in uMPS3 are "direct access" nonvolatile read/write devices. : The `umps3-mkdev` utility allows one to create an empty disk only; this way an OS developer may elect any desired disk data organization. ` ` : The created *DISKFILE* represents the entire disk contents, even when empty. : Hence this file may be very large. : It is recommended to create small disks which can be used to represent a little portion of an otherwise very large disk unit. ` ` : As with real disks, differing performance statistics result in differing simulated drive performance. : E.g. a faster rotation speed results in less latency delay and a smaller sector data occupancy percentage results in shorter read/write times. ` ` : The default values for all these parameters are shown when entering the `umps3-mkdev` alone without any parameters. `FLASH DEVICES`: : Flash devices in uMPS3 are "random access" nonvolatile read/write devices. : A uMPS3 flash device is essentially equivalent to a seek-free one-dimensional disk drive. : The `umps3-mkdev` utility allows one to create both slow flash devices (e.g. USB stick) or fast flash devices (e.g. SSDs). : Furthermore, the utility allows one to create both empty flash devices as well as ones preloaded with a specific file. ` ` : The created *FLASHFILE* represents the entire device contents, even when empty. : Hence this file may be very large. : It is recommended to create small flash devices which can be used to represent a little portion of an otherwise very large device. ` ` : uMPS3 caps the maximum block size for flash devices at 2\^24. : This translates to a maximum device size of 64GB. ` ` : As with real flash devices, read operations are faster than write operations. : The read speed for uMPS3 flash devices is fixed at 75% of the device write time in microseconds. ` ` : The default values for all these parameters are shown when entering the `umps3-mkdev` alone without any parameters. # OPTIONS `-d` : instructs the utility to build a disk file image. `-f` : instructs the utility to build a flash device file image. # FILES `DISKFILE` : is the name of the disk file image to be created. `FLASHFILE` : is the name of the flash device file image to be created. `FILE` : is the name of the file to be preloaded onto the device beginning with block 0. : If one wishes to create an empty flash device but still specify some of the additional parameters, use `/dev/null` as the *FILE* argument. : To load a flash device with a collection of files, it is recommended to initially create a single `.tar` file from the collection and then use this single `.tar` file for this parameter. : We recommend the `.tar` file format due to its simple structure. # DISKOPTIONS [*CYL* [*HEAD* [*SECT* [*RPM* [*SEEKT* [*DATAS*]]]]]] `CYL`: : Number of cylinders: [1..65535], default = 32 `HEAD`: : Number of heads/surfaces: [1..255], default = 2 `SECT`: : Number of 4KB sectors/tracks: [1..255], default = 8 `RPM`: : Disk rotations per minute: [360..10800], default = 3600 `SEEKT`: : Average cylinder to cylinder seek time in microseconds: [1..10000], default = 100 `DATAS`: : Sector data occupation %: [10%..90%], default = 80% # FLASHOPTIONS [*BLOCKS* [*WT*]] `BLOCKS`: : Number of blocks: [1..0xFFFFFF], default = 512 `WT`: : Average write time in microseconds: [1..10000], default = 1000 # AUTHOR Mauro Morsiani\ Mattia Biondi *mattiabiondi1@gmail.com*\ Contributors can be listed on GitHub. # BUGS Report issues on GitHub: *https://github.com/virtualsquare/umps3* # SEE ALSO `umps3`(1), `umps3-elf2umps`(1), `umps3-objdump`(1) Full documentation at: *https://github.com/virtualsquare/umps3*\ Project wiki: *https://wiki.virtualsquare.org/#!umps/umps.md* # COPYRIGHT 2004, Mauro Morsiani\ 2020, Mattia Biondi *mattiabiondi1@gmail.com* umps3-3.0.5/man/umps3-objdump.1000066400000000000000000000075541435132321600161430ustar00rootroot00000000000000.\" Copyright (C) 2020 Mattia Biondi, Mikey Goldweber, Renzo Davoli .\" .\" This is free documentation; you can redistribute it and/or .\" modify it under the terms of the GNU General Public License, .\" as published by the Free Software Foundation, either version 3 .\" of the License, or (at your option) any later version. .\" .\" The GNU General Public License's references to "object code" .\" and "executables" are to be interpreted as the output of any .\" document formatting or typesetting system, including .\" intermediate and printed output. .\" .\" This manual is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public .\" License along with this manual; if not, write to the Free .\" Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, .\" MA 02110-1301 USA. .\" .\" Automatically generated by Pandoc 2.17.1.1 .\" .\" Define V font for inline verbatim, using C font in formats .\" that render this, and otherwise B font. .ie "\f[CB]x\f[]"x" \{\ . ftr V B . ftr VI BI . ftr VB B . ftr VBI BI .\} .el \{\ . ftr V CR . ftr VI CI . ftr VB CB . ftr VBI CBI .\} .TH "UMPS3-OBJDUMP" "1" "December 2022" "" "General Commands Manual" .hy .SH NAME .PP \f[V]umps3-objdump\f[R] \[en] The umps3-objdump object file analysis utility .SH SYNOPSIS .PP \f[V]umps3-objdump\f[R] [\f[I]OPTIONS\f[R]] \f[I]FILE\f[R] .SH DESCRIPTION .PP The command-line \f[V]umps3-objdump\f[R] utility is used to analyze object files created by the \f[V]umps3-elf2umps\f[R] utility. .PP This utility performs the same functions as \f[V]mipsel-linux-gnu-objdump\f[R] (or \f[V]mips-linux-gnu-objdump\f[R]) which is included in the cross-platform development tool set. .PP \f[V]umps3-objdump\f[R] is used to analyze \f[V].core\f[R], \f[V].rom\f[R], and \f[V].aout\f[R] object files while \f[V]mipsel-linux-gnu-objdump\f[R] is used to analyze ELF-formatted object files. .PP The output from \f[V]umps3-objdump\f[R] is directed to stdout. .SH OPTIONS .TP \f[V]-h\f[R] Optional flag to show the \f[V].aout\f[R] program header, if present. .TP \f[V]-d\f[R] Optional flag to \[lq]disassemble\[rq] and display the \f[V].text\f[R] area in \f[I]FILE\f[R]. .PD 0 .P .PD .TP \f[V]\f[R] This is an \[lq]assembly\[rq] dump of the code, thus it will contain load and branch delay slots; differing from the machine language version of the same code. .TP \f[V]-x\f[R] Optional flag to produce a complete little-endian format hexadecimal word dump of \f[I]FILE\f[R]. .PD 0 .P .PD .TP \f[V]\f[R] Zero-filled blocks will be skipped and marked with *asterisks*. .PD 0 .P .PD .TP \f[V]\f[R] The output will appear identical regardless of whether \f[I]FILE\f[R] is little-endian or big-endian. .TP \f[V]-b\f[R] Optional flag to produce a complete byte dump of \f[I]FILE\f[R]. .PD 0 .P .PD .TP \f[V]\f[R] Zero-filled blocks will be skipped and marked with *asterisks*. .PD 0 .P .PD .TP \f[V]\f[R] Unlike with the \f[I]-x\f[R] flag, the endian-format of the output will depend on the endianness of \f[I]FILE\f[R]; i.e.\ if \f[I]FILE\f[R] is big-endian than the output will be big-endian. .TP \f[V]-a\f[R] Flag to perform all of the above optional operations. .SH FILES .PP \f[I]FILE\f[R] is the \f[V].core\f[R], \f[V].rom\f[R], or \f[V].aout\f[R] object file to be analyzed. .SH AUTHOR .PP Mauro Morsiani .PD 0 .P .PD Contributors can be listed on GitHub. .SH BUGS .PP Report issues on GitHub: \f[I]https://github.com/virtualsquare/umps3\f[R] .SH SEE ALSO .PP \f[B]umps3\f[R](1), \f[B]umps3-elf2umps\f[R](1), \f[B]umps3-mkdev\f[R](1) .PP Full documentation at: \f[I]https://github.com/virtualsquare/umps3\f[R] .PD 0 .P .PD Project wiki: \f[I]https://wiki.virtualsquare.org/#!umps/umps.md\f[R] .SH COPYRIGHT .PP 2004, Mauro Morsiani .PD 0 .P .PD umps3-3.0.5/man/umps3-objdump.1.md000066400000000000000000000060601435132321600165310ustar00rootroot00000000000000 # NAME `umps3-objdump` -- The umps3-objdump object file analysis utility # SYNOPSIS `umps3-objdump` [*OPTIONS*] *FILE* # DESCRIPTION The command-line `umps3-objdump` utility is used to analyze object files created by the `umps3-elf2umps` utility. This utility performs the same functions as `mipsel-linux-gnu-objdump` (or `mips-linux-gnu-objdump`) which is included in the cross-platform development tool set. `umps3-objdump` is used to analyze `.core`, `.rom`, and `.aout` object files while `mipsel-linux-gnu-objdump` is used to analyze ELF-formatted object files. The output from `umps3-objdump` is directed to stdout. # OPTIONS `-h` : Optional flag to show the `.aout` program header, if present. `-d` : Optional flag to "disassemble" and display the `.text` area in *FILE*.\ ` ` : This is an "assembly" dump of the code, thus it will contain load and branch delay slots; differing from the machine language version of the same code. `-x` : Optional flag to produce a complete little-endian format hexadecimal word dump of *FILE*.\ ` ` : Zero-filled blocks will be skipped and marked with \*asterisks\*.\ ` ` : The output will appear identical regardless of whether *FILE* is little-endian or big-endian. `-b` : Optional flag to produce a complete byte dump of *FILE*.\ ` ` : Zero-filled blocks will be skipped and marked with \*asterisks\*.\ ` ` : Unlike with the *-x* flag, the endian-format of the output will depend on the endianness of *FILE*; i.e. if *FILE* is big-endian than the output will be big-endian. `-a` : Flag to perform all of the above optional operations. # FILES *FILE* is the `.core`, `.rom`, or `.aout` object file to be analyzed. # AUTHOR Mauro Morsiani\ Contributors can be listed on GitHub. # BUGS Report issues on GitHub: *https://github.com/virtualsquare/umps3* # SEE ALSO **umps3**(1), **umps3-elf2umps**(1), **umps3-mkdev**(1) Full documentation at: *https://github.com/virtualsquare/umps3*\ Project wiki: *https://wiki.virtualsquare.org/#!umps/umps.md* # COPYRIGHT 2004, Mauro Morsiani\ umps3-3.0.5/man/umps3.1000066400000000000000000000063541435132321600145020ustar00rootroot00000000000000.\" Copyright (C) 2020 Mattia Biondi, Mikey Goldweber, Renzo Davoli .\" .\" This is free documentation; you can redistribute it and/or .\" modify it under the terms of the GNU General Public License, .\" as published by the Free Software Foundation, either version 3 .\" of the License, or (at your option) any later version. .\" .\" The GNU General Public License's references to "object code" .\" and "executables" are to be interpreted as the output of any .\" document formatting or typesetting system, including .\" intermediate and printed output. .\" .\" This manual is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public .\" License along with this manual; if not, write to the Free .\" Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, .\" MA 02110-1301 USA. .\" .\" Automatically generated by Pandoc 2.17.1.1 .\" .\" Define V font for inline verbatim, using C font in formats .\" that render this, and otherwise B font. .ie "\f[CB]x\f[]"x" \{\ . ftr V B . ftr VI BI . ftr VB B . ftr VBI BI .\} .el \{\ . ftr V CR . ftr VI CI . ftr VB CB . ftr VBI CBI .\} .TH "UMPS3" "1" "December 2022" "" "General Commands Manual" .hy .SH NAME .PP \f[V]umps3\f[R] \[en] Virtual machine simulator based around the MIPS R2/3000 microprocessor .SH SYNOPSIS .PP \f[V]umps3\f[R] [\f[I]CONFIGURATION\f[R]] .SH DESCRIPTION .PP \f[V]uMPS\f[R] is an educational computer system architecture and an accompanying emulator designed from the ground up to achieve the right trade-off between simplicity and elegance on one side, and realism on the other. This makes \f[V]uMPS\f[R] ideally suited for use in education, such as hands-on operating systems or computer architecture university courses. .PP The uMPS processor implements the MIPS I instruction set, and can therefore be supported out of the box by existing MIPS compilers. The architecture details a complete set of I/O devices (terminals, disks, flash devices, printers, and network adapters) that feature a clean, consistent, programming interface. The previous revision of the uMPS architecture (uMPS2) brings multiprocessor support. .PP The emulator comes with built-in debugging features and an easy to use graphical user interface. Apart from the emulator itself, several support utilities are provided that can get you quickly started in developing programs for \f[V]uMPS\f[R]. .SH AUTHOR .PP Mauro Morsiani, .PD 0 .P .PD Tomislav Jonjic \f[I]tjonjic\[at]gmail.com\f[R], .PD 0 .P .PD Mattia Biondi \f[I]mattiabiondi1\[at]gmail.com\f[R], .PD 0 .P .PD Contributors can be listed on GitHub. .SH BUGS .PP Report issues on GitHub: \f[I]https://github.com/virtualsquare/umps3\f[R] .SH SEE ALSO .PP \f[B]umps3-elf2umps\f[R](1), \f[B]umps3-mkdev\f[R](1), \f[B]umps3-objdump\f[R](1) .PP Full documentation at: *https://github.com/virtualsquare/umps3**br/\f[I] Project wiki: \f[R]https://wiki.virtualsquare.org/#!umps/umps.md* .SH COPYRIGHT .PP 2004, Mauro Morsiani, .PD 0 .P .PD 2010-2011, Tomislav Jonjic \f[I]tjonjic\[at]gmail.com\f[R], .PD 0 .P .PD 2020, Mattia Biondi \f[I]mattiabiondi1\[at]gmail.com\f[R] umps3-3.0.5/man/umps3.1.md000066400000000000000000000052501435132321600150730ustar00rootroot00000000000000 # NAME `umps3` -- Virtual machine simulator based around the MIPS R2/3000 microprocessor # SYNOPSIS `umps3` [*CONFIGURATION*] # DESCRIPTION `uMPS` is an educational computer system architecture and an accompanying emulator designed from the ground up to achieve the right trade-off between simplicity and elegance on one side, and realism on the other. This makes `uMPS` ideally suited for use in education, such as hands-on operating systems or computer architecture university courses. The uMPS processor implements the MIPS I instruction set, and can therefore be supported out of the box by existing MIPS compilers. The architecture details a complete set of I/O devices (terminals, disks, flash devices, printers, and network adapters) that feature a clean, consistent, programming interface. The previous revision of the uMPS architecture (uMPS2) brings multiprocessor support. The emulator comes with built-in debugging features and an easy to use graphical user interface. Apart from the emulator itself, several support utilities are provided that can get you quickly started in developing programs for `uMPS`. # AUTHOR Mauro Morsiani,\ Tomislav Jonjic *tjonjic@gmail.com*,\ Mattia Biondi *mattiabiondi1@gmail.com*,\ Contributors can be listed on GitHub. # BUGS Report issues on GitHub: *https://github.com/virtualsquare/umps3* # SEE ALSO **umps3-elf2umps**(1), **umps3-mkdev**(1), **umps3-objdump**(1) Full documentation at: *https://github.com/virtualsquare/umps3**br/* Project wiki: *https://wiki.virtualsquare.org/#!umps/umps.md* # COPYRIGHT 2004, Mauro Morsiani,\ 2010-2011, Tomislav Jonjic *tjonjic@gmail.com*,\ 2020, Mattia Biondi *mattiabiondi1@gmail.com* umps3-3.0.5/patch/000077500000000000000000000000001435132321600136655ustar00rootroot00000000000000umps3-3.0.5/patch/Qt5.patch000066400000000000000000000204441435132321600153630ustar00rootroot00000000000000From 1db4d6c82cbcd6b6a4f1bbd97e64b5a4f6c2f743 Mon Sep 17 00:00:00 2001 From: Mattia Biondi Date: Sun, 5 Apr 2020 21:50:44 +0200 Subject: [PATCH] Qt5 --- src/frontends/qmps/add_breakpoint_dialog.cc | 4 ++-- src/frontends/qmps/add_suspect_dialog.cc | 8 ++++---- src/frontends/qmps/add_tracepoint_dialog.cc | 4 ++-- src/frontends/qmps/code_view.cc | 4 ++-- src/frontends/qmps/hex_view.cc | 4 ++-- src/frontends/qmps/monitor_window.cc | 4 ++-- src/frontends/qmps/terminal_view.cc | 2 +- src/frontends/qmps/terminal_window.cc | 2 +- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/frontends/qmps/add_breakpoint_dialog.cc b/src/frontends/qmps/add_breakpoint_dialog.cc index 80d3d26..df2776f 100644 --- a/src/frontends/qmps/add_breakpoint_dialog.cc +++ b/src/frontends/qmps/add_breakpoint_dialog.cc @@ -41,8 +41,8 @@ AddBreakpointDialog::AddBreakpointDialog(QWidget* parent) layout->addWidget(new QLabel("ASID:"), 0, 0); asidEditor = new AsidLineEdit; layout->addWidget(asidEditor, 0, 1); - asidEditor->setMinimumWidth(asidEditor->fontMetrics().horizontalAdvance("0") * 5); - asidEditor->setMaximumWidth(asidEditor->fontMetrics().horizontalAdvance("0") * 6); + asidEditor->setMinimumWidth(asidEditor->fontMetrics().width("0") * 5); + asidEditor->setMaximumWidth(asidEditor->fontMetrics().width("0") * 6); layout->setColumnMinimumWidth(2, 12); diff --git a/src/frontends/qmps/add_suspect_dialog.cc b/src/frontends/qmps/add_suspect_dialog.cc index 480ddb6..61fb21d 100644 --- a/src/frontends/qmps/add_suspect_dialog.cc +++ b/src/frontends/qmps/add_suspect_dialog.cc @@ -46,14 +46,14 @@ AddSuspectDialog::AddSuspectDialog(QWidget* parent) layout->addWidget(new QLabel("ASID:"), 0, 0); asidEditor = new AsidLineEdit; layout->addWidget(asidEditor, 0, 1); - asidEditor->setMinimumWidth(asidEditor->fontMetrics().horizontalAdvance("0x00__")); - asidEditor->setMaximumWidth(asidEditor->fontMetrics().horizontalAdvance("0x00__")); + asidEditor->setMinimumWidth(asidEditor->fontMetrics().width("0x00__")); + asidEditor->setMaximumWidth(asidEditor->fontMetrics().width("0x00__")); layout->setColumnMinimumWidth(2, 12); layout->addWidget(new QLabel("Start:"), 0, 3); startAddressEdit = new AddressLineEdit; - startAddressEdit->setMinimumWidth(startAddressEdit->fontMetrics().horizontalAdvance("0xdead.beef__")); + startAddressEdit->setMinimumWidth(startAddressEdit->fontMetrics().width("0xdead.beef__")); layout->addWidget(startAddressEdit, 0, 4); connect(startAddressEdit, SIGNAL(textChanged(const QString&)), this, SLOT(validate())); @@ -61,7 +61,7 @@ AddSuspectDialog::AddSuspectDialog(QWidget* parent) layout->addWidget(new QLabel("End:"), 0, 6); endAddressEdit = new AddressLineEdit; - endAddressEdit->setMinimumWidth(endAddressEdit->fontMetrics().horizontalAdvance("0xdead.beef__")); + endAddressEdit->setMinimumWidth(endAddressEdit->fontMetrics().width("0xdead.beef__")); layout->addWidget(endAddressEdit, 0, 7); connect(endAddressEdit, SIGNAL(textChanged(const QString&)), this, SLOT(validate())); diff --git a/src/frontends/qmps/add_tracepoint_dialog.cc b/src/frontends/qmps/add_tracepoint_dialog.cc index 134b869..101c03d 100644 --- a/src/frontends/qmps/add_tracepoint_dialog.cc +++ b/src/frontends/qmps/add_tracepoint_dialog.cc @@ -47,7 +47,7 @@ AddTracepointDialog::AddTracepointDialog(QWidget* parent) layout->addWidget(new QLabel("Start:"), gridRow, 0); startAddressEdit = new AddressLineEdit; - startAddressEdit->setMinimumWidth(startAddressEdit->fontMetrics().horizontalAdvance("0xdead.beef__")); + startAddressEdit->setMinimumWidth(startAddressEdit->fontMetrics().width("0xdead.beef__")); layout->addWidget(startAddressEdit, gridRow, 1); connect(startAddressEdit, SIGNAL(textChanged(const QString&)), this, SLOT(validate())); @@ -55,7 +55,7 @@ AddTracepointDialog::AddTracepointDialog(QWidget* parent) layout->addWidget(new QLabel("End:"), gridRow, 3); endAddressEdit = new AddressLineEdit; - endAddressEdit->setMinimumWidth(endAddressEdit->fontMetrics().horizontalAdvance("0xdead.beef__")); + endAddressEdit->setMinimumWidth(endAddressEdit->fontMetrics().width("0xdead.beef__")); layout->addWidget(endAddressEdit, gridRow, 4); connect(endAddressEdit, SIGNAL(textChanged(const QString&)), this, SLOT(validate())); diff --git a/src/frontends/qmps/code_view.cc b/src/frontends/qmps/code_view.cc index 867738c..405bc33 100644 --- a/src/frontends/qmps/code_view.cc +++ b/src/frontends/qmps/code_view.cc @@ -63,7 +63,7 @@ CodeView::CodeView(Word cpuId) codeMargin->setFont(font); codeMargin->setCursor(Qt::PointingHandCursor); - setTabStopDistance(fontMetrics().horizontalAdvance("x") * TAB_STOP_CHARS); + setTabStopWidth(fontMetrics().width("x") * TAB_STOP_CHARS); // Compute viewport margins setViewportMargins(codeMargin->sizeHint().width(), 0, 0, 0); @@ -331,7 +331,7 @@ CodeViewMargin::CodeViewMargin(CodeView* view) QSize CodeViewMargin::sizeHint() const { QString sampleAddr = FormatAddress(0xdeadbeef); - return QSize(fontMetrics().horizontalAdvance("o") * sampleAddr.size() + kMarkerSize, 0); + return QSize(fontMetrics().width("o") * sampleAddr.size() + kMarkerSize, 0); } void CodeViewMargin::paintEvent(QPaintEvent* event) diff --git a/src/frontends/qmps/hex_view.cc b/src/frontends/qmps/hex_view.cc index e65bb3b..d3e5f83 100644 --- a/src/frontends/qmps/hex_view.cc +++ b/src/frontends/qmps/hex_view.cc @@ -52,7 +52,7 @@ HexView::HexView(Word start, Word end, QWidget* parent) setViewportMargins(margin->sizeHint().width(), 0, 0, 0); connect(this, SIGNAL(updateRequest(QRect,int)), this, SLOT(updateMargin(QRect,int))); - setCursorWidth(fontMetrics().horizontalAdvance("o")); + setCursorWidth(fontMetrics().width("o")); setLineWrapMode(NoWrap); setOverwriteMode(true); setTabChangesFocus(true); @@ -365,7 +365,7 @@ HexViewMargin::HexViewMargin(HexView* view) QSize HexViewMargin::sizeHint() const { - return QSize(kLeftPadding + fontMetrics().horizontalAdvance("0xdead.beef") + kRightPadding, 0); + return QSize(kLeftPadding + fontMetrics().width("0xdead.beef") + kRightPadding, 0); } void HexViewMargin::paintEvent(QPaintEvent* event) diff --git a/src/frontends/qmps/monitor_window.cc b/src/frontends/qmps/monitor_window.cc index 1dffa17..d0cc258 100644 --- a/src/frontends/qmps/monitor_window.cc +++ b/src/frontends/qmps/monitor_window.cc @@ -811,7 +811,7 @@ StatusDisplay::StatusDisplay(QWidget* parent) layout->addWidget(new QLabel("Status:")); statusLabel = new QLabel; - statusLabel->setFixedWidth(statusLabel->fontMetrics().horizontalAdvance("Powered off_")); + statusLabel->setFixedWidth(statusLabel->fontMetrics().width("Powered off_")); layout->addWidget(statusLabel); layout->addSpacing(kFieldSpacing); @@ -819,7 +819,7 @@ StatusDisplay::StatusDisplay(QWidget* parent) layout->addWidget(new QLabel("ToD:")); todLabel = new QLabel; todLabel->setFont(Appl()->getMonospaceFont()); - todLabel->setFixedWidth(todLabel->fontMetrics().horizontalAdvance("00000000:00000000")); + todLabel->setFixedWidth(todLabel->fontMetrics().width("00000000:00000000")); layout->addWidget(todLabel); statusLabel->setText("-"); diff --git a/src/frontends/qmps/terminal_view.cc b/src/frontends/qmps/terminal_view.cc index 3be1502..045c7bc 100644 --- a/src/frontends/qmps/terminal_view.cc +++ b/src/frontends/qmps/terminal_view.cc @@ -39,7 +39,7 @@ TerminalView::TerminalView(TerminalDevice* terminal, QWidget* parent) QFont font = Appl()->getMonospaceFont(); setFont(font); - setCursorWidth(fontMetrics().horizontalAdvance("o")); + setCursorWidth(fontMetrics().width("o")); // Disable features that look silly in a basic terminal widget setContextMenuPolicy(Qt::NoContextMenu); diff --git a/src/frontends/qmps/terminal_window.cc b/src/frontends/qmps/terminal_window.cc index cc61f72..6f6a422 100644 --- a/src/frontends/qmps/terminal_window.cc +++ b/src/frontends/qmps/terminal_window.cc @@ -60,7 +60,7 @@ TerminalWindow::TerminalWindow(unsigned int devNo, QWidget* parent) restoreGeometry(savedGeometry.toByteArray()); } else { QFontMetrics fm = terminalView->fontMetrics(); - resize(fm.horizontalAdvance("x") * kDefaultCols, fm.lineSpacing() * kDefaultRows); + resize(fm.width("x") * kDefaultCols, fm.lineSpacing() * kDefaultRows); } connect(debugSession, SIGNAL(MachineReset()), this, SLOT(onMachineReset())); -- 2.26.0 umps3-3.0.5/src/000077500000000000000000000000001435132321600133555ustar00rootroot00000000000000umps3-3.0.5/src/CMakeLists.txt000066400000000000000000000003161435132321600161150ustar00rootroot00000000000000set(UMPS_DATA_DIR ${CMAKE_INSTALL_FULL_DATADIR}/umps3) add_subdirectory(base) add_subdirectory(frontends) add_subdirectory(include) add_subdirectory(support) add_subdirectory(tests) add_subdirectory(umps) umps3-3.0.5/src/base/000077500000000000000000000000001435132321600142675ustar00rootroot00000000000000umps3-3.0.5/src/base/CMakeLists.txt000066400000000000000000000002551435132321600170310ustar00rootroot00000000000000add_library(base STATIC json.cc trackable_mixin.cc) target_include_directories(base PRIVATE ${PROJECT_SOURCE_DIR}/src) target_compile_options(base PRIVATE ${SIGCPP_CFLAGS}) umps3-3.0.5/src/base/basic_types.h000066400000000000000000000024031435132321600167440ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef BASE_BASIC_TYPES_H #define BASE_BASIC_TYPES_H // Not everyone has stdint.h appearently. Guess who lags behind? #ifndef _MSC_VER // Just use C99 `stdint.h'. Should be "anywhere it matters". # include #else # include // Pollute namespace as need arises! using boost::uint8_t; using boost::uint32_t; using boost::int32_t; using boost::uint64_t; #endif #include using std::size_t; #endif // BASE_BASIC_TYPES_H umps3-3.0.5/src/base/bit_tricks.h000066400000000000000000000025611435132321600166010ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // Fun bit twiddling stuff // See "Hacker's Delight", Henry S. Warren, Jr. #ifndef BASE_BIT_TRICKS_H #define BASE_BIT_TRICKS_H #include "base/basic_types.h" inline uint32_t FloorP2(uint32_t x) { x |= (x >> 1); x |= (x >> 2); x |= (x >> 4); x |= (x >> 8); x |= (x >> 16); return x - (x >> 1); } inline uint32_t CeilingP2(uint32_t x) { x -= 1; x |= (x >> 1); x |= (x >> 2); x |= (x >> 4); x |= (x >> 8); x |= (x >> 16); return x + 1; } inline uint32_t SwapEndian32(uint32_t x) { return (x << 24) | ((x << 8) & 0x00ff0000) | ((x >> 8) & 0x0000ff00) | (x >> 24); } #endif // BASE_BIT_TRICKS_H umps3-3.0.5/src/base/json.cc000066400000000000000000000250701435132321600155530ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "base/json.h" #include #include #include #include #include #include #include #include "base/lang.h" using std::unique_ptr; using std::string; static const char* const jsonTypeName[] = { "object", "array", "string", "number", "bool", "null" }; JsonNode::JsonNode(JsonType type) : type(type) { } bool JsonNode::Holds(JsonType type) const { return this->type == type; } JsonObject* JsonNode::AsObject() { CheckType(JSON_OBJECT); return static_cast(this); } const JsonObject* JsonNode::AsObject() const { CheckType(JSON_OBJECT); return static_cast(this); } JsonArray* JsonNode::AsArray() { CheckType(JSON_ARRAY); return static_cast(this); } const JsonArray* JsonNode::AsArray() const { CheckType(JSON_ARRAY); return static_cast(this); } string JsonNode::AsString() const { CheckType(JSON_STRING); return (static_cast(this))->Value(); } int JsonNode::AsNumber() const { CheckType(JSON_NUMBER); return (static_cast(this))->Value(); } bool JsonNode::AsBool() const { CheckType(JSON_BOOL); return (static_cast(this))->Value(); } void JsonNode::CheckType(JsonType requested) const { if (!Holds(requested)) { throw TypeError(boost::str(boost::format("type mismatch: expected <%s>, found <%s>") %jsonTypeName[requested] %jsonTypeName[type])); } } JsonObject::JsonObject() : JsonNode(JSON_OBJECT) { } JsonObject::~JsonObject() { for (NodeMap::iterator it = nodeMap.begin(); it != nodeMap.end(); ++it) delete it->second; } bool JsonObject::HasMember(const string& member) const { return nodeMap.find(member) != nodeMap.end(); } void JsonObject::Set(const string& member, JsonNode* value) { if (HasMember(member)) { assert(nodeMap[member] != value); Remove(member); } nodeMap[member] = value; } void JsonObject::Set(const string& member, const char* value) { Set(member, new JsonString(value)); } void JsonObject::Set(const string& member, const string& value) { Set(member, new JsonString(value)); } void JsonObject::Set(const string& member, int value) { Set(member, new JsonNumber(value)); } void JsonObject::Set(const string& member, bool value) { Set(member, new JsonBool(value)); } void JsonObject::Remove(const string& member) { NodeMap::iterator it = nodeMap.find(member); if (it != nodeMap.end()) { delete it->second; nodeMap.erase(it); } } const JsonNode* JsonObject::Get(const string& member) const { NodeMap::const_iterator it = nodeMap.find(member); if (it == nodeMap.end()) throw KeyError(member); return it->second; } JsonNode* JsonObject::Get(const string& member) { NodeMap::iterator it = nodeMap.find(member); if (it == nodeMap.end()) throw KeyError(member); return it->second; } void JsonObject::Serialize(string& result, bool indent, unsigned int indentWidth, unsigned int level) { result.push_back('{'); bool first = true; for (NodeMap::iterator it = nodeMap.begin(); it != nodeMap.end(); ++it) { if (!first) result.append(","); if (indent) { result.push_back('\n'); result.append(string(level + indentWidth, ' ')); } else { result.push_back(' '); } first = false; result.push_back('"'); result.append(it->first); result.append("\": "); it->second->Serialize(result, indent, indentWidth, level + indentWidth); } if (indent && !nodeMap.empty()) { result.push_back('\n'); result.append(string(level, ' ')); } result.push_back('}'); } JsonObject::iterator JsonObject::begin() { return nodeMap.begin(); } JsonObject::const_iterator JsonObject::begin() const { return nodeMap.begin(); } JsonObject::iterator JsonObject::end() { return nodeMap.end(); } JsonObject::const_iterator JsonObject::end() const { return nodeMap.end(); } JsonArray::JsonArray() : JsonNode(JSON_ARRAY) { } JsonArray::~JsonArray() { for (NodeVector::iterator it = nodes.begin(); it != nodes.end(); ++it) delete *it; } void JsonArray::Add(JsonNode* element) { nodes.push_back(element); } void JsonArray::Set(size_t index, JsonNode* element) { delete Get(index); nodes[index] = element; } void JsonArray::Remove(size_t index) { CheckIndex(index); delete nodes[index]; nodes.erase(nodes.begin() + index); } void JsonArray::Pop() { if (nodes.empty()) throw std::underflow_error("pop from empty array"); delete nodes.back(); nodes.pop_back(); } JsonNode* JsonArray::Get(size_t index) { CheckIndex(index); return nodes[index]; } const JsonNode* JsonArray::Get(size_t index) const { CheckIndex(index); return nodes[index]; } void JsonArray::Serialize(string& result, bool indent, unsigned int indentWidth, unsigned int level) { result.push_back('['); bool first = true; for (NodeVector::iterator it = nodes.begin(); it != nodes.end(); ++it) { if (!first) result.push_back(','); if (indent) { result.push_back('\n'); result.append(string(level + indentWidth, ' ')); } else if (!first) { result.push_back(' '); } first = false; (*it)->Serialize(result, indent, indentWidth, level + indentWidth); } if (indent && !nodes.empty()) { result.push_back('\n'); result.append(string(level, ' ')); } result.push_back(']'); } JsonArray::iterator JsonArray::begin() { return nodes.begin(); } JsonArray::const_iterator JsonArray::begin() const { return nodes.begin(); } JsonArray::iterator JsonArray::end() { return nodes.end(); } JsonArray::const_iterator JsonArray::end() const { return nodes.end(); } inline void JsonArray::CheckIndex(size_t index) const { if (index >= Length()) throw std::out_of_range("index out of range"); } JsonString::JsonString(const string& value) : JsonNode(JSON_STRING), value(value) { } void JsonString::Serialize(string& result, bool indent, unsigned int indentWidth, unsigned int level) { result.append('"' + Value() + '"'); } JsonNumber::JsonNumber(int value) : JsonNode(JSON_NUMBER), value(value) { } void JsonNumber::Serialize(string& result, bool indent, unsigned int indentWidth, unsigned int level) { std::stringstream stream; stream << Value(); string temp; stream >> temp; result.append(temp); } JsonBool::JsonBool(bool value) : JsonNode(JSON_BOOL), value(value) { } void JsonBool::Serialize(string& result, bool indent, unsigned int indentWidth, unsigned int level) { result.append(Value() ? "true" : "false"); } JsonNull::JsonNull() : JsonNode(JSON_NULL) { } void JsonNull::Serialize(string& result, bool indent, unsigned int indentWidth, unsigned int level) { result.append("null"); } JsonNode* JsonParser::Parse(std::istream& stream) { stream >> std::noskipws; input = std::istream_iterator(stream); lookAhead = false; return ParseNode(); } JsonNode* JsonParser::ParseNode() { TokenType type = NextToken(); switch (type) { case TOKEN_OBJECT_BEGIN: return ParseObject(); case TOKEN_ARRAY_BEGIN: return ParseArray(); case TOKEN_STRING: return new JsonString(token); case TOKEN_NUMBER: return new JsonNumber(strtol(token.c_str(), NULL, 10)); case TOKEN_TRUE: return new JsonBool(true); case TOKEN_FALSE: return new JsonBool(false); case TOKEN_NULL: return new JsonNull(); case TOKEN_NONE: default: throw SyntaxError(); } } JsonNode* JsonParser::ParseObject() { unique_ptr object(new JsonObject); TokenType type; type = NextToken(); while (type != TOKEN_OBJECT_END) { if (type != TOKEN_STRING) throw SyntaxError(); string member = token; NextTokenChecked(TOKEN_COLON); JsonNode* value = ParseNode(); object->Set(member, value); type = NextToken(); if (type != TOKEN_COMMA && type != TOKEN_OBJECT_END) throw SyntaxError(); if (type == TOKEN_COMMA) type = NextToken(); } return object.release(); } JsonNode* JsonParser::ParseArray() { unique_ptr array(new JsonArray); TokenType type = LookAhead(); if (type == TOKEN_ARRAY_END) NextToken(); while (type != TOKEN_ARRAY_END) { array->Add(ParseNode()); type = NextToken(); if (type != TOKEN_COMMA && type != TOKEN_ARRAY_END) throw SyntaxError(); } return array.release(); } JsonParser::TokenType JsonParser::NextToken() { if (lookAhead) { lookAhead = false; return laTokenType; } token.clear(); SkipWhitespace(); if (input == eos) return TOKEN_NONE; switch (*input) { case '{': ++input; return TOKEN_OBJECT_BEGIN; case '}': ++input; return TOKEN_OBJECT_END; case '[': ++input; return TOKEN_ARRAY_BEGIN; case ']': ++input; return TOKEN_ARRAY_END; case ',': ++input; return TOKEN_COMMA; case ':': ++input; return TOKEN_COLON; case '"': return ReadString(); case 'f': return ReadKeyword("false", TOKEN_FALSE); case 't': return ReadKeyword("true", TOKEN_TRUE); case 'n': return ReadKeyword("null", TOKEN_NULL); default: if (isdigit(*input)) return ReadNumber(); else return TOKEN_NONE; } } void JsonParser::NextTokenChecked(TokenType expectedType) { if (NextToken() != expectedType) throw SyntaxError(); } JsonParser::TokenType JsonParser::LookAhead() { assert(!lookAhead); laTokenType = NextToken(); lookAhead = true; return laTokenType; } void JsonParser::SkipWhitespace() { while (input != eos && isspace(*input)) ++input; } JsonParser::TokenType JsonParser::ReadString() { ++input; while (input != eos) { if (*input == '"') { ++input; return TOKEN_STRING; } token.push_back(*input); ++input; } return TOKEN_NONE; } JsonParser::TokenType JsonParser::ReadKeyword(const string& keyword, TokenType tokenType) { for (size_t i = 0; i < keyword.size(); i++) { if (input == eos || *input != keyword[i]) return TOKEN_NONE; ++input; } return tokenType; } JsonParser::TokenType JsonParser::ReadNumber() { assert(isdigit(*input)); while (input != eos && isdigit(*input)) token.push_back(*input++); return TOKEN_NUMBER; } umps3-3.0.5/src/base/json.h000066400000000000000000000135551435132321600154220ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef BASE_JSON_H #define BASE_JSON_H #include #include #include #include #include #include #include "base/lang.h" enum JsonType { JSON_OBJECT, JSON_ARRAY, JSON_STRING, JSON_NUMBER, JSON_BOOL, JSON_NULL }; class JsonObject; class JsonArray; class JsonNode { public: struct JsonError : public std::runtime_error { JsonError(const std::string& what) : std::runtime_error(what) { } }; struct TypeError : public JsonError { TypeError(const std::string& desc) : JsonError(desc) { } }; virtual ~JsonNode() { } JsonType GetType() const { return type; } bool Holds(JsonType type) const; JsonObject* AsObject(); const JsonObject* AsObject() const; JsonArray* AsArray(); const JsonArray* AsArray() const; std::string AsString() const; int AsNumber() const; bool AsBool() const; virtual void Serialize(std::string& result, bool indent = false, unsigned int indentWidth = 4, unsigned int level = 0) = 0; protected: JsonNode(JsonType type); private: void CheckType(JsonType requested) const; JsonType type; DISABLE_COPY_AND_ASSIGNMENT(JsonNode); }; class JsonObject: public JsonNode { public: struct KeyError : public JsonError { KeyError(const std::string& key) : JsonError(key) { } }; JsonObject(); ~JsonObject(); bool HasMember(const std::string& member) const; void Set(const std::string& member, JsonNode* value); void Set(const std::string& member, const char* value); void Set(const std::string& member, const std::string& value); void Set(const std::string& member, int value); void Set(const std::string& member, bool value); void Remove(const std::string& member); const JsonNode* Get(const std::string& member) const; JsonNode* Get(const std::string& member); size_t Size() const { return nodeMap.size(); } void Serialize(std::string& result, bool indent = false, unsigned int indentWidth = 4, unsigned int level = 0); private: typedef std::map NodeMap; NodeMap nodeMap; public: typedef NodeMap::iterator iterator; typedef NodeMap::const_iterator const_iterator; iterator begin(); const_iterator begin() const; iterator end(); const_iterator end() const; }; class JsonArray: public JsonNode { public: JsonArray(); ~JsonArray(); void Add(JsonNode* element); void Set(size_t index, JsonNode* element); void Remove(size_t index); void Pop(); JsonNode* Get(size_t index); const JsonNode* Get(size_t index) const; size_t Length() const { return nodes.size(); } void Serialize(std::string& result, bool indent = false, unsigned int indentWidth = 4, unsigned int level = 0); private: void CheckIndex(size_t index) const; typedef std::vector NodeVector; NodeVector nodes; public: typedef NodeVector::iterator iterator; typedef NodeVector::const_iterator const_iterator; iterator begin(); const_iterator begin() const; iterator end(); const_iterator end() const; }; class JsonString: public JsonNode { public: JsonString(const std::string& value); std::string Value() const { return value; } void Serialize(std::string& result, bool indent = false, unsigned int indentWidth = 4, unsigned int level = 0); private: std::string value; }; class JsonNumber: public JsonNode { public: JsonNumber(int value); int Value() const { return value; } void Serialize(std::string& result, bool indent = false, unsigned int indentWidth = 4, unsigned int level = 0); private: int value; }; class JsonBool: public JsonNode { public: JsonBool(bool value); bool Value() const { return value; } void Serialize(std::string& result, bool indent = false, unsigned int indentWidth = 4, unsigned int level = 0); private: bool value; }; class JsonNull: public JsonNode { public: JsonNull(); void Serialize(std::string& result, bool indent = false, unsigned int level = 0); void Serialize(std::string& result, bool indent = false, unsigned int indentWidth = 4, unsigned int level = 0); }; class JsonParser { public: class SyntaxError {}; JsonNode* Parse(std::istream& stream); private: enum TokenType { TOKEN_NONE, TOKEN_OBJECT_BEGIN, TOKEN_OBJECT_END, TOKEN_ARRAY_BEGIN, TOKEN_ARRAY_END, TOKEN_COMMA, TOKEN_COLON, TOKEN_STRING, TOKEN_NUMBER, TOKEN_TRUE, TOKEN_FALSE, TOKEN_NULL }; JsonNode* ParseNode(); JsonNode* ParseObject(); JsonNode* ParseArray(); TokenType NextToken(); void NextTokenChecked(TokenType expectedType); TokenType LookAhead(); void SkipWhitespace(); TokenType ReadString(); TokenType ReadKeyword(const std::string& keyword, TokenType tokenType); TokenType ReadNumber(); std::istream_iterator input; std::istream_iterator eos; std::string token; bool lookAhead; TokenType laTokenType; }; #endif // BASE_JSON_H umps3-3.0.5/src/base/lang.h000066400000000000000000000030231435132321600153570ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef BASE_LANG_H #define BASE_LANG_H // Smart pointers (here we could just as well depend on TR1 and use // its version of these i guess - the choice is arbitrary). #include #include using boost::shared_ptr; using boost::scoped_ptr; using boost::scoped_array; using boost::enable_shared_from_this; // Make the nature of "reference"-type classes excplicit to readers // and compilers by using the macro below somewhere in a private // section of a class definition. #define DISABLE_COPY_AND_ASSIGNMENT(Class) \ Class(const Class&); \ Class& operator=(const Class&) #define UNUSED_ARG(x) ((void) x) #endif // BASE_LANG_H umps3-3.0.5/src/base/trackable_mixin.cc000066400000000000000000000020531435132321600177320ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "base/trackable_mixin.h" #include "base/lang.h" TrackableMixin::~TrackableMixin() { for (sigc::connection& c : connections) c.disconnect(); } void TrackableMixin::RegisterSigc(sigc::connection c) { connections.push_back(c); } umps3-3.0.5/src/base/trackable_mixin.h000066400000000000000000000021261435132321600175750ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef BASE_TRACKABLE_MIXIN_H #define BASE_TRACKABLE_MIXIN_H #include #include class TrackableMixin { public: ~TrackableMixin(); void RegisterSigc(sigc::connection c); private: std::list connections; }; #endif // BASE_TRACKABLE_MIXIN_H umps3-3.0.5/src/frontends/000077500000000000000000000000001435132321600153575ustar00rootroot00000000000000umps3-3.0.5/src/frontends/CMakeLists.txt000066400000000000000000000000271435132321600201160ustar00rootroot00000000000000add_subdirectory(qmps) umps3-3.0.5/src/frontends/qmps/000077500000000000000000000000001435132321600163375ustar00rootroot00000000000000umps3-3.0.5/src/frontends/qmps/CMakeLists.txt000066400000000000000000000055401435132321600211030ustar00rootroot00000000000000add_subdirectory(data) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) add_executable(umps3 qmps.qrc main.cc application.h application.cc debug_session.h debug_session.cc monitor_window.h monitor_window_priv.h monitor_window.cc error_hooks.cc create_machine_dialog.h create_machine_dialog.cc stop_mask_view.h stop_mask_view.cc processor_list_model.h processor_list_model.cc machine_config_dialog.h machine_config_dialog_priv.h machine_config_dialog.cc machine_config_view.h machine_config_view.cc trace_browser.h trace_browser_priv.h trace_browser.cc memory_view_delegate.h hex_view.h hex_view_priv.h hex_view.cc symbol_table_model.h symbol_table_model.cc processor_window.h processor_window.cc code_view.h code_view_priv.h code_view.cc register_set_snapshot.h register_set_snapshot.cc register_set_widget.h register_set_widget.cc tlb_model.h tlb_model.cc terminal_view.h terminal_view.cc terminal_window.h terminal_window_priv.h terminal_window.cc cpu_status_map.h cpu_status_map.cc stoppoint_list_model.h stoppoint_list_model.cc suspect_type_delegate.h suspect_type_delegate.cc device_tree_view.h device_tree_view.cc device_tree_model.h device_tree_model.cc add_breakpoint_dialog.h add_breakpoint_dialog.cc add_suspect_dialog.h add_suspect_dialog.cc add_tracepoint_dialog.h add_tracepoint_dialog.cc address_line_edit.h address_line_edit.cc mac_id_edit.h mac_id_edit.cc boolean_item_delegate.h boolean_item_delegate.cc register_item_delegate.h register_item_delegate.cc ui_utils.h ui_utils.cc tree_view.h tree_view.cc flat_push_button.h flat_push_button.cc) target_include_directories(umps3 PRIVATE ${PROJECT_BINARY_DIR} ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/src/frontends ${PROJECT_SOURCE_DIR}/src/include) target_link_libraries(umps3 PRIVATE Qt5::Widgets) set(QT_DEFINES -DQT_NO_DEBUG -DQT_NO_KEYWORDS -DQT_GUI_LIB -DQT_CORE_LIB -DQT_SHARED) target_compile_options(umps3 PRIVATE ${QT_DEFINES} ${SIGCPP_CFLAGS}) add_dependencies(umps3 base umps) target_compile_definitions(umps3 PRIVATE -DPACKAGE_VERSION="${PROJECT_VERSION}") target_link_libraries(umps3 PRIVATE base umps Qt5::Widgets ${SIGCPP_LIBRARIES} ${LIBDL}) install(TARGETS umps3 RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) umps3-3.0.5/src/frontends/qmps/add_breakpoint_dialog.cc000066400000000000000000000067761435132321600231530ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "qmps/add_breakpoint_dialog.h" #include #include #include #include #include #include #include "umps/symbol_table.h" #include "qmps/application.h" #include "qmps/address_line_edit.h" #include "qmps/symbol_table_model.h" AddBreakpointDialog::AddBreakpointDialog(QWidget* parent) : QDialog(parent), stab(Appl()->getDebugSession()->getSymbolTable()) { QGridLayout* layout = new QGridLayout(this); layout->addWidget(new QLabel("ASID:"), 0, 0); asidEditor = new AsidLineEdit; layout->addWidget(asidEditor, 0, 1); asidEditor->setMinimumWidth(asidEditor->fontMetrics().horizontalAdvance("0") * 5); asidEditor->setMaximumWidth(asidEditor->fontMetrics().horizontalAdvance("0") * 6); layout->setColumnMinimumWidth(2, 12); layout->addWidget(new QLabel("Address:"), 0, 3); addressEditor = new AddressLineEdit; layout->addWidget(addressEditor, 0, 4); QAbstractTableModel* stabModel = new SymbolTableModel(this); proxyModel = new SortFilterSymbolTableModel(Symbol::TYPE_FUNCTION, this); proxyModel->setSourceModel(stabModel); QTreeView* symbolTableView = new QTreeView; symbolTableView->setSortingEnabled(true); symbolTableView->sortByColumn(0, Qt::AscendingOrder); symbolTableView->setAlternatingRowColors(true); symbolTableView->setModel(proxyModel); symbolTableView->resizeColumnToContents(0); symbolTableView->hideColumn(SymbolTableModel::COLUMN_END_ADDRESS); connect(symbolTableView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&,const QItemSelection&)), this, SLOT(onSelectionChanged(const QItemSelection&))); layout->addWidget(symbolTableView, 1, 0, 1, 5); QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); layout->addWidget(buttonBox, 2, 0, 1, 5); setWindowTitle("Add Breakpoint"); resize(kInitialWidth, kInitialHeight); //connect(addressEditor, SIGNAL(textChanged(const QString&)), this, SLOT(debugg())); } Word AddBreakpointDialog::getStartAddress() const { return addressEditor->getAddress(); } Word AddBreakpointDialog::getASID() const { return asidEditor->getAsid(); } void AddBreakpointDialog::onSelectionChanged(const QItemSelection& selected) { QModelIndexList indexes = selected.indexes(); if (!indexes.isEmpty()) { int row = proxyModel->mapToSource(indexes[0]).row(); const Symbol* symbol = stab->Get(row); addressEditor->setAddress(symbol->getStart()); asidEditor->setAsid(Appl()->getConfig()->getSymbolTableASID()); } } umps3-3.0.5/src/frontends/qmps/add_breakpoint_dialog.h000066400000000000000000000030321435132321600227730ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef QMPS_ADD_BREAKPOINT_DIALOG_H #define QMPS_ADD_BREAKPOINT_DIALOG_H #include #include #include "umps/types.h" class AddressLineEdit; class AsidLineEdit; class SymbolTable; class SortFilterSymbolTableModel; class AddBreakpointDialog : public QDialog { Q_OBJECT public: AddBreakpointDialog(QWidget* parent = 0); Word getStartAddress() const; Word getASID() const; private: static const int kInitialWidth = 380; static const int kInitialHeight = 340; AsidLineEdit* asidEditor; AddressLineEdit* addressEditor; const SymbolTable* const stab; SortFilterSymbolTableModel* proxyModel; private Q_SLOTS: void onSelectionChanged(const QItemSelection& selected); }; #endif // QMPS_ADD_BREAKPOINT_DIALOG_H umps3-3.0.5/src/frontends/qmps/add_suspect_dialog.cc000066400000000000000000000105411435132321600224640ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "qmps/add_suspect_dialog.h" #include #include #include #include #include #include #include #include #include "umps/symbol_table.h" #include "qmps/application.h" #include "qmps/address_line_edit.h" #include "qmps/symbol_table_model.h" AddSuspectDialog::AddSuspectDialog(QWidget* parent) : QDialog(parent), stab(Appl()->getDebugSession()->getSymbolTable()) { QGridLayout* layout = new QGridLayout(this); layout->setColumnStretch(4, 1); layout->setColumnStretch(7, 1); layout->addWidget(new QLabel("ASID:"), 0, 0); asidEditor = new AsidLineEdit; layout->addWidget(asidEditor, 0, 1); asidEditor->setMinimumWidth(asidEditor->fontMetrics().horizontalAdvance("0x00__")); asidEditor->setMaximumWidth(asidEditor->fontMetrics().horizontalAdvance("0x00__")); layout->setColumnMinimumWidth(2, 12); layout->addWidget(new QLabel("Start:"), 0, 3); startAddressEdit = new AddressLineEdit; startAddressEdit->setMinimumWidth(startAddressEdit->fontMetrics().horizontalAdvance("0xdead.beef__")); layout->addWidget(startAddressEdit, 0, 4); connect(startAddressEdit, SIGNAL(textChanged(const QString&)), this, SLOT(validate())); layout->setColumnMinimumWidth(5, 12); layout->addWidget(new QLabel("End:"), 0, 6); endAddressEdit = new AddressLineEdit; endAddressEdit->setMinimumWidth(endAddressEdit->fontMetrics().horizontalAdvance("0xdead.beef__")); layout->addWidget(endAddressEdit, 0, 7); connect(endAddressEdit, SIGNAL(textChanged(const QString&)), this, SLOT(validate())); QAbstractTableModel* stabModel = new SymbolTableModel(this); proxyModel = new SortFilterSymbolTableModel(Symbol::TYPE_OBJECT, this); proxyModel->setSourceModel(stabModel); QTreeView* symbolTableView = new QTreeView; symbolTableView->setSortingEnabled(true); symbolTableView->sortByColumn(SymbolTableModel::COLUMN_SYMBOL, Qt::AscendingOrder); symbolTableView->setAlternatingRowColors(true); symbolTableView->setModel(proxyModel); symbolTableView->resizeColumnToContents(SymbolTableModel::COLUMN_SYMBOL); connect(symbolTableView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&,const QItemSelection&)), this, SLOT(onSelectionChanged(const QItemSelection&))); layout->addWidget(symbolTableView, 1, 0, 1, 8); QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); okButton = buttonBox->button(QDialogButtonBox::Ok); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); layout->addWidget(buttonBox, 2, 0, 1, 8); setWindowTitle("Add Suspect"); resize(kInitialWidth, kInitialHeight); } Word AddSuspectDialog::getStartAddress() const { return startAddressEdit->getAddress(); } Word AddSuspectDialog::getEndAddress() const { return endAddressEdit->getAddress(); } Word AddSuspectDialog::getASID() const { return asidEditor->getAsid(); } void AddSuspectDialog::validate() { okButton->setEnabled(startAddressEdit->getAddress() <= endAddressEdit->getAddress()); } void AddSuspectDialog::onSelectionChanged(const QItemSelection& selected) { QModelIndexList indexes = selected.indexes(); if (!indexes.isEmpty()) { int row = proxyModel->mapToSource(indexes[0]).row(); const Symbol* symbol = stab->Get(row); startAddressEdit->setAddress(symbol->getStart()); endAddressEdit->setAddress(symbol->getEnd()); asidEditor->setAsid(Appl()->getConfig()->getSymbolTableASID()); } } umps3-3.0.5/src/frontends/qmps/add_suspect_dialog.h000066400000000000000000000032031435132321600223230ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef QMPS_ADD_SUSPECT_DIALOG_H #define QMPS_ADD_SUSPECT_DIALOG_H #include #include "umps/types.h" class AddressLineEdit; class AsidLineEdit; class SymbolTable; class SortFilterSymbolTableModel; class QItemSelection; class QPushButton; class AddSuspectDialog : public QDialog { Q_OBJECT public: AddSuspectDialog(QWidget* parent = 0); Word getStartAddress() const; Word getEndAddress() const; Word getASID() const; private: static const int kInitialWidth = 380; static const int kInitialHeight = 340; AsidLineEdit* asidEditor; AddressLineEdit* startAddressEdit; AddressLineEdit* endAddressEdit; const SymbolTable* const stab; SortFilterSymbolTableModel* proxyModel; QPushButton* okButton; private Q_SLOTS: void validate(); void onSelectionChanged(const QItemSelection& selected); }; #endif // QMPS_ADD_SUSPECT_DIALOG_H umps3-3.0.5/src/frontends/qmps/add_tracepoint_dialog.cc000066400000000000000000000117331435132321600231520ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "qmps/add_tracepoint_dialog.h" #include #include #include #include #include #include #include #include #include "umps/machine_config.h" #include "umps/symbol_table.h" #include "qmps/application.h" #include "qmps/address_line_edit.h" #include "qmps/symbol_table_model.h" AddTracepointDialog::AddTracepointDialog(QWidget* parent) : QDialog(parent), stab(Appl()->getDebugSession()->getSymbolTable()) { QGridLayout* layout = new QGridLayout(this); layout->setColumnStretch(1, 1); layout->setColumnStretch(4, 1); int gridRow = 0; layout->addWidget(new QLabel("Start:"), gridRow, 0); startAddressEdit = new AddressLineEdit; startAddressEdit->setMinimumWidth(startAddressEdit->fontMetrics().horizontalAdvance("0xdead.beef__")); layout->addWidget(startAddressEdit, gridRow, 1); connect(startAddressEdit, SIGNAL(textChanged(const QString&)), this, SLOT(validate())); layout->setColumnMinimumWidth(2, 12); layout->addWidget(new QLabel("End:"), gridRow, 3); endAddressEdit = new AddressLineEdit; endAddressEdit->setMinimumWidth(endAddressEdit->fontMetrics().horizontalAdvance("0xdead.beef__")); layout->addWidget(endAddressEdit, gridRow, 4); connect(endAddressEdit, SIGNAL(textChanged(const QString&)), this, SLOT(validate())); gridRow++; if (Appl()->getConfig()->getSymbolTableASID() == MachineConfig::MAX_ASID) { QAbstractTableModel* stabModel = new SymbolTableModel(this); proxyModel = new SortFilterSymbolTableModel(Symbol::TYPE_OBJECT, this); proxyModel->setSourceModel(stabModel); QTreeView* symbolTableView = new QTreeView; symbolTableView->setSortingEnabled(true); symbolTableView->sortByColumn(SymbolTableModel::COLUMN_SYMBOL, Qt::AscendingOrder); symbolTableView->setAlternatingRowColors(true); symbolTableView->setModel(proxyModel); symbolTableView->resizeColumnToContents(SymbolTableModel::COLUMN_SYMBOL); connect(symbolTableView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&,const QItemSelection&)), this, SLOT(onSelectionChanged(const QItemSelection&))); layout->addWidget(symbolTableView, gridRow++, 0, 1, 5); // Some sensible initial size since we're showing a symbol table resize(kInitialWidth, kInitialHeight); } layout->addWidget(inputErrorLabel = new QLabel, gridRow++, 0, 1, 5); QPalette pError = inputErrorLabel->palette(); pError.setColor(inputErrorLabel->foregroundRole(), Qt::red); inputErrorLabel->setPalette(pError); QFrame* separator = new QFrame; separator->setFrameShape(QFrame::HLine); separator->setFrameShadow(QFrame::Sunken); layout->addWidget(separator, gridRow++, 0, 1, 5); QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); okButton = buttonBox->button(QDialogButtonBox::Ok); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); layout->addWidget(buttonBox, gridRow, 0, 1, 5); setWindowTitle("Add Traced Region"); } Word AddTracepointDialog::getStartAddress() const { return startAddressEdit->getAddress(); } Word AddTracepointDialog::getEndAddress() const { return endAddressEdit->getAddress(); } void AddTracepointDialog::validate() { Word s = startAddressEdit->getAddress(); Word e = endAddressEdit->getAddress(); if (s > e) { okButton->setEnabled(false); inputErrorLabel->setText("Start address must not be higher than end address"); return; } if (((e - s) >> 2) + 1 > kMaxTracedRangeSize) { okButton->setEnabled(false); inputErrorLabel->setText(QString("Range size must not exceed %1 bytes") .arg(kMaxTracedRangeSize * WS)); return; } okButton->setEnabled(true); inputErrorLabel->setText(""); } void AddTracepointDialog::onSelectionChanged(const QItemSelection& selected) { QModelIndexList indexes = selected.indexes(); if (!indexes.isEmpty()) { int row = proxyModel->mapToSource(indexes[0]).row(); const Symbol* symbol = stab->Get(row); startAddressEdit->setAddress(symbol->getStart()); endAddressEdit->setAddress(symbol->getEnd()); } } umps3-3.0.5/src/frontends/qmps/add_tracepoint_dialog.h000066400000000000000000000032441435132321600230120ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef QMPS_ADD_TRACEPOINT_DIALOG_H #define QMPS_ADD_TRACEPOINT_DIALOG_H #include #include "umps/types.h" class AddressLineEdit; class SymbolTable; class SortFilterSymbolTableModel; class QItemSelection; class QLabel; class QPushButton; class AddTracepointDialog : public QDialog { Q_OBJECT public: static const Word kMaxTracedRangeSize = 1025; AddTracepointDialog(QWidget* parent = 0); Word getStartAddress() const; Word getEndAddress() const; private: static const int kInitialWidth = 430; static const int kInitialHeight = 340; AddressLineEdit* startAddressEdit; AddressLineEdit* endAddressEdit; const SymbolTable* const stab; SortFilterSymbolTableModel* proxyModel; QLabel* inputErrorLabel; QPushButton* okButton; private Q_SLOTS: void validate(); void onSelectionChanged(const QItemSelection& selected); }; #endif // QMPS_ADD_TRACEPOINT_DIALOG_H umps3-3.0.5/src/frontends/qmps/address_line_edit.cc000066400000000000000000000063101435132321600223070ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "qmps/address_line_edit.h" #include #include #include "base/lang.h" //#include "umps/umps/const.h" #include "qmps/application.h" class AddressValidator : public QValidator { public: AddressValidator(QObject* parent = 0) : QValidator(parent) { } virtual State validate(QString& input, int& pos) const; }; QValidator::State AddressValidator::validate(QString& input, int& pos) const { UNUSED_ARG(pos); input.replace(' ', '0'); // Beware of the non-const reference parameter; make a copy since // we need to modify it for convenience. QString temp = input; bool ok; unsigned int value = temp.remove(0, 2).remove(4, 1).toUInt(&ok, 16); // What's left _has_ to be valid hex _already_! assert(ok); return (value & 3U) ? Invalid : Acceptable; } AddressLineEdit::AddressLineEdit(QWidget* parent) : QLineEdit(parent) { setInputMask("\\0\\xHHHH.HHHH"); setValidator(new AddressValidator(this)); setFont(Appl()->getMonospaceFont()); setText("0000.0000"); } Word AddressLineEdit::getAddress() const { bool ok; Word result = text().remove(0, 2).remove(4, 1).toUInt(&ok, 16); // Better be ok! Otherwise our validation is broken. assert(ok); return result; } void AddressLineEdit::setAddress(Word addr) { if (!(addr & 3U)) { setText(QString("0x%1.%2") .arg((quint32) (addr >> 16), 4, 16, QChar('0')) .arg((quint32) (addr & 0x0000ffffU), 4, 16, QChar('0'))); } } class AsidValidator : public QValidator { public: AsidValidator(QObject* parent = 0) : QValidator(parent) { } virtual State validate(QString& input, int& pos) const; }; QValidator::State AsidValidator::validate(QString& input, int& pos) const { if (input.endsWith(' ')) { input.remove(3, 1); input.insert(2, '0'); } input.replace(' ', '0'); QString temp = input; bool ok; unsigned int value = temp.remove(0, 2).toUInt(&ok, 16); // What's left _has_ to be valid hex _already_! assert(ok); return (value <= MachineConfig::MAX_ASID) ? Acceptable : Invalid; } AsidLineEdit::AsidLineEdit(QWidget* parent) : QLineEdit(parent) { setInputMask("\\0\\xHH"); setValidator(new AsidValidator(this)); setFont(Appl()->getMonospaceFont()); setText("00"); } Word AsidLineEdit::getAsid() const { bool ok; Word result = text().remove(0, 2).toUInt(&ok, 16); assert(ok); return result; } void AsidLineEdit::setAsid(Word asid) { if (asid <= MachineConfig::MAX_ASID) setText(QString::number(asid, 16)); } umps3-3.0.5/src/frontends/qmps/address_line_edit.h000066400000000000000000000023751435132321600221600ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef QMPS_ADDRESS_LINE_EDIT_H #define QMPS_ADDRESS_LINE_EDIT_H #include #include "umps/types.h" class AddressLineEdit : public QLineEdit { Q_OBJECT public: AddressLineEdit(QWidget* parent = 0); Word getAddress() const; void setAddress(Word address); }; class AsidLineEdit : public QLineEdit { Q_OBJECT public: AsidLineEdit(QWidget* parent = 0); Word getAsid() const; void setAsid(Word asid); }; #endif // QMPS_ADDRESS_LINE_EDIT_H umps3-3.0.5/src/frontends/qmps/application.cc000066400000000000000000000067411435132321600211610ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "qmps/application.h" #include #include #include #include #include "umps/error.h" #include "qmps/monitor_window.h" Application::Application(int& argc, char** argv) : QApplication(argc, argv), settings("umps3", "umps"), config(NULL) { setApplicationName("uMPS"); setWindowIcon(QIcon(":/icons/umps3-48.svg")); dbgSession.reset(new DebugSession); monitorWindow.reset(new MonitorWindow); monitorWindow->show(); if (argc > 1) LoadConfig(argv[1]); } Application::~Application() { } void Application::CreateConfig(const QString& path) { MachineConfig* newConfig; try { newConfig = MachineConfig::Create(QFile::encodeName(path).constData()); } catch (FileError& e) { newConfig = NULL; QMessageBox::critical(monitorWindow.get(), QString("%1: Error").arg(applicationName()), QString("Could not create machine configuration: %1").arg(e.what())); } if (newConfig) setCurrentConfig(path, newConfig); } void Application::LoadConfig(const QString& path) { std::string error; MachineConfig* newConfig = MachineConfig::LoadFromFile(QFile::encodeName(path).constData(), error); if (newConfig) setCurrentConfig(path, newConfig); else QMessageBox::critical(monitorWindow.get(), QString("%1: Error").arg(applicationName()), QString("Error loading machine configuration: %1").arg(error.c_str())); } void Application::LoadRecentConfig(unsigned int i) { QStringList recent = settings.value("RecentFiles").toStringList(); assert(i < (unsigned int) recent.size()); LoadConfig(recent[i]); } MachineConfig* Application::getConfig() { return config.get(); } QWidget* Application::getApplWindow() { return monitorWindow.get(); } QFont Application::getMonospaceFont() { QFont monospaceFont("monospace"); monospaceFont.setStyleHint(QFont::TypeWriter); return monospaceFont; } QFont Application::getBoldFont() { QFont font; font.setBold(true); return font; } void Application::setCurrentConfig(const QString& path, MachineConfig* newConfig) { dbgSession->halt(); config.reset(newConfig); QFileInfo info(path); document = info.fileName(); dir = info.absolutePath(); QDir::setCurrent(dir); QString absolutePath = info.absoluteFilePath(); QStringList recentFiles = settings.value("RecentFiles").toStringList(); recentFiles.removeAll(absolutePath); recentFiles.prepend(absolutePath); while ((unsigned int) recentFiles.size() > kMaxRecentConfigs) recentFiles.removeLast(); settings.setValue("RecentFiles", recentFiles); Q_EMIT MachineConfigChanged(); } Application* Appl() { return static_cast(qApp); } umps3-3.0.5/src/frontends/qmps/application.h000066400000000000000000000037361435132321600210240ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef QMPS_APPLICATION_H #define QMPS_APPLICATION_H #include #include #include #include "base/lang.h" #include "umps/machine_config.h" #include "umps/machine.h" #include "qmps/debug_session.h" class MonitorWindow; class QWidget; class Application: public QApplication { Q_OBJECT public: static const unsigned int kMaxRecentConfigs = 5; Application(int& argc, char** argv); ~Application(); MachineConfig* getConfig(); void CreateConfig(const QString& path); void LoadConfig(const QString& path); void LoadRecentConfig(unsigned int i); DebugSession* getDebugSession() { return dbgSession.get(); } QWidget* getApplWindow(); const QString& getCurrentDir() const { return dir; } QFont getMonospaceFont(); QFont getBoldFont(); QSettings settings; QString document; Q_SIGNALS: void MachineConfigChanged(); private: void setCurrentConfig(const QString& path, MachineConfig* newConfig); scoped_ptr dbgSession; scoped_ptr config; QString dir; scoped_ptr monitorWindow; }; Application* Appl(); #define debugSession (Appl()->getDebugSession()) #endif // QMPS_APPLICATION_H umps3-3.0.5/src/frontends/qmps/boolean_item_delegate.cc000066400000000000000000000066631435132321600231500ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "qmps/boolean_item_delegate.h" #include #include #include "qmps/application.h" BooleanItemDelegate::BooleanItemDelegate(QObject* parent) : QStyledItemDelegate(parent) { } void BooleanItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { QVariant variant = index.data(); if (variant.canConvert()) { if (option.state & QStyle::State_Selected) painter->fillRect(option.rect, option.palette.highlight()); if (option.state & QStyle::State_HasFocus) { QStyleOptionFocusRect focusOptions; focusOptions.rect = option.rect; QApplication::style()->drawPrimitive(QStyle::PE_FrameFocusRect, &focusOptions, painter); } QStyleOptionButton buttonStyleOptions; buttonStyleOptions.rect = buttonGeometry(option.rect); if (index.flags() & Qt::ItemIsEnabled) buttonStyleOptions.state |= QStyle::State_Enabled; else buttonStyleOptions.state &= ~QStyle::State_Enabled; if (variant.toBool()) buttonStyleOptions.state |= QStyle::State_On; else buttonStyleOptions.state |= QStyle::State_Off; QApplication::style()->drawControl(QStyle::CE_CheckBox, &buttonStyleOptions, painter); } else { QStyledItemDelegate::paint(painter, option, index); } } bool BooleanItemDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex &index) { if ((event->type() == QEvent::MouseButtonRelease) || (event->type() == QEvent::MouseButtonDblClick)) { QMouseEvent *mouseEvent = static_cast(event); if (mouseEvent->button() != Qt::LeftButton || !buttonGeometry(option.rect).contains(mouseEvent->pos())) { return false; } if (event->type() == QEvent::MouseButtonDblClick) { return true; } } else if (event->type() == QEvent::KeyPress) { if (static_cast(event)->key() != Qt::Key_Space && static_cast(event)->key() != Qt::Key_Select) { return false; } } else { return false; } return model->setData(index, !index.data().toBool(), Qt::EditRole); } QRect BooleanItemDelegate::buttonGeometry(const QRect& viewItemRect) { QStyleOptionButton dummy; QRect er = QApplication::style()->subElementRect(QStyle::SE_CheckBoxIndicator, &dummy); return QRect(viewItemRect.x() + viewItemRect.width() / 2 - er.width() / 2, viewItemRect.y() + viewItemRect.height() / 2 - er.height() / 2, er.width(), er.height()); } umps3-3.0.5/src/frontends/qmps/boolean_item_delegate.h000066400000000000000000000031011435132321600227720ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef QMPS_BOOLEAN_ITEM_DELEGATE_H #define QMPS_BOOLEAN_ITEM_DELEGATE_H #include class BooleanItemDelegate : public QStyledItemDelegate { Q_OBJECT public: BooleanItemDelegate(QObject* parent = 0); void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; // We reimplement `editorEvent' as the implementation in // QStyledItemDelegate isn't of any use for us (i.e. we don't want // a default editor!). bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index); private: static QRect buttonGeometry(const QRect& viewItemRect); }; #endif // QMPS_BOOLEAN_ITEM_DELEGATE_H umps3-3.0.5/src/frontends/qmps/code_view.cc000066400000000000000000000263011435132321600206140ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * Copyright (C) 2020 Mattia Biondi * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "qmps/code_view.h" #include #include #include #include #include #include #include #include #include #include "base/lang.h" #include "umps/machine_config.h" #include "umps/processor.h" #include "umps/processor_defs.h" #include "umps/machine.h" #include "umps/symbol_table.h" #include "umps/disassemble.h" #include "umps/stoppoint.h" #include "qmps/application.h" #include "qmps/debug_session.h" #include "qmps/stoppoint_list_model.h" #include "qmps/code_view_priv.h" #include "qmps/ui_utils.h" using namespace boost::placeholders; CodeView::CodeView(Word cpuId) : QPlainTextEdit(), codeMargin(new CodeViewMargin(this)), dbgSession(Appl()->getDebugSession()), cpuId(cpuId), pcMarkerPixmap(":/icons/pc-16.svg"), enabledBpMarkerPixmap(":/icons/breakpoint_enabled-16.svg"), disabledBpMarkerPixmap(":/icons/breakpoint_disabled-16.svg") { setLineWrapMode(NoWrap); setReadOnly(true); QFont font = Appl()->getMonospaceFont(); setFont(font); codeMargin->setFont(font); codeMargin->setCursor(Qt::PointingHandCursor); setTabStopDistance(fontMetrics().horizontalAdvance("x") * TAB_STOP_CHARS); // Compute viewport margins setViewportMargins(codeMargin->sizeHint().width(), 0, 0, 0); // The updateRequest() signal is emitted when QPlainTextEdit has // to update its viewport, e.g. after a vertical or horizontal // scroll. This signal was in fact devised especially for // QPlainTextEdit subclasses that want to add surrounding content // to the widget. connect(this, SIGNAL(updateRequest(QRect,int)), this, SLOT(updateMargin(QRect,int))); // Connect to debugger events of interest connect(dbgSession, SIGNAL(MachineStopped()), this, SLOT(onMachineStopped())); connect(dbgSession, SIGNAL(MachineRan()), this, SLOT(update())); connect(dbgSession, SIGNAL(MachineReset()), this, SLOT(reset())); disasmMap[BEQ] = boost::bind(&CodeView::disasmBranch, this, _1, _2); disasmMap[BNE] = boost::bind(&CodeView::disasmBranch, this, _1, _2); disasmMap[JAL] = boost::bind(&CodeView::disasmJump, this, _1, _2); disasmMap[J] = boost::bind(&CodeView::disasmJump, this, _1, _2); reset(); } // We need to handle resize events since we contain a child widget (a // margin); on any resize, we must resize the margin accordingly. void CodeView::resizeEvent(QResizeEvent* event) { QPlainTextEdit::resizeEvent(event); // The contentsRect() function gives us the complete area // (geometry) assigned to the code view, _including_ the margins, // so we can finally "materialize" the margin: it will lay at the // top left corner of the abovementioned rect, has the codeviews' // height and its desired width. QRect rect = contentsRect(); codeMargin->setGeometry(QRect(rect.left(), rect.top(), codeMargin->sizeHint().width(), rect.height())); } void CodeView::loadCode() { const MachineConfig* config = Appl()->getConfig(); Machine* machine = dbgSession->getMachine(); codeLoaded = false; clear(); if (cpu->isHalted()) return; Word pc = cpu->getPC(); if (pc >= RAM_BASE) { if (pc < config->getTLBFloorAddress() && config->getSymbolTableASID() == MAXASID) { const Symbol* symbol = symbolTable->Probe(MAXASID, pc, false); if (symbol != NULL) { startPC = symbol->getStart(); endPC = symbol->getEnd(); codeLoaded = true; } } } else if (pc >= KSEG0_BOOT_BASE) { Word bootSize; machine->ReadMemory(BUS_REG_BOOT_SIZE, &bootSize); if (pc <= KSEG0_BOOT_BASE + bootSize - WS) { startPC = KSEG0_BOOT_BASE; endPC = startPC + bootSize - WS; codeLoaded = true; } } else { Word biosSize; machine->ReadMemory(BUS_REG_BIOS_SIZE, &biosSize); if (pc <= KSEG0_BIOS_BASE + biosSize - WS) { startPC = KSEG0_BIOS_BASE; endPC = startPC + biosSize - WS; codeLoaded = true; } } if (codeLoaded) { for (Word addr = startPC; addr <= endPC; addr += WS) { Word instr; machine->ReadMemory(addr, &instr); appendPlainText(disassemble(instr, addr)); } ensureCurrentInstructionVisible(); } } void CodeView::onBreakpointInserted() { update(); } void CodeView::onBreakpointChanged(size_t) { update(); } QString CodeView::disassemble(Word instr, Word pc) const { DisasmMap::const_iterator it = disasmMap.find(OPCODE(instr)); if (it != disasmMap.end()) return it->second(instr, pc); else return StrInstr(instr); } QString CodeView::disasmBranch(Word instr, Word pc) const { Word target = pc + WS + (SignExtImm(instr) << 2); #if 0 // Resolve symbol, if possible SWord offset; const char* symbol = GetSymbolicAddress(symbolTable, MachineConfig::MAX_ASID, target, true, &offset); return (QString("%1\t$%2, $%3, %4%5") .arg(InstructionMnemonic(instr)) .arg(RegName(RS(instr))) .arg(RegName(RT(instr))) .arg(target, 8, 16, QChar('0')) .arg(symbol ? QString(" <%1+0x%2>").arg(symbol).arg(offset, 0, 16) : QString())); #else return (QString("%1\t$%2, $%3, %4") .arg(InstructionMnemonic(instr)) .arg(RegName(RS(instr))) .arg(RegName(RT(instr))) .arg(target, 8, 16, QChar('0'))); #endif } QString CodeView::disasmJump(Word instr, Word pc) const { Word target = JUMPTO(pc, instr); SWord offset; const char* symbol = GetSymbolicAddress(symbolTable, MachineConfig::MAX_ASID, target, true, &offset); return (QString("%1\t%2%3") .arg(InstructionMnemonic(instr)) .arg(target, 8, 16, QChar('0')) .arg(symbol ? QString(" <%1+0x%2>").arg(symbol).arg(offset, 0, 16) : QString())); } void CodeView::onMachineStopped() { const MachineConfig* config = Appl()->getConfig(); Word pc = cpu->getPC(); if (!codeLoaded) { loadCode(); } else { if (pc >= config->getTLBFloorAddress()) { clear(); codeLoaded = false; } else if (startPC <= pc && pc <= endPC) { codeMargin->update(); ensureCurrentInstructionVisible(); } else { loadCode(); } } } void CodeView::updateMargin(const QRect& rect, int dy) { if (dy != 0) codeMargin->scroll(0, dy); else codeMargin->update(0, rect.y(), codeMargin->width(), rect.height()); } void CodeView::reset() { cpu = dbgSession->getMachine()->getProcessor(cpuId); breakpoints = dbgSession->getBreakpoints(); breakpoints->SignalStoppointInserted.connect( sigc::mem_fun(this, &CodeView::onBreakpointInserted)); breakpoints->SignalStoppointRemoved.connect( sigc::mem_fun(this, &CodeView::onBreakpointChanged)); breakpoints->SignalEnabledChanged.connect( sigc::mem_fun(this, &CodeView::onBreakpointChanged)); symbolTable = dbgSession->getSymbolTable(); bplModel = dbgSession->getBreakpointListModel(); loadCode(); } void CodeView::paintMargin(QPaintEvent* event) { QPainter painter(codeMargin); painter.fillRect(event->rect(), palette().window().color()); // Nothing left to do unless we are displaying a code block. if (!codeLoaded) return; painter.setPen(palette().windowText().color()); QTextBlock block = firstVisibleBlock(); if (!block.isValid()) return; std::list localBreakpoints; breakpoints->GetStoppointsInRange(MachineConfig::MAX_ASID, startPC + block.blockNumber() * WS, endPC, std::back_inserter(localBreakpoints)); for (Stoppoint* breakpoint : localBreakpoints) { unsigned int bpOffset = (breakpoint->getRange().getStart() - startPC) >> 2; QTextBlock b = document()->findBlockByNumber(bpOffset); int y0 = (int) blockBoundingGeometry(b).translated(contentOffset()).y(); if (y0 > event->rect().bottom()) break; int y1 = y0 + (int) blockBoundingRect(b).height(); if (event->rect().top() <= y1) painter.drawPixmap(codeMargin->width() - CodeViewMargin::kMarkerSize, y0, breakpoint->IsEnabled() ? enabledBpMarkerPixmap : disabledBpMarkerPixmap); } Word addr = startPC + block.blockNumber() * WS; int y0 = (int) blockBoundingGeometry(block).translated(contentOffset()).y(); int y1 = y0 + (int) blockBoundingRect(block).height(); Word pc = cpu->getPC(); while (y0 <= event->rect().bottom()) { if (event->rect().top() <= y1) { if (addr == pc && dbgSession->isStopped()) painter.drawPixmap(codeMargin->width() - CodeViewMargin::kMarkerSize, y0, pcMarkerPixmap); painter.drawText(0, y0, codeMargin->width(), codeMargin->fontMetrics().height(), Qt::AlignLeft, FormatAddress(addr)); } block = block.next(); if (!block.isValid()) break; y0 = y1; y1 += blockBoundingRect(block).height(); addr += WS; } } void CodeView::ensureCurrentInstructionVisible() { QTextCursor cursor = textCursor(); cursor.setPosition(0); unsigned int offset = (cpu->getPC() - startPC) >> 2; cursor.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor, offset); setTextCursor(cursor); } CodeViewMargin::CodeViewMargin(CodeView* view) : QWidget(view), codeView(view) { } QSize CodeViewMargin::sizeHint() const { QString sampleAddr = FormatAddress(0xdeadbeef); return QSize(fontMetrics().horizontalAdvance("o") * sampleAddr.size() + kMarkerSize, 0); } void CodeViewMargin::paintEvent(QPaintEvent* event) { codeView->paintMargin(event); } void CodeViewMargin::mousePressEvent(QMouseEvent* event) { if (event->button() != Qt::LeftButton || !codeView->codeLoaded) return; int index = indexAt(event->pos()); if (index == -1) return; Word addr = codeView->startPC + index * WS; Stoppoint* p = codeView->breakpoints->Find(MachineConfig::MAX_ASID, addr); if (p == NULL) codeView->bplModel->Add(AddressRange(MachineConfig::MAX_ASID, addr, addr), AM_EXEC); else codeView->bplModel->Remove(p); } bool CodeViewMargin::event(QEvent* event) { if (event->type() == QEvent::ToolTip) { QHelpEvent* helpEvent = static_cast(event); int index = indexAt(helpEvent->pos()); if (index != -1) { Word addr = codeView->startPC + index * WS; Stoppoint* p = codeView->breakpoints->Find(MachineConfig::MAX_ASID, addr); if (p != NULL) QToolTip::showText(helpEvent->globalPos(), QString("Breakpoint B%1").arg(p->getId())); } else { QToolTip::hideText(); event->ignore(); } return true; } return QWidget::event(event); } void CodeViewMargin::wheelEvent(QWheelEvent* event) { codeView->wheelEvent(event); } int CodeViewMargin::indexAt(const QPoint& pos) const { QTextCursor cursor = codeView->cursorForPosition(pos); QTextBlock block = cursor.block(); return block.isValid() ? block.blockNumber() : -1; } umps3-3.0.5/src/frontends/qmps/code_view.h000066400000000000000000000045101435132321600204540ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef QMPS_CODE_VIEW_H #define QMPS_CODE_VIEW_H #include #include #include #include #include "umps/types.h" class QPaintEvent; class QPixmap; class CodeViewMargin; class DebugSession; class Processor; class Symbol; class SymbolTable; class StoppointSet; class StoppointListModel; class CodeView : public QPlainTextEdit, public sigc::trackable { Q_OBJECT public: CodeView(Word cpuId); protected: // We need to handle the resizeEvent since we're responsible of // resizing our margin. void resizeEvent(QResizeEvent* event); private Q_SLOTS: void loadCode(); void onMachineStopped(); void updateMargin(const QRect& rect, int dy); void reset(); private: static const int TAB_STOP_CHARS = 8; void paintMargin(QPaintEvent* event); void ensureCurrentInstructionVisible(); void onBreakpointInserted(); void onBreakpointChanged(size_t); QString disassemble(Word instr, Word pc) const; QString disasmBranch(Word instr, Word pc) const; QString disasmJump(Word instr, Word pc) const; CodeViewMargin* codeMargin; DebugSession* const dbgSession; const Word cpuId; Processor* cpu; SymbolTable* symbolTable; StoppointSet* breakpoints; bool codeLoaded; Word startPC, endPC; StoppointListModel* bplModel; QPixmap pcMarkerPixmap; QPixmap enabledBpMarkerPixmap; QPixmap disabledBpMarkerPixmap; typedef boost::function DisasmFunc; typedef std::map DisasmMap; DisasmMap disasmMap; friend class CodeViewMargin; }; #endif // QMPS_CODE_VIEW_H umps3-3.0.5/src/frontends/qmps/code_view_priv.h000066400000000000000000000030241435132321600215130ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef QMPS_CODE_VIEW_PRIV_H #define QMPS_CODE_VIEW_PRIV_H #include #include "umps/types.h" class CodeView; class CodeViewMargin : public QWidget { Q_OBJECT public: static const int kMarkerSize = 16; CodeViewMargin(CodeView* codeView); virtual QSize sizeHint() const; protected: bool event(QEvent* event); void paintEvent(QPaintEvent* event); void mousePressEvent(QMouseEvent* event); // For some reason we have to proxy this to QAbstractScollArea // _explicitely_! Cannot figure out why, since by default the // parent should handle it automatically if ignored. void wheelEvent(QWheelEvent* event); private: int indexAt(const QPoint& pos) const; CodeView* const codeView; }; #endif // QMPS_CODE_VIEW_PRIV_H umps3-3.0.5/src/frontends/qmps/cpu_status_map.cc000066400000000000000000000105471435132321600217040ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * Copyright (C) 2020 Mattia Biondi * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "qmps/cpu_status_map.h" #include "umps/symbol_table.h" #include "umps/machine.h" #include "umps/processor.h" #include "qmps/application.h" #include "qmps/debug_session.h" #include "qmps/ui_utils.h" CpuStatusMap::CpuStatusMap(DebugSession* dbgSession) : QObject(), dbgSession(dbgSession), machine(dbgSession->getMachine()), statusMap(Appl()->getConfig()->getNumProcessors()) { connect(dbgSession, SIGNAL(MachineStopped()), this, SLOT(update())); connect(dbgSession, SIGNAL(MachineRan()), this, SLOT(update())); connect(dbgSession, SIGNAL(DebugIterationCompleted()), this, SLOT(update())); update(); } const QString& CpuStatusMap::getStatus(unsigned int cpuId) const { return statusMap[cpuId].status; } const QString& CpuStatusMap::getLocation(unsigned int cpuId) const { return statusMap[cpuId].location; } const char* const CpuStatusMap::statusTemplates[] = { "Stopped", "Stopped: User", "Stopped: Breakpoint(B%brkpt%)", "Stopped: User+Breakpoint(B%brkpt%)", "Stopped: Suspect(S%susp%)", "Stopped: User+Suspect(S%susp%)", "Stopped: Breakpoint(B%brkpt%)+Suspect(S%susp%)", "Stopped: User+Breakpoint(B%brkpt%)+Suspect(S%susp%)", "Stopped: Exception(%excpt%)", "Stopped: User+Exception(%excpt%)", "Stopped: Breakpoint(B%brkpt%)+Exception(%excpt%)", "Stopped: User+Breakpoint(B%brkpt%)+Exception(%excpt%)", "Stopped: Suspect(S%susp%)+Exception(%excpt%)", "Stopped: User+Suspect(S%susp%)+Exception(%excpt%)", "Stopped: Breakpoint(B%brkpt%)+Suspect(S%susp%)+Exception(%excpt%)", "Stopped: User+Breakpoint(B%brkpt%)+Suspect(S%susp%)+Exception(%excpt%)" }; void CpuStatusMap::update() { MachineConfig* config = Appl()->getConfig(); for (unsigned int cpuId = 0; cpuId < config->getNumProcessors(); cpuId++) { Processor* cpu = machine->getProcessor(cpuId); switch (cpu->getStatus()) { case PS_HALTED: statusMap[cpuId].status = "Halted"; statusMap[cpuId].location = ""; break; case PS_IDLE: statusMap[cpuId].status = "Idle"; formatActiveCpuLocation(cpu); break; case PS_RUNNING: formatActiveCpuStatus(cpu); formatActiveCpuLocation(cpu); break; default: statusMap[cpuId].status = "Unknown"; break; } } Q_EMIT Changed(); } void CpuStatusMap::formatActiveCpuStatus(Processor* cpu) { unsigned int stopCause; QString str; switch (dbgSession->getStatus()) { case MS_STOPPED: stopCause = machine->getStopCause(cpu->getId()); if (dbgSession->isStoppedByUser()) stopCause |= SC_USER; str = statusTemplates[stopCause]; if (stopCause & SC_BREAKPOINT) str.replace("%brkpt%", QString::number(machine->getActiveBreakpoint(cpu->getId()))); if (stopCause & SC_SUSPECT) str.replace("%susp%", QString::number(machine->getActiveSuspect(cpu->getId()))); if (stopCause & SC_EXCEPTION) str.replace("%excpt%", cpu->getExcCauseStr()); statusMap[cpu->getId()].status = str; break; case MS_RUNNING: statusMap[cpu->getId()].status = "Running"; break; default: // We should never get here! statusMap[cpu->getId()].status = QString(); break; } } void CpuStatusMap::formatActiveCpuLocation(Processor* cpu) { SymbolTable* stab = dbgSession->getSymbolTable(); const MachineConfig* config = Appl()->getConfig(); Word pc = cpu->getPC(); Word asid = (pc >= config->getTLBFloorAddress()) ? cpu->getASID() : MAXASID; SWord offset; const char* symbol = GetSymbolicAddress(stab, asid, cpu->getPC(), true, &offset); if (symbol) statusMap[cpu->getId()].location = QString("%1+0x%2").arg(symbol).arg(offset, 0, 16); else statusMap[cpu->getId()].location = FormatAddress(cpu->getPC()); } umps3-3.0.5/src/frontends/qmps/cpu_status_map.h000066400000000000000000000030731435132321600215420ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef QMPS_CPU_STATUS_MAP_H #define QMPS_CPU_STATUS_MAP_H #include #include #include class DebugSession; class Machine; class Processor; class CpuStatusMap : public QObject { Q_OBJECT public: CpuStatusMap(DebugSession* dbgSession); const QString& getStatus(unsigned int cpuId) const; const QString& getLocation(unsigned int cpuId) const; Q_SIGNALS: void Changed(); private: struct StatusInfo { QString status; QString location; }; void formatActiveCpuStatus(Processor* cpu); void formatActiveCpuLocation(Processor* cpu); DebugSession* const dbgSession; Machine* const machine; static const char* const statusTemplates[]; std::vector statusMap; private Q_SLOTS: void update(); }; #endif // QMPS_CPU_STATUS_MAP_H umps3-3.0.5/src/frontends/qmps/create_machine_dialog.cc000066400000000000000000000066511435132321600231240ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "qmps/create_machine_dialog.h" #include #include #include #include #include #include #include #include #include CreateMachineDialog::CreateMachineDialog(QWidget* parent) : QDialog(parent) { setWindowTitle("Create a Machine Configuration"); QGridLayout* layout = new QGridLayout(this); layout->setSizeConstraint(QLayout::SetFixedSize); layout->setColumnStretch(1, 1); QLabel* message = new QLabel( QString("A basic machine configuration will be created which you can later
" "customize to your needs " "(Machine %1 Edit Configuration).").arg(QChar(0x2192)) ); layout->addWidget(message, 0, 0, 1, 3); layout->setRowMinimumHeight(1, 16); layout->addWidget(new QLabel("Create in:"), 2, 0); dirEdit = new QLineEdit; layout->addWidget(dirEdit, 2, 1); QPushButton* fileChooserButton = new QPushButton("&Browse..."); connect(fileChooserButton, SIGNAL(clicked()), this, SLOT(browseDir())); layout->addWidget(fileChooserButton, 2, 2); layout->addWidget(new QLabel("Name:"), 3, 0); nameEdit = new QLineEdit; layout->addWidget(nameEdit, 3, 1, 1, 2); layout->setRowMinimumHeight(4, 16); QFrame* separator = new QFrame; separator->setFrameShape(QFrame::HLine); separator->setFrameShadow(QFrame::Sunken); layout->addWidget(separator, 5, 0, 1, 3); QDialogButtonBox* buttonBox = new QDialogButtonBox; createButton = buttonBox->addButton("Create", QDialogButtonBox::AcceptRole); createButton->setEnabled(false); buttonBox->addButton(QDialogButtonBox::Cancel); layout->addWidget(buttonBox, 6, 0, 1, 3); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); connect(dirEdit, SIGNAL(textChanged(const QString&)), this, SLOT(validate())); connect(nameEdit, SIGNAL(textChanged(const QString&)), this, SLOT(validate())); } QString CreateMachineDialog::getFileName() const { assert(hasValidInput()); return QDir::cleanPath(QDir(dirEdit->text()).filePath(nameEdit->text())); } bool CreateMachineDialog::hasValidInput() const { QDir dir(dirEdit->text()); return (!dirEdit->text().isEmpty() && dir.isAbsolute() && dir.exists() && !nameEdit->text().isEmpty()); } void CreateMachineDialog::validate() { createButton->setEnabled(hasValidInput()); } void CreateMachineDialog::browseDir() { QString fileName = QFileDialog::getExistingDirectory(this, "Choose Directory", dirEdit->text()); if (!fileName.isEmpty()) dirEdit->setText(fileName); } umps3-3.0.5/src/frontends/qmps/create_machine_dialog.h000066400000000000000000000024171435132321600227620ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef QMPS_CREATE_MACHINE_DIALOG_H #define QMPS_CREATE_MACHINE_DIALOG_H #include class QLineEdit; class QPushButton; class CreateMachineDialog : public QDialog { Q_OBJECT public: CreateMachineDialog(QWidget* parent = 0); QString getFileName() const; private: bool hasValidInput() const; QLineEdit* dirEdit; QLineEdit* nameEdit; QPushButton* createButton; private Q_SLOTS: void validate(); void browseDir(); }; #endif // QMPS_CREATE_MACHINE_DIALOG_H umps3-3.0.5/src/frontends/qmps/data/000077500000000000000000000000001435132321600172505ustar00rootroot00000000000000umps3-3.0.5/src/frontends/qmps/data/CMakeLists.txt000066400000000000000000000003521435132321600220100ustar00rootroot00000000000000set(ID org.virtualsquare.umps3) add_subdirectory(icons) install(FILES ${ID}.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications) install(FILES ${ID}.metainfo.xml DESTINATION ${CMAKE_INSTALL_DATADIR}/metainfo) umps3-3.0.5/src/frontends/qmps/data/icons/000077500000000000000000000000001435132321600203635ustar00rootroot00000000000000umps3-3.0.5/src/frontends/qmps/data/icons/16x16/000077500000000000000000000000001435132321600211505ustar00rootroot00000000000000umps3-3.0.5/src/frontends/qmps/data/icons/16x16/org.virtualsquare.umps3.svg000066400000000000000000000135771435132321600264510ustar00rootroot00000000000000 image/svg+xml µ umps3-3.0.5/src/frontends/qmps/data/icons/22x22/000077500000000000000000000000001435132321600211425ustar00rootroot00000000000000umps3-3.0.5/src/frontends/qmps/data/icons/22x22/org.virtualsquare.umps3.svg000066400000000000000000000142331435132321600264310ustar00rootroot00000000000000 image/svg+xml µ umps3-3.0.5/src/frontends/qmps/data/icons/24x24/000077500000000000000000000000001435132321600211465ustar00rootroot00000000000000umps3-3.0.5/src/frontends/qmps/data/icons/24x24/org.virtualsquare.umps3.svg000066400000000000000000000135671435132321600264460ustar00rootroot00000000000000 image/svg+xml µ umps3-3.0.5/src/frontends/qmps/data/icons/32x32/000077500000000000000000000000001435132321600211445ustar00rootroot00000000000000umps3-3.0.5/src/frontends/qmps/data/icons/32x32/org.virtualsquare.umps3.svg000066400000000000000000000135171435132321600264370ustar00rootroot00000000000000 image/svg+xml µ umps3-3.0.5/src/frontends/qmps/data/icons/48x48/000077500000000000000000000000001435132321600211625ustar00rootroot00000000000000umps3-3.0.5/src/frontends/qmps/data/icons/48x48/org.virtualsquare.umps3.svg000066400000000000000000000135231435132321600264520ustar00rootroot00000000000000 image/svg+xml µ umps3-3.0.5/src/frontends/qmps/data/icons/64x64/000077500000000000000000000000001435132321600211565ustar00rootroot00000000000000umps3-3.0.5/src/frontends/qmps/data/icons/64x64/org.virtualsquare.umps3.svg000066400000000000000000000135231435132321600264460ustar00rootroot00000000000000 image/svg+xml µ umps3-3.0.5/src/frontends/qmps/data/icons/CMakeLists.txt000066400000000000000000000003641435132321600231260ustar00rootroot00000000000000set(ICONS_DIR ${CMAKE_INSTALL_DATADIR}/icons/hicolor) set(ICONS_SIZE 16x16 22x22 24x24 32x32 48x48 64x64) foreach(SIZE ${ICONS_SIZE}) install(FILES ${SIZE}/${ID}.svg DESTINATION ${ICONS_DIR}/${SIZE}/apps) endforeach() umps3-3.0.5/src/frontends/qmps/data/org.virtualsquare.umps3.desktop000066400000000000000000000003641435132321600254110ustar00rootroot00000000000000[Desktop Entry] Version=1.0 Type=Application Name=µMPS3 Comment=Virtual machine simulator based around the MIPS R2/3000 microprocessor Categories=Education;Emulator; Icon=org.virtualsquare.umps3 Exec=umps3 Terminal=false Keywords=umps;umps3 umps3-3.0.5/src/frontends/qmps/data/org.virtualsquare.umps3.metainfo.xml000066400000000000000000000034301435132321600263360ustar00rootroot00000000000000 org.virtualsquare.umps3 µMPS3 Virtual machine simulator based around the MIPS R2/3000 microprocessor FSFAP GPL-3.0-or-later

µMPS is an educational computer system architecture and an accompanying emulator designed from the ground up to achieve the right trade-off between simplicity and elegance on one side, and realism on the other. This makes µMPS ideally suited for use in education, such as hands-on operating systems or computer architecture university courses.

The µMPS processor implements the MIPS I instruction set, and can therefore be supported out of the box by existing MIPS compilers. The architecture details a complete set of I/O devices (terminals, disks, flash devices, printers, and network adapters) that feature a clean, consistent, programming interface.

The emulator comes with built-in debugging features and an easy to use graphical user interface. Apart from the emulator itself, several support utilities are provided that can get you quickly started in developing programs for µMPS.

org.virtualsquare.umps3.desktop https://wiki.virtualsquare.org/education/pictures/umps3-1.png https://wiki.virtualsquare.org/education/pictures/umps3-2.png https://wiki.virtualsquare.org/education/pictures/umps3-3.png
umps3-3.0.5/src/frontends/qmps/debug_session.cc000066400000000000000000000313171435132321600215040ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010, 2011 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "qmps/debug_session.h" #include #include #include #include #include #include #include "umps/error.h" #include "qmps/application.h" const unsigned int DebugSession::kIterCycles[kNumSpeedLevels] = { 5, 25, 250, 2500, 20000 }; const unsigned int DebugSession::kIterInterval[kNumSpeedLevels] = { 50, 25, 5, 1, 0 }; DebugSession::DebugSession() : status(MS_HALTED), idleSteps(0) { createActions(); updateActionSensitivity(); connect(this, SIGNAL(StatusChanged()), this, SLOT(updateActionSensitivity())); connect(Appl(), SIGNAL(MachineConfigChanged()), this, SLOT(onMachineConfigChanged())); timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(runIteration())); idleTimer = new QTimer(this); connect(idleTimer, SIGNAL(timeout()), this, SLOT(skip())); setSpeed(Appl()->settings.value("SimulationSpeed", kMaxSpeed).toInt()); stopMask = Appl()->settings.value("StopMask", kDefaultStopMask).toUInt(); } void DebugSession::halt() { if (!isStarted()) return; timer->stop(); idleTimer->stop(); machine.reset(); bplModel.reset(); Q_EMIT MachineHalted(); setStatus(MS_HALTED); } void DebugSession::setSpeed(int value) { value = qBound(0, value, kMaxSpeed); if (speed != value) { speed = value; Appl()->settings.setValue("SimulationSpeed", speed); timer->setInterval(kIterInterval[speed]); Q_EMIT SpeedChanged(speed); } } void DebugSession::setStopMask(unsigned int value) { stopMask = value; Appl()->settings.setValue("StopMask", stopMask); if (machine.get()) machine->setStopMask(stopMask); } void DebugSession::createActions() { startMachineAction = new QAction("Power On", this); startMachineAction->setShortcut(QKeySequence("F5")); startMachineAction->setIcon(QIcon(":/icons/start-22.svg")); connect(startMachineAction, SIGNAL(triggered()), this, SLOT(startMachine())); startMachineAction->setEnabled(false); haltMachineAction = new QAction("Power Off", this); haltMachineAction->setShortcut(QKeySequence("Shift+F5")); haltMachineAction->setIcon(QIcon(":/icons/halt-22.svg")); connect(haltMachineAction, SIGNAL(triggered()), this, SLOT(onHaltMachine())); haltMachineAction->setEnabled(false); toggleMachineAction = new QAction("Power On", this); toggleMachineAction->setIcon(QIcon(":/icons/start-22.svg")); connect(toggleMachineAction, SIGNAL(triggered()), this, SLOT(toggleMachine())); toggleMachineAction->setEnabled(false); resetMachineAction = new QAction("Reset", this); resetMachineAction->setShortcut(QKeySequence("F6")); resetMachineAction->setIcon(QIcon(":/icons/reset-22.svg")); connect(resetMachineAction, SIGNAL(triggered()), this, SLOT(onResetMachine())); resetMachineAction->setEnabled(false); debugContinueAction = new QAction("&Continue", this); debugContinueAction->setShortcut(QKeySequence("F9")); debugContinueAction->setIcon(QIcon(":/icons/continue-22.svg")); connect(debugContinueAction, SIGNAL(triggered()), this, SLOT(onContinue())); debugStepAction = new QAction("Step", this); debugStepAction->setShortcut(QKeySequence("F10")); debugStepAction->setIcon(QIcon(":/icons/step-22.svg")); connect(debugStepAction, SIGNAL(triggered()), this, SLOT(onStep())); debugStopAction = new QAction("&Stop", this); debugStopAction->setShortcut(QKeySequence("F12")); debugStopAction->setIcon(QIcon(":/icons/stop-22.svg")); connect(debugStopAction, SIGNAL(triggered()), this, SLOT(stop())); debugToggleAction = new QAction("Continue", this); debugToggleAction->setIcon(QIcon(":/icons/continue-22.svg")); connect(debugToggleAction, SIGNAL(triggered()), this, SLOT(toggleDebug())); } void DebugSession::updateActionSensitivity() { bool started = (status != MS_HALTED); bool stopped = (status == MS_STOPPED); bool running = (status == MS_RUNNING); startMachineAction->setEnabled(Appl()->getConfig() != NULL && !started); haltMachineAction->setEnabled(started); toggleMachineAction->setEnabled(Appl()->getConfig() != NULL); resetMachineAction->setEnabled(started); debugContinueAction->setEnabled(stopped); debugStepAction->setEnabled(stopped); debugStopAction->setEnabled(running); debugToggleAction->setEnabled(Appl()->getConfig() != NULL && started); if (startMachineAction->isEnabled() || Appl()->getConfig() == NULL) { toggleMachineAction->setToolTip("Power On"); toggleMachineAction->setIcon(QIcon(":/icons/start-22.svg")); } else { toggleMachineAction->setToolTip("Power Off"); toggleMachineAction->setIcon(QIcon(":/icons/halt-22.svg")); } if (debugStopAction->isEnabled()) { debugToggleAction->setToolTip("Stop"); debugToggleAction->setIcon(QIcon(":/icons/stop-22.svg")); } else { debugToggleAction->setToolTip("Continue"); debugToggleAction->setIcon(QIcon(":/icons/continue-22.svg")); } } void DebugSession::setStatus(MachineStatus newStatus) { if (newStatus != status) { status = newStatus; Q_EMIT StatusChanged(); } } void DebugSession::initializeMachine() { MachineConfig* config = Appl()->getConfig(); assert(config != NULL); std::list errors; if (!config->Validate(&errors)) { QString el; el += "
    "; for (const std::string& s : errors) el += QString("
  • %1
  • ").arg(s.c_str()); el += "
"; QMessageBox::critical( 0, QString("%1: Error").arg(Appl()->applicationName()), "Invalid and/or incomplete machine configuration: " + el); return; } try { machine.reset(new Machine(config, &breakpoints, &suspects, &tracepoints)); } catch (const FileError& e) { QMessageBox::critical( Appl()->getApplWindow(), QString("%1: Error").arg(Appl()->applicationName()), QString("Could not initialize machine: " "the file `%1' is nonexistent or inaccessible").arg(e.fileName.c_str())); return; } catch (const InvalidCoreFileError& e) { QMessageBox::critical( Appl()->getApplWindow(), QString("%1: Error").arg(Appl()->applicationName()), QString("Could not initialize machine: " "the file `%1' does not appear to be a valid Core file; " "make sure you are creating the file with the umps3-elf2umps utility") .arg(e.fileName.c_str())); return; } catch (const CoreFileOverflow& e) { QMessageBox::critical( Appl()->getApplWindow(), QString("%1: Error").arg(Appl()->applicationName()), "Could not initialize machine: " "the core file does not fit in memory; " "please increase available RAM and try again"); return; } catch (const InvalidFileFormatError& e) { QMessageBox::critical( Appl()->getApplWindow(), QString("%1: Error").arg(Appl()->applicationName()), QString("Could not initialize machine: " "the file `%1' has wrong format").arg(e.fileName.c_str())); return; } catch (const EthError& e) { QMessageBox::critical( Appl()->getApplWindow(), QString("%1: Error").arg(Appl()->applicationName()), QString("Could not initialize machine: " "error initializing network device %1").arg(e.devNo)); return; } machine->setStopMask(stopMask); SymbolTable* stab; try { stab = new SymbolTable(config->getSymbolTableASID(), config->getROM(ROM_TYPE_STAB).c_str()); } catch (const Error& e) { QMessageBox::critical( Appl()->getApplWindow(), QString("%1: Error").arg(Appl()->applicationName()), "Could not initialize machine: " "invalid or missing symbol table file"); machine.reset(); return; } if (symbolTable && stab->getASID() == symbolTable->getASID()) { relocateStoppoints(stab, breakpoints); relocateStoppoints(stab, suspects); relocateStoppoints(stab, tracepoints); } symbolTable.reset(stab); bplModel.reset(new StoppointListModel(&breakpoints, "Breakpoint", 'B')); stoppedByUser = true; setStatus(MS_STOPPED); cpuStatusMap.reset(new CpuStatusMap(this)); } void DebugSession::onMachineConfigChanged() { if (Appl()->getConfig() != NULL) { startMachineAction->setEnabled(true); toggleMachineAction->setEnabled(true); } breakpoints.Clear(); suspects.Clear(); tracepoints.Clear(); } void DebugSession::startMachine() { assert(status == MS_HALTED); initializeMachine(); if (machine) Q_EMIT MachineStarted(); } void DebugSession::onHaltMachine() { assert(status != MS_HALTED); halt(); } void DebugSession::toggleMachine() { if (startMachineAction->isEnabled()) startMachine(); else onHaltMachine(); } void DebugSession::onResetMachine() { assert(isStarted()); stop(); machine.reset(); initializeMachine(); if (machine) { Q_EMIT MachineReset(); } else { Q_EMIT MachineHalted(); setStatus(MS_HALTED); } } void DebugSession::onContinue() { assert(status == MS_STOPPED); stepping = false; stoppedByUser = false; Q_EMIT MachineRan(); setStatus(MS_RUNNING); timer->start(); } void DebugSession::onStep() { step(1); } void DebugSession::stop() { if (isRunning()) { stoppedByUser = true; timer->stop(); idleTimer->stop(); setStatus(MS_STOPPED); Q_EMIT MachineStopped(); } } void DebugSession::toggleDebug() { if (debugContinueAction->isEnabled()) onContinue(); else stop(); } void DebugSession::step(unsigned int steps) { assert(status == MS_STOPPED); stepping = true; stepsLeft = steps; stoppedByUser = false; Q_EMIT MachineRan(); setStatus(MS_RUNNING); // Always step through at least one cycle (might be a bit too // pedantic but oh well...) bool stopped; machine->step(&stopped); --stepsLeft; if (machine->IsHalted()) { halt(); } else if (!stepsLeft || stopped) { stoppedByUser = !stepsLeft; setStatus(MS_STOPPED); Q_EMIT MachineStopped(); } else { timer->start(); } } void DebugSession::runIteration() { if (stepping) runStepIteration(); else runContIteration(); } void DebugSession::runStepIteration() { unsigned int steps = std::min(stepsLeft, kIterCycles[speed]); bool stopped = false; unsigned int stepped; machine->step(steps, &stepped, &stopped); stepsLeft -= stepped; if (machine->IsHalted()) { halt(); } else if (stopped || stepsLeft == 0) { stoppedByUser = (stepsLeft == 0); timer->stop(); setStatus(MS_STOPPED); Q_EMIT MachineStopped(); } else { Q_EMIT DebugIterationCompleted(); } } void DebugSession::runContIteration() { idleSteps = machine->idleCycles(); if (idleSteps > 0) { // Enter low-power mode! timer->stop(); const qreal ticksPerMsec = 1000.0 * qreal(Appl()->getConfig()->getClockRate()); const int interval = qRound(qreal(std::min(idleSteps, kMaxSkipped)) / ticksPerMsec); idleTimer->start(interval); } else { bool stopped; machine->step(kIterCycles[speed], NULL, &stopped); if (machine->IsHalted()) { halt(); } else if (stopped) { setStatus(MS_STOPPED); Q_EMIT MachineStopped(); timer->stop(); } else { Q_EMIT DebugIterationCompleted(); } } } void DebugSession::skip() { assert(idleSteps > 0); const uint32_t skipped = std::min(idleSteps, kMaxSkipped); machine->skip(skipped); idleSteps -= skipped; // Keep skipping cycles while the machine is idle. if (idleSteps == 0) { idleTimer->stop(); timer->start(); } else if (idleSteps < kMaxSkipped) { const qreal ticksPerMsec = 1000.0 * qreal(Appl()->getConfig()->getClockRate()); idleTimer->setInterval(qRound(qreal(idleSteps) / ticksPerMsec)); } Q_EMIT DebugIterationCompleted(); } void DebugSession::relocateStoppoints(const SymbolTable* newTable, StoppointSet& set) { StoppointSet rset; for (Stoppoint::Ptr sp : set) { const AddressRange& origin = sp->getRange(); const Symbol* symbol = symbolTable->Probe(origin.getASID(), origin.getStart(), true); if (symbol != NULL) { std::list dest = newTable->Lookup(symbol->getName(), symbol->getType()); if (dest.size() == 1) { Word start = dest.front()->getStart() + symbol->Offset(origin.getStart()); Word end = start + (origin.getEnd() - origin.getStart()); rset.Add(AddressRange(origin.getASID(), start, end), sp->getAccessMode(), sp->getId(), sp->IsEnabled()); continue; } } rset.Add(origin, sp->getAccessMode(), sp->getId(), sp->IsEnabled()); } set = rset; } umps3-3.0.5/src/frontends/qmps/debug_session.h000066400000000000000000000101411435132321600213360ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef QMPS_DEBUG_SESSION_H #define QMPS_DEBUG_SESSION_H #include #include "base/lang.h" #include "umps/machine.h" #include "umps/symbol_table.h" #include "umps/stoppoint.h" #include "qmps/cpu_status_map.h" #include "qmps/stoppoint_list_model.h" enum MachineStatus { MS_HALTED, MS_RUNNING, MS_STOPPED }; class QAction; class Machine; class QTimer; class DebugSession: public QObject { Q_OBJECT public: static const int kNumSpeedLevels = 5; static const int kMaxSpeed = kNumSpeedLevels - 1; static const int kDefaultStopMask = (SC_BREAKPOINT | SC_SUSPECT | SC_EXCEPTION); DebugSession(); MachineStatus getStatus() const { return status; } bool isStopped() const { return status == MS_STOPPED; } bool isStoppedByUser() const { return stoppedByUser; } bool isRunning() const { return status == MS_RUNNING; } bool isStarted() const { return status != MS_HALTED; } void halt(); unsigned int getStopMask() const { return stopMask; } int getSpeed() const { return speed; } Machine* getMachine() const { return machine.get(); } SymbolTable* getSymbolTable() { return symbolTable.get(); } StoppointSet* getBreakpoints() { return &breakpoints; } StoppointListModel* getBreakpointListModel() { return bplModel.get(); } StoppointSet* getSuspects() { return &suspects; } StoppointSet* getTracepoints() { return &tracepoints; } const CpuStatusMap* getCpuStatusMap() const { return cpuStatusMap.get(); } // Global actions QAction* startMachineAction; QAction* haltMachineAction; QAction* toggleMachineAction; QAction* resetMachineAction; QAction* debugContinueAction; QAction* debugStepAction; QAction* debugStopAction; QAction* debugToggleAction; public Q_SLOTS: void setStopMask(unsigned int value); void setSpeed(int value); void stop(); Q_SIGNALS: void StatusChanged(); void MachineStarted(); void MachineStopped(); void MachineRan(); void MachineHalted(); void MachineReset(); void DebugIterationCompleted(); void SpeedChanged(int); private: static const uint32_t kMaxSkipped = 50000; void createActions(); void setStatus(MachineStatus newStatus); void initializeMachine(); void step(unsigned int steps); void runStepIteration(); void runContIteration(); void relocateStoppoints(const SymbolTable* newTable, StoppointSet& set); MachineStatus status; scoped_ptr machine; scoped_ptr symbolTable; // We need a "proxy" stop mask here since it has to live through // machine reconfigurations, resets, etc. unsigned int stopMask; int speed; static const unsigned int kIterCycles[kNumSpeedLevels]; static const unsigned int kIterInterval[kNumSpeedLevels]; StoppointSet breakpoints; scoped_ptr bplModel; StoppointSet suspects; StoppointSet tracepoints; scoped_ptr cpuStatusMap; bool stoppedByUser; bool stepping; unsigned int stepsLeft; QTimer* timer; QTimer* idleTimer; uint32_t idleSteps; private Q_SLOTS: void onMachineConfigChanged(); void startMachine(); void onHaltMachine(); void toggleMachine(); void onResetMachine(); void onContinue(); void onStep(); void toggleDebug(); void updateActionSensitivity(); void runIteration(); void skip(); }; #endif // QMPS_DEBUG_SESSION_H umps3-3.0.5/src/frontends/qmps/device_tree_model.cc000066400000000000000000000161701435132321600223110ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * Copyright (C) 2020 Mattia Biondi * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "qmps/device_tree_model.h" #include #include "umps/machine.h" #include "umps/device.h" const char* const DeviceTreeModel::headerNames[N_COLUMNS] = { "Device", "HW Failure", "Status", "Completion Time" }; const char* const DeviceTreeModel::iconMap[N_EXT_IL] = { ":/icons/disk-22.svg", ":/icons/flash-22.svg", ":/icons/network-22.svg", ":/icons/printer-22.svg", ":/icons/terminal-22.svg" }; static const char* const devTypeStr[N_EXT_IL + 1] = { "NULLDEV ", "DISK ", "FLASH ", "NETWORK ", "PRINTER ", "TERMINAL" }; DeviceTreeModel::DeviceTreeModel(Machine* m) : QAbstractItemModel(), machine(m) { for (unsigned int il = 0; il < N_EXT_IL; ++il) { for (unsigned int devNo = 0; devNo < N_DEV_PER_IL; ++devNo) { Device* dev = machine->getDevice(il, devNo); dev->SignalStatusChanged.connect( sigc::bind(sigc::mem_fun(this, &DeviceTreeModel::onDeviceStatusChanged), dev) ); dev->SignalConditionChanged.connect( sigc::bind(sigc::mem_fun(this, &DeviceTreeModel::onDeviceConditionChanged), dev) ); } } for (unsigned int i = 0; i < N_EXT_IL; ++i) deviceTypeIcons[i] = QIcon(iconMap[i]); } QModelIndex DeviceTreeModel::index(int row, int column, const QModelIndex& parent) const { // QAbstractItemModel::hasIndex() kindly checks for us if a valid // index with the requested row/col and parent could actually // exist: in other words, if parent.row() is in the range [0, // #rows(parent.parent()) - 1]. if (!hasIndex(row, column, parent)) return QModelIndex(); if (parent.isValid()) { unsigned int devNo = (unsigned int) row; if (devNo < N_DEV_PER_IL) return createIndex(row, column, machine->getDevice(parent.row(), devNo)); else return QModelIndex(); } else { if (row <= N_EXT_IL) return createIndex(row, column, (void*) NULL); else return QModelIndex(); } } QModelIndex DeviceTreeModel::parent(const QModelIndex& index) const { if (!index.isValid()) return QModelIndex(); // Presumably the index is valid. In our case that means it either // points to a top-level item (device class node) or a // second-level (device) node. The QAbstractItemModel API contract // specifies we have to return an invalid index for any top-level // items. Also, by convention only the first column "nodes" have // children, so we specify 0 for the "column" of the parent. Yuck. Device* device = static_cast(index.internalPointer()); if (device != NULL) return createIndex(device->getInterruptLine(), 0, (void*) NULL); else return QModelIndex(); } int DeviceTreeModel::rowCount(const QModelIndex& parent) const { // Remember the convention (or is it?) that only indexes with // column equal 0 identify nodes with children! if (parent.column() > 0) return 0; // Well then, column 0 it is. We're in one of 3 cases: 1) parent // is the phantom root node; 2) parent is a top level node; 3) // parent is a leaf (device) node. In code: if (!parent.isValid()) return N_EXT_IL; else if (parent.internalPointer() == NULL) return N_DEV_PER_IL; else return 0; } int DeviceTreeModel::columnCount(const QModelIndex& parent) const { UNUSED_ARG(parent); return N_COLUMNS; } QVariant DeviceTreeModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) return headerNames[section]; else return QVariant(); } QVariant DeviceTreeModel::data(const QModelIndex& index, int role) const { // Unlike in the cases above, here an invalid index does _not_ // consitute valid input, and we wouldn't be expecting one at // all. Sanity check then. if (!index.isValid()) return QVariant(); // So it's either a device-class row or a device row (leaf). Which // one is it? Device* device = static_cast(index.internalPointer()); if (device == NULL) { if (index.column() == 0) { // Device class (interrupt line) if (role == Qt::DisplayRole) { static const char* const dtName[N_EXT_IL] = { "Int. Line 3 (Disks)", "Int. Line 4 (Flash devices)", "Int. Line 5 (Ethernet)", "Int. Line 6 (Printers)", "Int. Line 7 (Terminals)" }; return dtName[index.row()]; } else if (role == Qt::DecorationRole) { return deviceTypeIcons[index.row()]; } else if (role == Qt::FontRole) { QFont f = QFont(); f.setBold(true); return f; } else { return QVariant(); } } else { return QVariant(); } } if (role == Qt::DisplayRole) { switch (index.column()) { case COLUMN_DEVICE_NUMBER: return device->getNumber(); case COLUMN_DEVICE_CONDITION: return device->getDevNotWorking(); case COLUMN_DEVICE_STATUS: return device->getDevSStr(); case COLUMN_COMPLETION_TOD: return device->getCTimeInfo().c_str(); } } // Catch-all case: something we were not interested in handling. return QVariant(); } Qt::ItemFlags DeviceTreeModel::flags(const QModelIndex& index) const { if (!index.isValid()) return Qt::NoItemFlags; // FIXME: Should the items here really be disabled? #if 0 Device* device = static_cast(index.internalPointer()); if (device && index.column() == COLUMN_DEVICE_CONDITION && device->Type() == NULLDEV) return Qt::ItemIsSelectable; else #endif return Qt::ItemIsEnabled | Qt::ItemIsSelectable; } bool DeviceTreeModel::setData(const QModelIndex& index, const QVariant& value, int role) { Device* device = static_cast(index.internalPointer()); if (device && index.column() == COLUMN_DEVICE_CONDITION && role == Qt::EditRole && value.canConvert()) { device->setCondition(!value.toBool()); return true; } return false; } void DeviceTreeModel::onDeviceStatusChanged(const char* status, Device* device) { UNUSED_ARG(status); QModelIndex idx1 = createIndex(device->getNumber(), COLUMN_DEVICE_STATUS, device); QModelIndex idx2 = createIndex(device->getNumber(), COLUMN_COMPLETION_TOD, device); Q_EMIT dataChanged(idx1, idx2); } void DeviceTreeModel::onDeviceConditionChanged(bool operational, Device* device) { UNUSED_ARG(operational); QModelIndex idx = createIndex(device->getNumber(), COLUMN_DEVICE_CONDITION, device); Q_EMIT dataChanged(idx, idx); } umps3-3.0.5/src/frontends/qmps/device_tree_model.h000066400000000000000000000041521435132321600221500ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef QMPS_DEVICE_TREE_MODEL_H #define QMPS_DEVICE_TREE_MODEL_H #include #include #include #include "umps/arch.h" class Device; class Machine; class DeviceTreeModel : public QAbstractItemModel, public sigc::trackable { Q_OBJECT public: enum { COLUMN_DEVICE_NUMBER = 0, COLUMN_DEVICE_CONDITION, COLUMN_DEVICE_STATUS, COLUMN_COMPLETION_TOD, N_COLUMNS }; DeviceTreeModel(Machine* machine); QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const; QModelIndex parent(const QModelIndex& index) const; int rowCount(const QModelIndex& parent = QModelIndex()) const; int columnCount(const QModelIndex& parent = QModelIndex()) const; QVariant headerData(int section, Qt::Orientation orientation, int role) const; QVariant data(const QModelIndex& index, int role) const; bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole); Qt::ItemFlags flags(const QModelIndex& index) const; private: void onDeviceStatusChanged(const char* status, Device* device); void onDeviceConditionChanged(bool operational, Device* device); Machine* const machine; static const char* const headerNames[N_COLUMNS]; QIcon deviceTypeIcons[N_EXT_IL]; static const char* const iconMap[N_EXT_IL]; }; #endif // QMPS_DEVICE_TREE_MODEL_H umps3-3.0.5/src/frontends/qmps/device_tree_view.cc000066400000000000000000000045061435132321600221630ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "qmps/device_tree_view.h" #include #include "qmps/application.h" #include "qmps/boolean_item_delegate.h" #include "qmps/device_tree_model.h" #include "qmps/ui_utils.h" DeviceTreeView::DeviceTreeView(QWidget* parent) : QTreeView(parent) { BooleanItemDelegate* delegate = new BooleanItemDelegate(parent); setItemDelegateForColumn(DeviceTreeModel::COLUMN_DEVICE_CONDITION, delegate); setAlternatingRowColors(true); connect(header(), SIGNAL(sectionResized(int,int,int)), this, SLOT(sectionResized(int,int,int))); } void DeviceTreeView::setModel(QAbstractItemModel* model) { QTreeView::setModel(model); if (model == NULL) return; SetFirstColumnSpanned(this, true); bool resizeCols = true; for (unsigned int i = 0; i < DeviceTreeModel::N_COLUMNS; ++i) { QVariant v = Appl()->settings.value(QString("DeviceTreeView/Section%1Size").arg(i)); if (v.canConvert() && v.toInt()) { header()->resizeSection(i, v.toInt()); resizeCols = false; } } if (resizeCols) { resizeColumnToContents(DeviceTreeModel::COLUMN_DEVICE_NUMBER); resizeColumnToContents(DeviceTreeModel::COLUMN_DEVICE_CONDITION); resizeColumnToContents(DeviceTreeModel::COLUMN_COMPLETION_TOD); } // Why oh why is this not the default for tree views? header()->setSectionsMovable(false); } void DeviceTreeView::sectionResized(int logicalIndex, int oldSize, int newSize) { UNUSED_ARG(oldSize); Appl()->settings.setValue(QString("DeviceTreeView/Section%1Size").arg(logicalIndex), newSize); } umps3-3.0.5/src/frontends/qmps/device_tree_view.h000066400000000000000000000022671435132321600220270ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef QMPS_DEVICE_TREE_VIEW_H #define QMPS_DEVICE_TREE_VIEW_H #include class QAbstractItemModel; class DeviceTreeView : public QTreeView { Q_OBJECT public: DeviceTreeView(QWidget* parent = 0); virtual void setModel(QAbstractItemModel* model); private Q_SLOTS: void sectionResized(int logicalIndex, int oldSize, int newSize); }; #endif // QMPS_DEVICE_TREE_VIEW_H umps3-3.0.5/src/frontends/qmps/error_hooks.cc000066400000000000000000000020041435132321600211760ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include "umps/error.h" #include "qmps/application.h" void Panic(const char* message) { QMessageBox::critical(0, "PANIC", QString("PANIC: %1").arg(message)); Appl()->quit(); } umps3-3.0.5/src/frontends/qmps/flat_push_button.cc000066400000000000000000000026101435132321600222250ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "qmps/flat_push_button.h" #include "base/lang.h" FlatPushButton::FlatPushButton(QWidget* parent) : QPushButton(parent) { setFlat(true); } FlatPushButton::FlatPushButton(const QString& text, QWidget* parent) : QPushButton(text, parent) { setFlat(true); } FlatPushButton::FlatPushButton(const QIcon& icon, const QString& text, QWidget* parent) : QPushButton(icon, text, parent) { setFlat(true); } void FlatPushButton::enterEvent(QEvent* event) { UNUSED_ARG(event); setFlat(false); } void FlatPushButton::leaveEvent(QEvent* event) { UNUSED_ARG(event); setFlat(true); } umps3-3.0.5/src/frontends/qmps/flat_push_button.h000066400000000000000000000024011435132321600220650ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef QUMPS_FLAT_PUSH_BUTTON_H #define QUMPS_FLAT_PUSH_BUTTON_H #include class FlatPushButton : public QPushButton { Q_OBJECT public: FlatPushButton(QWidget* parent = 0); FlatPushButton(const QString& text, QWidget* parent = 0); FlatPushButton(const QIcon& icon, const QString& text, QWidget* parent = 0); protected: virtual void enterEvent(QEvent* event); virtual void leaveEvent(QEvent* event); }; #endif // QUMPS_FLAT_PUSH_BUTTON_H umps3-3.0.5/src/frontends/qmps/hex_view.cc000066400000000000000000000226671435132321600205010ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "qmps/hex_view.h" #include #include #include #include #include #include "umps/types.h" #include "umps/arch.h" #include "umps/machine.h" #include "qmps/application.h" #include "qmps/debug_session.h" #include "qmps/hex_view_priv.h" #include "qmps/ui_utils.h" HexView::HexView(Word start, Word end, QWidget* parent) : QPlainTextEdit(parent), start(start), end(end), length(((end - start) >> 2) + 1), invalidByteRepr(QString("%1%2") .arg(QChar(kInvalidLocationChar)) .arg(QChar(kInvalidLocationChar))), margin(new HexViewMargin(this)) { QFont font = Appl()->getMonospaceFont(); setFont(font); setViewportMargins(margin->sizeHint().width(), 0, 0, 0); connect(this, SIGNAL(updateRequest(QRect,int)), this, SLOT(updateMargin(QRect,int))); setCursorWidth(fontMetrics().horizontalAdvance("o")); setLineWrapMode(NoWrap); setOverwriteMode(true); setTabChangesFocus(true); setContextMenuPolicy(Qt::NoContextMenu); setUndoRedoEnabled(false); viewport()->setCursor(Qt::ArrowCursor); Refresh(); moveCursor(QTextCursor::Start); highlightWord(); connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(onCursorPositionChanged())); } void HexView::setReversedByteOrder(bool setting) { if (revByteOrder != setting) { revByteOrder = setting; Refresh(); } } void HexView::Refresh() { int savedPosition = textCursor().position(); int hScrollValue = horizontalScrollBar()->value(); int vScrollValue = verticalScrollBar()->value(); Machine* m = debugSession->getMachine(); QString buf; buf.reserve(length * kCharsPerWord); for (Word addr = start; addr <= end; addr += WS) { unsigned int wi = (addr - start) >> 2; if (wi && !(wi % kWordsPerRow)) buf += '\n'; Word data; if (m->ReadMemory(addr, &data)) { for (unsigned int bi = 0; bi < WS; bi++) { if (bi > 0 || wi % kWordsPerRow) buf += ' '; buf += invalidByteRepr; } } else { for (unsigned int bi = 0; bi < WS; bi++) { if (bi > 0 || wi % kWordsPerRow) buf += ' '; unsigned int byteVal; if (revByteOrder) byteVal = ((unsigned char *) &data)[WS - bi - 1]; else byteVal = ((unsigned char *) &data)[bi]; buf += QString("%1").arg(byteVal, 2, 16, QLatin1Char('0')); } } } setPlainText(buf); QTextCursor cursor = textCursor(); cursor.setPosition(savedPosition); setTextCursor(cursor); horizontalScrollBar()->setValue(hScrollValue); verticalScrollBar()->setValue(vScrollValue); } void HexView::resizeEvent(QResizeEvent* event) { QPlainTextEdit::resizeEvent(event); QRect cr = contentsRect(); margin->setGeometry(QRect(cr.left(), cr.top(), margin->sizeHint().width(), cr.height())); } bool HexView::canInsertFromMimeData(const QMimeData* source) const { UNUSED_ARG(source); return false; } void HexView::insertFromMimeData(const QMimeData* source) { UNUSED_ARG(source); } void HexView::keyPressEvent(QKeyEvent* event) { switch (event->key()) { case Qt::Key_Left: if (event->modifiers() & Qt::ControlModifier) { setPoint(currentWord() - 1, currentByte(), currentNibble()); } else { if (currentNibble() == COL_LO_NIBBLE) moveCursor(QTextCursor::Left); else moveCursor(QTextCursor::Left, 1 + kHorizontalSpacing); } break; case Qt::Key_Right: if (event->modifiers() & Qt::ControlModifier) { setPoint(currentWord() + 1, currentByte(), currentNibble()); } else { if (currentNibble() == COL_HI_NIBBLE) moveCursor(QTextCursor::Right); else moveCursor(QTextCursor::Right, 1 + kHorizontalSpacing); } break; case Qt::Key_Up: case Qt::Key_Down: case Qt::Key_PageUp: case Qt::Key_PageDown: case Qt::Key_Home: case Qt::Key_End: QPlainTextEdit::keyPressEvent(event); break; default: if (event->text().isEmpty()) { event->ignore(); return; } QString digit = event->text().left(1).toLower(); bool isHex; digit.toUInt(&isHex, 16); if (!isHex) { event->ignore(); return; } unsigned int nibble = currentNibble(); QTextCursor cursor = textCursor(); int cp = cursor.position(); cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor); cursor.insertText(digit); cursor.setPosition(cp); setTextCursor(cursor); Word paddr = start + currentWord() * WS; if (debugSession->getMachine()->WriteMemory(paddr, dataAtCursor())) { QMessageBox::warning(this, "Warning", QString("Could not write location %1").arg(FormatAddress(paddr))); Refresh(); } else { moveCursor(QTextCursor::Right, 1 + kHorizontalSpacing * nibble); } break; } } void HexView::mousePressEvent(QMouseEvent* event) { if (event->button() != Qt::LeftButton) return; QTextCursor cursor = cursorForPosition(event->pos()); setPoint(currentWord(cursor), currentByte(cursor), std::min(currentNibble(cursor), (unsigned int) COL_LO_NIBBLE)); } void HexView::updateMargin(const QRect& rect, int dy) { if (dy != 0) margin->scroll(0, dy); else margin->update(0, rect.y(), margin->width(), rect.height()); } void HexView::onCursorPositionChanged() { if (currentNibble() == COL_SPACING) setPoint(currentWord()); highlightWord(); } unsigned int HexView::currentWord(const QTextCursor& cursor) const { if (cursor.isNull()) return textCursor().position() / kCharsPerWord; else return cursor.position() / kCharsPerWord; } unsigned int HexView::currentByte(const QTextCursor& cursor) const { if (cursor.isNull()) return (textCursor().position() / N_COLS_PER_BYTE) % WS; else return (cursor.position() / N_COLS_PER_BYTE) % WS; } unsigned int HexView::currentNibble(const QTextCursor& cursor) const { if (cursor.isNull()) return textCursor().position() % N_COLS_PER_BYTE; else return cursor.position() % N_COLS_PER_BYTE; } unsigned char HexView::byteValue(unsigned int word, unsigned int byte) const { return toPlainText().mid(word * kCharsPerWord + N_COLS_PER_BYTE * byte, 2).toUInt(0, 16); } Word HexView::dataAtCursor() const { Word data; for (unsigned int i = 0; i < WS; i++) { if (revByteOrder) ((unsigned char *) &data)[i] = byteValue(currentWord(), WS - i - 1); else ((unsigned char *) &data)[i] = byteValue(currentWord(), i); } return data; } void HexView::moveCursor(QTextCursor::MoveOperation operation, int n) { QTextCursor cursor = textCursor(); if (cursor.movePosition(operation, QTextCursor::MoveAnchor, n)) setTextCursor(cursor); } void HexView::setPoint(unsigned int word, unsigned int byte, unsigned int nibble) { assert(byte < WS); assert(nibble <= 1); if (word >= length) return; if (nibble > COL_LO_NIBBLE) nibble = COL_LO_NIBBLE; QTextCursor c = textCursor(); c.setPosition(word * kCharsPerWord + byte * N_COLS_PER_BYTE + nibble); setTextCursor(c); } void HexView::paintMargin(QPaintEvent* event) { QPainter painter(margin); const QRect& er = event->rect(); painter.fillRect(er, palette().window().color()); painter.setPen(palette().shadow().color()); painter.drawLine(er.topRight(), er.bottomRight()); painter.setPen(palette().windowText().color()); QTextBlock block = firstVisibleBlock(); if (!block.isValid()) return; int y0 = (int) blockBoundingGeometry(block).translated(contentOffset()).y(); int y1 = y0 + (int) blockBoundingRect(block).height(); Word addr = start + block.blockNumber() * kWordsPerRow * WS; while (y0 <= er.bottom()) { if (er.top() <= y1) { painter.drawText(HexViewMargin::kLeftPadding, y0, margin->width(), margin->fontMetrics().height(), Qt::AlignLeft, FormatAddress(addr)); } block = block.next(); if (!block.isValid()) break; y0 = y1; y1 += blockBoundingRect(block).height(); addr += WS * kWordsPerRow; } } void HexView::highlightWord() { QList extraSelections; QTextEdit::ExtraSelection selection; QTextCursor cursor = textCursor(); cursor.clearSelection(); cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, currentByte() * N_COLS_PER_BYTE + currentNibble()); cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, kCharsPerWord - 1); selection.cursor = cursor; selection.format.setBackground(palette().highlight()); selection.format.setForeground(palette().highlightedText()); extraSelections.append(selection); setExtraSelections(extraSelections); } HexViewMargin::HexViewMargin(HexView* view) : QWidget(view), hexView(view) { } QSize HexViewMargin::sizeHint() const { return QSize(kLeftPadding + fontMetrics().horizontalAdvance("0xdead.beef") + kRightPadding, 0); } void HexViewMargin::paintEvent(QPaintEvent* event) { hexView->paintMargin(event); } void HexViewMargin::wheelEvent(QWheelEvent* event) { hexView->wheelEvent(event); } umps3-3.0.5/src/frontends/qmps/hex_view.h000066400000000000000000000055511435132321600203340ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef QMPS_HEX_VIEW_H #define QMPS_HEX_VIEW_H #include #include "umps/arch.h" #include "umps/stoppoint.h" #include "qmps/memory_view_delegate.h" class HexViewMargin; class HexView : public QPlainTextEdit, public MemoryViewDelegate { Q_OBJECT Q_PROPERTY(bool reversedByteOrder READ HasReversedByteOrder WRITE setReversedByteOrder) public: enum ByteOrder { BYTE_ORDER_NATIVE, BYTE_ORDER_REVERSED }; HexView(Word start, Word end, QWidget* parent = 0); bool HasReversedByteOrder() const { return revByteOrder; } void setReversedByteOrder(bool setting); public Q_SLOTS: void Refresh(); protected: void resizeEvent(QResizeEvent* event); bool canInsertFromMimeData(const QMimeData* source) const; void insertFromMimeData(const QMimeData* source); void keyPressEvent(QKeyEvent* event); void mousePressEvent(QMouseEvent* event); private Q_SLOTS: void updateMargin(const QRect& rect, int dy); void onCursorPositionChanged(); private: enum { COL_HI_NIBBLE = 0, COL_LO_NIBBLE = 1, COL_SPACING = 2, N_COLS_PER_BYTE }; static const unsigned int kWordsPerRow = 2; static const unsigned int kCharsPerWord = WS * N_COLS_PER_BYTE; static const unsigned int kCharsPerRow = kWordsPerRow * kCharsPerWord; static const unsigned int kHorizontalSpacing = 1; static const unsigned int kInvalidLocationChar = 0x2592; unsigned int currentWord(const QTextCursor& cursor = QTextCursor()) const; unsigned int currentByte(const QTextCursor& cursor = QTextCursor()) const; unsigned int currentNibble(const QTextCursor& cursor = QTextCursor()) const; unsigned char byteValue(unsigned int word, unsigned int byte) const; Word dataAtCursor() const; void moveCursor(QTextCursor::MoveOperation operation, int n = 1); void setPoint(unsigned int word, unsigned int byte = 0, unsigned int nibble = 0); void paintMargin(QPaintEvent* event); void highlightWord(); friend class HexViewMargin; const Word start; const Word end; const Word length; bool revByteOrder; const QString invalidByteRepr; HexViewMargin* margin; }; #endif // QMPS_HEX_VIEW_H umps3-3.0.5/src/frontends/qmps/hex_view_priv.h000066400000000000000000000023661435132321600213750ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef QMPS_HEX_VIEW_PRIV_H #define QMPS_HEX_VIEW_PRIV_H #include class HexView; class HexViewMargin : public QWidget { Q_OBJECT public: static const int kLeftPadding = 3; static const int kRightPadding = 5; HexViewMargin(HexView* hexView); virtual QSize sizeHint() const; protected: void paintEvent(QPaintEvent* event); void wheelEvent(QWheelEvent* event); private: HexView* const hexView; }; #endif // QMPS_HEX_VIEW_PRIV_H umps3-3.0.5/src/frontends/qmps/icons/000077500000000000000000000000001435132321600174525ustar00rootroot00000000000000umps3-3.0.5/src/frontends/qmps/icons/breakpoint_disabled-16.svg000066400000000000000000000051111435132321600244020ustar00rootroot00000000000000 image/svg+xml umps3-3.0.5/src/frontends/qmps/icons/breakpoint_enabled-16.svg000066400000000000000000000045331435132321600242340ustar00rootroot00000000000000 image/svg+xml umps3-3.0.5/src/frontends/qmps/icons/continue-22.svg000066400000000000000000000062131435132321600222420ustar00rootroot00000000000000 image/svg+xml umps3-3.0.5/src/frontends/qmps/icons/create-22.svg000066400000000000000000000014321435132321600216570ustar00rootroot00000000000000 umps3-3.0.5/src/frontends/qmps/icons/disk-22.svg000066400000000000000000000013341435132321600213470ustar00rootroot00000000000000 umps3-3.0.5/src/frontends/qmps/icons/disk-32.svg000066400000000000000000000011731435132321600213510ustar00rootroot00000000000000 umps3-3.0.5/src/frontends/qmps/icons/down-16.svg000066400000000000000000000057341435132321600213770ustar00rootroot00000000000000 image/svg+xml umps3-3.0.5/src/frontends/qmps/icons/flash-22.svg000066400000000000000000000017541435132321600215200ustar00rootroot00000000000000 umps3-3.0.5/src/frontends/qmps/icons/flash-32.svg000066400000000000000000000021751435132321600215170ustar00rootroot00000000000000 umps3-3.0.5/src/frontends/qmps/icons/halt-22.svg000066400000000000000000000103741435132321600213510ustar00rootroot00000000000000 image/svg+xml umps3-3.0.5/src/frontends/qmps/icons/memory-22.svg000066400000000000000000000050421435132321600217250ustar00rootroot00000000000000 umps3-3.0.5/src/frontends/qmps/icons/network-22.svg000066400000000000000000000022611435132321600221060ustar00rootroot00000000000000 umps3-3.0.5/src/frontends/qmps/icons/network-32.svg000066400000000000000000000022011435132321600221010ustar00rootroot00000000000000 umps3-3.0.5/src/frontends/qmps/icons/open-22.svg000066400000000000000000000015201435132321600213530ustar00rootroot00000000000000 umps3-3.0.5/src/frontends/qmps/icons/pc-16.svg000066400000000000000000000101451435132321600210220ustar00rootroot00000000000000 image/svg+xml umps3-3.0.5/src/frontends/qmps/icons/printer-22.svg000066400000000000000000000033371435132321600221050ustar00rootroot00000000000000 umps3-3.0.5/src/frontends/qmps/icons/printer-32.svg000066400000000000000000000042271435132321600221050ustar00rootroot00000000000000 umps3-3.0.5/src/frontends/qmps/icons/processor-22.svg000066400000000000000000000150421435132321600224350ustar00rootroot00000000000000 umps3-3.0.5/src/frontends/qmps/icons/reset-22.svg000066400000000000000000000175151435132321600215470ustar00rootroot00000000000000 image/svg+xml umps3-3.0.5/src/frontends/qmps/icons/settings-22.svg000066400000000000000000000122131435132321600222530ustar00rootroot00000000000000 umps3-3.0.5/src/frontends/qmps/icons/start-22.svg000066400000000000000000000103751435132321600215570ustar00rootroot00000000000000 image/svg+xml umps3-3.0.5/src/frontends/qmps/icons/step-22.svg000066400000000000000000000135121435132321600213710ustar00rootroot00000000000000 image/svg+xml umps3-3.0.5/src/frontends/qmps/icons/stop-22.svg000066400000000000000000000114541435132321600214060ustar00rootroot00000000000000 image/svg+xml umps3-3.0.5/src/frontends/qmps/icons/system-22.svg000066400000000000000000000020441435132321600217400ustar00rootroot00000000000000 umps3-3.0.5/src/frontends/qmps/icons/terminal-22.svg000066400000000000000000000023641435132321600222340ustar00rootroot00000000000000 umps3-3.0.5/src/frontends/qmps/icons/terminal-32.svg000066400000000000000000000024461435132321600222360ustar00rootroot00000000000000 umps3-3.0.5/src/frontends/qmps/icons/umps3-48.svg000066400000000000000000000135251435132321600215010ustar00rootroot00000000000000 image/svg+xml µ umps3-3.0.5/src/frontends/qmps/icons/up-16.svg000066400000000000000000000063771435132321600210600ustar00rootroot00000000000000 image/svg+xml umps3-3.0.5/src/frontends/qmps/mac_id_edit.cc000066400000000000000000000031501435132321600210660ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2011 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "qmps/mac_id_edit.h" #include #include "base/lang.h" #include "umps/utility.h" class Validator : public QValidator { public: Validator(QObject* parent = 0) : QValidator(parent) { } virtual State validate(QString& input, int& pos) const; }; QValidator::State Validator::validate(QString& input, int& pos) const { UNUSED_ARG(pos); input.replace(' ', '0'); if (input.left(2).toUInt(0, 16) % 2) return Invalid; else return Acceptable; } MacIdEdit::MacIdEdit(QWidget* parent) : QLineEdit(parent) { setText("00:00:00:00:00:00"); setInputMask("HH:HH:HH:HH:HH:HH"); setValidator(new Validator); } uint8_t* MacIdEdit::getMacId(uint8_t* id) const { return ParseMACId(qPrintable(text()), id); } void MacIdEdit::setMacId(const uint8_t* id) { setText(MACIdToString(id).c_str()); } umps3-3.0.5/src/frontends/qmps/mac_id_edit.h000066400000000000000000000021341435132321600207310ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2011 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef QMPS_MAC_ID_EDIT_H #define QMPS_MAC_ID_EDIT_H #include #include "base/basic_types.h" class MacIdEdit : public QLineEdit { public: MacIdEdit(QWidget* parent = 0); uint8_t* getMacId(uint8_t* id) const; void setMacId(const uint8_t* id); }; #endif // QMPS_MAC_ID_EDIT_H umps3-3.0.5/src/frontends/qmps/machine_config_dialog.cc000066400000000000000000000437141435132321600231270ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010, 2011 Tomislav Jonjic * Copyright (C) 2020 Mattia Biondi * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "qmps/machine_config_dialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "umps/arch.h" #include "umps/const.h" #include "qmps/application.h" #include "qmps/address_line_edit.h" #include "qmps/mac_id_edit.h" #include "qmps/machine_config_dialog_priv.h" #include "qmps/ui_utils.h" constexpr Word MachineConfig::TLB_FLOOR_ADDRESS[]; MachineConfigDialog::MachineConfigDialog(MachineConfig* config, QWidget* parent) : QDialog(parent), config(config) { setWindowTitle("Machine Configuration"); QTabWidget* tabWidget = new QTabWidget; tabWidget->addTab(createGeneralTab(), "&General"); tabWidget->addTab(createDeviceTab(), "&Devices"); QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(this, SIGNAL(accepted()), this, SLOT(saveConfigChanges())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); QLayout* layout = new QVBoxLayout; layout->addWidget(tabWidget); layout->addWidget(buttonBox); setLayout(layout); } QWidget* MachineConfigDialog::createGeneralTab() { QWidget* tabWidget = new QWidget; QGridLayout* layout = new QGridLayout(tabWidget); layout->setContentsMargins(11, 13, 11, 11); layout->addWidget(new QLabel("Hardware"), 0, 0, 1, 3); layout->addWidget(new QLabel("Processors:"), 1, 1); cpuSpinner = new QSpinBox(); cpuSpinner->setMinimum(MachineConfig::MIN_CPUS); cpuSpinner->setMaximum(MachineConfig::MAX_CPUS); cpuSpinner->setValue(config->getNumProcessors()); layout->addWidget(cpuSpinner, 1, 3); layout->addWidget(new QLabel("Clock Rate (MHz):"), 2, 1); clockRateSpinner = new QSpinBox(); clockRateSpinner->setMinimum(MachineConfig::MIN_CLOCK_RATE); clockRateSpinner->setMaximum(MachineConfig::MAX_CLOCK_RATE); clockRateSpinner->setValue(config->getClockRate()); layout->addWidget(clockRateSpinner, 2, 3); layout->addWidget(new QLabel("TLB Size:"), 3, 1); tlbSizeList = new QComboBox; int currentIndex = 0; for (unsigned int val = MachineConfig::MIN_TLB; val <= MachineConfig::MAX_TLB; val <<= 1) { tlbSizeList->addItem(QString::number(val)); if (config->getTLBSize() == val) tlbSizeList->setCurrentIndex(currentIndex); currentIndex++; } layout->addWidget(tlbSizeList, 3, 3); layout->addWidget(new QLabel("TLB Floor Address:"), 4, 1); tlbFloorAddressList = new QComboBox; currentIndex = 0; bool ramtop = true; for (unsigned int val : MachineConfig::TLB_FLOOR_ADDRESS) { switch (val) { case MINWORDVAL: tlbFloorAddressList->addItem("RAMTOP"); break; case MAXWORDVAL: tlbFloorAddressList->addItem("VM OFF"); break; default: tlbFloorAddressList->addItem(FormatAddress(val)); } if (config->getTLBFloorAddress() == val) { tlbFloorAddressList->setCurrentIndex(currentIndex); ramtop = false; } currentIndex++; } if (ramtop) tlbFloorAddressList->setCurrentIndex(0); layout->addWidget(tlbFloorAddressList, 4, 3); layout->addWidget(new QLabel("RAM Size (Frames):"), 5, 1); ramSizeSpinner = new QSpinBox(); ramSizeSpinner->setMinimum(MachineConfig::MIN_RAM); ramSizeSpinner->setMaximum(MachineConfig::MAX_RAM); ramSizeSpinner->setValue(config->getRamSize()); layout->addWidget(ramSizeSpinner, 5, 3); QSignalMapper* fileChooserMapper = new QSignalMapper(this); connect(fileChooserMapper, SIGNAL(mapped(int)), this, SLOT(getROMFileName(int))); QPushButton* fileChooserButton; layout->addWidget(new QLabel("BIOS"), 7, 0, 1, 3); layout->addWidget(new QLabel("Bootstrap BIOS:"), 8, 1); romFileInfo[ROM_TYPE_BOOT].description = "Bootstrap BIOS"; romFileInfo[ROM_TYPE_BOOT].lineEdit = new QLineEdit; layout->addWidget(romFileInfo[ROM_TYPE_BOOT].lineEdit, 8, 3, 1, 2); romFileInfo[ROM_TYPE_BOOT].lineEdit->setText(config->getROM(ROM_TYPE_BOOT).c_str()); fileChooserButton = new QPushButton("Browse..."); connect(fileChooserButton, SIGNAL(clicked()), fileChooserMapper, SLOT(map())); fileChooserMapper->setMapping(fileChooserButton, ROM_TYPE_BOOT); layout->addWidget(fileChooserButton, 8, 5); layout->addWidget(new QLabel("Execution BIOS:"), 9, 1); romFileInfo[ROM_TYPE_BIOS].description = "Execution BIOS"; romFileInfo[ROM_TYPE_BIOS].lineEdit = new QLineEdit; layout->addWidget(romFileInfo[ROM_TYPE_BIOS].lineEdit, 9, 3, 1, 2); romFileInfo[ROM_TYPE_BIOS].lineEdit->setText(config->getROM(ROM_TYPE_BIOS).c_str()); fileChooserButton = new QPushButton("Browse..."); connect(fileChooserButton, SIGNAL(clicked()), fileChooserMapper, SLOT(map())); fileChooserMapper->setMapping(fileChooserButton, ROM_TYPE_BIOS); layout->addWidget(fileChooserButton, 9, 5); layout->addWidget(new QLabel("Boot"), 11, 0, 1, 3); coreBootCheckBox = new QCheckBox("Load core file"); coreBootCheckBox->setChecked(config->isLoadCoreEnabled()); layout->addWidget(coreBootCheckBox, 12, 1, 1, 3); layout->addWidget(new QLabel("Core file:"), 13, 1); romFileInfo[ROM_TYPE_CORE].description = "Core"; romFileInfo[ROM_TYPE_CORE].lineEdit = new QLineEdit; layout->addWidget(romFileInfo[ROM_TYPE_CORE].lineEdit, 13, 3, 1, 2); romFileInfo[ROM_TYPE_CORE].lineEdit->setText(config->getROM(ROM_TYPE_CORE).c_str()); fileChooserButton = new QPushButton("Browse..."); connect(fileChooserButton, SIGNAL(clicked()), fileChooserMapper, SLOT(map())); fileChooserMapper->setMapping(fileChooserButton, ROM_TYPE_CORE); layout->addWidget(fileChooserButton, 13, 5); layout->addWidget(new QLabel("Debugging Support"), 15, 0, 1, 3); layout->addWidget(new QLabel("Symbol Table:"), 16, 1); romFileInfo[ROM_TYPE_STAB].description = "Symbol Table"; romFileInfo[ROM_TYPE_STAB].lineEdit = new QLineEdit; layout->addWidget(romFileInfo[ROM_TYPE_STAB].lineEdit, 16, 3, 1, 2); romFileInfo[ROM_TYPE_STAB].lineEdit->setText(config->getROM(ROM_TYPE_STAB).c_str()); fileChooserButton = new QPushButton("Browse..."); connect(fileChooserButton, SIGNAL(clicked()), fileChooserMapper, SLOT(map())); fileChooserMapper->setMapping(fileChooserButton, ROM_TYPE_STAB); layout->addWidget(fileChooserButton, 16, 5); layout->addWidget(new QLabel("Symbol Table ASID:
(default = 0x40)"), 17, 1); stabAsidEdit = new AsidLineEdit; stabAsidEdit->setMaximumWidth(100); stabAsidEdit->setAsid(config->getSymbolTableASID()); layout->addWidget(stabAsidEdit, 17, 3); layout->setColumnMinimumWidth(0, 10); layout->setColumnMinimumWidth(2, 10); layout->setColumnMinimumWidth(3, 100); layout->setColumnMinimumWidth(5, 75); layout->setRowMinimumHeight(5, 11); layout->setRowMinimumHeight(9, 11); layout->setRowMinimumHeight(13, 11); layout->setRowStretch(17, 1); layout->setColumnStretch(4, 1); return tabWidget; } QWidget* MachineConfigDialog::createDeviceTab() { static const int TAB_MARGIN_TOP = 3; static const int TAB_MARGIN_BOTTOM = 3; static const int TAB_MARGIN_LEFT = 3; static const int TAB_MARGIN_RIGHT = 3; QWidget* tab = new QWidget; QHBoxLayout* tabLayout = new QHBoxLayout; tab->setLayout(tabLayout); tabLayout->setContentsMargins(TAB_MARGIN_TOP, TAB_MARGIN_BOTTOM, TAB_MARGIN_LEFT, TAB_MARGIN_RIGHT); devClassView = new QListWidget; devClassView->setIconSize(QSize(32, 32)); devClassView->setSelectionMode(QAbstractItemView::SingleSelection); devClassView->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); devClassView->setMaximumWidth(210); tabLayout->addWidget(devClassView); devFileChooserStack = new QStackedLayout; tabLayout->addLayout(devFileChooserStack); connect(devClassView, SIGNAL(itemSelectionChanged()), this, SLOT(onDeviceClassChanged())); registerDeviceClass("Disks\n Interrupt Line 3", ":/icons/disk-32.svg", EXT_IL_INDEX(IL_DISK), "Disks", "Disk", true); registerDeviceClass("Flash Devices\n Interrupt Line 4", ":/icons/flash-32.svg", EXT_IL_INDEX(IL_FLASH), "Flash Devices", "Flash"); registerDeviceClass("Network\n Interrupt Line 5", ":/icons/network-32.svg", EXT_IL_INDEX(IL_ETHERNET), "Network Interfaces", "Net"); registerDeviceClass("Printers\n Interrupt Line 6", ":/icons/printer-32.svg", EXT_IL_INDEX(IL_PRINTER), "Printers", "Printer"); registerDeviceClass("Terminals\n Interrupt Line 7", ":/icons/terminal-32.svg", EXT_IL_INDEX(IL_TERMINAL), "Terminals", "Terminal"); return tab; } void MachineConfigDialog::registerDeviceClass(const QString& label, const QString& icon, unsigned int devClassIndex, const QString& devClassName, const QString& devName, bool selected) { QWidget* devfc; if (devClassIndex == EXT_IL_INDEX(IL_ETHERNET)) devfc = new NetworkConfigWidget(); else devfc = new DeviceFileChooser(devClassName, devName, devClassIndex); connect(this, SIGNAL(accepted()), devfc, SLOT(Save())); devFileChooserStack->addWidget(devfc); QListWidgetItem* item = new QListWidgetItem(QIcon(icon), label); item->setData(Qt::UserRole, QVariant(devClassIndex)); devClassView->addItem(item); item->setSelected(selected); } void MachineConfigDialog::getROMFileName(int index) { QString title = QString("Select a %1 File").arg(romFileInfo[index].description); QString fileName = QFileDialog::getOpenFileName(this, title); if (!fileName.isEmpty()) romFileInfo[index].lineEdit->setText(fileName); } void MachineConfigDialog::onDeviceClassChanged() { QList selected = devClassView->selectedItems(); assert(selected.size() == 1); devFileChooserStack->setCurrentIndex(selected[0]->data(Qt::UserRole).toInt()); } void MachineConfigDialog::saveConfigChanges() { config->setNumProcessors(cpuSpinner->value()); config->setClockRate(clockRateSpinner->value()); config->setTLBSize(MachineConfig::MIN_TLB << tlbSizeList->currentIndex()); config->setRamSize(ramSizeSpinner->value()); config->setTLBFloorAddress(MachineConfig::TLB_FLOOR_ADDRESS[tlbFloorAddressList->currentIndex()]); config->setROM(ROM_TYPE_BOOT, QFile::encodeName(romFileInfo[ROM_TYPE_BOOT].lineEdit->text()).constData()); config->setROM(ROM_TYPE_BIOS, QFile::encodeName(romFileInfo[ROM_TYPE_BIOS].lineEdit->text()).constData()); config->setROM(ROM_TYPE_CORE, QFile::encodeName(romFileInfo[ROM_TYPE_CORE].lineEdit->text()).constData()); config->setROM(ROM_TYPE_STAB, QFile::encodeName(romFileInfo[ROM_TYPE_STAB].lineEdit->text()).constData()); config->setLoadCoreEnabled(coreBootCheckBox->isChecked()); config->setSymbolTableASID(stabAsidEdit->getAsid()); } DeviceFileChooser::DeviceFileChooser(const QString& deviceClassName, const QString& deviceName, unsigned int line, QWidget* parent) : QWidget(parent), il(line), deviceName(deviceName) { QSignalMapper* signalMapper = new QSignalMapper(this); QGridLayout* grid = new QGridLayout(this); QLabel* header = new QLabel(deviceClassName); QFont font; font.setPointSizeF(font.pointSizeF() * 1.5); header->setFont(font); grid->addWidget(header, 0, 0, 1, 2); grid->addWidget(new QLabel("Device File"), 1, 1); grid->addWidget(new QLabel("Enable"), 1, 3); const MachineConfig* config = Appl()->getConfig(); for (unsigned int i = 0; i < N_DEV_PER_IL; i++) { QLabel* fileLabel = new QLabel(QString("&%1:").arg(i)); fileNameEdit[i] = new QLineEdit; fileLabel->setBuddy(fileNameEdit[i]); fileNameEdit[i]->setText(config->getDeviceFile(il, i).c_str()); QPushButton* bt = new QPushButton("Browse..."); connect(bt, SIGNAL(clicked()), signalMapper, SLOT(map())); signalMapper->setMapping(bt, (int) i); enabledCB[i] = new QCheckBox; enabledCB[i]->setChecked(config->getDeviceEnabled(il, i)); grid->addWidget(fileLabel, i + 2, 0); grid->addWidget(fileNameEdit[i], i + 2, 1); grid->addWidget(bt, i + 2, 2); grid->addWidget(enabledCB[i], i + 2, 3, Qt::AlignCenter); } grid->setColumnMinimumWidth(1, 190); grid->setRowStretch(11, 1); connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(browseDeviceFile(int))); } QString DeviceFileChooser::getDeviceFile(unsigned int devNo) { assert(devNo < N_DEV_PER_IL); return fileNameEdit[devNo]->text(); } bool DeviceFileChooser::IsDeviceEnabled(unsigned int devNo) { assert(devNo < N_DEV_PER_IL); return enabledCB[devNo]->isChecked(); } void DeviceFileChooser::Save() { MachineConfig* config = Appl()->getConfig(); for (unsigned int devNo = 0; devNo < N_DEV_PER_IL; devNo++) { config->setDeviceFile(il, devNo, QFile::encodeName(fileNameEdit[devNo]->text()).constData()); config->setDeviceEnabled(il, devNo, enabledCB[devNo]->isChecked()); } } void DeviceFileChooser::browseDeviceFile(int devNo) { QString title = QString("Select %1 device file").arg(deviceName); QString fileName = QFileDialog::getOpenFileName(this, title); if (!fileName.isNull()) { fileNameEdit[devNo]->setText(fileName); } } NetworkConfigWidget::NetworkConfigWidget(QWidget* parent) : QWidget(parent) { const unsigned int il = EXT_IL_INDEX(IL_ETHERNET); QVBoxLayout* layout = new QVBoxLayout(this); QLabel* header = new QLabel("Network Adapters"); QFont font; font.setPointSizeF(font.pointSizeF() * 1.5); header->setFont(font); layout->addWidget(header); QFrame* separator = new QFrame; separator->setFrameShape(QFrame::HLine); separator->setFrameShadow(QFrame::Sunken); layout->addWidget(separator); QComboBox* nics = new QComboBox; for (unsigned int i = 0; i < N_DEV_PER_IL; i++) nics->addItem(QString("Network Adapter %1").arg(i)); layout->addWidget(nics); QStackedLayout* nicConfigStack = new QStackedLayout; layout->addLayout(nicConfigStack); connect(nics, SIGNAL(currentIndexChanged(int)), nicConfigStack, SLOT(setCurrentIndex(int))); const MachineConfig* config = Appl()->getConfig(); QSignalMapper* signalMapper = new QSignalMapper(this); connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(browseDeviceFile(int))); for (unsigned int i = 0; i < N_DEV_PER_IL; i++) { QWidget* widget = new QWidget; nicConfigStack->addWidget(widget); QVBoxLayout* box = new QVBoxLayout(widget); box->setContentsMargins(0, 6, 0, 0); enabledCB[i] = new QCheckBox("&Enable"); box->addWidget(enabledCB[i]); QWidget* form = new QWidget; QGridLayout* grid = new QGridLayout(form); box->addWidget(form); grid->setContentsMargins(0, 0, 0, 0); connect(enabledCB[i], SIGNAL(toggled(bool)), form, SLOT(setEnabled(bool))); enabledCB[i]->setChecked(config->getDeviceEnabled(il, i)); form->setEnabled(config->getDeviceEnabled(il, i)); QLabel* fileLabel = new QLabel("Device &File:"); fileEdit[i] = new QLineEdit; fileLabel->setBuddy(fileEdit[i]); fileEdit[i]->setText(config->getDeviceFile(il, i).c_str()); grid->addWidget(fileLabel, 1, 0); grid->addWidget(fileEdit[i], 1, 1); QPushButton* fileBt = new QPushButton("&Browse..."); grid->addWidget(fileBt, 1, 2); connect(fileBt, SIGNAL(clicked()), signalMapper, SLOT(map())); signalMapper->setMapping(fileBt, (int) i); fixedMacId[i] = new QCheckBox("Fixed &MAC address"); grid->addWidget(fixedMacId[i], 2, 0, 1, 3); QLabel* macIdLabel = new QLabel("MAC &Address"); grid->addWidget(macIdLabel, 3, 0); macIdEdit[i] = new MacIdEdit; macIdEdit[i]->setFont(Appl()->getMonospaceFont()); macIdLabel->setBuddy(macIdEdit[i]); if (config->getMACId(i)) macIdEdit[i]->setMacId(config->getMACId(i)); grid->addWidget(macIdEdit[i], 3, 1, 1, 2); connect(fixedMacId[i], SIGNAL(toggled(bool)), macIdLabel, SLOT(setEnabled(bool))); connect(fixedMacId[i], SIGNAL(toggled(bool)), macIdEdit[i], SLOT(setEnabled(bool))); fixedMacId[i]->setChecked(config->getMACId(i) != NULL); macIdLabel->setEnabled(config->getMACId(i) != NULL); macIdEdit[i]->setEnabled(config->getMACId(i) != NULL); } layout->addStretch(1); } void NetworkConfigWidget::Save() { MachineConfig* config = Appl()->getConfig(); const unsigned int il = EXT_IL_INDEX(IL_ETHERNET); for (unsigned int devNo = 0; devNo < N_DEV_PER_IL; devNo++) { config->setDeviceFile(il, devNo, QFile::encodeName(fileEdit[devNo]->text()).constData()); config->setDeviceEnabled(il, devNo, enabledCB[devNo]->isChecked()); if (fixedMacId[devNo]->isChecked()) { uint8_t macId[6]; assert(macIdEdit[devNo]->getMacId(macId)); config->setMACId(devNo, macId); } else { config->setMACId(devNo, NULL); } } } void NetworkConfigWidget::browseDeviceFile(int devNo) { QString fileName = QFileDialog::getOpenFileName(this, "Select Device File"); if (!fileName.isNull()) fileEdit[devNo]->setText(fileName); } umps3-3.0.5/src/frontends/qmps/machine_config_dialog.h000066400000000000000000000041331435132321600227610ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * Copyright (C) 2020 Mattia Biondi * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef QMPS_MACHINE_CONFIG_DIALOG_H #define QMPS_MACHINE_CONFIG_DIALOG_H #include #include "umps/machine_config.h" class QListWidget; class QStackedLayout; class QSpinBox; class QComboBox; class QLineEdit; class QCheckBox; class AsidLineEdit; class MachineConfigDialog : public QDialog { Q_OBJECT public: MachineConfigDialog(MachineConfig* config, QWidget* parent = 0); private: QWidget* createGeneralTab(); QWidget* createDeviceTab(); void registerDeviceClass(const QString& label, const QString& icon, unsigned int devClassIndex, const QString& deviceClassName, const QString& deviceName, bool selected = false); MachineConfig* const config; QSpinBox* cpuSpinner; QSpinBox* clockRateSpinner; QComboBox* tlbSizeList; QComboBox* tlbFloorAddressList; QSpinBox* ramSizeSpinner; QCheckBox* coreBootCheckBox; AsidLineEdit* stabAsidEdit; struct { const char* description; QLineEdit* lineEdit; } romFileInfo[N_ROM_TYPES]; QListWidget* devClassView; QStackedLayout* devFileChooserStack; private Q_SLOTS: void getROMFileName(int index); void onDeviceClassChanged(); void saveConfigChanges(); }; #endif // QMPS_MACHINE_CONFIG_DIALOG_H umps3-3.0.5/src/frontends/qmps/machine_config_dialog_priv.h000066400000000000000000000036161435132321600240260ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010, 2011 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef QMPS_MACHINE_CONFIG_DIALOG_PRIV_H #define QMPS_MACHINE_CONFIG_DIALOG_PRIV_H #include #include "umps/arch.h" class QLineEdit; class QCheckBox; class MacIdEdit; class DeviceFileChooser : public QWidget { Q_OBJECT public: DeviceFileChooser(const QString& deviceClassName, const QString& deviceName, unsigned int line, QWidget* parent = 0); QString getDeviceFile(unsigned int devNo); bool IsDeviceEnabled(unsigned int devNo); public Q_SLOTS: void Save(); private: unsigned int il; QString deviceName; QLineEdit* fileNameEdit[N_DEV_PER_IL]; QCheckBox* enabledCB[N_DEV_PER_IL]; private Q_SLOTS: void browseDeviceFile(int devNo); }; class NetworkConfigWidget : public QWidget { Q_OBJECT public: NetworkConfigWidget(QWidget* parent = 0); public Q_SLOTS: void Save(); private: QCheckBox* enabledCB[N_DEV_PER_IL]; QLineEdit* fileEdit[N_DEV_PER_IL]; QCheckBox* fixedMacId[N_DEV_PER_IL]; MacIdEdit* macIdEdit[N_DEV_PER_IL]; private Q_SLOTS: void browseDeviceFile(int devNo); }; #endif // QMPS_MACHINE_CONFIG_DIALOG_PRIV_H umps3-3.0.5/src/frontends/qmps/machine_config_view.cc000066400000000000000000000123231435132321600226320ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * Copyright (C) 2020 Mattia Biondi * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "qmps/machine_config_view.h" #include "qmps/ui_utils.h" #include #include #include "umps/const.h" #include "umps/machine_config.h" #include "qmps/application.h" MachineConfigView::MachineConfigView(QWidget* parent) : QWidget(parent) { QGridLayout* layout = new QGridLayout(); const int headerColumnSpan = 2; const int propertyValueColumn = 3; int rows = 0; layout->addWidget(new QLabel("Hardware"), rows++, 0, 1, headerColumnSpan); layout->addWidget(new QLabel("Processors:"), rows, 1); numCpusLabel = new QLabel; layout->addWidget(numCpusLabel, rows++, propertyValueColumn); layout->addWidget(new QLabel("Clock rate:"), rows, 1); clockRateLabel = new QLabel; layout->addWidget(clockRateLabel, rows++, propertyValueColumn); layout->addWidget(new QLabel("TLB size:"), rows, 1); tlbSizeLabel = new QLabel; layout->addWidget(tlbSizeLabel, rows++, propertyValueColumn); layout->addWidget(new QLabel("RAM size:"), rows, 1); ramSizeLabel = new QLabel; layout->addWidget(ramSizeLabel, rows++, propertyValueColumn); layout->addWidget(new QLabel("RAMTOP value:"), rows, 1); ramtopLabel = new QLabel; layout->addWidget(ramtopLabel, rows++, propertyValueColumn); layout->addWidget(new QLabel("TLB Floor Address:"), rows, 1); tlbFloorAddressLabel = new QLabel; layout->addWidget(tlbFloorAddressLabel, rows++, propertyValueColumn); layout->addWidget(new QLabel("Byte order:"), rows, 1); QLabel* byteOrderLabel = new QLabel(BIGENDIANCPU ? "Big-endian" : "Little-endian"); layout->addWidget(byteOrderLabel, rows++, propertyValueColumn); layout->setRowMinimumHeight(rows++, 10); layout->addWidget(new QLabel("BIOS"), rows++, 0, 1, headerColumnSpan); layout->addWidget(new QLabel("Bootstrap BIOS:"), rows, 1); bootstrapROMLabel = new QLabel; layout->addWidget(bootstrapROMLabel, rows++, propertyValueColumn); layout->addWidget(new QLabel("Execution BIOS:"), rows, 1); executionROMLabel = new QLabel; layout->addWidget(executionROMLabel, rows++, propertyValueColumn); layout->setRowMinimumHeight(rows++, 10); layout->addWidget(new QLabel("Kernel Boot"), rows++, 0, 1, headerColumnSpan); layout->addWidget(new QLabel("Load core file:"), rows, 1); loadCoreLabel = new QLabel; layout->addWidget(loadCoreLabel, rows++, propertyValueColumn); layout->addWidget(new QLabel("Core file:"), rows, 1); coreFileLabel = new QLabel; layout->addWidget(coreFileLabel, rows++, propertyValueColumn); layout->setRowMinimumHeight(rows++, 10); layout->addWidget(new QLabel("Debugging"), rows++, 0, 1, headerColumnSpan); layout->addWidget(new QLabel("Symbol table:"), rows, 1); stabLabel = new QLabel; layout->addWidget(stabLabel, rows++, propertyValueColumn); layout->addWidget(new QLabel("ASID:"), rows, 1); stabAsidLabel = new QLabel; layout->addWidget(stabAsidLabel, rows++, propertyValueColumn); layout->setColumnMinimumWidth(0, 10); layout->setColumnMinimumWidth(3, 70); layout->setColumnMinimumWidth(2, 35); layout->setColumnStretch(4, 1); layout->setRowStretch(rows, 1); setLayout(layout); Update(); } void MachineConfigView::Update() { const MachineConfig* config = Appl()->getConfig(); Word tlbFloorAddr = config->getTLBFloorAddress(); Word ramtop = RAMBASE + (config->getRamSize() * FRAMESIZE * FRAMEKB); numCpusLabel->setNum((int) config->getNumProcessors()); clockRateLabel->setText(QString("%1 MHz").arg(config->getClockRate())); tlbSizeLabel->setNum((int) config->getTLBSize()); ramSizeLabel->setText(QString("%1 Frames").arg(config->getRamSize())); ramtopLabel->setText(FormatAddress(ramtop)); if (tlbFloorAddr == ramtop) tlbFloorAddressLabel->setText("RAMTOP"); else if (tlbFloorAddr == MAXWORDVAL) tlbFloorAddressLabel->setText("VM OFF"); else tlbFloorAddressLabel->setText(FormatAddress(tlbFloorAddr)); bootstrapROMLabel->setText(config->getROM(ROM_TYPE_BOOT).c_str()); executionROMLabel->setText(config->getROM(ROM_TYPE_BIOS).c_str()); loadCoreLabel->setText(config->isLoadCoreEnabled() ? "Yes" : "No"); coreFileLabel->setText(checkedFileName(config->getROM(ROM_TYPE_CORE).c_str())); coreFileLabel->setEnabled(config->isLoadCoreEnabled()); stabLabel->setText(config->getROM(ROM_TYPE_STAB).c_str()); stabAsidLabel->setText(QString("0x%1").arg(config->getSymbolTableASID(), 2, 16, QChar('0'))); } QString MachineConfigView::checkedFileName(const QString& fileName) { return fileName.isEmpty() ? "-" : fileName; } umps3-3.0.5/src/frontends/qmps/machine_config_view.h000066400000000000000000000027271435132321600225030ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * Copyright (C) 2020 Mattia Biondi * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef QMPS_MACHINE_CONFIG_VIEW_H #define QMPS_MACHINE_CONFIG_VIEW_H #include class QLabel; class MachineConfigView : public QWidget { Q_OBJECT public: MachineConfigView(QWidget* parent = 0); public Q_SLOTS: void Update(); private: static QString checkedFileName(const QString& fileName); QLabel* numCpusLabel; QLabel* clockRateLabel; QLabel* tlbSizeLabel; QLabel* ramSizeLabel; QLabel* ramtopLabel; QLabel* tlbFloorAddressLabel; QLabel* bootstrapROMLabel; QLabel* executionROMLabel; QLabel* loadCoreLabel; QLabel* coreFileLabel; QLabel* stabLabel; QLabel* stabAsidLabel; }; #endif // QMPS_MACHINE_CONFIG_VIEW_H umps3-3.0.5/src/frontends/qmps/main.cc000066400000000000000000000016771435132321600176050ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "qmps/application.h" int main(int argc, char** argv) { Application application(argc, argv); return application.exec(); } umps3-3.0.5/src/frontends/qmps/memory_view_delegate.h000066400000000000000000000017531435132321600227120ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef QMPS_MEMORY_VIEW_DELEGATE_H #define QMPS_MEMORY_VIEW_DELEGATE_H class MemoryViewDelegate { public: virtual void Refresh() = 0; }; #endif // QMPS_MEMORY_VIEW_DELEGATE_H umps3-3.0.5/src/frontends/qmps/monitor_window.cc000066400000000000000000000735001435132321600217310ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * Copyright (C) 2020 Mattia Biondi * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "qmps/monitor_window.h" #include #include #include #include #include "umps/machine.h" #include "umps/device.h" #include "umps/systembus.h" #include "umps/error.h" #include "qmps/application.h" #include "qmps/debug_session.h" #include "qmps/stop_mask_view.h" #include "qmps/machine_config_dialog.h" #include "qmps/machine_config_view.h" #include "qmps/processor_list_model.h" #include "qmps/stoppoint_list_model.h" #include "qmps/suspect_type_delegate.h" #include "qmps/device_tree_view.h" #include "qmps/add_breakpoint_dialog.h" #include "qmps/add_suspect_dialog.h" #include "qmps/add_tracepoint_dialog.h" #include "qmps/flat_push_button.h" #include "qmps/create_machine_dialog.h" #include "qmps/trace_browser.h" #include "qmps/processor_window.h" #include "qmps/terminal_window.h" #include "qmps/monitor_window_priv.h" #include "qmps/tree_view.h" using boost::assign::list_of; static const int kDefaultWidth = 640; static const int kDefaultHeight = 480; static const unsigned int kCodePtMicro = 0x00b5U; const char* const MonitorWindow::simSpeedMnemonics[DebugSession::kNumSpeedLevels] = { "Slowest", "Slow", "Medium", "Fast", "Fastest" }; MonitorWindow::MonitorWindow() : QMainWindow(), dbgSession(Appl()->getDebugSession()) { setWindowTitle("uMPS"); QVariant savedGeometry = Appl()->settings.value("MonitorWindow/geometry"); if (savedGeometry.isValid()) restoreGeometry(savedGeometry.toByteArray()); else resize(kDefaultWidth, kDefaultHeight); connect(Appl(), SIGNAL(MachineConfigChanged()), this, SLOT(onMachineConfigChanged())); connect(dbgSession, SIGNAL(MachineStarted()), this, SLOT(onMachineStarted())); connect(dbgSession, SIGNAL(MachineReset()), this, SLOT(onMachineStarted())); connect(dbgSession, SIGNAL(MachineHalted()), this, SLOT(onMachineHalted())); connect(dbgSession, SIGNAL(StatusChanged()), this, SLOT(updateStoppointActionsSensitivity())); createActions(); updateRecentConfigList(); toolBar = addToolBar("Toolbar"); createMenu(); initializeToolBar(); createTabs(); statusBar()->addPermanentWidget(new StatusDisplay); QWidget* centralWidget = new QWidget; QVBoxLayout* centralLayout = new QVBoxLayout(centralWidget); centralLayout->setContentsMargins(0, 0, 0, 0); centralLayout->addWidget(tabWidget); StopMaskView* stopMaskView = new StopMaskView(stopMaskActions); centralLayout->addWidget(stopMaskView); stopMaskView->setVisible(viewStopMaskAction->isChecked()); connect(viewStopMaskAction, SIGNAL(toggled(bool)), stopMaskView, SLOT(setVisible(bool))); setCentralWidget(centralWidget); configView = NULL; } void MonitorWindow::closeEvent(QCloseEvent* event) { for (unsigned int i = 0; i < MachineConfig::MAX_CPUS; i++) if (cpuWindows[i]) cpuWindows[i]->close(); for (unsigned int i = 0; i < N_DEV_PER_IL; i++) if (terminalWindows[i]) terminalWindows[i]->close(); Appl()->settings.setValue("MonitorWindow/geometry", saveGeometry()); Appl()->settings.setValue("MonitorWindow/ShowStopMask", viewStopMaskAction->isChecked()); event->accept(); } void MonitorWindow::createActions() { newConfigAction = new QAction("&New configuration", this); newConfigAction->setIcon(QIcon(":/icons/create-22.svg")); newConfigAction->setShortcut(QKeySequence("Ctrl+N")); newConfigAction->setStatusTip("Create a new machine configuration"); connect(newConfigAction, SIGNAL(triggered()), this, SLOT(onCreateConfig())); loadConfigAction = new QAction("&Open configuration...", this); loadConfigAction->setShortcut(QKeySequence("Ctrl+O")); loadConfigAction->setIcon(QIcon(":/icons/open-22.svg")); loadConfigAction->setStatusTip("Open a machine configuration"); connect(loadConfigAction, SIGNAL(triggered()), this, SLOT(onLoadConfig())); for (unsigned int i = 0; i < Application::kMaxRecentConfigs; i++) { loadRecentConfigActions[i] = new QAction(this); loadRecentConfigActions[i]->setVisible(false); connect(loadRecentConfigActions[i], SIGNAL(triggered()), this, SLOT(onLoadRecentConfig())); } clearRecentConfigsAction = new QAction("Clear recent", this); clearRecentConfigsAction->setStatusTip("Clear recent configurations"); connect(clearRecentConfigsAction, SIGNAL(triggered()), this, SLOT(clearConfigs())); clearRecentConfigsAction->setVisible(false); quitAction = new QAction("&Quit", this); quitAction->setShortcut(QKeySequence("Ctrl+Q")); quitAction->setStatusTip("Quit uMPS"); connect(quitAction, SIGNAL(triggered()), this, SLOT(close())); viewStopMaskAction = new QAction("Stop Mask", this); viewStopMaskAction->setCheckable(true); viewStopMaskAction->setChecked(Appl()->settings.value("MonitorWindow/ShowStopMask", true).toBool()); editConfigAction = new QAction("&Edit configuration...", this); editConfigAction->setIcon(QIcon(":/icons/settings-22.svg")); editConfigAction->setStatusTip("Edit current machine configuration"); connect(editConfigAction, SIGNAL(triggered()), this, SLOT(editConfig())); editConfigAction->setEnabled(Appl()->getConfig() != NULL); addBreakpointAction = new QAction("Add Breakpoint...", this); connect(addBreakpointAction, SIGNAL(triggered()), this, SLOT(onAddBreakpoint())); addBreakpointAction->setShortcut(QKeySequence("Ctrl+B")); addBreakpointAction->setEnabled(false); removeBreakpointAction = new QAction("Remove Breakpoint", this); connect(removeBreakpointAction, SIGNAL(triggered()), this, SLOT(onRemoveBreakpoint())); removeBreakpointAction->setEnabled(false); addSuspectAction = new QAction("Add Suspect...", this); connect(addSuspectAction, SIGNAL(triggered()), this, SLOT(onAddSuspect())); addSuspectAction->setEnabled(false); removeSuspectAction = new QAction("Remove Suspect", this); connect(removeSuspectAction, SIGNAL(triggered()), this, SLOT(onRemoveSuspect())); removeSuspectAction->setEnabled(false); addTraceAction = new QAction("Add Traced Region...", this); connect(addTraceAction, SIGNAL(triggered()), this, SLOT(onAddTracepoint())); addTraceAction->setEnabled(false); removeTraceAction = new QAction("Remove Traced Region", this); removeTraceAction->setEnabled(false); speedActionGroup = new QActionGroup(this); for (int i = 0; i < DebugSession::kNumSpeedLevels; i++) { simSpeedActions[i] = new QAction(simSpeedMnemonics[i], speedActionGroup); simSpeedActions[i]->setCheckable(true); simSpeedActions[i]->setData(QVariant(i)); connect(simSpeedActions[i], SIGNAL(triggered()), this, SLOT(onSpeedActionChecked())); } simSpeedActions[dbgSession->getSpeed()]->setChecked(true); connect(dbgSession, SIGNAL(SpeedChanged(int)), this, SLOT(onSpeedChanged(int))); increaseSpeedAction = new QAction("Increase Speed", this); connect(increaseSpeedAction, SIGNAL(triggered()), this, SLOT(increaseSpeed())); increaseSpeedAction->setShortcut(QKeySequence("Ctrl++")); increaseSpeedAction->setEnabled(dbgSession->getSpeed() != DebugSession::kMaxSpeed); decreaseSpeedAction = new QAction("Decrease Speed", this); connect(decreaseSpeedAction, SIGNAL(triggered()), this, SLOT(decreaseSpeed())); decreaseSpeedAction->setShortcut(QKeySequence("Ctrl+-")); decreaseSpeedAction->setEnabled(dbgSession->getSpeed() != 0); QSignalMapper* showCpuWindowMapper = new QSignalMapper(this); connect(showCpuWindowMapper, SIGNAL(mapped(int)), this, SLOT(showCpuWindow(int))); for (unsigned int i = 0; i < MachineConfig::MAX_CPUS; ++i) { showCpuWindowActions[i] = new QAction(QString("Processor %1").arg(i), this); if (i < 10) showCpuWindowActions[i]->setShortcut(QKeySequence(QString("Alt+Shift+%1").arg(i))); connect(showCpuWindowActions[i], SIGNAL(triggered()), showCpuWindowMapper, SLOT(map())); showCpuWindowMapper->setMapping(showCpuWindowActions[i], i); showCpuWindowActions[i]->setEnabled(false); } for (unsigned int i = 0; i < N_DEV_PER_IL; ++i) { showTerminalActions[i] = new QAction(QString("Terminal %1").arg(i), this); showTerminalActions[i]->setShortcut(QKeySequence(QString("Alt+%1").arg(i))); showTerminalActions[i]->setData(QVariant(i)); connect(showTerminalActions[i], SIGNAL(triggered()), this, SLOT(showTerminal())); showTerminalActions[i]->setEnabled(false); } aboutAction = new QAction("&About", this); connect(aboutAction, SIGNAL(triggered()), this, SLOT(showAboutInfo())); addStopMaskAction("Breakpoints", SC_BREAKPOINT); addStopMaskAction("Suspects", SC_SUSPECT); addStopMaskAction("Exceptions", SC_EXCEPTION); addStopMaskAction("Kernel UTLB", SC_UTLB_KERNEL); addStopMaskAction("User UTLB", SC_UTLB_USER); } void MonitorWindow::addStopMaskAction(const char* text, StopCause sc) { QAction* a = new QAction(text, this); a->setCheckable(true); a->setData(QVariant(sc)); a->setChecked(dbgSession->getStopMask() & sc); connect(a, SIGNAL(toggled(bool)), this, SLOT(onStopMaskChanged())); stopMaskActions[sc] = a; } void MonitorWindow::createMenu() { QMenu* simulatorMenu = menuBar()->addMenu("&Simulator"); simulatorMenu->addAction(newConfigAction); simulatorMenu->addAction(loadConfigAction); simulatorMenu->addAction(editConfigAction); simulatorMenu->addSeparator(); for (unsigned int i = 0; i < Application::kMaxRecentConfigs; i++) simulatorMenu->addAction(loadRecentConfigActions[i]); simulatorMenu->addAction(clearRecentConfigsAction); simulatorMenu->addSeparator(); simulatorMenu->addAction(quitAction); QMenu* viewMenu = menuBar()->addMenu("&View"); viewMenu->addAction(toolBar->toggleViewAction()); viewMenu->addAction(viewStopMaskAction); QMenu* machineMenu = menuBar()->addMenu("M&achine"); machineMenu->addAction(dbgSession->startMachineAction); machineMenu->addAction(dbgSession->haltMachineAction); machineMenu->addAction(dbgSession->resetMachineAction); QMenu* debugMenu = menuBar()->addMenu("&Debug"); debugMenu->addAction(dbgSession->debugContinueAction); debugMenu->addAction(dbgSession->debugStepAction); debugMenu->addAction(dbgSession->debugStopAction); debugMenu->addSeparator(); debugMenu->addAction(addBreakpointAction); debugMenu->addAction(removeBreakpointAction); debugMenu->addSeparator(); debugMenu->addAction(addSuspectAction); debugMenu->addAction(removeSuspectAction); debugMenu->addSeparator(); debugMenu->addAction(addTraceAction); debugMenu->addAction(removeTraceAction); debugMenu->addSeparator(); QMenu* stopMaskSubMenu = debugMenu->addMenu("Stop On"); for (StopMaskActionMap::const_iterator it = stopMaskActions.begin(); it != stopMaskActions.end(); ++it) { stopMaskSubMenu->addAction(it->second); } QMenu* settingsMenu = menuBar()->addMenu("Se&ttings"); QMenu* speedLevelsSubMenu = settingsMenu->addMenu("Simulation Speed"); for (int i = 0; i < DebugSession::kNumSpeedLevels; i++) speedLevelsSubMenu->addAction(simSpeedActions[i]); settingsMenu->addAction(increaseSpeedAction); settingsMenu->addAction(decreaseSpeedAction); QMenu* windowMenu = menuBar()->addMenu("&Windows"); for (unsigned int i = 0; i < N_DEV_PER_IL; ++i) windowMenu->addAction(showTerminalActions[i]); windowMenu->addSeparator(); for (unsigned int i = 0; i < MachineConfig::MAX_CPUS; ++i) windowMenu->addAction(showCpuWindowActions[i]); QMenu* helpMenu = menuBar()->addMenu("&Help"); helpMenu->addAction(aboutAction); } void MonitorWindow::initializeToolBar() { toolBar->addAction(newConfigAction); toolBar->addAction(loadConfigAction); toolBar->addAction(editConfigAction); toolBar->addSeparator(); toolBar->addAction(dbgSession->toggleMachineAction); toolBar->addAction(dbgSession->resetMachineAction); toolBar->addSeparator(); toolBar->addAction(dbgSession->debugToggleAction); toolBar->addAction(dbgSession->debugStepAction); toolBar->addSeparator(); QWidget* speedWidget = new QWidget; speedWidget->setToolTip("Adjust simulation speed"); QHBoxLayout* speedLayout = new QHBoxLayout(speedWidget); speedLayout->setSpacing(3); toolBar->addWidget(speedWidget); QLabel* slowestLabel = new QLabel("Slowest"); QFont speedTipFont = slowestLabel->font(); speedTipFont.setItalic(true); speedTipFont.setPointSizeF(speedTipFont.pointSizeF() * .8); slowestLabel->setFont(speedTipFont); speedLayout->addWidget(slowestLabel); QSlider* slider = new QSlider(Qt::Horizontal); slider->setRange(0, DebugSession::kMaxSpeed); slider->setSingleStep(1); slider->setPageStep(1); slider->setValue(dbgSession->getSpeed()); connect(slider, SIGNAL(valueChanged(int)), dbgSession, SLOT(setSpeed(int))); connect(dbgSession, SIGNAL(SpeedChanged(int)), slider, SLOT(setValue(int))); speedLayout->addWidget(slider); QLabel* fastestLabel = new QLabel("Fastest"); fastestLabel->setFont(speedTipFont); speedLayout->addWidget(fastestLabel); speedLayout->addStretch(1); } void MonitorWindow::createTabs() { tabWidget = new QTabWidget; connect(tabWidget, SIGNAL(currentChanged(int)), this, SLOT(updateStoppointActionsSensitivity())); tabWidget->addTab(createWelcomeTab(), QIcon(":/icons/system-22.svg"), "&Overview"); tabWidget->addTab(createCpuTab(), QIcon(":/icons/processor-22.svg"), "&Processors"); tabWidget->addTab(createMemoryTab(), QIcon(":/icons/memory-22.svg"), "&Memory"); tabWidget->addTab(createDeviceTab(), QIcon(":/icons/flash-22.svg"), "D&evice Status"); tabWidget->setTabEnabled(TAB_INDEX_CPU, false); tabWidget->setTabEnabled(TAB_INDEX_MEMORY, false); tabWidget->setTabEnabled(TAB_INDEX_DEVICES, false); } QPushButton* MonitorWindow::linkButtonFromAction(const QAction* action, const QString& text) { QPushButton* b = new FlatPushButton(action->icon(), text); b->setStyleSheet("text-decoration: underline;"); connect(b, SIGNAL(clicked()), action, SLOT(trigger())); return b; } QWidget* MonitorWindow::createWelcomeTab() { QScrollArea* scroller = new QScrollArea; QWidget* tab = new QWidget; tab->setObjectName("WelcomeTab"); scroller->setWidget(tab); scroller->setWidgetResizable(true); QGridLayout* layout = new QGridLayout(tab); scroller->setFrameShape(QFrame::NoFrame); scroller->setStyleSheet("QAbstractScrollArea, #WelcomeTab {background: transparent}"); layout->setColumnMinimumWidth(0, 16); layout->setColumnStretch(2, 1); layout->setContentsMargins(11, 22, 11, 11); QLabel* heading = new QLabel(QString("Welcome to %1MPS").arg(QChar(0x00b5))); QFont font = heading->font(); font.setPointSizeF(font.pointSizeF() * 1.3); font.setBold(true); heading->setFont(font); layout->addWidget(heading, 0, 0, 1, 3); QLabel* hint = new QLabel("To start using the simulator, you can:"); hint->setWordWrap(true); layout->addWidget(hint, 1, 0, 1, 3); layout->addWidget(linkButtonFromAction(newConfigAction, "Create a new machine configuration"), 2, 1, Qt::AlignLeft); layout->addWidget(linkButtonFromAction(loadConfigAction, "Open an existing machine configuration"), 3, 1, Qt::AlignLeft); layout->addWidget(new QLabel("Recent machines"), 4, 0, 1, 3); int recentRow = 5; for (unsigned int i = 0; i < Application::kMaxRecentConfigs; i++) { if (loadRecentConfigActions[i]->isEnabled()) layout->addWidget(linkButtonFromAction(loadRecentConfigActions[i], loadRecentConfigActions[i]->text()), recentRow++, 1, Qt::AlignLeft); } layout->setRowStretch(recentRow, 1); return scroller; } QWidget* MonitorWindow::createConfigTab() { configView = new MachineConfigView; QScrollArea* scroller = new QScrollArea; scroller->setWidget(configView); scroller->setFrameShape(QFrame::NoFrame); scroller->setStyleSheet("QAbstractScrollArea, MachineConfigView {background: transparent}"); return scroller; } QWidget* MonitorWindow::createCpuTab() { cpuListView = new TreeView("CPUListView", list_of (ProcessorListModel::COLUMN_CPU_ID) (ProcessorListModel::COLUMN_CPU_STATUS) (ProcessorListModel::COLUMN_CPU_ADDRESS)); cpuListView->setRootIsDecorated(false); cpuListView->setAlternatingRowColors(true); connect(cpuListView, SIGNAL(activated(const QModelIndex&)), this, SLOT(onCpuItemActivated(const QModelIndex&))); breakpointListView = new TreeView("BreakpointListView", list_of (StoppointListModel::COLUMN_STOPPOINT_ID) (StoppointListModel::COLUMN_ACCESS_TYPE) (StoppointListModel::COLUMN_ASID)); breakpointListView->setRootIsDecorated(false); breakpointListView->setAlternatingRowColors(true); breakpointListView->setContextMenuPolicy(Qt::ActionsContextMenu); breakpointListView->addAction(addBreakpointAction); breakpointListView->addAction(removeBreakpointAction); QSplitter* splitter = new QSplitter(Qt::Vertical); splitter->setChildrenCollapsible(false); splitter->addWidget(cpuListView); splitter->addWidget(breakpointListView); return splitter; } QWidget* MonitorWindow::createMemoryTab() { suspectListView = new TreeView("SuspectListView", list_of (StoppointListModel::COLUMN_STOPPOINT_ID) (StoppointListModel::COLUMN_ASID)); suspectListView->setRootIsDecorated(false); suspectListView->setAlternatingRowColors(true); suspectListView->setContextMenuPolicy(Qt::ActionsContextMenu); suspectListView->addAction(addSuspectAction); suspectListView->addAction(removeSuspectAction); suspectListView->setItemDelegateForColumn(StoppointListModel::COLUMN_ACCESS_TYPE, new SuspectTypeDelegate(this)); QSplitter* splitter = new QSplitter(Qt::Vertical); splitter->addWidget(suspectListView); traceBrowser = new TraceBrowser(addTraceAction, removeTraceAction); splitter->addWidget(traceBrowser); return splitter; } QWidget* MonitorWindow::createDeviceTab() { deviceTreeView = new DeviceTreeView(this); return deviceTreeView; } void MonitorWindow::updateRecentConfigList() { QStringList files = Appl()->settings.value("RecentFiles").toStringList(); for (unsigned int i = 0; i < (unsigned int) files.size() && i < Application::kMaxRecentConfigs; i++) { QString baseName = QFileInfo(files[i]).fileName(); loadRecentConfigActions[i]->setText(QString("%1. %2").arg(i + 1).arg(baseName)); loadRecentConfigActions[i]->setVisible(true); loadRecentConfigActions[i]->setData(files[i]); } for (unsigned int i = files.size(); i < Application::kMaxRecentConfigs; i++) { loadRecentConfigActions[i]->setVisible(false); loadRecentConfigActions[i]->setData(QVariant()); } clearRecentConfigsAction->setVisible(files.size()); } void MonitorWindow::clearConfigs() { QStringList recentFiles = Appl()->settings.value("RecentFiles").toStringList(); recentFiles.clear(); Appl()->settings.setValue("RecentFiles", recentFiles); updateRecentConfigList(); if (configView == NULL) { delete tabWidget->widget(TAB_INDEX_CONFIG_VIEW); tabWidget->insertTab(0, createWelcomeTab(), QIcon(":/icons/system-22.svg"), "&Overview"); tabWidget->setCurrentIndex(TAB_INDEX_CONFIG_VIEW); } } bool MonitorWindow::discardMachineConfirmed() { if (!dbgSession->isStarted()) return true; QMessageBox::StandardButton reply; reply = QMessageBox::question(this, "Warning", "A machine is currently active. " "Do you really want to do switch to a different machine?", QMessageBox::Yes | QMessageBox::No, QMessageBox::No); return reply == QMessageBox::Yes; } void MonitorWindow::onCreateConfig() { if (!discardMachineConfirmed()) return; CreateMachineDialog dialog(this); if (dialog.exec() == QDialog::Accepted) Appl()->CreateConfig(dialog.getFileName()); } void MonitorWindow::onLoadConfig() { if (!discardMachineConfirmed()) return; QString fileName = QFileDialog::getOpenFileName(this, "Open machine configuration"); if (!fileName.isEmpty()) Appl()->LoadConfig(fileName); } void MonitorWindow::onLoadRecentConfig() { QAction* action = static_cast(sender()); Appl()->LoadConfig(action->data().toString()); } void MonitorWindow::editConfig() { assert(Appl()->getConfig()); MachineConfigDialog dialog(Appl()->getConfig(), this); if (dialog.exec() == QDialog::Accepted) { try { Appl()->getConfig()->Save(); } catch (FileError& e) { QMessageBox::critical(this, QString("%1: error").arg(Appl()->applicationName()), e.what()); return; } configView->Update(); } } void MonitorWindow::onStopMaskChanged() { unsigned int acc = 0; for (StopMaskActionMap::const_iterator it = stopMaskActions.begin(); it != stopMaskActions.end(); ++it) { if (it->second->isChecked()) acc |= it->first; } dbgSession->setStopMask(acc); } void MonitorWindow::onMachineConfigChanged() { if (configView == NULL) { delete tabWidget->widget(TAB_INDEX_CONFIG_VIEW); tabWidget->insertTab(0, createConfigTab(), QIcon(":/icons/system-22.svg"), "&Overview"); tabWidget->setCurrentIndex(TAB_INDEX_CONFIG_VIEW); editConfigAction->setEnabled(true); } else { configView->Update(); } setWindowTitle(QString("%1 (%2) - uMPS").arg(Appl()->document, Appl()->getCurrentDir())); updateRecentConfigList(); } void MonitorWindow::onSpeedActionChecked() { dbgSession->setSpeed(speedActionGroup->checkedAction()->data().toInt()); } void MonitorWindow::onSpeedChanged(int speed) { simSpeedActions[speed]->setChecked(true); increaseSpeedAction->setEnabled(speed != DebugSession::kMaxSpeed); decreaseSpeedAction->setEnabled(speed != 0); } void MonitorWindow::increaseSpeed() { dbgSession->setSpeed(dbgSession->getSpeed() + 1); } void MonitorWindow::decreaseSpeed() { dbgSession->setSpeed(dbgSession->getSpeed() - 1); } void MonitorWindow::showCpuWindow(int cpuId) { if (cpuWindows[cpuId].isNull()) { cpuWindows[cpuId] = new ProcessorWindow(cpuId); cpuWindows[cpuId]->setAttribute(Qt::WA_QuitOnClose, false); cpuWindows[cpuId]->setAttribute(Qt::WA_DeleteOnClose); cpuWindows[cpuId]->show(); } else { cpuWindows[cpuId]->activateWindow(); cpuWindows[cpuId]->raise(); } } void MonitorWindow::onCpuItemActivated(const QModelIndex& index) { showCpuWindow(index.row()); } void MonitorWindow::showTerminal() { QAction* action = static_cast(sender()); unsigned int devNo = action->data().toUInt(); if (terminalWindows[devNo].isNull()) { terminalWindows[devNo] = new TerminalWindow(devNo); terminalWindows[devNo]->setAttribute(Qt::WA_QuitOnClose, false); terminalWindows[devNo]->setAttribute(Qt::WA_DeleteOnClose); terminalWindows[devNo]->show(); } else { terminalWindows[devNo]->activateWindow(); terminalWindows[devNo]->raise(); } } void MonitorWindow::showAboutInfo() { QString name = QString("%1MPS").arg(QChar(kCodePtMicro)); QString text = QString( "

µMPS %1

" "An educational computer system emulator" "

github.com/virtualsquare/umps3

" "Copyright © 1998-2020 µMPS authors" "
" "

Credits

" "

" " Created by: Mauro Morsiani, Tomislav Jonjic, Mattia Biondi" "
" " Contributions: Renzo Davoli (network/VDE support)" "
" " Documentation: Michael Goldweber, Renzo Davoli" "

" "
" "

License

" "

" " µMPS is free software, licensed under" " the GNU" " General Public License, version 3." "

") .arg(PACKAGE_VERSION); QMessageBox::about(this, QString("About %1").arg(name), text); } void MonitorWindow::onMachineStarted() { cpuListModel.reset(new ProcessorListModel); cpuListView->setModel(cpuListModel.get()); breakpointListView->setModel(dbgSession->getBreakpointListModel()); breakpointListView->hideColumn(StoppointListModel::COLUMN_ACCESS_TYPE); suspectListModel.reset(new StoppointListModel(dbgSession->getSuspects(), "Suspect", 'S')); suspectListView->setModel(suspectListModel.get()); deviceTreeModel.reset(new DeviceTreeModel(dbgSession->getMachine())); deviceTreeView->setModel(deviceTreeModel.get()); Machine* machine = dbgSession->getMachine(); for (unsigned int i = 0; i < N_DEV_PER_IL; ++i) { const Device* d = machine->getDevice(EXT_IL_INDEX(IL_TERMINAL), i); showTerminalActions[i]->setEnabled(d->Type() == TERMDEV); } const MachineConfig* config = Appl()->getConfig(); for (unsigned int i = 0; i < config->getNumProcessors(); i++) showCpuWindowActions[i]->setEnabled(true); tabWidget->setTabEnabled(TAB_INDEX_CPU, true); tabWidget->setTabEnabled(TAB_INDEX_MEMORY, true); tabWidget->setTabEnabled(TAB_INDEX_DEVICES, true); editConfigAction->setEnabled(false); } void MonitorWindow::onMachineHalted() { cpuListModel.reset(); suspectListModel.reset(); deviceTreeModel.reset(); for (unsigned int i = 0; i < MachineConfig::MAX_CPUS; i++) if (cpuWindows[i]) cpuWindows[i]->close(); for (unsigned int i = 0; i < N_DEV_PER_IL; i++) if (terminalWindows[i]) terminalWindows[i]->close(); tabWidget->setTabEnabled(TAB_INDEX_CPU, false); tabWidget->setTabEnabled(TAB_INDEX_MEMORY, false); tabWidget->setTabEnabled(TAB_INDEX_DEVICES, false); for (unsigned int i = 0; i < N_DEV_PER_IL; ++i) showTerminalActions[i]->setEnabled(false); for (unsigned int i = 0; i < MachineConfig::MAX_CPUS; ++i) showCpuWindowActions[i]->setEnabled(false); editConfigAction->setEnabled(true); } void MonitorWindow::updateStoppointActionsSensitivity() { bool stopped = dbgSession->isStopped(); addBreakpointAction->setEnabled(stopped); addSuspectAction->setEnabled(stopped); addTraceAction->setEnabled(stopped); removeBreakpointAction->setEnabled(stopped && tabWidget->currentIndex() == TAB_INDEX_CPU); removeSuspectAction->setEnabled(stopped && tabWidget->currentIndex() == TAB_INDEX_MEMORY); removeTraceAction->setEnabled(stopped && tabWidget->currentIndex() == TAB_INDEX_MEMORY); } void MonitorWindow::onAddBreakpoint() { AddBreakpointDialog dialog(this); if (dialog.exec() == QDialog::Accepted) { AddressRange r(dialog.getASID(), dialog.getStartAddress(), dialog.getStartAddress()); dbgSession->getBreakpointListModel()->Add(r, AM_EXEC); } } void MonitorWindow::onRemoveBreakpoint() { QModelIndexList idx = breakpointListView->selectionModel()->selectedRows(); if (!idx.isEmpty()) dbgSession->getBreakpointListModel()->Remove(idx.first().row()); } void MonitorWindow::onAddSuspect() { AddSuspectDialog dialog; if (dialog.exec() == QDialog::Accepted) { AddressRange r(dialog.getASID(), dialog.getStartAddress(), dialog.getEndAddress()); if (!suspectListModel->Add(r, AM_WRITE)) { QMessageBox::warning(this, "Warning", "Could not insert suspect range: " "it overlaps with an already inserted range"); } } } void MonitorWindow::onRemoveSuspect() { QModelIndexList idx = suspectListView->selectionModel()->selectedRows(); if (!idx.isEmpty()) suspectListModel->Remove(idx.first().row()); } void MonitorWindow::onAddTracepoint() { AddTracepointDialog dialog; if (dialog.exec() != QDialog::Accepted) return; if (!traceBrowser->AddTracepoint(dialog.getStartAddress(), dialog.getEndAddress())) QMessageBox::warning(this, "Warning", "Could not insert traced range: " "it overlaps with an already inserted range"); } StatusDisplay::StatusDisplay(QWidget* parent) : QWidget(parent) { QHBoxLayout* layout = new QHBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(new QLabel("Status:")); statusLabel = new QLabel; statusLabel->setFixedWidth(statusLabel->fontMetrics().horizontalAdvance("Powered off_")); layout->addWidget(statusLabel); layout->addSpacing(kFieldSpacing); layout->addWidget(new QLabel("ToD:")); todLabel = new QLabel; todLabel->setFont(Appl()->getMonospaceFont()); todLabel->setFixedWidth(todLabel->fontMetrics().horizontalAdvance("00000000:00000000")); layout->addWidget(todLabel); statusLabel->setText("-"); connect(Appl(), SIGNAL(MachineConfigChanged()), this, SLOT(refreshAll())); connect(debugSession, SIGNAL(StatusChanged()), this, SLOT(refreshAll())); connect(debugSession, SIGNAL(MachineReset()), this, SLOT(refreshAll())); connect(debugSession, SIGNAL(DebugIterationCompleted()), this, SLOT(refreshTod())); refreshAll(); } void StatusDisplay::refreshAll() { setEnabled(Appl()->getConfig() != NULL); if (Appl()->getConfig()) { todLabel->setEnabled(debugSession->getStatus() != MS_HALTED); switch (debugSession->getStatus()) { case MS_HALTED: statusLabel->setText("Powered off"); todLabel->setText("-"); break; case MS_RUNNING: statusLabel->setText("Running"); refreshTod(); break; case MS_STOPPED: statusLabel->setText("Stopped"); refreshTod(); break; } } else { statusLabel->setText("-"); todLabel->setText("-"); } } void StatusDisplay::refreshTod() { SystemBus* bus = debugSession->getMachine()->getBus(); todLabel->setText(QString("%1:%2") .arg(bus->getToDHI(), 8, 16, QLatin1Char('0')) .arg(bus->getToDLO(), 8, 16, QLatin1Char('0'))); } umps3-3.0.5/src/frontends/qmps/monitor_window.h000066400000000000000000000105031435132321600215650ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef QMPS_MONITOR_WINDOW_H #define QMPS_MONITOR_WINDOW_H #include #include #include #include "umps/machine_config.h" #include "umps/machine.h" #include "umps/arch.h" #include "qmps/application.h" #include "qmps/debug_session.h" #include "qmps/processor_list_model.h" #include "qmps/stoppoint_list_model.h" #include "qmps/device_tree_model.h" class QAction; class QActionGroup; class QTreeView; class QLabel; class QPushButton; class QToolBar; class QSlider; class MachineConfigView; class ProcessorWindow; class TraceBrowser; class TerminalWindow; class QModelIndex; class MonitorWindow: public QMainWindow { Q_OBJECT public: MonitorWindow(); protected: virtual void closeEvent(QCloseEvent* event); private: static const int TAB_INDEX_CONFIG_VIEW = 0; static const int TAB_INDEX_CPU = 1; static const int TAB_INDEX_MEMORY = 2; static const int TAB_INDEX_DEVICES = 3; void createActions(); void addStopMaskAction(const char* text, StopCause sc); void createMenu(); void initializeToolBar(); void createTabs(); QPushButton* linkButtonFromAction(const QAction* action, const QString& text = QString()); QWidget* createWelcomeTab(); QWidget* createConfigTab(); QWidget* createCpuTab(); QWidget* createMemoryTab(); QWidget* createDeviceTab(); void updateRecentConfigList(); bool discardMachineConfirmed(); DebugSession* const dbgSession; Machine* machine; scoped_ptr cpuListModel; scoped_ptr suspectListModel; scoped_ptr deviceTreeModel; QAction* newConfigAction; QAction* loadConfigAction; QAction* loadRecentConfigActions[Application::kMaxRecentConfigs]; QAction* clearRecentConfigsAction; QAction* recentConfigSeparatorAction; QAction* quitAction; QAction* viewToolbarAction; QAction* viewStopMaskAction; QAction* editConfigAction; QAction* addBreakpointAction; QAction* removeBreakpointAction; QAction* addSuspectAction; QAction* removeSuspectAction; QAction* addTraceAction; QAction* removeTraceAction; QActionGroup* speedActionGroup; QAction* simSpeedActions[DebugSession::kNumSpeedLevels]; static const char* const simSpeedMnemonics[DebugSession::kNumSpeedLevels]; QAction* increaseSpeedAction; QAction* decreaseSpeedAction; QAction* showCpuWindowActions[MachineConfig::MAX_CPUS]; QAction* showTerminalActions[N_DEV_PER_IL]; QAction* aboutAction; typedef std::map StopMaskActionMap; StopMaskActionMap stopMaskActions; QToolBar* toolBar; QSlider* speedSlider; QTabWidget* tabWidget; MachineConfigView* configView; QWidget* cpuListPane; QTreeView* cpuListView; QTreeView* breakpointListView; QTreeView* suspectListView; TraceBrowser* traceBrowser; QTreeView* deviceTreeView; QPointer cpuWindows[MachineConfig::MAX_CPUS]; QPointer terminalWindows[N_DEV_PER_IL]; private Q_SLOTS: void onCreateConfig(); void onLoadConfig(); void clearConfigs(); void onLoadRecentConfig(); void editConfig(); void onStopMaskChanged(); void onSpeedActionChecked(); void onSpeedChanged(int speed); void increaseSpeed(); void decreaseSpeed(); void showCpuWindow(int cpuId); void onCpuItemActivated(const QModelIndex& index); void showTerminal(); void showAboutInfo(); void onMachineConfigChanged(); void onMachineStarted(); void onMachineHalted(); void updateStoppointActionsSensitivity(); void onAddBreakpoint(); void onRemoveBreakpoint(); void onAddSuspect(); void onRemoveSuspect(); void onAddTracepoint(); }; #endif // QMPS_MONITOR_WINDOW_H umps3-3.0.5/src/frontends/qmps/monitor_window_priv.h000066400000000000000000000022701435132321600226270ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef QMPS_MONITOR_WINDOW_PRIV_H #define QMPS_MONITOR_WINDOW_PRIV_H #include class QLabel; class StatusDisplay : public QWidget { Q_OBJECT public: StatusDisplay(QWidget* parent = 0); private Q_SLOTS: void refreshAll(); void refreshTod(); private: static const int kFieldSpacing = 10; QLabel* statusLabel; QLabel* todLabel; }; #endif // QMPS_MONITOR_WINDOW_PRIV_H umps3-3.0.5/src/frontends/qmps/processor_list_model.cc000066400000000000000000000053161435132321600231050ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include "umps/machine.h" #include "qmps/application.h" #include "qmps/processor_list_model.h" #include "umps/processor.h" #include "qmps/cpu_status_map.h" const char* ProcessorListModel::headers[ProcessorListModel::N_COLUMNS] = { "Processor", "Status", "Location" }; ProcessorListModel::ProcessorListModel(QObject* parent) : QAbstractTableModel(parent), config(Appl()->getConfig()), dbgSession(Appl()->getDebugSession()), cpuStatusMap(dbgSession->getCpuStatusMap()) { connect(cpuStatusMap, SIGNAL(Changed()), this, SLOT(notifyStatusChanged())); } int ProcessorListModel::rowCount(const QModelIndex& parent) const { if (!parent.isValid()) return config->getNumProcessors(); else return 0; } int ProcessorListModel::columnCount(const QModelIndex& parent) const { if (!parent.isValid()) return N_COLUMNS; else return 0; } QVariant ProcessorListModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) return headers[section]; else return QVariant(); } QVariant ProcessorListModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) return QVariant(); switch (index.column()) { case COLUMN_CPU_ID: if (role == Qt::DisplayRole) return index.row(); break; case COLUMN_CPU_STATUS: if (role == Qt::DisplayRole) return cpuStatusMap->getStatus(index.row()); break; case COLUMN_CPU_ADDRESS: if (role == Qt::DisplayRole) return cpuStatusMap->getLocation(index.row()); if (role == Qt::FontRole) return Appl()->getMonospaceFont(); default: return QVariant(); } return QVariant(); } void ProcessorListModel::notifyStatusChanged() { for (unsigned int i = 0; i < config->getNumProcessors(); i++) { QModelIndex idx0 = index(i, COLUMN_CPU_STATUS); QModelIndex idx1 = index(i, COLUMN_CPU_ADDRESS); Q_EMIT dataChanged(idx0, idx1); } } umps3-3.0.5/src/frontends/qmps/processor_list_model.h000066400000000000000000000032371435132321600227470ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef QMPS_PROCESSOR_LIST_MODEL_H #define QMPS_PROCESSOR_LIST_MODEL_H #include #include class MachineConfig; class DebugSession; class CpuStatusMap; class ProcessorListModel : public QAbstractTableModel { Q_OBJECT public: enum { COLUMN_CPU_ID, COLUMN_CPU_STATUS, COLUMN_CPU_ADDRESS, N_COLUMNS }; ProcessorListModel(QObject* parent = 0); int rowCount(const QModelIndex& parent) const; int columnCount(const QModelIndex& parent) const; QVariant headerData(int section, Qt::Orientation orientation, int role) const; QVariant data(const QModelIndex& index, int role) const; private: static const char* headers[N_COLUMNS]; const MachineConfig* const config; const DebugSession* const dbgSession; const CpuStatusMap* const cpuStatusMap; private Q_SLOTS: void notifyStatusChanged(); }; #endif // QMPS_PROCESSOR_LIST_MODEL_H umps3-3.0.5/src/frontends/qmps/processor_window.cc000066400000000000000000000177661435132321600222750ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * Copyright (C) 2020 Mattia Biondi * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "qmps/processor_window.h" #include #include #include #include #include #include #include #include #include #include #include #include "umps/processor.h" #include "umps/disassemble.h" #include "qmps/application.h" #include "qmps/code_view.h" #include "qmps/ui_utils.h" #include "qmps/register_set_widget.h" #include "qmps/tlb_model.h" #include "qmps/register_item_delegate.h" static const int kDefaultWidth = 420; static const int kDefaultHeight = 580; ProcessorWindow::ProcessorWindow(Word cpuId, QWidget* parent) : QMainWindow(parent), dbgSession(Appl()->getDebugSession()), cpuId(cpuId) { cpu = dbgSession->getMachine()->getProcessor(cpuId); setWindowTitle(QString("uMPS Processor %1").arg(cpuId)); setDockOptions(AnimatedDocks | AllowTabbedDocks); createToolBar(); createDockableWidgets(); createMenu(); QWidget* centralWidget = new QWidget(this); setCentralWidget(centralWidget); QVBoxLayout* centralLayout = new QVBoxLayout(centralWidget); centralLayout->setContentsMargins(0, 7, 0, 0); statusLabel = new QLabel; statusLabel->setIndent(3); statusLabel->setFont(Appl()->getMonospaceFont()); centralLayout->addWidget(statusLabel); centralLayout->addWidget(new CodeView(cpuId)); centralLayout->addLayout(createInstrPanel()); QWidget* secStatusWidget = new QWidget; QHBoxLayout* secStatusLayout = new QHBoxLayout(secStatusWidget); secStatusLayout->setSpacing(12); secStatusLayout->setContentsMargins(0, 0, 0, 0); secStatusLayout->addWidget(bdIndicator = new QLabel("BD")); secStatusLayout->addWidget(ldIndicator = new QLabel("LD")); statusBar()->addPermanentWidget(secStatusWidget); updateStatusInfo(); QString key; key = QString("CpuWindow%1/geometry").arg((quint32) cpu->getId()); QVariant savedGeometry = Appl()->settings.value(key); if (savedGeometry.isValid()) restoreGeometry(savedGeometry.toByteArray()); else resize(kDefaultWidth, kDefaultHeight); key = QString("CpuWindow%1/state").arg((quint32) cpu->getId()); QVariant savedState = Appl()->settings.value(key); if (savedState.isValid()) restoreState(savedState.toByteArray()); else { tlbWidget->hide(); } connect(dbgSession->getCpuStatusMap(), SIGNAL(Changed()), this, SLOT(updateStatusInfo())); connect(dbgSession, SIGNAL(MachineReset()), this, SLOT(onMachineReset())); } void ProcessorWindow::closeEvent(QCloseEvent* event) { Appl()->settings.setValue(QString("CpuWindow%1/geometry").arg((quint32) cpu->getId()), saveGeometry()); Appl()->settings.setValue(QString("CpuWindow%1/state").arg((quint32) cpu->getId()), saveState()); event->accept(); } void ProcessorWindow::createMenu() { QMenu* machineMenu = menuBar()->addMenu("M&achine"); machineMenu->addAction(dbgSession->resetMachineAction); QMenu* debugMenu = menuBar()->addMenu("&Debug"); debugMenu->addAction(dbgSession->debugContinueAction); debugMenu->addAction(dbgSession->debugStepAction); debugMenu->addAction(dbgSession->debugStopAction); QMenu* viewMenu = menuBar()->addMenu("&View"); viewMenu->addAction(toolBar->toggleViewAction()); viewMenu->addSeparator(); viewMenu->addAction(regView->toggleViewAction()); viewMenu->addAction(tlbWidget->toggleViewAction()); QMenu* helpMenu = menuBar()->addMenu("&Help"); (void) helpMenu; } void ProcessorWindow::createToolBar() { toolBar = addToolBar("ToolBar"); toolBar->setObjectName("ToolBar"); toolBar->addAction(dbgSession->resetMachineAction); toolBar->addSeparator(); toolBar->addAction(dbgSession->debugToggleAction); toolBar->addAction(dbgSession->debugStepAction); } QLayout* ProcessorWindow::createInstrPanel() { static const int MIN_COL_SPACING = 12; static const int HEADER_COL = 0; static const int PC_COL = 2; QGridLayout* grid = new QGridLayout; grid->setColumnMinimumWidth(1, MIN_COL_SPACING); grid->setContentsMargins(0, 0, 0, 0); grid->setColumnStretch(PC_COL, 2); int rows = 0; prevPCLabel = new QLabel; prevPCLabel->setMinimumWidth(30); prevPCLabel->setFont(Appl()->getMonospaceFont()); prevPCLabel->setFrameStyle(QFrame::Panel | QFrame::Sunken); grid->addWidget(new QLabel("Prev. PC:"), rows, HEADER_COL, Qt::AlignLeft); grid->addWidget(prevPCLabel, rows++, PC_COL); pcLabel = new QLabel; pcLabel->setMinimumWidth(30); pcLabel->setFont(Appl()->getMonospaceFont()); pcLabel->setFrameStyle(QFrame::Panel | QFrame::Sunken); grid->addWidget(new QLabel("PC:"), rows, HEADER_COL, Qt::AlignLeft); grid->addWidget(pcLabel, rows++, PC_COL); return grid; } void ProcessorWindow::createDockableWidgets() { regView = new RegisterSetWidget(cpuId); regView->setObjectName("RegisterSetWidget"); addDockWidget(Qt::BottomDockWidgetArea, regView); QTableView* tlbView = new QTableView; tlbView->setModel(new TLBModel(cpuId, this)); tlbView->setSelectionMode(QAbstractItemView::SingleSelection); tlbView->setSelectionBehavior(QAbstractItemView::SelectRows); tlbView->horizontalHeader()->setStretchLastSection(true); tlbView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); tlbView->horizontalHeader()->setHighlightSections(false); tlbView->verticalHeader()->setHighlightSections(false); tlbView->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed); tlbView->setAlternatingRowColors(true); tlbView->setItemDelegate(new RIDelegateHex(this)); tlbView->resizeRowsToContents(); tlbWidget = new QDockWidget("TLB"); tlbWidget->setWidget(tlbView); tlbWidget->setObjectName("TLBWidget"); tlbWidget->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable); connect(tlbWidget, SIGNAL(topLevelChanged(bool)), this, SLOT(updateTLBViewTitle(bool))); addDockWidget(Qt::BottomDockWidgetArea, tlbWidget); tabifyDockWidget(regView, tlbWidget); } void ProcessorWindow::updateStatusInfo() { QString str; if (!cpu->isHalted()) { Word prevPC, prevInstr; cpu->getPrevStatus(&prevPC, &prevInstr); prevPCLabel->setText(str.asprintf("0x%.8X: %s", prevPC, StrInstr(prevInstr))); Word asid, pc, instr; bool isLD, isBD; cpu->getCurrStatus(&asid, &pc, &instr, &isLD, &isBD); pcLabel->setText(str.asprintf("0x%.8X: %s", pc, StrInstr(instr))); bdIndicator->setEnabled(isBD); ldIndicator->setEnabled(isLD); } else { prevPCLabel->clear(); pcLabel->clear(); bdIndicator->setEnabled(false); ldIndicator->setEnabled(false); } if (!cpu->isHalted()) { QString newStatus = dbgSession->getCpuStatusMap()->getLocation(cpuId); if (dbgSession->isStopped() || cpu->isIdle()) newStatus.append(QString(" [%1]").arg(dbgSession->getCpuStatusMap()->getStatus(cpu->getId()))); statusLabel->setText(newStatus); } else { statusLabel->setText("[Halted]"); } } void ProcessorWindow::onMachineReset() { cpu = dbgSession->getMachine()->getProcessor(cpuId); connect(dbgSession->getCpuStatusMap(), SIGNAL(Changed()), this, SLOT(updateStatusInfo())); updateStatusInfo(); } void ProcessorWindow::updateTLBViewTitle(bool topLevel) { if (topLevel) tlbWidget->setWindowTitle(QString("Processor %1 TLB").arg(cpuId)); else tlbWidget->setWindowTitle("TLB"); } umps3-3.0.5/src/frontends/qmps/processor_window.h000066400000000000000000000035051435132321600221210ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * Copyright (C) 2020 Mattia Biondi * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef QMPS_PROCESSOR_WINDOW_H #define QMPS_PROCESSOR_WINDOW_H #include #include "umps/types.h" class QToolBar; class Processor; class DebugSession; class QLabel; class CodeView; class QLayout; class QDockWidget; class RegisterSetWidget; class ProcessorWindow : public QMainWindow { Q_OBJECT public: ProcessorWindow(Word cpuId, QWidget* parent = 0); protected: virtual void closeEvent(QCloseEvent* event); private: void createMenu(); void createToolBar(); QLayout* createInstrPanel(); void createDockableWidgets(); DebugSession* const dbgSession; const Word cpuId; Processor* cpu; QToolBar* toolBar; QLabel* statusLabel; CodeView* codeView; QLabel* prevPCLabel; QLabel* prevInstructionLabel; QLabel* pcLabel; QLabel* instructionLabel; QLabel* bdIndicator; QLabel* ldIndicator; RegisterSetWidget* regView; QDockWidget* tlbWidget; private Q_SLOTS: void onMachineReset(); void updateStatusInfo(); void updateTLBViewTitle(bool topLevel); }; #endif // QMPS_PROCESSOR_WINDOW_H umps3-3.0.5/src/frontends/qmps/qmps.qrc000066400000000000000000000021471435132321600200320ustar00rootroot00000000000000 icons/create-22.svg icons/open-22.svg icons/system-22.svg icons/processor-22.svg icons/memory-22.svg icons/flash-22.svg icons/start-22.svg icons/halt-22.svg icons/reset-22.svg icons/settings-22.svg icons/continue-22.svg icons/step-22.svg icons/stop-22.svg icons/disk-22.svg icons/disk-32.svg icons/flash-32.svg icons/network-22.svg icons/network-32.svg icons/printer-22.svg icons/printer-32.svg icons/terminal-22.svg icons/terminal-32.svg icons/up-16.svg icons/down-16.svg icons/pc-16.svg icons/breakpoint_enabled-16.svg icons/breakpoint_disabled-16.svg icons/umps3-48.svg umps3-3.0.5/src/frontends/qmps/register_item_delegate.cc000066400000000000000000000126751435132321600233550ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010, 2011 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "qmps/register_item_delegate.h" #include #include #include #include "qmps/application.h" class RIValidator : public QValidator { public: RIValidator(QObject* parent = 0) : QValidator(parent) { } virtual State validate(QString& input, int& pos) const; }; QValidator::State RIValidator::validate(QString& input, int& pos) const { UNUSED_ARG(pos); input.replace(' ', '0'); return Acceptable; } QString RegisterItemDelegate::displayText(const QVariant& variant, const QLocale& locale) const { if (variant.canConvert()) return Text(variant.value()); else return QStyledItemDelegate::displayText(variant, locale); } void RegisterItemDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const { QLineEdit* lineEdit = static_cast(editor); lineEdit->setText(Text(index.data().value())); } void RegisterItemDelegate::updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const { UNUSED_ARG(index); editor->setGeometry(option.rect); } QWidget* RIDelegateHex::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const { UNUSED_ARG(option); UNUSED_ARG(index); QLineEdit* editor = new QLineEdit(parent); editor->setInputMask("\\0\\xHHHHHHHH"); editor->setValidator(new RIValidator(editor)); editor->setFont(Appl()->getMonospaceFont()); return editor; } void RIDelegateHex::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const { QLineEdit* lineEdit = static_cast(editor); Word data = lineEdit->text().remove(0, 2).toUInt(0, 16); model->setData(index, data, Qt::EditRole); } QWidget* RIDelegateSignedDecimal::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const { UNUSED_ARG(option); UNUSED_ARG(index); QLineEdit* editor = new QLineEdit(parent); editor->setValidator(new QIntValidator(editor)); editor->setFont(Appl()->getMonospaceFont()); return editor; } void RIDelegateSignedDecimal::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const { QLineEdit* lineEdit = static_cast(editor); Word data = (Word) lineEdit->text().toInt(0, 10); model->setData(index, data, Qt::EditRole); } QWidget* RIDelegateUnsignedDecimal::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const { UNUSED_ARG(option); UNUSED_ARG(index); QLineEdit* editor = new QLineEdit(parent); QIntValidator* validator = new QIntValidator(editor); validator->setBottom(0); editor->setValidator(validator); editor->setFont(Appl()->getMonospaceFont()); return editor; } void RIDelegateUnsignedDecimal::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const { QLineEdit* lineEdit = static_cast(editor); model->setData(index, lineEdit->text().toUInt(0, 10), Qt::EditRole); } QWidget* RIDelegateBinary::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const { UNUSED_ARG(option); UNUSED_ARG(index); QLineEdit* editor = new QLineEdit(parent); editor->setInputMask("BBBBBBBB|BBBBBBBB|BBBBBBBB|BBBBBBBB"); editor->setValidator(new RIValidator(editor)); editor->setFont(Appl()->getMonospaceFont()); return editor; } void RIDelegateBinary::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const { QLineEdit* lineEdit = static_cast(editor); Word data = lineEdit->text().remove("|").toUInt(0, 2); model->setData(index, data, Qt::EditRole); } QString RIDelegateBinary::Text(Word value) const { return (QString("%1|%2|%3|%4") .arg((value >> 24) & 0xFFU, 8, 2, QLatin1Char('0')) .arg((value >> 16) & 0xFFU, 8, 2, QLatin1Char('0')) .arg((value >> 8) & 0xFFU, 8, 2, QLatin1Char('0')) .arg((value >> 0) & 0xFFU, 8, 2, QLatin1Char('0'))); } umps3-3.0.5/src/frontends/qmps/register_item_delegate.h000066400000000000000000000073751435132321600232200ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010, 2011 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef QMPS_REGISTER_ITEM_DELEGATE_H #define QMPS_REGISTER_ITEM_DELEGATE_H #include #include "umps/types.h" class RegisterItemDelegate : public QStyledItemDelegate { public: RegisterItemDelegate(QObject* parent = 0) : QStyledItemDelegate(parent) { } virtual QString displayText(const QVariant& value, const QLocale& locale) const; virtual void setEditorData(QWidget* editor, const QModelIndex& index) const; virtual void updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const; protected: virtual QString Text(Word value) const = 0; }; class RIDelegateHex : public RegisterItemDelegate { public: RIDelegateHex(QObject* parent = 0) : RegisterItemDelegate(parent) { } virtual QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const; virtual void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const; protected: virtual QString Text(Word value) const { return QString("0x%1").arg(value, 8, 16, QLatin1Char('0')); } }; class RIDelegateSignedDecimal : public RegisterItemDelegate { public: RIDelegateSignedDecimal(QObject* parent = 0) : RegisterItemDelegate(parent) { } virtual QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const; virtual void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const; protected: virtual QString Text(Word value) const { return QString::number((SWord) value, 10); } }; class RIDelegateUnsignedDecimal : public RegisterItemDelegate { public: RIDelegateUnsignedDecimal(QObject* parent = 0) : RegisterItemDelegate(parent) { } virtual QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const; virtual void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const; protected: virtual QString Text(Word value) const { return QString::number(value, 10); } }; class RIDelegateBinary : public RegisterItemDelegate { public: RIDelegateBinary(QObject* parent = 0) : RegisterItemDelegate(parent) { } virtual QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const; virtual void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const; protected: virtual QString Text(Word value) const; }; #endif // QMPS_REGISTER_ITEM_DELEGATE_H umps3-3.0.5/src/frontends/qmps/register_set_snapshot.cc000066400000000000000000000204731435132321600232720ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "qmps/register_set_snapshot.h" #include #include #include "base/lang.h" #include "umps/disassemble.h" #include "umps/systembus.h" #include "qmps/application.h" using namespace boost::placeholders; const char* const RegisterSetSnapshot::headers[RegisterSetSnapshot::N_COLUMNS] = { "Register", "Value" }; const char* const RegisterSetSnapshot::registerTypeNames[RegisterSetSnapshot::kNumRegisterTypes] = { "CPU Registers", "CP0 Registers", "Other Registers" }; RegisterSetSnapshot::RegisterSetSnapshot(Word cpuId, QObject* parent) : QAbstractItemModel(parent), cpuId(cpuId) { connect(debugSession, SIGNAL(MachineStopped()), this, SLOT(updateCache())); connect(debugSession, SIGNAL(MachineRan()), this, SLOT(updateCache())); connect(debugSession, SIGNAL(DebugIterationCompleted()), this, SLOT(updateCache())); connect(debugSession, SIGNAL(MachineReset()), this, SLOT(reset())); topLevelFont.setBold(true); for (Word& v : gprCache) v = 0; for (Word& v : cp0Cache) v = 0; reset(); } QModelIndex RegisterSetSnapshot::index(int row, int column, const QModelIndex& parent) const { if (!hasIndex(row, column, parent) || column >= N_COLUMNS) return QModelIndex(); if (parent.isValid()) { switch (parent.row() + 1) { case RT_GENERAL: if ((unsigned int) row < Processor::kNumCPURegisters) return createIndex(row, column, (quint32) RT_GENERAL); break; case RT_CP0: if ((unsigned int) row < Processor::kNumCP0Registers) return createIndex(row, column, (quint32) RT_CP0); break; case RT_OTHER: if ((unsigned int) row < sprCache.size()) return createIndex(row, column, (quint32) RT_OTHER); break; } } else if (row < kNumRegisterTypes) { return createIndex(row, column, (quintptr) 0); } // Fallback case - clearly something bogus return QModelIndex(); } QModelIndex RegisterSetSnapshot::parent(const QModelIndex& index) const { if (!index.isValid()) return QModelIndex(); if (index.internalId() == 0) return QModelIndex(); else return createIndex(index.internalId() - 1, 0, (quintptr) 0); } int RegisterSetSnapshot::rowCount(const QModelIndex& parent) const { if (parent.column() > 0) return 0; if (!parent.isValid()) { // It's the root index, so return the number of toplevel // items. return kNumRegisterTypes; } else if (parent.internalId() == 0) { // 2nd level items switch (parent.row() + 1) { case RT_GENERAL: return Processor::kNumCPURegisters; case RT_CP0: return Processor::kNumCP0Registers; case RT_OTHER: return sprCache.size(); default: // Assert not reached assert(0); // Error return -1; } } else { // Leaf items have no children. return 0; } } int RegisterSetSnapshot::columnCount(const QModelIndex& parent) const { UNUSED_ARG(parent); return N_COLUMNS; } QVariant RegisterSetSnapshot::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) return headers[section]; else return QVariant(); } QVariant RegisterSetSnapshot::data(const QModelIndex& index, int role) const { if (!index.isValid()) return QVariant(); if (index.internalId() == 0) { if (index.column() != 0) return QVariant(); if (role == Qt::DisplayRole) return registerTypeNames[index.row()]; if (role == Qt::FontRole) return topLevelFont; } else { if (role == Qt::DisplayRole) { switch (index.column()) { case COL_REGISTER_MNEMONIC: switch (index.internalId()) { case RT_GENERAL: return RegName(index.row()); case RT_CP0: return CP0RegName(index.row()); case RT_OTHER: return sprCache[index.row()].name; default: return QVariant(); } case COL_REGISTER_VALUE: switch (index.internalId()) { case RT_GENERAL: return gprCache[index.row()]; case RT_CP0: return cp0Cache[index.row()]; break; case RT_OTHER: return sprCache[index.row()].value; break; default: return QVariant(); } default: // Assert not reached assert(0); } } else if (role == Qt::FontRole) { return Appl()->getMonospaceFont(); } } return QVariant(); } bool RegisterSetSnapshot::setData(const QModelIndex& index, const QVariant& variant, int role) { if (!(index.isValid() && role == Qt::EditRole && variant.canConvert() && index.internalId() && index.column() == COL_REGISTER_VALUE)) { return false; } int r = index.row(); switch (index.internalId()) { case RT_GENERAL: cpu->setGPR(r, variant.value()); if (gprCache[r] != (Word) cpu->getGPR(r)) { gprCache[r] = cpu->getGPR(r); Q_EMIT dataChanged(index, index); } break; case RT_CP0: cpu->setCP0Reg(r, variant.value()); if (cp0Cache[r] != cpu->getCP0Reg(r)) { cp0Cache[r] = cpu->getCP0Reg(r); Q_EMIT dataChanged(index, index); } break; case RT_OTHER: if (sprCache[r].setter) { sprCache[r].setter(variant.value()); if (sprCache[r].value != sprCache[r].getter()) { sprCache[r].value = sprCache[r].getter(); Q_EMIT dataChanged(index, index); } } break; default: // Assert not reached assert(0); } return true; } Qt::ItemFlags RegisterSetSnapshot::flags(const QModelIndex& index) const { if (!index.isValid()) return Qt::NoItemFlags; if (index.internalId() == 0) return Qt::ItemIsEnabled | Qt::ItemIsSelectable; switch (index.column()) { case COL_REGISTER_MNEMONIC: return Qt::ItemIsEnabled | Qt::ItemIsSelectable; case COL_REGISTER_VALUE: return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable; default: // Assert not reached assert(0); } return Qt::NoItemFlags; } void RegisterSetSnapshot::reset() { cpu = debugSession->getMachine()->getProcessor(cpuId); sprCache.clear(); sprCache.push_back(SpecialRegisterInfo("nextPC", boost::bind(&Processor::getNextPC, cpu), boost::bind(&Processor::setNextPC, cpu, _1))); sprCache.push_back(SpecialRegisterInfo("succPC", boost::bind(&Processor::getSuccPC, cpu), boost::bind(&Processor::setSuccPC, cpu, _1))); sprCache.push_back(SpecialRegisterInfo("prevPhysPC", boost::bind(&Processor::getPrevPPC, cpu))); sprCache.push_back(SpecialRegisterInfo("currPhysPC", boost::bind(&Processor::getCurrPPC, cpu))); SystemBus* bus = debugSession->getMachine()->getBus(); sprCache.push_back(SpecialRegisterInfo("Timer", boost::bind(&SystemBus::getTimer, bus), boost::bind(&SystemBus::setTimer, bus, _1))); updateCache(); } void RegisterSetSnapshot::updateCache() { for (unsigned int i = 0; i < Processor::kNumCPURegisters; i++) { Word value = cpu->getGPR(i); if (gprCache[i] != value) { gprCache[i] = value; QModelIndex index = createIndex(i, COL_REGISTER_VALUE, RT_GENERAL); Q_EMIT dataChanged(index, index); } } for (unsigned int i = 0; i < Processor::kNumCP0Registers; i++) { Word value = cpu->getCP0Reg(i); if (cp0Cache[i] != value) { cp0Cache[i] = value; QModelIndex index = createIndex(i, COL_REGISTER_VALUE, RT_CP0); Q_EMIT dataChanged(index, index); } } int row = 0; for (SpecialRegisterInfo& sr : sprCache) { Word value = sr.getter(); if (value != sr.value) { sr.value = value; QModelIndex index = createIndex(row, COL_REGISTER_VALUE, RT_OTHER); Q_EMIT dataChanged(index, index); } row++; } } umps3-3.0.5/src/frontends/qmps/register_set_snapshot.h000066400000000000000000000053731435132321600231360ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef QMPS_REGISTER_SET_SNAPSHOT_H #define QMPS_REGISTER_SET_SNAPSHOT_H #include #include #include #include #include "umps/types.h" #include "umps/processor.h" class RegisterSetSnapshot: public QAbstractItemModel { Q_OBJECT public: enum { COL_REGISTER_MNEMONIC = 0, COL_REGISTER_VALUE, N_COLUMNS }; RegisterSetSnapshot(Word cpuId, QObject* parent = 0); QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const; QModelIndex parent(const QModelIndex& index) const; int rowCount(const QModelIndex& parent = QModelIndex()) const; int columnCount(const QModelIndex& parent = QModelIndex()) const; QVariant headerData(int section, Qt::Orientation orientation, int role) const; QVariant data(const QModelIndex& index, int role) const; bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole); Qt::ItemFlags flags(const QModelIndex& index) const; private: static const int kNumRegisterTypes = 3; enum RegisterType { RT_GENERAL = 1, RT_CP0 = 2, RT_OTHER = 3 }; struct SpecialRegisterInfo { SpecialRegisterInfo(const char* str, boost::function get) : name(str), getter(get), value(0) { } SpecialRegisterInfo(const char* str, boost::function get, boost::function set) : name(str), getter(get), setter(set), value(0) { } const char* name; boost::function getter; boost::function setter; Word value; }; static const char* const headers[N_COLUMNS]; static const char* const registerTypeNames[kNumRegisterTypes]; const Word cpuId; Processor* cpu; Word gprCache[Processor::kNumCPURegisters]; Word cp0Cache[Processor::kNumCP0Registers]; std::vector sprCache; QFont topLevelFont; private Q_SLOTS: void reset(); void updateCache(); }; #endif // QMPS_REGISTER_SET_SNAPSHOT_H umps3-3.0.5/src/frontends/qmps/register_set_widget.cc000066400000000000000000000077501435132321600227210ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010, 2011 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "qmps/register_set_widget.h" #include #include #include #include #include "qmps/register_set_snapshot.h" #include "qmps/ui_utils.h" #include "qmps/register_item_delegate.h" #include "qmps/tree_view.h" #include "qmps/application.h" RegisterSetWidget::RegisterSetWidget(Word cpuId, QWidget* parent) : QDockWidget("Registers", parent), model(new RegisterSetSnapshot(cpuId, this)), cpuId(cpuId), delegateKey(QString("RegisterSetWidget%1/delegate").arg(cpuId)) { connect(this, SIGNAL(topLevelChanged(bool)), this, SLOT(updateWindowTitle())); QWidget* widget = new QWidget; QVBoxLayout* layout = new QVBoxLayout(widget); layout->setSpacing(0); layout->setContentsMargins(0, 0, 0, 0); setWidget(widget); QActionGroup* displayGroup = new QActionGroup(this); QToolBar* toolBar = new QToolBar; addDisplayAction("Hex", new RIDelegateHex(this), displayGroup, toolBar); addDisplayAction("Signed Decimal", new RIDelegateSignedDecimal(this), displayGroup, toolBar); addDisplayAction("Unsigned Decimal", new RIDelegateUnsignedDecimal(this), displayGroup, toolBar); addDisplayAction("Binary", new RIDelegateBinary(this), displayGroup, toolBar); connect(displayGroup, SIGNAL(triggered(QAction*)), this, SLOT(setDisplayType(QAction*))); layout->addWidget(toolBar, 0, Qt::AlignRight); QFont toolBarFont = toolBar->font(); toolBarFont.setPointSizeF(toolBarFont.pointSizeF() * .75); toolBar->setFont(toolBarFont); toolBar->setStyleSheet("QToolButton { padding: 0; }"); treeView = new TreeView(QString("RegisterSetView%1").arg(cpuId), boost::assign::list_of(RegisterSetSnapshot::COL_REGISTER_MNEMONIC), true); treeView->setItemDelegateForColumn(RegisterSetSnapshot::COL_REGISTER_VALUE, delegates[currentDelegate()]); treeView->setAlternatingRowColors(true); treeView->setModel(model); SetFirstColumnSpanned(treeView, true); layout->addWidget(treeView); setAllowedAreas(Qt::AllDockWidgetAreas); setFeatures(DockWidgetClosable | DockWidgetMovable | DockWidgetFloatable); } void RegisterSetWidget::updateWindowTitle() { if (isFloating()) setWindowTitle(QString("Processor %1 Registers").arg(cpuId)); else setWindowTitle("Registers"); } void RegisterSetWidget::setDisplayType(QAction* action) { int i = action->data().toInt(); treeView->setItemDelegateForColumn(RegisterSetSnapshot::COL_REGISTER_VALUE, delegates[i]); Appl()->settings.setValue(delegateKey, i); } void RegisterSetWidget::addDisplayAction(const QString& text, QStyledItemDelegate* delegate, QActionGroup* group, QToolBar* toolBar) { QAction* action = new QAction(text, group); action->setCheckable(true); int index = delegates.size(); action->setData(QVariant::fromValue(index)); action->setChecked(currentDelegate() == index); delegates.push_back(delegate); toolBar->addAction(action); } int RegisterSetWidget::currentDelegate() const { return Appl()->settings.value(delegateKey).toInt(); } umps3-3.0.5/src/frontends/qmps/register_set_widget.h000066400000000000000000000032721435132321600225560ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010, 2011 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef QMPS_REGISTER_SET_WIDGET_H #define QMPS_REGISTER_SET_WIDGET_H #include #include #include "umps/types.h" class QAction; class QTreeView; class Processor; class RegisterSetSnapshot; class QStyledItemDelegate; class QActionGroup; class QToolBar; class RegisterSetWidget : public QDockWidget { Q_OBJECT public: RegisterSetWidget(Word cpuId, QWidget* parent = 0); protected: RegisterSetSnapshot* model; private Q_SLOTS: void updateWindowTitle(); void setDisplayType(QAction* action); private: void addDisplayAction(const QString& text, QStyledItemDelegate* delegate, QActionGroup* group, QToolBar* toolBar); int currentDelegate() const; const Word cpuId; QTreeView* treeView; std::vector delegates; const QString delegateKey; }; #endif // QMPS_REGISTER_SET_WIDGET_H umps3-3.0.5/src/frontends/qmps/stop_mask_view.cc000066400000000000000000000031271435132321600217030ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "stop_mask_view.h" StopMaskView::StopMaskView(const std::map& actions, QWidget* parent) : QGroupBox("Stop Mask", parent) { QGridLayout* layout = new QGridLayout; std::map::const_iterator it; int col = 0; for (it = actions.begin(); it != actions.end(); ++it) { QAction* action = it->second; QCheckBox* cb = new QCheckBox(action->text()); cb->setChecked(action->isChecked()); connect(action, SIGNAL(triggered(bool)), cb, SLOT(setChecked(bool))); connect(cb, SIGNAL(toggled(bool)), action, SLOT(setChecked(bool))); layout->addWidget(cb, 0, col++, Qt::AlignLeft); } --col; layout->setColumnStretch(col, 1); layout->setHorizontalSpacing(11); setLayout(layout); } umps3-3.0.5/src/frontends/qmps/stop_mask_view.h000066400000000000000000000022041435132321600215400ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef QMPS_STOP_MASK_VIEW_H #define QMPS_STOP_MASK_VIEW_H #include #include #include "umps/machine.h" class QWidget; class QAction; class StopMaskView : public QGroupBox { Q_OBJECT public: StopMaskView(const std::map& actions, QWidget* parent = 0); }; #endif // QMPS_STOP_MASK_VIEW_H umps3-3.0.5/src/frontends/qmps/stoppoint_list_model.cc000066400000000000000000000211001435132321600231120ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "stoppoint_list_model.h" #include #include #include "umps/stoppoint.h" #include "umps/symbol_table.h" #include "umps/types.h" #include "umps/processor.h" #include "qmps/application.h" #include "qmps/debug_session.h" #include "qmps/ui_utils.h" BaseStoppointListModel::BaseStoppointListModel(StoppointSet* set, QObject* parent) : QAbstractTableModel(parent), stoppoints(set), symbolTable(Appl()->getDebugSession()->getSymbolTable()) { // Just a dumb sanity check: out lifetime is from powerup to // shutdown of a single machine, which implies that a symbol table // exists and is valid. assert(symbolTable != NULL); for (Stoppoint::Ptr sp : *stoppoints) { formattedRangeCache.push_back(formatAddressRange(sp->getRange())); } } int BaseStoppointListModel::rowCount(const QModelIndex& parent) const { if (!parent.isValid()) return stoppoints->Size(); else return 0; } bool BaseStoppointListModel::Add(const AddressRange& range, AccessMode mode) { if (!stoppoints->CanInsert(range)) return false; beginInsertRows(QModelIndex(), stoppoints->Size(), stoppoints->Size()); stoppoints->Add(range, mode); formattedRangeCache.push_back(formatAddressRange(range)); StoppointAdded(); endInsertRows(); return true; } void BaseStoppointListModel::Remove(int index) { beginRemoveRows(QModelIndex(), index, index); stoppoints->Remove(index); formattedRangeCache.erase(formattedRangeCache.begin() + index); StoppointRemoved(index); endRemoveRows(); } void BaseStoppointListModel::Remove(Stoppoint* sp) { for (size_t i = 0; i < stoppoints->Size(); i++) { if (stoppoints->Get(i) == sp) { Remove(i); break; } } } QString BaseStoppointListModel::getAddressRange(int i) const { return formattedRangeCache[i]; } QString BaseStoppointListModel::formatAddressRange(const AddressRange& range) { Word start = range.getStart(), end = range.getEnd(); const char *saStart, *saEnd; SWord ofsStart, ofsEnd; saStart = GetSymbolicAddress(symbolTable, range.getASID(), start, false, &ofsStart); if (start != end) saEnd = GetSymbolicAddress(symbolTable, range.getASID(), end, false, &ofsEnd); else saEnd = NULL; if (saStart == NULL || (start != end && saEnd == NULL)) { if (start != end) return (QString("%1 %2 %3") .arg(FormatAddress(start)) .arg(QChar(0x2192)) .arg(FormatAddress(end))); else return QString(FormatAddress(start)); } else { if (start != end) { return (QString("%1+0x%2 %3 %4+0x%5") .arg(saStart) .arg((quint32) ofsStart, 0, 16) .arg(QChar(0x2192)) .arg(saEnd) .arg((quint32) ofsEnd, 0, 16)); } else { return QString("%1+0x%2").arg(saStart).arg((quint32) ofsStart, 0, 16); } } } const char* const StoppointListModel::headers[StoppointListModel::N_COLUMNS] = { "Id", "Type", "ASID", "Location", "Victims" }; StoppointListModel::StoppointListModel(StoppointSet* spSet, const char* collectionName, char idPrefix, QObject* parent) : BaseStoppointListModel(spSet, parent), collectionName(collectionName), idPrefix(idPrefix), victims(spSet->Size(), 0) { RegisterSigc(stoppoints->SignalHit.connect( sigc::mem_fun(this, &StoppointListModel::onHit))); RegisterSigc(stoppoints->SignalEnabledChanged.connect( sigc::mem_fun(this, &StoppointListModel::onEnabledChanged))); connect(Appl()->getDebugSession(), SIGNAL(MachineRan()), this, SLOT(onMachineRan())); } int StoppointListModel::columnCount(const QModelIndex& parent) const { if (!parent.isValid()) return N_COLUMNS; else return 0; } QVariant StoppointListModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole && section < N_COLUMNS) { if (section == COLUMN_STOPPOINT_ID) return collectionName; else return headers[section]; } return QVariant(); } QVariant StoppointListModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) return QVariant(); Stoppoint* sp = stoppoints->Get(index.row()); switch (index.column()) { case COLUMN_STOPPOINT_ID: if (role == Qt::DisplayRole) return QString("%1%2").arg(idPrefix).arg(sp->getId()); if (role == Qt::CheckStateRole) return sp->IsEnabled() ? Qt::Checked : Qt::Unchecked; if (role == Qt::FontRole) { QFont font; font.setBold(victims[index.row()]); return font; } break; case COLUMN_ACCESS_TYPE: if (role == Qt::DisplayRole) { switch (sp->getAccessMode()) { case AM_WRITE: return "Write"; case AM_READ: return "Read"; case AM_READ_WRITE: return "Read/Write"; default: return "Invalid"; } } if (role == Qt::EditRole) return sp->getAccessMode(); break; case COLUMN_ASID: if (role == Qt::DisplayRole) return QString("0x%1").arg((unsigned int) sp->getRange().getASID(), 0, 16); if (role == Qt::FontRole) return Appl()->getMonospaceFont(); break; case COLUMN_ADDRESS_RANGE: if (role == Qt::DisplayRole) return getAddressRange(index.row()); if (role == Qt::FontRole) return Appl()->getMonospaceFont(); break; case COLUMN_VICTIMS: if (role == Qt::DisplayRole) { QString cpus; uint32_t temp = victims[index.row()]; for (uint32_t i = 0; temp && i < 32; ++i) { if (temp & (1U << i)) { if (!cpus.isEmpty()) cpus += ", "; cpus += QString("CPU%1").arg(i); } temp &= ~(1U << i); } return cpus; } if (role == Qt::FontRole) { QFont font; font.setBold(true); return font; } break; default: // Assert not reached assert(0); } return QVariant(); } Qt::ItemFlags StoppointListModel::flags(const QModelIndex& index) const { if (!index.isValid()) return Qt::NoItemFlags; switch (index.column()) { case COLUMN_STOPPOINT_ID: return QAbstractTableModel::flags(index) | Qt::ItemIsUserCheckable; case COLUMN_ACCESS_TYPE: return QAbstractTableModel::flags(index) | Qt::ItemIsEditable; default: return QAbstractTableModel::flags(index); } } bool StoppointListModel::setData(const QModelIndex& index, const QVariant& value, int role) { if (!index.isValid()) return false; if (index.column() == COLUMN_STOPPOINT_ID && role == Qt::CheckStateRole) { Stoppoint* sp = stoppoints->Get(index.row()); stoppoints->SetEnabled(index.row(), !sp->IsEnabled()); } if (index.column() == COLUMN_ACCESS_TYPE && role == Qt::EditRole && value.canConvert()) { // This is as unkosher as it gets... AccessMode newMode = (AccessMode) value.toUInt(); stoppoints->Get(index.row())->setAccessMode(newMode); Q_EMIT dataChanged(index, index); return true; } return false; } void StoppointListModel::onHit(size_t spIndex, const Stoppoint* stoppoint, Word addr, const Processor* cpu) { victims[spIndex] |= 1 << cpu->getId(); QModelIndex idIndex = index(spIndex, COLUMN_STOPPOINT_ID); Q_EMIT dataChanged(idIndex, idIndex); QModelIndex victimIndex = index(spIndex, COLUMN_VICTIMS); Q_EMIT dataChanged(victimIndex, victimIndex); } void StoppointListModel::onEnabledChanged(size_t spIndex) { QModelIndex idx = index(spIndex, COLUMN_STOPPOINT_ID); Q_EMIT dataChanged(idx, idx); } void StoppointListModel::onMachineRan() { for (unsigned int i = 0; i < stoppoints->Size(); i++) { if (victims[i]) { victims[i] = 0; QModelIndex idIndex = index(i, COLUMN_STOPPOINT_ID); Q_EMIT dataChanged(idIndex, idIndex); QModelIndex victimIndex = index(i, COLUMN_VICTIMS); Q_EMIT dataChanged(victimIndex, victimIndex); } } } void StoppointListModel::StoppointAdded() { victims.push_back(0); } void StoppointListModel::StoppointRemoved(int index) { victims.erase(victims.begin() + index); } umps3-3.0.5/src/frontends/qmps/stoppoint_list_model.h000066400000000000000000000060011435132321600227570ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef QMPS_STOPPINT_LIST_MODEL_H #define QMPS_STOPPINT_LIST_MODEL_H #include #include #include #include #include #include "base/basic_types.h" #include "base/lang.h" #include "base/trackable_mixin.h" #include "umps/stoppoint.h" class SymbolTable; class BaseStoppointListModel: public QAbstractTableModel { Q_OBJECT public: BaseStoppointListModel(StoppointSet* set, QObject* parent = 0); int rowCount(const QModelIndex& parent = QModelIndex()) const; // We have to proxy these operations because of annoying // QAbstractItemModel API obligations. bool Add(const AddressRange& range, AccessMode mode); void Remove(int index); void Remove(Stoppoint* sp); protected: QString getAddressRange(int i) const; virtual void StoppointAdded() { } virtual void StoppointRemoved(int index) { UNUSED_ARG(index); } StoppointSet* stoppoints; private: QString formatAddressRange(const AddressRange& range); std::vector formattedRangeCache; SymbolTable* const symbolTable; }; class StoppointListModel: public BaseStoppointListModel, public TrackableMixin { Q_OBJECT public: enum { COLUMN_STOPPOINT_ID = 0, COLUMN_ACCESS_TYPE, COLUMN_ASID, COLUMN_ADDRESS_RANGE, COLUMN_VICTIMS, N_COLUMNS }; StoppointListModel(StoppointSet* stoppoints, const char* collectionName, char idPrefix, QObject* parent = 0); int columnCount(const QModelIndex& parent) const; QVariant headerData(int section, Qt::Orientation orientation, int role) const; QVariant data(const QModelIndex& index, int role) const; Qt::ItemFlags flags(const QModelIndex& index) const; bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole); protected: virtual void StoppointAdded(); virtual void StoppointRemoved(int index); private: void onHit(size_t index, const Stoppoint* stoppoint, Word addr, const Processor* cpu); void onEnabledChanged(size_t spIndex); static const char* const headers[N_COLUMNS]; const char* const collectionName; char idPrefix; std::vector victims; private Q_SLOTS: void onMachineRan(); }; #endif // QMPS_STOPPINT_LIST_MODEL_H umps3-3.0.5/src/frontends/qmps/suspect_type_delegate.cc000066400000000000000000000047341435132321600232370ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "qmps/suspect_type_delegate.h" #include #include "base/lang.h" #include "umps/stoppoint.h" SuspectTypeDelegate::ItemInfo SuspectTypeDelegate::valueMap[] = { { AM_WRITE, "Write" }, { AM_READ, "Read" }, { AM_READ_WRITE, "Read/Write" } }; SuspectTypeDelegate::SuspectTypeDelegate(QWidget* parent) : QStyledItemDelegate(parent) { } QWidget* SuspectTypeDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const { UNUSED_ARG(option); UNUSED_ARG(index); QComboBox* editor = new QComboBox(parent); for (unsigned int i = 0; i < kValidTypes; i++) editor->addItem(valueMap[i].label); return editor; } void SuspectTypeDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const { QComboBox* comboBox = static_cast(editor); unsigned int value = index.data(Qt::EditRole).toUInt(); unsigned int i; for (i = 0; i < kValidTypes; i++) if (value == valueMap[i].value) break; comboBox->setCurrentIndex(i); } void SuspectTypeDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const { QComboBox* comboBox = static_cast(editor); model->setData(index, valueMap[comboBox->currentIndex()].value, Qt::EditRole); } void SuspectTypeDelegate::updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const { editor->setGeometry(option.rect); } umps3-3.0.5/src/frontends/qmps/suspect_type_delegate.h000066400000000000000000000033501435132321600230720ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef QMPS_SUSPECT_TYPE_DELEGATE_H #define QMPS_SUSPECT_TYPE_DELEGATE_H #include class SuspectTypeDelegate : public QStyledItemDelegate { Q_OBJECT public: SuspectTypeDelegate(QWidget* parent = 0); QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const; void setEditorData(QWidget* editor, const QModelIndex& index) const; void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const; void updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const; private: static const unsigned int kValidTypes = 3; struct ItemInfo { unsigned int value; const char* label; }; static ItemInfo valueMap[kValidTypes]; }; #endif // QMPS_SUSPECT_TYPE_DELEGATE_H umps3-3.0.5/src/frontends/qmps/symbol_table_model.cc000066400000000000000000000063701435132321600225100ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "qmps/symbol_table_model.h" #include #include "umps/symbol_table.h" #include "qmps/application.h" #include "qmps/debug_session.h" SymbolTableModel::SymbolTableModel(QObject* parent) : QAbstractTableModel(parent), table(Appl()->getDebugSession()->getSymbolTable()) { } int SymbolTableModel::rowCount(const QModelIndex& parent) const { if (!parent.isValid()) return table->Size(); else return 0; } int SymbolTableModel::columnCount(const QModelIndex& parent) const { if (!parent.isValid()) return N_COLUMNS; else return 0; } QVariant SymbolTableModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { switch (section) { case COLUMN_SYMBOL: return "Symbol"; case COLUMN_START_ADDRESS: return "Start"; case COLUMN_END_ADDRESS: return "End"; } } return QVariant(); } QVariant SymbolTableModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) return QVariant(); if (role == Qt::DisplayRole) { const Symbol* symbol = table->Get(index.row()); switch (index.column()) { case COLUMN_SYMBOL: return symbol->getName(); case COLUMN_START_ADDRESS: return QString("0x%1").arg((quint32) symbol->getStart(), 8, 16, QChar('0')); case COLUMN_END_ADDRESS: return QString("0x%1").arg((quint32) symbol->getEnd(), 8, 16, QChar('0')); default: return QVariant(); } } else if (role == Qt::FontRole) { return Appl()->getMonospaceFont(); } return QVariant(); } SortFilterSymbolTableModel::SortFilterSymbolTableModel(Symbol::Type type, QObject* parent) : QSortFilterProxyModel(parent), table(Appl()->getDebugSession()->getSymbolTable()), tableType(type) { setSortCaseSensitivity(Qt::CaseInsensitive); } QVariant SortFilterSymbolTableModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole && section == SymbolTableModel::COLUMN_SYMBOL) { if (tableType == Symbol::TYPE_OBJECT) return "Object"; else return "Function"; } return QSortFilterProxyModel::headerData(section, orientation, role); } bool SortFilterSymbolTableModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const { return table->Get(sourceRow)->getType() == tableType; } umps3-3.0.5/src/frontends/qmps/symbol_table_model.h000066400000000000000000000037201435132321600223460ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef QMPS_SYMBOL_TABLE_MODEL_H #define QMPS_SYMBOL_TABLE_MODEL_H #include #include #include "umps/symbol_table.h" class SymbolTableModel : public QAbstractTableModel { Q_OBJECT public: enum Column { COLUMN_SYMBOL, COLUMN_START_ADDRESS, COLUMN_END_ADDRESS, N_COLUMNS }; SymbolTableModel(QObject* parent = 0); int rowCount(const QModelIndex& parent) const; int columnCount(const QModelIndex& parent) const; QVariant headerData(int section, Qt::Orientation orientation, int role) const; QVariant data(const QModelIndex& index, int role) const; private: const SymbolTable* const table; }; class SortFilterSymbolTableModel : public QSortFilterProxyModel { Q_OBJECT public: SortFilterSymbolTableModel(Symbol::Type tableType, QObject* parent = 0); virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; protected: virtual bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const; private: const SymbolTable* const table; const Symbol::Type tableType; }; #endif // QMPS_SYMBOL_TABLE_MODEL_H umps3-3.0.5/src/frontends/qmps/terminal_view.cc000066400000000000000000000061271435132321600215210ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "qmps/terminal_view.h" #include #include #include #include #include #include "base/lang.h" #include "umps/device.h" #include "qmps/application.h" TerminalView::TerminalView(TerminalDevice* terminal, QWidget* parent) : QPlainTextEdit(parent), terminal(terminal) { terminal->SignalTransmitted.connect(sigc::mem_fun(this, &TerminalView::onCharTransmitted)); QFont font = Appl()->getMonospaceFont(); setFont(font); setCursorWidth(fontMetrics().horizontalAdvance("o")); // Disable features that look silly in a basic terminal widget setContextMenuPolicy(Qt::NoContextMenu); setUndoRedoEnabled(false); // Wrapping setWordWrapMode(QTextOption::WrapAnywhere); std::string devFile = Appl()->getConfig()->getDeviceFile(terminal->getInterruptLine(), terminal->getNumber()); QFile file(devFile.c_str()); if (!file.open(QFile::ReadOnly | QFile::Text)) { // TODO: fix this and another gazillion critical points assert(0); } QTextStream in(&file); setPlainText(in.readAll()); moveCursor(QTextCursor::End); } void TerminalView::keyPressEvent(QKeyEvent* e) { if (e->modifiers() & ~Qt::ShiftModifier) return; int key = e->key(); if (key == Qt::Key_Return || key == Qt::Key_Enter) { flushInput(); QPlainTextEdit::keyPressEvent(e); } else if (key == Qt::Key_Backspace && !input.isEmpty()) { input.chop(1); QPlainTextEdit::keyPressEvent(e); } else if (!e->text().isEmpty() && Qt::Key_Space <= key && key <= Qt::Key_nobreakspace) { input.append(e->text().toLatin1()); QPlainTextEdit::keyPressEvent(e); } } void TerminalView::mousePressEvent(QMouseEvent* e) { UNUSED_ARG(e); } bool TerminalView::canInsertFromMimeData(const QMimeData* source) const { UNUSED_ARG(source); return false; } void TerminalView::insertFromMimeData(const QMimeData* source) { // It seems that Qt forces us to reimplement (stub out) this if we // want to avoid drop and clipboard paste ops in all cases. UNUSED_ARG(source); } void TerminalView::flushInput() { terminal->Input(input.constData()); input.clear(); } void TerminalView::onCharTransmitted(char c) { insertPlainText(QString(c)); QTextCursor cursor = textCursor(); cursor.movePosition(QTextCursor::End); setTextCursor(cursor); } umps3-3.0.5/src/frontends/qmps/terminal_view.h000066400000000000000000000027421435132321600213620ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef QMPS_TERMINAL_VIEW_H #define QMPS_TERMINAL_VIEW_H #include #include #include class TerminalDevice; class TerminalView : public QPlainTextEdit, public sigc::trackable { Q_OBJECT public: TerminalView(TerminalDevice* terminal, QWidget* parent = 0); protected: virtual void keyPressEvent(QKeyEvent* e); virtual void mousePressEvent(QMouseEvent* e); virtual bool canInsertFromMimeData(const QMimeData* source) const; virtual void insertFromMimeData(const QMimeData* source); private: void flushInput(); void onCharTransmitted(char c); TerminalDevice* const terminal; QByteArray input; }; #endif // QMPS_TERMINAL_VIEW_H umps3-3.0.5/src/frontends/qmps/terminal_window.cc000066400000000000000000000142651435132321600220600ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "qmps/terminal_window.h" #include #include #include #include #include #include #include #include "umps/types.h" #include "umps/device.h" #include "qmps/application.h" #include "qmps/terminal_view.h" #include "qmps/terminal_window_priv.h" #include "qmps/flat_push_button.h" TerminalWindow::TerminalWindow(unsigned int devNo, QWidget* parent) : QMainWindow(parent), devNo(devNo) { setWindowTitle(QString("uMPS Terminal %1").arg(devNo)); setWindowIcon(QIcon(":/icons/terminal-32.svg")); TerminalDevice* terminal = getTerminal(devNo); QWidget* centralWidget = new QWidget; layout = new QVBoxLayout(centralWidget); layout->setContentsMargins(0, 0, 0, 0); setCentralWidget(centralWidget); terminalView = new TerminalView(terminal); layout->addWidget(terminalView); statusWidget = new TerminalStatusWidget(terminal); layout->addWidget(statusWidget); QString key = QString("TerminalWindow%1/geometry").arg(devNo); QVariant savedGeometry = Appl()->settings.value(key); if (savedGeometry.isValid()) { restoreGeometry(savedGeometry.toByteArray()); } else { QFontMetrics fm = terminalView->fontMetrics(); resize(fm.horizontalAdvance("x") * kDefaultCols, fm.lineSpacing() * kDefaultRows); } connect(debugSession, SIGNAL(MachineReset()), this, SLOT(onMachineReset())); } void TerminalWindow::closeEvent(QCloseEvent* event) { QString key = QString("TerminalWindow%1/geometry").arg(devNo); Appl()->settings.setValue(key, saveGeometry()); event->accept(); } void TerminalWindow::onMachineReset() { delete terminalView; delete statusWidget; TerminalDevice* terminal = getTerminal(devNo); terminalView = new TerminalView(terminal); layout->addWidget(terminalView); statusWidget = new TerminalStatusWidget(terminal); layout->addWidget(statusWidget); } TerminalDevice* TerminalWindow::getTerminal(unsigned int devNo) { Device* device = debugSession->getMachine()->getDevice(4, devNo); assert(device->Type() == TERMDEV); return static_cast(device); } TerminalStatusWidget::TerminalStatusWidget(TerminalDevice* t, QWidget* parent) : QWidget(parent), terminal(t), expanded(false), expandedIcon(":/icons/down-16.svg"), collapsedIcon(":/icons/up-16.svg") { QGridLayout* layout = new QGridLayout(this); layout->setContentsMargins(5, 0, 5, 0); layout->setColumnStretch(0, 1); hwFailureCheckBox = new QCheckBox("Hardware Failure"); hwFailureCheckBox->setChecked(terminal->getDevNotWorking()); connect(hwFailureCheckBox, SIGNAL(clicked(bool)), this, SLOT(onHardwareFailureButtonClicked(bool))); layout->addWidget(hwFailureCheckBox, 0, 0); expanderButton = new FlatPushButton(collapsedIcon, "Show Status"); connect(expanderButton, SIGNAL(clicked()), this, SLOT(onExpanderButtonClicked())); expanderButton->setIconSize(QSize(16, 16)); layout->addWidget(expanderButton, 0, 1); statusAreaWidget = new QWidget; QGridLayout* statusAreaLayout = new QGridLayout(statusAreaWidget); statusAreaLayout->setContentsMargins(0, 0, 0, 5); statusAreaLayout->setVerticalSpacing(5); statusAreaLayout->setHorizontalSpacing(15); statusAreaLayout->setColumnStretch(1, 1); statusAreaLayout->setColumnStretch(3, 1); statusAreaLayout->addWidget(new QLabel("RX:"), 0, 0); statusAreaLayout->addWidget(new QLabel("TX:"), 1, 0); rxStatusLabel = new QLabel; rxStatusLabel->setMinimumWidth(kStatusLabelsMinimumWidth); rxStatusLabel->setFrameStyle(QFrame::Panel | QFrame::Sunken); statusAreaLayout->addWidget(rxStatusLabel, 0, 1); txStatusLabel = new QLabel; txStatusLabel->setMinimumWidth(kStatusLabelsMinimumWidth); txStatusLabel->setFrameStyle(QFrame::Panel | QFrame::Sunken); statusAreaLayout->addWidget(txStatusLabel, 1, 1); statusAreaLayout->addWidget(new QLabel("At:"), 0, 2); statusAreaLayout->addWidget(new QLabel("At:"), 1, 2); rxCompletionTime = new QLabel; rxCompletionTime->setMinimumWidth(kStatusLabelsMinimumWidth); rxCompletionTime->setFrameStyle(QFrame::Panel | QFrame::Sunken); statusAreaLayout->addWidget(rxCompletionTime, 0, 3); txCompletionTime = new QLabel; txCompletionTime->setMinimumWidth(kStatusLabelsMinimumWidth); txCompletionTime->setFrameStyle(QFrame::Panel | QFrame::Sunken); statusAreaLayout->addWidget(txCompletionTime, 1, 3); layout->addWidget(statusAreaWidget, 1, 0, 1, 2); statusAreaWidget->hide(); terminal->SignalStatusChanged.connect( sigc::hide(sigc::mem_fun(*this, &TerminalStatusWidget::updateStatus)) ); terminal->SignalConditionChanged.connect( sigc::mem_fun(*this, &TerminalStatusWidget::onConditionChanged) ); updateStatus(); } void TerminalStatusWidget::updateStatus() { rxStatusLabel->setText(terminal->getRXStatus()); txStatusLabel->setText(terminal->getTXStatus()); rxCompletionTime->setText(terminal->getRXCTimeInfo().c_str()); txCompletionTime->setText(terminal->getTXCTimeInfo().c_str()); } void TerminalStatusWidget::onConditionChanged(bool isWorking) { hwFailureCheckBox->setChecked(!isWorking); } void TerminalStatusWidget::onHardwareFailureButtonClicked(bool checked) { terminal->setCondition(!checked); } void TerminalStatusWidget::onExpanderButtonClicked() { if (expanded) { expanded = false; expanderButton->setIcon(collapsedIcon); expanderButton->setText("Show Status"); statusAreaWidget->hide(); } else { expanded = true; expanderButton->setIcon(expandedIcon); expanderButton->setText("Hide Status"); statusAreaWidget->show(); } } umps3-3.0.5/src/frontends/qmps/terminal_window.h000066400000000000000000000027621435132321600217210ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef QMPS_TERMINAL_WINDOW_H #define QMPS_TERMINAL_WINDOW_H #include class TerminalDevice; class QLabel; class QVBoxLayout; class TerminalView; class TerminalStatusWidget; class TerminalWindow : public QMainWindow { Q_OBJECT public: TerminalWindow(unsigned int devNo, QWidget* parent = 0); protected: virtual void closeEvent(QCloseEvent* event); private Q_SLOTS: void onMachineReset(); private: static const int kDefaultCols = 60; static const int kDefaultRows = 20; static TerminalDevice* getTerminal(unsigned int devNo); const unsigned int devNo; QVBoxLayout* layout; TerminalView* terminalView; TerminalStatusWidget* statusWidget; }; #endif // QMPS_TERMINAL_WINDOW_H umps3-3.0.5/src/frontends/qmps/terminal_window_priv.h000066400000000000000000000035411435132321600227550ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef QMPS_TERMINAL_WINDOW_PRIV_H #define QMPS_TERMINAL_WINDOW_PRIV_H #include #include #include class QLabel; class QPushButton; class TerminalDevice; class QCheckBox; class TerminalStatusWidget : public QWidget, public sigc::trackable { Q_OBJECT public: TerminalStatusWidget(TerminalDevice* terminal, QWidget* parent = 0); private: // We _have_ to set the minimumSize() property on dynamic labels // in resizable containers; the constant is here mainly as a // remainder for that :-) static const int kStatusLabelsMinimumWidth = 16; void updateStatus(); void onConditionChanged(bool isWorking); TerminalDevice* const terminal; bool expanded; QWidget* statusAreaWidget; QLabel* rxStatusLabel; QLabel* rxCompletionTime; QLabel* txStatusLabel; QLabel* txCompletionTime; QIcon expandedIcon; QIcon collapsedIcon; QPushButton* expanderButton; QCheckBox* hwFailureCheckBox; private Q_SLOTS: void onHardwareFailureButtonClicked(bool checked); void onExpanderButtonClicked(); }; #endif // QMPS_TERMINAL_WINDOW_PRIV_H umps3-3.0.5/src/frontends/qmps/tlb_model.cc000066400000000000000000000132441435132321600206130ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2011 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "qmps/tlb_model.h" #include #include "umps/processor.h" #include "umps/processor_defs.h" #include "umps/utility.h" #include "qmps/application.h" const char* const TLBModel::detailsTemplate = " \
TLB Ebtry %EntryNo%
\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \
EntryHiEntryLo
VPN:0x%EntryHi.VPN%PFN:0x%EntryLo.PFN%
ASID:0x%EntryHi.ASID%D:%EntryLo.D%
V:%EntryLo.V%
G:%EntryLo.G%
\ "; TLBModel::TLBModel(Word cpuId, QObject* parent) : QAbstractTableModel(parent), cpuId(cpuId) { connect(debugSession, SIGNAL(MachineReset()), this, SLOT(onMachineReset())); onMachineReset(); } Qt::ItemFlags TLBModel::flags(const QModelIndex& index) const { if (!index.isValid()) return Qt::NoItemFlags; return QAbstractTableModel::flags(index) | Qt::ItemIsEditable; } int TLBModel::rowCount(const QModelIndex& parent) const { if (!parent.isValid()) return Appl()->getConfig()->getTLBSize(); else return 0; } int TLBModel::columnCount(const QModelIndex& parent) const { if (!parent.isValid()) return N_COLUMNS; else return 0; } QVariant TLBModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole) return QVariant(); if (orientation == Qt::Horizontal) { switch (section) { case COLUMN_PTE_HI: return "EntryHi"; case COLUMN_PTE_LO: return "EntryLo"; default: return QVariant(); } } if (orientation == Qt::Vertical) return section; return QVariant(); } QVariant TLBModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) return QVariant(); if (role == Qt::DisplayRole || role == Qt::EditRole) { switch (index.column()) { case COLUMN_PTE_HI: return cpu->getTLBHi(index.row()); case COLUMN_PTE_LO: return cpu->getTLBLo(index.row()); default: return QVariant(); } } if (role == Qt::ToolTipRole) return tlbEntryDetails(index.row()); if (role == Qt::FontRole) return Appl()->getMonospaceFont(); return QVariant(); } bool TLBModel::setData(const QModelIndex& index, const QVariant& value, int role) { if (!(index.isValid() && role == Qt::EditRole && value.canConvert())) return false; switch (index.column()) { case COLUMN_PTE_HI: cpu->setTLBHi(index.row(), value.value()); break; case COLUMN_PTE_LO: cpu->setTLBLo(index.row(), value.value()); break; default: // Assert not reached assert(0); } return true; } void TLBModel::onMachineReset() { beginResetModel(); cpu = debugSession->getMachine()->getProcessor(cpuId); cpu->SignalTLBChanged.connect(sigc::mem_fun(this, &TLBModel::onTLBChanged)); endResetModel(); } void TLBModel::onTLBChanged(unsigned int tlbIndex) { Q_EMIT dataChanged(index(tlbIndex, COLUMN_PTE_HI), index(tlbIndex, COLUMN_PTE_LO)); } QString TLBModel::tlbEntryDetails(unsigned int index) const { Word hi = cpu->getTLBHi(index); Word lo = cpu->getTLBLo(index); QString buf(detailsTemplate); buf.replace("%EntryNo%", QString::number(index)); buf.replace("%EntryHi.VPN%", QString("%1").arg(VPN(hi) >> 12, 5, 16, QLatin1Char('0'))); buf.replace("%EntryHi.ASID%", QString::number(ASID(hi) >> 6, 16)); buf.replace("%EntryLo.PFN%", QString("%1").arg(VPN(lo) >> 12, 5, 16, QLatin1Char('0'))); buf.replace("%EntryLo.D%", QString::number(BitVal(lo, DBITPOS))); buf.replace("%EntryLo.V%", QString::number(BitVal(lo, VBITPOS))); buf.replace("%EntryLo.G%", QString::number(BitVal(lo, GBITPOS))); return buf; } umps3-3.0.5/src/frontends/qmps/tlb_model.h000066400000000000000000000035341435132321600204560ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2011 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef QMPS_TLB_MODEL_H #define QMPS_TLB_MODEL_H #include #include #include "umps/types.h" class Processor; class TLBModel : public QAbstractTableModel, public sigc::trackable { Q_OBJECT public: enum Column { COLUMN_PTE_HI, COLUMN_PTE_LO, N_COLUMNS }; TLBModel(Word cpuId, QObject* parent = 0); Qt::ItemFlags flags(const QModelIndex& index) const; virtual int rowCount(const QModelIndex& parent = QModelIndex()) const; virtual int columnCount(const QModelIndex& parent = QModelIndex()) const; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole); private Q_SLOTS: void onMachineReset(); private: static const char* const detailsTemplate; void onTLBChanged(unsigned int index); QString tlbEntryDetails(unsigned int index) const; const Word cpuId; Processor* cpu; }; #endif // QMPS_TLB_MODEL_H umps3-3.0.5/src/frontends/qmps/trace_browser.cc000066400000000000000000000241531435132321600215140ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "qmps/trace_browser.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "base/lang.h" #include "umps/arch.h" #include "qmps/application.h" #include "qmps/debug_session.h" #include "qmps/hex_view.h" #include "qmps/trace_browser_priv.h" using namespace boost::placeholders; TraceBrowser::TraceBrowser(QAction* insertTraceAct, QAction* removeTraceAct, QWidget* parent) : QWidget(parent), dbgSession(Appl()->getDebugSession()) { #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN delegateFactory.push_back(ViewDelegateType("Hex Dump (Big-endian)", boost::bind(&TraceBrowser::createHexView, _1, _2, false))); #endif delegateFactory.push_back(ViewDelegateType("Hex Dump", boost::bind(&TraceBrowser::createHexView, _1, _2, true))); delegateFactory.push_back(ViewDelegateType("ASCII", boost::bind(&AsciiView::Create, _1, _2))); QGridLayout* layout = new QGridLayout(this); layout->setContentsMargins(0, 0, 0, 0); layout->setColumnStretch(0, 1); layout->setVerticalSpacing(4); QLabel* heading = new QLabel("Traced Regions"); heading->setIndent(2); layout->addWidget(heading, 0, 0); layout->addWidget(new QLabel("Display:"), 0, 1); delegateTypeCombo = new QComboBox; for (const ViewDelegateType& dt : delegateFactory) delegateTypeCombo->addItem(dt.name); layout->addWidget(delegateTypeCombo, 0, 2); connect(delegateTypeCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(onDelegateTypeChanged(int))); delegateTypeCombo->setEnabled(false); tplView = new QListView; tplView->setContextMenuPolicy(Qt::ActionsContextMenu); tplView->addAction(insertTraceAct); tplView->addAction(removeTraceAct); connect(removeTraceAct, SIGNAL(triggered()), this, SLOT(removeTracepoint())); splitter = new QSplitter; splitter->addWidget(tplView); layout->addWidget(splitter, 1, 0, 1, 3); QLabel* placeholder = new QLabel( QString("No trace region selected " "(use Debug %1 Add Traced Region to insert one)").arg(QChar(0x2192))); placeholder->setWordWrap(true); placeholder->setFrameStyle(QFrame::StyledPanel); placeholder->setAlignment(Qt::AlignLeft | Qt::AlignTop); viewStack = new QStackedWidget; viewStack->addWidget(placeholder); splitter->addWidget(viewStack); connect(dbgSession, SIGNAL(MachineStarted()), this, SLOT(onMachineStarted())); connect(dbgSession, SIGNAL(MachineReset()), this, SLOT(onMachineStarted())); connect(dbgSession, SIGNAL(MachineHalted()), this, SLOT(onMachineHalted())); connect(dbgSession, SIGNAL(DebugIterationCompleted()), this, SLOT(refreshView())); connect(dbgSession, SIGNAL(MachineStopped()), this, SLOT(refreshView())); } TraceBrowser::~TraceBrowser() { } bool TraceBrowser::AddTracepoint(Word start, Word end) { assert(tplModel.get()); return tplModel->Add(AddressRange(MachineConfig::MAX_ASID, start, end), AM_WRITE); } void TraceBrowser::onMachineStarted() { for (ViewDelegateMap::iterator it = viewMap.begin(); it != viewMap.end(); ++it) delete it->second.widget; tplModel.reset(new TracepointListModel(dbgSession->getTracepoints())); tplView->setModel(tplModel.get()); connect(tplView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&,const QItemSelection&)), this, SLOT(onSelectionChanged(const QItemSelection&))); connect(tplModel.get(), SIGNAL(rowsInserted(const QModelIndex&,int,int)), this, SLOT(onTracepointAdded())); if (tplModel->rowCount() > 0) tplView->setCurrentIndex(tplModel->index(0, 0)); } void TraceBrowser::onMachineHalted() { for (ViewDelegateMap::iterator it = viewMap.begin(); it != viewMap.end(); ++it) delete it->second.widget; tplModel.reset(); } void TraceBrowser::onSelectionChanged(const QItemSelection& selected) { QModelIndexList indexes = selected.indexes(); if (!indexes.isEmpty()) { Stoppoint* sp = selectedTracepoint(); ViewDelegateMap::iterator it = viewMap.find(sp->getId()); if (it == viewMap.end()) { const AddressRange& r = sp->getRange(); ViewDelegateInfo info; info.type = kDefaultViewDelegate; info.widget = delegateFactory[kDefaultViewDelegate].ctor(r.getStart(), r.getEnd()); viewMap[sp->getId()] = info; viewStack->setCurrentIndex(viewStack->addWidget(info.widget)); delegateTypeCombo->setCurrentIndex(kDefaultViewDelegate); } else if (it->second.widget.isNull()) { const AddressRange& r = sp->getRange(); it->second.widget = delegateFactory[it->second.type].ctor(r.getStart(), r.getEnd()); viewStack->setCurrentIndex(viewStack->addWidget(it->second.widget)); delegateTypeCombo->setCurrentIndex(it->second.type); } else { QWidget* widget = it->second.widget; viewStack->setCurrentWidget(widget); delegateTypeCombo->setCurrentIndex(it->second.type); if (tplModel->IsDirty(indexes[0].row())) { MemoryViewDelegate* d = dynamic_cast(widget); if (d) d->Refresh(); tplModel->ClearDirty(indexes[0].row()); } } delegateTypeCombo->setEnabled(true); } else { delegateTypeCombo->setEnabled(false); } } void TraceBrowser::refreshView() { QModelIndexList indexes = tplView->selectionModel()->selectedIndexes(); if (indexes.empty()) return; int row = indexes.first().row(); if (tplModel->IsDirty(row)) { tplModel->ClearDirty(row); MemoryViewDelegate* delegate = dynamic_cast(viewStack->currentWidget()); if (delegate) delegate->Refresh(); } } void TraceBrowser::onDelegateTypeChanged(int index) { Stoppoint* sp = selectedTracepoint(); assert(sp != NULL); if (viewMap[sp->getId()].type != index) { const AddressRange& r = sp->getRange(); delete viewMap[sp->getId()].widget; viewMap[sp->getId()].widget = delegateFactory[index].ctor(r.getStart(), r.getEnd()); viewMap[sp->getId()].type = index; viewStack->setCurrentIndex(viewStack->addWidget(viewMap[sp->getId()].widget)); } } Stoppoint* TraceBrowser::selectedTracepoint() const { QModelIndexList indexes = tplView->selectionModel()->selectedIndexes(); if (indexes.empty()) return NULL; return dbgSession->getTracepoints()->Get(indexes[0].row()); } void TraceBrowser::onTracepointAdded() { if (tplView->selectionModel()->selectedIndexes().empty()) tplView->setCurrentIndex(tplModel->index(0, 0)); } void TraceBrowser::removeTracepoint() { Stoppoint* sp = selectedTracepoint(); if (sp) { delete viewMap[sp->getId()].widget; viewMap.erase(sp->getId()); tplModel->Remove(sp); } } QWidget* TraceBrowser::createHexView(Word start, Word end, bool nativeOrder) { HexView* hexView = new HexView(start, end); hexView->setReversedByteOrder(!nativeOrder); return hexView; } TracepointListModel::TracepointListModel(StoppointSet* spSet, QObject* parent) : BaseStoppointListModel(spSet, parent), dirtySet(spSet->Size(), false) { RegisterSigc(spSet->SignalHit.connect(sigc::mem_fun(*this, &TracepointListModel::onHit))); } QVariant TracepointListModel::headerData(int section, Qt::Orientation orientation, int role) const { UNUSED_ARG(section); UNUSED_ARG(orientation); UNUSED_ARG(role); return QVariant(); } QVariant TracepointListModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) return QVariant(); if (index.column() > 0 || (size_t) index.row() >= stoppoints->Size()) return QVariant(); if (role == Qt::DisplayRole) { return getAddressRange(index.row()); } else if (role == Qt::FontRole) { QFont font; font.setBold(dirtySet[index.row()]); return font; } else { return QVariant(); } } void TracepointListModel::ClearDirty(int row) { dirtySet[row] = false; QModelIndex idx = index(row, 0); Q_EMIT dataChanged(idx, idx); } void TracepointListModel::StoppointAdded() { dirtySet.push_back(false); } void TracepointListModel::StoppointRemoved(int index) { dirtySet.erase(dirtySet.begin() + index); } void TracepointListModel::onHit(size_t spIndex, const Stoppoint* stoppoint, Word addr, const Processor* cpu) { UNUSED_ARG(stoppoint); UNUSED_ARG(addr); UNUSED_ARG(cpu); dirtySet[spIndex] = true; QModelIndex idx = index(spIndex, 0); Q_EMIT dataChanged(idx, idx); } AsciiView::AsciiView(Word start, Word end) : QPlainTextEdit(), start(start), end(end) { setFont(Appl()->getMonospaceFont()); setWordWrapMode(QTextOption::WrapAnywhere); setReadOnly(true); Refresh(); } AsciiView* AsciiView::Create(Word start, Word end) { return new AsciiView(start, end); } void AsciiView::Refresh() { Machine* machine = debugSession->getMachine(); QString buffer; buffer.reserve((((end - start) >> 2) + 1) * WS); Word val; char bytes[WS]; for (Word addr = start; addr <= end; addr += WS) { machine->ReadMemory(addr, &val); qToLittleEndian(val, (unsigned char *) bytes); for (unsigned int i = 0; i < WS; i++) { if (isprint(bytes[i])) buffer += bytes[i]; else buffer += QChar(kUnicodeReplacementChar); } } setPlainText(buffer); } umps3-3.0.5/src/frontends/qmps/trace_browser.h000066400000000000000000000047131435132321600213560ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef QMPS_TRACE_BROWSER_H #define QMPS_TRACE_BROWSER_H #include #include #include #include "base/lang.h" #include "umps/types.h" #include "qmps/stoppoint_list_model.h" #include "qmps/hex_view.h" class QAction; class QListView; class DebugSession; class QItemSelection; class QComboBox; class QSplitter; class QStackedWidget; class TracepointListModel; class TraceBrowser: public QWidget { Q_OBJECT public: TraceBrowser(QAction* insertAction, QAction* removeAction, QWidget* parent = 0); ~TraceBrowser(); bool AddTracepoint(Word start, Word end); private Q_SLOTS: void onMachineStarted(); void onMachineHalted(); void onTracepointAdded(); void removeTracepoint(); void onSelectionChanged(const QItemSelection&); void onDelegateTypeChanged(int index); void refreshView(); private: static const int kDefaultViewDelegate = 0; struct ViewDelegateInfo { int type; QPointer widget; }; typedef boost::function DelegateFactoryFunc; struct ViewDelegateType { ViewDelegateType(const char* name, DelegateFactoryFunc func) : name(name), ctor(func) { } const char* name; DelegateFactoryFunc ctor; }; Stoppoint* selectedTracepoint() const; static QWidget* createHexView(Word start, Word end, bool nativeOrder); DebugSession* const dbgSession; scoped_ptr tplModel; QComboBox* delegateTypeCombo; QSplitter* splitter; QListView* tplView; QStackedWidget* viewStack; typedef std::map ViewDelegateMap; ViewDelegateMap viewMap; std::vector delegateFactory; }; #endif // QMPS_TRACE_BROWSER_H umps3-3.0.5/src/frontends/qmps/trace_browser_priv.h000066400000000000000000000051211435132321600224100ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef QMPS_TRACE_BROWSER_PRIV_H #define QMPS_TRACE_BROWSER_PRIV_H #include #include #include #include "base/trackable_mixin.h" #include "umps/types.h" #include "qmps/stoppoint_list_model.h" #include "qmps/memory_view_delegate.h" class Stoppoint; class StoppointSet; class Processor; class TracepointListModel: public BaseStoppointListModel, public TrackableMixin { Q_OBJECT public: TracepointListModel(StoppointSet* tracepoints, QObject* parent = 0); int columnCount(const QModelIndex&) const { return 1; } QVariant headerData(int section, Qt::Orientation orientation, int role) const; QVariant data(const QModelIndex& index, int role) const; bool IsDirty(int row) const { return dirtySet[row]; } void ClearDirty(int row); protected: virtual void StoppointAdded(); virtual void StoppointRemoved(int index); private: void onHit(size_t spIndex, const Stoppoint* stoppoint, Word addr, const Processor* cpu); // For the uninitiated: beware of the vector specialization! // It might be efficient but has weird semantics. Just ask the // mighty Internet. typedef std::vector BitSet; BitSet dirtySet; // We keep this here because of pure desperation: in a perfect // world disconnections should work automagically via // sigc::trackable. It refuses to work, however, for some reason // or other. Sigh. #if 0 sigc::connection onHitConnection; #endif }; class AsciiView: public QPlainTextEdit, public MemoryViewDelegate { Q_OBJECT public: static AsciiView* Create(Word start, Word end); virtual void Refresh(); private: static const unsigned int kUnicodeReplacementChar = 0xFFFD; AsciiView(Word start, Word end); const Word start; const Word end; }; #endif // QMPS_TRACE_BROWSER_PRIV_H umps3-3.0.5/src/frontends/qmps/tree_view.cc000066400000000000000000000057621435132321600206510ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2011 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "qmps/tree_view.h" #include #include "base/lang.h" #include "qmps/application.h" #include "qmps/ui_utils.h" TreeView::TreeView(const QString& name, const std::list& resizedToContents, bool persistItemState, QWidget* parent) : QTreeView(parent), resizedToContents(resizedToContents), persistItemState(persistItemState) { setObjectName(name); connect(header(), SIGNAL(sectionResized(int,int,int)), this, SLOT(sectionResized(int,int,int))); if (persistItemState) { itemStateKey = QString("%1/ExpandedItems").arg(objectName()); connect(this, SIGNAL(expanded(const QModelIndex&)), this, SLOT(saveItemState())); connect(this, SIGNAL(collapsed(const QModelIndex&)), this, SLOT(saveItemState())); } } void TreeView::setModel(QAbstractItemModel* model) { QTreeView::setModel(model); if (model == NULL) return; // Not really sure if this should be here, but otoh cannot think // of a _single case_ where it would be undesired. header()->setSectionsMovable(false); bool resizeCols = true; for (int i = 0; i < model->columnCount(); ++i) { QVariant v = Appl()->settings.value(QString("%1/Section%2Size").arg(objectName()).arg(i)); if (v.canConvert() && v.toInt()) { header()->resizeSection(i, v.toInt()); resizeCols = false; } } if (resizeCols) { for (int col : resizedToContents) resizeColumnToContents(col); } if (persistItemState) { QVariant var = Appl()->settings.value(itemStateKey); for (const QString& s : var.toStringList()) { bool ok; int row = s.toInt(&ok); if (!ok) continue; QModelIndex idx = model->index(row, 0); if (idx.isValid()) setExpanded(idx, true); } } } void TreeView::sectionResized(int logicalIndex, int oldSize, int newSize) { UNUSED_ARG(oldSize); Appl()->settings.setValue(QString("%1/Section%2Size").arg(objectName()).arg(logicalIndex), newSize); } void TreeView::saveItemState() { QStringList list; int nr = model()->rowCount(); for (int i = 0; i < nr; i++) if (isExpanded(model()->index(i, 0))) list << QString::number(i); Appl()->settings.setValue(itemStateKey, list); } umps3-3.0.5/src/frontends/qmps/tree_view.h000066400000000000000000000026231435132321600205040ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2011 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef QMPS_TREE_VIEW_H #define QMPS_TREE_VIEW_H #include #include class QAbstractItemModel; class TreeView : public QTreeView { Q_OBJECT public: TreeView(const QString& name, const std::list& resizedToContents, bool persistItemState = false, QWidget* parent = 0); void setModel(QAbstractItemModel* model); private Q_SLOTS: void sectionResized(int logicalIndex, int oldSize, int newSize); void saveItemState(); private: const std::list resizedToContents; const bool persistItemState; QString itemStateKey; }; #endif // QMPS_TREE_VIEW_H umps3-3.0.5/src/frontends/qmps/ui_utils.cc000066400000000000000000000040421435132321600205030ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "ui_utils.h" #include #include #include "umps/arch.h" #include "umps/symbol_table.h" static const char* const kBiosSpaceName = "ExecROM"; static const char* const kBootSpaceName = "BootROM"; const char* GetSymbolicAddress(const SymbolTable* symbolTable, Word asid, Word address, bool onlyFunctions, SWord* offset) { if (address >= RAM_BASE) { return symbolTable->Probe(asid, address, !onlyFunctions, offset); } else if (address >= KSEG0_BOOT_BASE) { *offset = address - KSEG0_BOOT_BASE; return kBootSpaceName; } else { *offset = address - KSEG0_BASE; return kBiosSpaceName; } } QString FormatAddress(Word addr) { return (QString("0x%1.%2") .arg((quint32) (addr >> 16), 4, 16, QChar('0')) .arg((quint32) (addr & 0x0000ffffU), 4, 16, QChar('0'))); } void SetFirstColumnSpanned(QTreeView* treeView, bool setting) { QAbstractItemModel* model = treeView->model(); QModelIndex rootIndex = treeView->rootIndex(); int rows = model->rowCount(rootIndex); for (int i = 0; i < rows; i++) treeView->setFirstColumnSpanned(i, rootIndex, setting); } umps3-3.0.5/src/frontends/qmps/ui_utils.h000066400000000000000000000025101435132321600203430ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef QMPS_UI_UTILS_H #define QMPS_UI_UTILS_H #include #include "umps/types.h" class SymbolTable; class QTreeView; const char* GetSymbolicAddress(const SymbolTable* symbolTable, Word asid, Word address, bool onlyFunctions, SWord* offset); QString FormatAddress(Word address); void SetFirstColumnSpanned(QTreeView* treeView, bool setting = true); #endif // QMPS_UI_UTILS_H umps3-3.0.5/src/include/000077500000000000000000000000001435132321600150005ustar00rootroot00000000000000umps3-3.0.5/src/include/CMakeLists.txt000066400000000000000000000000271435132321600175370ustar00rootroot00000000000000add_subdirectory(umps) umps3-3.0.5/src/include/umps/000077500000000000000000000000001435132321600157645ustar00rootroot00000000000000umps3-3.0.5/src/include/umps/CMakeLists.txt000066400000000000000000000001631435132321600205240ustar00rootroot00000000000000install(FILES aout.h arch.h bios_defs.h cp0.h regdef.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/umps3/umps) umps3-3.0.5/src/include/umps/aout.h000066400000000000000000000031461435132321600171110ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2004 Mauro Morsiani * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * This header file contains constant & macro definitions for .aout/.core * file types. */ #ifndef UMPS_AOUT_H #define UMPS_AOUT_H /* Number of BIOS reserved page frames */ #define N_BIOS_PAGES 1 /* * Core file header size in words: core file id tag (1W) + 1 full page frame * for BIOS exclusive use */ #define CORE_HDR_SIZE (N_BIOS_PAGES * 1024 + 1) /* * AOUT header entries */ #define AOUT_HE_TAG 0 #define AOUT_HE_ENTRY 1 #define AOUT_HE_TEXT_VADDR 2 #define AOUT_HE_TEXT_MEMSZ 3 #define AOUT_HE_TEXT_OFFSET 4 #define AOUT_HE_TEXT_FILESZ 5 #define AOUT_HE_DATA_VADDR 6 #define AOUT_HE_DATA_MEMSZ 7 #define AOUT_HE_DATA_OFFSET 8 #define AOUT_HE_DATA_FILESZ 9 #define AOUT_HE_GP_VALUE 42 #define N_AOUT_HDR_ENT 43 #endif /* !defined(UMPS_AOUT_H) */ umps3-3.0.5/src/include/umps/arch.h000066400000000000000000000142211435132321600170520ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010, 2011 Tomislav Jonjic * Copyright (C) 2020 Mattia Biondi, Mikey Goldweber * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * uMPS machine-specific constants, most notably bus & device memory * mapped register addresses. * * IMPORTANT: Keep this header assembler-safe! */ #ifndef UMPS_ARCH_H #define UMPS_ARCH_H /* * Generalities */ #define WORD_SIZE 4 #define WS WORD_SIZE #define MMIO_BASE 0x10000000 #define RAM_BASE 0x20000000 /* Segment-related constants */ #define KSEG0_BASE 0x00000000 #define KSEG0_BIOS_BASE 0x00000000 #define KSEG0_BOOT_BASE 0x1FC00000 #define KUSEG_BASE 0x80000000 /* Device register size */ #define DEV_REG_SIZE_W 4 #define DEV_REG_SIZE (DEV_REG_SIZE_W * WS) /* * Interrupt lines */ #define N_INTERRUPT_LINES 8 #define N_IL N_INTERRUPT_LINES /* Number of interrupt lines available to devices */ #define N_EXT_IL 5 /* Devices per interrupt line */ #define N_DEV_PER_IL 8 #define DEV_IL_START (N_INTERRUPT_LINES - N_EXT_IL) #define IL_IPI 0 #define IL_CPUTIMER 1 #define IL_TIMER 2 #define IL_DISK 3 #define IL_FLASH 4 #define IL_ETHERNET 5 #define IL_PRINTER 6 #define IL_TERMINAL 7 #define EXT_IL_INDEX(il) ((il) - DEV_IL_START) /* * Bus and device register definitions * * Device interrupt lines are identified by the range [3, 7], * i.e. their repsective physical interrupt lines. Keep this in mind * when using the macros below. This is slightly confusing, but so is * any alternative. */ /* Bus register space */ #define BUS_REG_RAM_BASE 0x10000000 #define BUS_REG_RAM_SIZE 0x10000004 #define BUS_REG_BIOS_BASE 0x10000008 #define BUS_REG_BIOS_SIZE 0x1000000C #define BUS_REG_BOOT_BASE 0x10000010 #define BUS_REG_BOOT_SIZE 0x10000014 #define BUS_REG_TOD_HI 0x10000018 #define BUS_REG_TOD_LO 0x1000001C #define BUS_REG_TIMER 0x10000020 #define BUS_REG_TIME_SCALE 0x10000024 /* TLB floor address */ #define TLB_FLOOR_ADDR 0x10000028 /* Installed devices bitmap */ #define IDEV_BITMAP_BASE 0x1000002C #define IDEV_BITMAP_END (IDEV_BITMAP_BASE + N_EXT_IL * WS) #define IDEV_BITMAP_ADDR(line) (IDEV_BITMAP_BASE + ((line) - DEV_IL_START) * WS) /* Interrupting devices bitmap */ #define CDEV_BITMAP_BASE 0x10000040 #define CDEV_BITMAP_END (CDEV_BITMAP_BASE + N_EXT_IL * WS) #define CDEV_BITMAP_ADDR(line) (CDEV_BITMAP_BASE + ((line) - DEV_IL_START) * WS) /* Device register area */ #define DEV_REG_START 0x10000054 #define DEV_REG_ADDR(line, dev) (DEV_REG_START + ((line) - DEV_IL_START) * N_DEV_PER_IL * DEV_REG_SIZE + (dev) * DEV_REG_SIZE) /* End of memory mapped external device registers area */ #define DEV_REG_END (DEV_REG_START + N_EXT_IL * N_DEV_PER_IL * DEV_REG_SIZE) /* * Interrupt Routing Table (IRT) */ #define IRT_BASE 0x10000300 #define IRT_END 0x100003c0 /* (IRT_BASE + (N_EXT_IL + 1) * N_DEV_PER_IL * WS) */ #define IRT_ENTRY(line, dev) (IRT_BASE + WS * (((line) - IL_TIMER) * N_DEV_PER_IL + dev)) #define IRT_ENTRY_POLICY_MASK 0x10000000 #define IRT_ENTRY_POLICY_BIT 28 #define IRT_ENTRY_GET_POLICY(x) (((x) & IRT_ENTRY_POLICY_MASK) >> IRT_ENTRY_POLICY_BIT) #define IRT_ENTRY_DEST_MASK 0x0000ffff #define IRT_ENTRY_DEST_BIT 0 #define IRT_ENTRY_GET_DEST(x) (((x) & IRT_ENTRY_DEST_MASK) >> IRT_ENTRY_DEST_BIT) /* Interrupt routing policies */ #define IRT_POLICY_FIXED 0 #define IRT_POLICY_DYNAMIC 1 /* * Int. controller cpu inteface (banked) register set */ #define CPUCTL_INBOX 0x10000400 #define CPUCTL_INBOX_MSG_MASK 0x000000ff #define CPUCTL_INBOX_MSG_BIT 0 #define CPUCTL_INBOX_GET_MSG(x) (((x) & CPUCTL_INBOX_MSG_MASK) >> CPUCTL_INBOX_MSG_BIT) #define CPUCTL_INBOX_ORIGIN_MASK 0x00000f00 #define CPUCTL_INBOX_ORIGIN_BIT 8 #define CPUCTL_INBOX_GET_ORIGIN(x) (((x) & CPUCTL_INBOX_ORIGIN_MASK) >> CPUCTL_INBOX_ORIGIN_BIT) #define CPUCTL_OUTBOX 0x10000404 #define CPUCTL_OUTBOX_MSG_MASK 0x000000ff #define CPUCTL_OUTBOX_MSG_BIT 0 #define CPUCTL_OUTBOX_GET_MSG(x) (((x) & CPUCTL_OUTBOX_MSG_MASK) >> CPUCTL_OUTBOX_MSG_BIT) #define CPUCTL_OUTBOX_RECIP_MASK 0x00ffff00 #define CPUCTL_OUTBOX_RECIP_BIT 8 #define CPUCTL_OUTBOX_GET_RECIP(x) (((x) & CPUCTL_OUTBOX_RECIP_MASK) >> CPUCTL_OUTBOX_RECIP_BIT) #define CPUCTL_TPR 0x10000408 #define CPUCTL_TPR_PRIORITY_MASK 0x0000000f #define CPUCTL_BIOS_RES_0 0x1000040c #define CPUCTL_BIOS_RES_1 0x10000410 #define CPUCTL_BASE CPUCTL_INBOX #define CPUCTL_END (CPUCTL_BIOS_RES_1 + WS) /* * Machine control registers */ #define MCTL_NCPUS 0x10000500 #define MCTL_RESET_CPU 0x10000504 #define MCTL_RESET_CPU_CPUID_MASK 0x0000000f /* Reset vector and initial $sp */ #define MCTL_BOOT_PC 0x10000508 #define MCTL_BOOT_SP 0x1000050c #define MCTL_DEFAULT_BOOT_PC 0x1fc00000 #define MCTL_DEFAULT_BOOT_SP 0x00000000 #define MCTL_HALT_CPU 0x10000510 #define MCTL_POWER 0x10000514 #define MCTL_BASE MCTL_NCPUS #define MCTL_END (MCTL_POWER + WS) #define MMIO_END MCTL_END #endif /* !defined(UMPS_ARCH_H) */ umps3-3.0.5/src/include/umps/bios_defs.h000066400000000000000000000026441435132321600201000ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef BIOS_DEFS_H #define BIOS_DEFS_H /* * BIOS services, invoked via break traps * FIXME: Are all of these safe to use (abuse?) as break codes? */ #define BIOS_SRV_LDCXT 0 #define BIOS_SRV_LDST 1 #define BIOS_SRV_PANIC 2 #define BIOS_SRV_HALT 3 /* * We use the BIOS-reserved registers as pointers to BIOS related data * structures (the exception vector and the PC/SP exception handlers) */ #define BIOS_EXCPT_VECT_BASE CPUCTL_BIOS_RES_0 #define BIOS_PC_AREA_BASE CPUCTL_BIOS_RES_1 /* BIOS Data Page base address */ #define BIOS_DATA_PAGE_BASE 0x0FFFF000 #define BIOS_EXEC_HANDLERS_ADDRS 0x0FFFF900 #endif /* BIOS_DEFS_H */ umps3-3.0.5/src/include/umps/cp0.h000066400000000000000000000076531435132321600166320ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef UMPS_CP0_H #define UMPS_CP0_H /* * CP0 registers */ #define CP0_Index 0 #define CP0_Random 1 #define CP0_EntryLo 2 #define CP0_BadVAddr 8 #define CP0_Timer 9 #define CP0_EntryHi 10 #define CP0_Status 12 #define CP0_Cause 13 #define CP0_EPC 14 #define CP0_PRID 15 /* * CP0 Status fields */ #define STATUS_IEc 0x00000001 #define STATUS_IEc_BIT 0 #define STATUS_KUc 0x00000002 #define STATUS_KUc_BIT 1 #define STATUS_IEp 0x00000004 #define STATUS_IEp_BIT 2 #define STATUS_KUp 0x00000008 #define STATUS_KUp_BIT 3 #define STATUS_IEo 0x00000010 #define STATUS_IEo_BIT 4 #define STATUS_KUo 0x00000020 #define STATUS_KUo_BIT 5 #define STATUS_IM_MASK 0x0000ff00 #define STATUS_IM(line) (1U << (8 + (line))) #define STATUS_IM_BIT(line) (8 + (line)) #define STATUS_BEV 0x00400000 #define STATUS_BEV_BIT 22 #define STATUS_TE 0x08000000 #define STATUS_TE_BIT 27 #define STATUS_CU0 0x10000000 #define STATUS_CU0_BIT 28 #define STATUS_CU1 0x20000000 #define STATUS_CU1_BIT 29 #define STATUS_CU2 0x40000000 #define STATUS_CU2_BIT 30 #define STATUS_CU3 0x80000000 #define STATUS_CU3_BIT 31 /* * CP0 Cause fields */ #define CAUSE_EXCCODE_MASK 0x0000007c #define CAUSE_EXCCODE_BIT 2 #define CAUSE_GET_EXCCODE(x) (((x) & CAUSE_EXCCODE_MASK) >> CAUSE_EXCCODE_BIT) /* Exception codes - naming follows standard MIPS mnemonics */ #define EXC_INT 0 #define EXC_MOD 1 #define EXC_TLBL 2 #define EXC_TLBS 3 #define EXC_ADEL 4 #define EXC_ADES 5 #define EXC_IBE 6 #define EXC_DBE 7 #define EXC_SYS 8 #define EXC_BP 9 #define EXC_RI 10 #define EXC_CPU 11 #define EXC_OV 12 #define CAUSE_IP_MASK 0x0000ff00 #define CAUSE_IP(line) (1U << (8 + (line))) #define CAUSE_IP_BIT(line) (8 + (line)) #define CAUSE_CE_MASK 0x30000000 #define CAUSE_CE_BIT 28 #define CAUSE_GET_CE(x) (((x) & CAUSE_CE_MASK) >> CAUSE_CE_BIT) #define CAUSE_BD 0x80000000 #define CAUSE_BD_BIT 31 /* * CP0 EntryHi fields */ #define ENTRYHI_SEGNO_MASK 0xc0000000 #define ENTRYHI_SEGNO_BIT 30 #define ENTRYHI_GET_SEGNO(x) (((x) & ENTRYHI_SEGNO_MASK) >> ENTRYHI_SEGNO_BIT) #define ENTRYHI_VPN_MASK 0x3ffff000 #define ENTRYHI_VPN_BIT 12 #define ENTRYHI_GET_VPN(x) (((x) & ENTRYHI_VPN_MASK) >> ENTRYHI_VPN_BIT) #define ENTRYHI_ASID_MASK 0x00000fc0 #define ENTRYHI_ASID_BIT 6 #define ENTRYHI_GET_ASID(x) (((x) & ENTRYHI_ASID_MASK) >> ENTRYHI_ASID_BIT) /* * CP0 EntryLo fields */ #define ENTRYLO_PFN_MASK 0xfffff000 #define ENTRYLO_PFN_BIT 12 #define ENTRYLO_GET_PFN(x) (((x) & ENTRYLO_PFN_MASK) >> ENTRYLO_PFN_BIT) #define ENTRYLO_DIRTY 0x00000400 #define ENTRYLO_DIRTY_BIT 10 #define ENTRYLO_VALID 0x00000200 #define ENTRYLO_VALID_BIT 9 #define ENTRYLO_GLOBAL 0x00000100 #define ENTRYLO_GLOBAL_BIT 8 #endif /* !defined(UMPS_CP0_H) */ umps3-3.0.5/src/include/umps/regdef.h000066400000000000000000000024721435132321600173760ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef REGDEF_H #define REGDEF_H /* * General purpose registers */ #define v0 2 #define v1 3 #define a0 4 #define a1 5 #define a2 6 #define a3 7 #define t0 8 #define t1 9 #define t2 10 #define t3 11 #define t4 12 #define t5 13 #define t6 14 #define t7 15 #define s0 16 #define s1 17 #define s2 18 #define s3 19 #define s4 20 #define s5 21 #define s6 22 #define s7 23 #define t8 24 #define t9 25 #define k0 26 #define k1 27 #define gp 28 #define sp 29 #define fp 30 #define s8 30 /* s8 == fp */ #define ra 31 #endif /* !defined(REGDEF_H) */ umps3-3.0.5/src/support/000077500000000000000000000000001435132321600150715ustar00rootroot00000000000000umps3-3.0.5/src/support/CMakeLists.txt000066400000000000000000000002241435132321600176270ustar00rootroot00000000000000set(UMPS_LIB_DIR ${CMAKE_INSTALL_LIBDIR}/umps3) add_subdirectory(bios) add_subdirectory(crt) add_subdirectory(ldscripts) add_subdirectory(libumps) umps3-3.0.5/src/support/bios/000077500000000000000000000000001435132321600160255ustar00rootroot00000000000000umps3-3.0.5/src/support/bios/CMakeLists.txt000066400000000000000000000034141435132321600205670ustar00rootroot00000000000000set(ROM_TYPES coreboot exec) foreach(TYPE ${ROM_TYPES}) install(FILES ${TYPE}.S DESTINATION ${UMPS_DATA_DIR}) endforeach() set(BIOS_CFLAGS -mips1 -mabi=32 -mno-abicalls -fno-pic -ffreestanding -mfp32) set(BIOS_CPPFLAGS -I${PROJECT_SOURCE_DIR}/src/include) foreach(TYPE ${ROM_TYPES}) add_custom_target(${TYPE}.eb ALL COMMAND ${XCGCC} -c -EB ${BIOS_CPPFLAGS} ${BIOS_CFLAGS} -o ${CMAKE_CURRENT_BINARY_DIR}/${TYPE}.eb ${TYPE}.S WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) add_custom_target(${TYPE}.el ALL COMMAND ${XCGCC} -c -EL ${BIOS_CPPFLAGS} ${BIOS_CFLAGS} -o ${CMAKE_CURRENT_BINARY_DIR}/${TYPE}.el ${TYPE}.S WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) add_custom_target(${TYPE}.eb.rom.umps ALL COMMAND umps3-elf2umps -v -b ${TYPE}.eb WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) add_dependencies(${TYPE}.eb.rom.umps ${TYPE}.eb umps3-elf2umps) add_custom_target(${TYPE}.el.rom.umps ALL COMMAND umps3-elf2umps -v -b ${TYPE}.el WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) add_dependencies(${TYPE}.el.rom.umps ${TYPE}.el umps3-elf2umps) set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES ${TYPE}.eb ${TYPE}.el ${TYPE}.eb.rom.umps ${TYPE}.el.rom.umps) endforeach() if(${WORDS_BIGENDIAN}) set(ENDIAN eb) else() set(ENDIAN el) endif() foreach(TYPE ${ROM_TYPES}) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${TYPE}.${ENDIAN}.rom.umps DESTINATION ${UMPS_DATA_DIR} RENAME ${TYPE}.rom.umps) endforeach() umps3-3.0.5/src/support/bios/coreboot.S000066400000000000000000000060671435132321600177760ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2004 Mauro Morsiani * Copyright (C) 2020 Mattia Biondi, Mikey Goldweber * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "umps/regdef.h" #include "umps/arch.h" #include "umps/cp0.h" #include "umps/bios_defs.h" /* Constants */ STATUSMASK = 0xFFBFFFF3 BIOSPANIC = 0x00000108 VECTAREANUM = 8 VECTPCPOS = 12 VECTSIZE = 140 PROGVSTART = 0x20001004 PADDINGTLB = 0x100 - 8 PADDINGEXC = 0x80 - 16 .text .align 2 .globl coreboot .type coreboot,@function .ent coreboot coreboot: .frame $fp,0,$ra .mask 0x00000000,0 .fmask 0x00000000,0 .set noat .set noreorder .set nomacro /* boot starts here */ b LCoreBootStart nop .space PADDINGTLB /* TLB miss should not happen during boot phase: jumps to BIOS PANIC */ lui $k0, 0x0000 ori $k0, $k0, 0x108 jr $k0 nop .space PADDINGEXC /* no other exceptions should happen during boot phase: jumps to BIOS PANIC */ lui $k0, 0x0000 ori $k0, $k0, 0x108 jr $k0 nop .set reorder .set macro LCoreBootStart: /* * Mapping all exceptions to BIOS code * This is done resetting bit 22 in STATUS CP0 register * we also assure that kernel will start in kernel mode with * interrupts disabled: this is done zeroing bits 3 and 2. */ mfc0 $a0, $CP0_Status li $a1, STATUSMASK and $a0, $a0, $a1 mtc0 $a0, $CP0_Status /* * Setting Exception PCs, both for general exceptions and for TLB * refills to cause a kernel panic(). This is done by loading * panic() BIOS routine address into both PC fields */ li $a0, BIOS_EXEC_HANDLERS_ADDRS li $a1, BIOSPANIC /* panic() address */ sw $a1, 0($a0) /* set TLB_Refill handler addr */ sw $a1, 8($a0) /* Set exception handler addr */ /* We use per/cpu bios reserved registers to point to the */ /* BIOS exception vector and the Exception PCs/SPs */ li $t0, BIOS_EXCPT_VECT_BASE li $t1, BIOS_DATA_PAGE_BASE sw $t1, 0($t0) li $t0, BIOS_PC_AREA_BASE li $t1, BIOS_EXEC_HANDLERS_ADDRS sw $t1, 0($t0) /* * finally, we set EntryHI and STATUS for kernel: * being EntryHI = 0, and STATUS good as it is now, * do not touch anything */ /* Start the kernel: we find its starting address at a fixed place in the aout. */ li $a0, PROGVSTART lw $ra, 0($a0) .set noreorder .set nomacro jr $ra rfe .set reorder .set macro .end coreboot .size coreboot, . - coreboot umps3-3.0.5/src/support/bios/exec.S000066400000000000000000000210321435132321600170730ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2004 Mauro Morsiani * Copyright (C) 2011 Tomislav Jonjic * Copyright (C) 2020 Mattia Biondi, Mikey Goldweber * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "umps/regdef.h" #include "umps/arch.h" #include "umps/cp0.h" #include "umps/bios_defs.h" /* Constants */ #define ASIDSTEP 0x00000040 #define VPNMASK 0xFFFFF000 #define VECTSIZE 140 /* NOP padding: 30 nops - string length (32 bytes) */ #define GENEXC_PADDING (30 * 4) - 32 #define ENDSTRADDR 0x00000008 #define PANICSTRADDR (ENDSTRADDR + 16) /* Terminal device related stuff */ #define TERM0COMMAND DEV_REG_ADDR(IL_TERMINAL, 0) + 0xC #define BUSYCODE 3 #define BYTELEN 8 #define PRINTCHR 2 #define CAUSENEGMASK 0xFFFFFF83 #define KUPSTATUSMASK 0x00000008 #define SAFESTATUSMASK 0xFFFFFFFC /* * Code start */ .text .align 2 .globl bios .type bios,@function .ent bios bios: .frame $fp,0,$k1 .mask 0x00000000,0 .fmask 0x00000000,0 /* * Tell gas not to use $at in pseudoop expansions */ .set noat /* * 0x00000000 address * This is the entry point for UTLB type exceptions. */ .set noreorder .set nomacro b LUTLBHandler nop EndStr: .asciiz "System halted \n" PanicStr: .asciiz "kernel panic()\n" .space GENEXC_PADDING /* * 0x00000080 address * This is the entry point for general ("other", non-TLB) exceptions. */ b LEXCHandler nop .space 0x80 - 0x8 /* * 0x00000100 address * Secondary processor startup routine */ b LInitSecondaryProcessor nop /* * 0x00000108: panic() * Print a message on terminal 0 and loop forever */ .set reorder .set macro LPanic: li $a0, PANICSTRADDR - 1 LTermOp: li $a3, TERM0COMMAND li $t0, BUSYCODE LWaitForReady: lw $a2, -4($a3) beq $a2, $t0, LWaitForReady LPrintLoop: addiu $a0, 1 lbu $a1, 0($a0) # char 0 ends the string beq $0, $a1, LInfinite #prepares PRINTCHAR command sll $a1, BYTELEN addi $a1, PRINTCHR sw $a1, 0($a3) b LWaitForReady LInfinite: b LInfinite LHalt: # this call prints a message on terminal 0 and loops forever li $a0, ENDSTRADDR - 1 b LTermOp /* * Get a cpu up and runing: initialize BIOS related structures and * load the supplied processor state. */ LInitSecondaryProcessor: /* Initialize ptr to exception state vector */ li $t0, VECTSIZE mfc0 $t1, $CP0_PRID mult $t0, $t1 mflo $t0 li $t2, BIOS_DATA_PAGE_BASE add $t0, $t0, $t2 li $t2, BIOS_EXCPT_VECT_BASE sw $t0, 0($t2) /* Initialize ptr to PC/SP area */ li $t0, 16 mult $t0, $t1 mflo $t0 li $t1, BIOS_EXEC_HANDLERS_ADDRS add $t0, $t0, $t1 li $t2, BIOS_PC_AREA_BASE sw $t0, 0($t2) /* Load the new state - address of start_state cached at start of exception vector */ li $t0, BIOS_EXCPT_VECT_BASE sw $k0, 0($t0) b LLoadStart /* * Handle TLBL/TLBS refill misses * $k1 is address of kernel branch address (TLB Refill handler) */ LUTLBHandler: lw $k1, BIOS_PC_AREA_BASE b LSaveExcptAndJmp /* * General exception handler * * k0 and k1 registers are always available (never have live * values outside BIOS) */ LEXCHandler: /* Copy Cause.ExcCode to k0 and dispatch accordingly */ mfc0 $k0, $CP0_Cause andi $k0, $k0, CAUSE_EXCCODE_MASK srl $k0, CAUSE_EXCCODE_BIT /* cause 9 is BREAK */ addi $k0, -9 beq $k0, $0, LBreakHandler /* Handle all other exception types the same */ /* If EPC address is in BIOS area, something in kernel has * gone horribly wrong: eg. BIOS running with int unmasked (a big * bug for BIOS). */ mfc0 $k0, $CP0_EPC li $k1, BUS_REG_RAM_BASE subu $k0, $k0, $k1 bltz $k0, LPanic /* Else: save processor state into BIOS data page and pass * along handling to the kernel. Put Addr of kernel PC handler's * location in $k1 */ lw $k1, BIOS_PC_AREA_BASE addi $k1, $k1, 8 b LSaveExcptAndJmp LBreakHandler: /* If EPC address is in BIOS area, something in kernel or in BIOS * has gone horribly wrong (a BIOS bug probably). */ mfc0 $k0, $CP0_EPC li $k1, BUS_REG_RAM_BASE subu $k0, $k0, $k1 bltz $k0, LPanic /* * A BIOS service routine is requested: * look into $a0 register for identification. */ move $k1, $a0 /* any BREAK may be executed only in kernel mode * kernel mode of caller is set when KUP bit in STATUS mask is 0 */ mfc0 $k0, $CP0_Status andi $k0, $k0, KUPSTATUSMASK beq $k0, $0, LisKernel /* If in user mode, pass along to kernel */ li $k1, BIOS_PC_AREA_BASE addi $k1, $k1, 8 b LSaveExcptAndJmp LisKernel: /* 0 is LDCXT(stackPtr, STATUS, PC) */ beq $0, $k1, LLDCXT addi $k1, -1 /* 1 is LDST */ beq $0, $k1, LLDST addi $k1, -1 /* 2 is PANIC routine */ beq $0, $k1, LPanic addi $k1, -1 /* 3 is HALT routine */ beq $0, $k1, LHalt /* any other break is passed up to SYS handler */ li $k1, BIOS_PC_AREA_BASE addi $k1, $k1, 8 b LSaveExcptAndJmp LLDCXT: # $a1 is stackPtr, $a2 is new STATUS, $a3 is PC # STATUS preparation move $k1, $a2 # this for BIOS safety: no KU or IE bits on li $k0, SAFESTATUSMASK and $k1, $k1, $k0 # STATUS loading mtc0 $k1, $CP0_Status # stackPtr loading move $sp, $a1 # get new PC and jump move $k1, $a3 move $t9, $a3 .set noreorder .set nomacro jr $k1 rfe .set reorder .set macro LLDST: # this means load from physical address in $a1 move $k0, $a1 b LLoadStart /* * Save cpu (exception) state into BIOS data page, load PC and SP * and pass along handling to the kernel * Kernel handler address (PC) is in $k1 and SP is in $k1 + 4 */ LSaveExcptAndJmp: lw $k0, BIOS_EXCPT_VECT_BASE LSave: sw $1, 16($k0) sw $v0, 20($k0) sw $v1, 24($k0) sw $a0, 28($k0) sw $a1, 32($k0) sw $a2, 36($k0) sw $a3, 40($k0) sw $t0, 44($k0) sw $t1, 48($k0) sw $t2, 52($k0) sw $t3, 56($k0) sw $t4, 60($k0) sw $t5, 64($k0) sw $t6, 68($k0) sw $t7, 72($k0) sw $s0, 76($k0) sw $s1, 80($k0) sw $s2, 84($k0) sw $s3, 88($k0) sw $s4, 92($k0) sw $s5, 96($k0) sw $s6, 100($k0) sw $s7, 104($k0) sw $t8, 108($k0) sw $t9, 112($k0) /* $k0 and $k1 are not saved */ sw $gp, 116($k0) sw $sp, 120($k0) sw $fp, 124($k0) sw $ra, 128($k0) /* Need a third register use $t0 - temporarily */ mfhi $t0 sw $t0, 132($k0) mflo $t0 sw $t0, 136($k0) /* all processor registers saved */ /* gets EntryHI and stores it */ mfc0 $t0, $CP0_EntryHi sw $t0, 0($k0) /* gets CAUSE register and stores it */ mfc0 $t0, $CP0_Cause sw $t0, 4($k0) /* and now save STATUS and EPC registers */ mfc0 $t0, $CP0_Status sw $t0, 8($k0) mfc0 $t0, $CP0_EPC sw $t0, 12($k0) /* Restore $t0 */ lw $t0, 44($k0) /* All registers saved */ /* Load SP with supplied value */ lw $sp, 4($k1) /* Pass Control along to kernel */ lw $k1, 0($k1) jr $k1 nop /* * Load CPU/CP0 registers from (state_t*) $k0 */ LLoadStart: lw $1, 16($k0) lw $v0, 20($k0) lw $v1, 24($k0) lw $a0, 28($k0) lw $a1, 32($k0) lw $a2, 36($k0) lw $a3, 40($k0) lw $t0, 44($k0) lw $t1, 48($k0) lw $t2, 52($k0) lw $t3, 56($k0) lw $t4, 60($k0) lw $t5, 64($k0) lw $t6, 68($k0) lw $t7, 72($k0) lw $s0, 76($k0) lw $s1, 80($k0) lw $s2, 84($k0) lw $s3, 88($k0) lw $s4, 92($k0) lw $s5, 96($k0) lw $s6, 100($k0) lw $s7, 104($k0) lw $t8, 108($k0) lw $t9, 112($k0) /* $k0 and $k1 are not saved so they are not loaded too */ lw $gp, 116($k0) lw $sp, 120($k0) lw $fp, 124($k0) lw $ra, 128($k0) lw $k1, 132($k0) mthi $k1 lw $k1, 136($k0) mtlo $k1 /* all processor registers loaded (almost) */ /* storing new EntryHI into CP0 register */ lw $k1, 0($k0) mtc0 $k1, $CP0_EntryHi /* storing new CAUSE into CP0 register */ lw $k1, 4($k0) mtc0 $k1, $CP0_Cause /* now load STATUS register */ lw $k1, 8($k0) /* This is for avoiding trouble if STATUS has (erroneously) * set bit 0/1. It would cause an immediate EXC trap or expose * BIOS to interrupts. */ srl $k1, 2 sll $k1, 2 mtc0 $k1, $CP0_Status lw $k1, 12($k0) /* load new PC and jump */ .set noreorder .set nomacro jr $k1 rfe .set reorder .set macro .end bios .size bios, . - bios umps3-3.0.5/src/support/crt/000077500000000000000000000000001435132321600156615ustar00rootroot00000000000000umps3-3.0.5/src/support/crt/CMakeLists.txt000066400000000000000000000012001435132321600204120ustar00rootroot00000000000000set(CRT_FILES crtso crti) set(CRT_CFLAGS -ffreestanding -ansi -Wall -c -mips1 -mabi=32 -mfp32 -mno-gpopt -G 0 -mno-abicalls -fno-pic) set(CRT_CPPFLAGS -I${PROJECT_SOURCE_DIR}/src/include) foreach(FILE ${CRT_FILES}) add_custom_target(${FILE}.o ALL COMMAND ${XCGCC} ${CRT_CPPFLAGS} ${CRT_CFLAGS} -o ${CMAKE_CURRENT_BINARY_DIR}/${FILE}.o ${FILE}.S WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES ${FILE}.o) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${FILE}.o DESTINATION ${UMPS_LIB_DIR}) install(FILES ${FILE}.S DESTINATION ${UMPS_DATA_DIR}) endforeach() umps3-3.0.5/src/support/crt/crti.S000066400000000000000000000034601435132321600167510ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2011 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * This module contains startup code for non-PIC o32 .aout * executables. * * PLEASE NOTE: * * - The stack pointer ($sp) must be initialized by the kernel! * * - The TERMINATE_SYSCALL (see below) must be set to the appropriate * code, if the program is ever expected to return from main(). */ #include #include #define TEXT_VADDR 0x80000000 /* Set this to a suitable value */ #define TERMINATE_SYSCALL 0 .text .align 2 .globl __start .type __start, @function .ent __start .extern main __start: .frame $sp, 16, $ra .mask 0x00000000, 0 .fmask 0x00000000, 0 /* Initialize the $gp register. The initial gp value is found in the .aout header (and also in the linker-defined `_gp' symbol). */ li $t0, TEXT_VADDR + (AOUT_HE_GP_VALUE << 2) lw $gp, 0($t0) /* Call main */ addiu $sp, $sp, -16 jal main addiu $sp, $sp, 16 /* Invoke "terminate" syscall */ li $a0, TERMINATE_SYSCALL syscall /* Not reached! */ .end __start .size __start, .-__start umps3-3.0.5/src/support/crt/crtso.S000066400000000000000000000030341435132321600171370ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #define CORE_TEXT_VADDR 0x20001000 #define KERNELSTACKSIZE 0x00001000 .text .align 2 .globl __start .type __start, @function .ent __start .extern main __start: .frame $sp, 16, $ra .mask 0x00000000, 0 .fmask 0x00000000, 0 /* computes stack bottom (end of first page of RAM) using $k0 and $k1 (k0 and k1) */ lw $k0, BUS_REG_RAM_BASE li $k1, KERNELSTACKSIZE addu $k0, $k0, $k1 addiu $sp, $k0, -8 /* Initialize $gp */ lw $gp, CORE_TEXT_VADDR + (AOUT_HE_GP_VALUE << 2) addiu $sp, $sp, -16 jal main addiu $sp, $sp, 16 .set noreorder .set nomacro li $a0, BIOS_SRV_HALT nop break nop .set reorder .set macro .end __start .size __start, .-__start umps3-3.0.5/src/support/ldscripts/000077500000000000000000000000001435132321600171005ustar00rootroot00000000000000umps3-3.0.5/src/support/ldscripts/CMakeLists.txt000066400000000000000000000002151435132321600216360ustar00rootroot00000000000000install(FILES umpsaout.ldscript DESTINATION ${UMPS_DATA_DIR}) install(FILES umpscore.ldscript DESTINATION ${UMPS_DATA_DIR}) umps3-3.0.5/src/support/ldscripts/umpsaout.ldscript000066400000000000000000000047651435132321600225370ustar00rootroot00000000000000OUTPUT_ARCH(mips) ENTRY(__start) PHDRS { text PT_LOAD FILEHDR PHDRS; data PT_LOAD; } SECTIONS { /* * Text segment content */ . = 0x800000B0; .text : { _ftext = . ; *(.text .text.*) } :text =0 PROVIDE (_etext = .); .rodata : { *(.rodata .rodata.*) } :text .rodata1 : { *(.rodata1) } :text /* Contrary to what the names might lead you to believe, .sdata2 and .sbss2 are used for _const_ initialized/uninitialized _small_ data only! Hence they belong in the text segment. Also note, parenthetically, that .sdata2 and .sbss2 are utterly idiotic names for those sections. In any case, the toolchain does _not_ create them automatically (for gpopt, MIPS uses .sdata and .sbss instead). They are here for completeness (and to confuse people). */ .sdata2 : { *(.sdata2 .sdata2.*) } :text .sbss2 : { *(.sbss2 .sbss2.*) } :text /* * Data segment */ /* Adjust the address for the data segment. We want to adjust up to the same address within the page on the next page up. */ . = ALIGN(0x1000); .data : { _fdata = . ; *(.data .data.*) } :data .data1 : { *(.data1) } :data _gp = ALIGN(16) + 0x7ff0; .got : { *(.got.plt) *(.got) } :data /* We want the small data sections together, so single-instruction offsets can access them all, and initialized data all before uninitialized, so we can shorten the on-disk segment size. */ .sdata : { *(.sdata .sdata.*) } :data .lit8 : { *(.lit8) } :data .lit4 : { *(.lit4) } :data _edata = .; __bss_start = .; _fbss = .; .sbss : { PROVIDE (__sbss_start = .); *(.sbss .sbss.*) *(.scommon) PROVIDE (__sbss_end = .); } :data .bss : { *(.bss .bss.*) *(COMMON) /* Align here to ensure that the .bss section occupies space up to _end. Align after .bss to ensure correct alignment even if the .bss section disappears because there are no input sections. */ . = ALIGN(32 / 8); } :data . = ALIGN(32 / 8); _end = .; PROVIDE (end = .); /DISCARD/ : { /* Get rid of MIPS SVR4 ABI mandated .reginfo garbage, since different targets appear to have different ideas about this section's properties (e.g. allocatable vs non). We can obtain ri_gp_value from the symbol table anyway. */ *(.reginfo) } } umps3-3.0.5/src/support/ldscripts/umpscore.ldscript000066400000000000000000000047631435132321600225150ustar00rootroot00000000000000OUTPUT_ARCH(mips) ENTRY(__start) PHDRS { text PT_LOAD; data PT_LOAD; } SECTIONS { /* * Text segment content */ . = 0x20001000; .text : { _ftext = . ; . = 0xb0; *(.text .text.*) } :text =0 PROVIDE (_etext = .); .rodata : { *(.rodata .rodata.*) } :text .rodata1 : { *(.rodata1) } :text /* Contrary to what the names might lead you to believe, .sdata2 and .sbss2 are used for _const_ initialized/uninitialized _small_ data only! Hence they belong in the text segment. Also note, parenthetically, that .sdata2 and .sbss2 are utterly idiotic names for those sections. In any case, the toolchain does _not_ create them automatically (for gpopt, MIPS uses .sdata and .sbss instead). They are here for completeness (and to confuse people). */ .sdata2 : { *(.sdata2 .sdata2.*) } :text .sbss2 : { *(.sbss2 .sbss2.*) } :text /* * Data segment */ /* Adjust the address for the data segment. We want to adjust up to the same address within the page on the next page up. */ . = ALIGN(0x1000); .data : { _fdata = . ; *(.data .data.*) } :data .data1 : { *(.data1) } :data _gp = ALIGN(0x800); .got : { *(.got.plt) *(.got) } :data /* We want the small data sections together, so single-instruction offsets can access them all, and initialized data all before uninitialized, so we can shorten the on-disk segment size. */ .sdata : { *(.sdata .sdata.*) } :data .lit8 : { *(.lit8) } :data .lit4 : { *(.lit4) } :data _edata = .; __bss_start = .; _fbss = .; .sbss : { PROVIDE (__sbss_start = .); *(.sbss .sbss.*) *(.scommon) PROVIDE (__sbss_end = .); } :data .bss : { *(.bss .bss.*) *(COMMON) /* Align here to ensure that the .bss section occupies space up to _end. Align after .bss to ensure correct alignment even if the .bss section disappears because there are no input sections. */ . = ALIGN(32 / 8); } :data . = ALIGN(32 / 8); _end = .; PROVIDE (end = .); /DISCARD/ : { /* Get rid of MIPS SVR4 ABI mandated .reginfo garbage, since different targets appear to have different ideas about this section's properties (e.g. allocatable vs non). We can obtain ri_gp_value from the symbol table anyway. */ *(.reginfo) } } umps3-3.0.5/src/support/libumps/000077500000000000000000000000001435132321600165445ustar00rootroot00000000000000umps3-3.0.5/src/support/libumps/CMakeLists.txt000066400000000000000000000017771435132321600213200ustar00rootroot00000000000000set(UMPS_INCLUDE_DIR ${CMAKE_INSTALL_INCLUDEDIR}/umps3/umps) set(LIBUMPS_CFLAGS -ffreestanding -ansi -Wall -c -mips1 -mabi=32 -mfp32 -mno-gpopt -G 0 -mno-abicalls -fno-pic) set(LIBUMPS_CPPFLAGS -I${PROJECT_SOURCE_DIR}/src/include) add_custom_target(libumps.o ALL COMMAND ${XCGCC} ${LIBUMPS_CPPFLAGS} ${LIBUMPS_CFLAGS} -o ${CMAKE_CURRENT_BINARY_DIR}/libumps.o libumps.S WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) add_custom_target(libumps.e ALL COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/libumps.h ${CMAKE_CURRENT_BINARY_DIR} COMMAND ${CMAKE_COMMAND} -E create_symlink libumps.h libumps.e WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES libumps.o libumps.h libumps.e) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libumps.o DESTINATION ${UMPS_LIB_DIR}) install(FILES libumps.h ${CMAKE_CURRENT_BINARY_DIR}/libumps.e const.h types.h DESTINATION ${UMPS_INCLUDE_DIR}) install(FILES libumps.S DESTINATION ${UMPS_DATA_DIR}) umps3-3.0.5/src/support/libumps/const.h000066400000000000000000000063351435132321600200520ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2020 Mikey Goldweber * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /**************************************************************************** * * This header file contains utility constants & macro definitions. * ****************************************************************************/ #ifndef UMPS_CONSTS_H #define UMPS_CONSTS_H /* Hardware & software constants */ #define PAGESIZE 4096 /* page size in bytes */ #define WORDLEN 4 /* word size in bytes */ /* timer, timescale, TOD-LO and other bus regs */ #define RAMBASEADDR 0x10000000 #define TODLOADDR 0x1000001C #define INTERVALTMR 0x10000020 #define TIMESCALEADDR 0x10000024 /* utility constants */ #define TRUE 1 #define FALSE 0 #define HIDDEN static #define EOS '\0' #define NULL ((void *)0) /* device interrupts */ #define DISKINT 3 #define FLASHINT 4 #define NETWINT 5 #define PRNTINT 6 #define TERMINT 7 #define DEVINTNUM 5 /* interrupt lines used by devices */ #define DEVPERINT 8 /* devices per interrupt line */ #define DEVREGLEN 4 /* device register field length in bytes, and regs per dev */ #define DEVREGSIZE 16 /* device register size in bytes */ /* device register field number for non-terminal devices */ #define STATUS 0 #define COMMAND 1 #define DATA0 2 #define DATA1 3 /* device register field number for terminal devices */ #define RECVSTATUS 0 #define RECVCOMMAND 1 #define TRANSTATUS 2 #define TRANCOMMAND 3 /* device common STATUS codes */ #define UNINSTALLED 0 #define READY 1 #define BUSY 3 /* device common COMMAND codes */ #define RESET 0 #define ACK 1 /* Memory related constants */ #define KSEG0 0x00000000 #define KSEG1 0x20000000 #define KSEG2 0x40000000 #define KUSEG 0x80000000 #define RAMSTART 0x20000000 #define BIOSDATAPAGE 0x0FFFF000 /* Useful Operations */ #define MIN(A,B) ((A) < (B) ? A : B) #define MAX(A,B) ((A) < (B) ? B : A) #define ALIGNED(A) (((unsigned)A & 0x3) == 0) /* Macro to load the Interval Timer */ #define LDIT(T) ((*((cpu_t *) INTERVALTMR)) = (T) *(*((cpu_t *) TIMESCALEADDR))) /* Macro to read the TOD clock */ #define STCK(T) ((T) = ((*((cpu_t *) TODLOADDR)) / (*((cpu_t *) TIMESCALEADDR)))) #endif /* !defined(UMPS_CONST_H) */ umps3-3.0.5/src/support/libumps/libumps.S000066400000000000000000000174571435132321600203610ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "umps/regdef.h" #include "umps/arch.h" #include "umps/cp0.h" #include "umps/bios_defs.h" #define casopc movn /* * Helper macros */ #define LEAF_FUNC(func) \ .globl func; \ .type func, @function; \ .ent func; \ func: .frame $sp, 0, $ra; \ .mask 0x00000000,0; \ .fmask 0x00000000,0 #define END_LEAF_FUNC(func) \ .end func; \ .size func, . - func #define DEFINE_CP0_GETTER(suffix, reg) \ LEAF_FUNC(get ## suffix); \ .set noreorder; \ .set nomacro; \ nop; \ mfc0 $v0, reg; \ nop; \ .set reorder; \ .set macro; \ jr $ra; \ END_LEAF_FUNC(get ## suffix) #define DEFINE_CP0_SETTER(suffix, reg) \ LEAF_FUNC(set ## suffix); \ .set noreorder; \ .set nomacro; \ nop; \ mtc0 $a0, reg; \ nop; \ mfc0 $v0, reg; \ nop; \ .set reorder; \ .set macro; \ jr $ra; \ END_LEAF_FUNC(set ## suffix) /* We don't want abicalls unconditionally. */ #ifdef ABICALLS .abicalls #endif /* * Code start */ .text .set noat .align 2 /* * This function cause a system call trap. * System call code is in $a0, return value in $v0. * It is programmer's task to load the return value into * register, and to set PC correctly for returning _after_ * syscall */ LEAF_FUNC(SYSCALL) .set noreorder .set nomacro nop syscall nop .set reorder .set macro jr $ra END_LEAF_FUNC(SYSCALL) /* * Define CP0 register accessors. * * Each accessor is of the form: u32 get(void) */ DEFINE_CP0_GETTER(INDEX, $CP0_Index) DEFINE_CP0_GETTER(RANDOM, $CP0_Random) DEFINE_CP0_GETTER(ENTRYLO, $CP0_EntryLo) DEFINE_CP0_GETTER(BADVADDR, $CP0_BadVAddr) DEFINE_CP0_GETTER(TIMER, $CP0_Timer) DEFINE_CP0_GETTER(ENTRYHI, $CP0_EntryHi) DEFINE_CP0_GETTER(STATUS, $CP0_Status) DEFINE_CP0_GETTER(CAUSE, $CP0_Cause) DEFINE_CP0_GETTER(EPC, $CP0_EPC) DEFINE_CP0_GETTER(PRID, $CP0_PRID) /* * CP0 setter functions * Each setter is of the form: u32 set(u32 value) * Each function returns the updated register value. */ DEFINE_CP0_SETTER(INDEX, $CP0_Index) DEFINE_CP0_SETTER(ENTRYLO, $CP0_EntryLo) DEFINE_CP0_SETTER(TIMER, $CP0_Timer) DEFINE_CP0_SETTER(ENTRYHI, $CP0_EntryHi) DEFINE_CP0_SETTER(STATUS, $CP0_Status) DEFINE_CP0_SETTER(CAUSE, $CP0_Cause) /* * TLBWR instruction wrapper */ LEAF_FUNC(TLBWR) .set noreorder .set nomacro nop tlbwr nop .set reorder .set macro jr $ra END_LEAF_FUNC(TLBWR) /* * TLBWI instruction wrapper */ LEAF_FUNC(TLBWI) .set noreorder .set nomacro nop tlbwi nop .set reorder .set macro jr $ra END_LEAF_FUNC(TLBWI) /* * TLBP instruction wrapper */ LEAF_FUNC(TLBP) .set noreorder .set nomacro nop tlbp nop .set reorder .set macro jr $ra END_LEAF_FUNC(TLBP) /* * TLBR instruction wrapper * Reads TLB entry at INDEX and returns result in ENTRYHI and ENTRYLO */ LEAF_FUNC(TLBR) .set noreorder .set nomacro nop tlbr nop .set reorder .set macro jr $ra END_LEAF_FUNC(TLBR) /* * TLBCLR instruction wrapper */ LEAF_FUNC(TLBCLR) .set noreorder .set nomacro nop mtc0 $a0, $4 nop .set reorder .set macro jr $ra END_LEAF_FUNC(TLBCLR) /* * WAIT instruction wrapper */ LEAF_FUNC(WAIT) .set noreorder .set nomacro nop .set mips32 wait .set mips0 nop .set reorder .set macro jr $ra END_LEAF_FUNC(WAIT) /* * CAS instruction wrapper */ LEAF_FUNC(CAS) .set mips32 casopc $a2, $a0, $a1 .set mips0 move $v0, $a2 jr $ra END_LEAF_FUNC(CAS) /* * LDCXT * * SYNOPSIS: * void LDCXT(unsigned int stackPtr, unsigned int status, unsigned int pc) * * This function forces a processor to change its operating mode reloading * SP ($a0), status ($a1) and PC ($a2) registers: it works only in * kernel mode. There is no real return: $a1 is used as BIOS * argument, but it is reloaded too. */ LEAF_FUNC(LDCXT) move $a3, $a2 move $a2, $a1 move $a1, $a0 .set noreorder .set nomacro li $a0, BIOS_SRV_LDCXT break nop .set reorder .set macro jr $ra END_LEAF_FUNC(LDCXT) /* * STST * * SYNOPSIS: * void STST(state t *statep) * * This function will save processor status to memory block pointed by * register $a0 (a0), and return PC value of instruction immediately * following the call as return value in $v0. * PC field itself is intentionally left at 0 value */ LEAF_FUNC(STST) .set noat sw $1, 16($a0) sw $v0, 20($a0) /* gets CAUSE register and stores it */ mfc0 $v0, $CP0_Cause sw $v0, 4($a0) sw $v1, 24($a0) sw $a0, 28($a0) sw $a1, 32($a0) sw $a2, 36($a0) sw $a3, 40($a0) sw $t0, 44($a0) sw $t1, 48($a0) sw $t2, 52($a0) sw $t3, 56($a0) sw $t4, 60($a0) sw $t5, 64($a0) sw $t6, 68($a0) sw $t7, 72($a0) sw $s0, 76($a0) sw $s1, 80($a0) sw $s2, 84($a0) sw $s3, 88($a0) sw $s4, 92($a0) sw $s5, 96($a0) sw $s6, 100($a0) sw $s7, 104($a0) sw $t8, 108($a0) sw $t9, 112($a0) /* $k0 and $k1 are not saved */ sw $gp, 116($a0) sw $sp, 120($a0) sw $fp, 124($a0) sw $ra, 128($a0) mfhi $a1 sw $a1, 132($a0) mflo $a1 sw $a1, 136($a0) /* all processor registers saved */ /* gets EntryHI and stores it */ mfc0 $v0, $CP0_EntryHi sw $v0, 0($a0) /* and now saves STATUS register and zeroes PC */ mfc0 $v0, $CP0_Status sw $v0, 8($a0) sw $0, 12($a0) /* reloads $v1 and $a1 registers */ lw $v1, 24($a0) lw $a1, 32($a0) /* sets $v0 to return value */ move $v0, $ra jr $ra END_LEAF_FUNC(STST) /* * HALT */ LEAF_FUNC(HALT) .set noreorder .set nomacro li $a0, BIOS_SRV_HALT break nop .set reorder .set macro jr $ra END_LEAF_FUNC(HALT) /* * PANIC */ LEAF_FUNC(PANIC) .set noreorder .set nomacro li $a0, BIOS_SRV_PANIC break nop .set reorder .set macro jr $ra END_LEAF_FUNC(PANIC) /* * LDST * * SYNOPSIS: * void LDST(state t *statep) * * This function forces the complete reload of processor state from * vector state area pointed by argument in $a0 (a0): it works only in * kernel mode. There is no real return: $a1 is used as BIOS * argument, but it is reloaded too. */ LEAF_FUNC(LDST) move $a1, $a0 .set noreorder .set nomacro li $a0, BIOS_SRV_LDST break nop .set reorder .set macro jr $ra END_LEAF_FUNC(LDST) /* * INITCPU * * SYNOPSIS: * void INITCPU(u32 cpu_id, state t *start_state) */ LEAF_FUNC(INITCPU) li $t0, MCTL_BOOT_PC li $t1, 0x00000100 sw $t1, 0($t0) /* Compute starting address for this CPUs stored exception vector */ li $t0, 140 /* 140 is the size of a state_t vector */ mult $t0, $a0 mflo $t0 li $t1, BIOS_DATA_PAGE_BASE add $t0, $t1, $t0 /* store start_state at start of this CPU's stored exception vector */ sw $a1, 0($t0) .set noreorder li $t0, MCTL_RESET_CPU sw $a0, 0($t0) .set reorder jr $ra END_LEAF_FUNC(INITCPU) umps3-3.0.5/src/support/libumps/libumps.h000066400000000000000000000116231435132321600203730ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * External declarations for uMPS library module. */ #ifndef UMPS_LIBUMPS_H #define UMPS_LIBUMPS_H /* * "Forward declaration" hack!! * Many functions in this module accept a pointer to a cpu state * (STATE_PTR) structure. We cannot just forward declare that because * state_t was commonly defined by clients as an anonymous struct * typedef. */ #define STATE_PTR void* /* Functions valid in user mode */ /* This function cause a system call trap */ extern unsigned int SYSCALL(unsigned int number, unsigned int arg1, unsigned int arg2, unsigned int arg3); /* All these functions access CP0 registers. * Access to CP0 registers is always possible in kernel mode, or in user * mode with CPU 0 bit _set_ in STATUS register */ extern unsigned int getINDEX(void); extern unsigned int getRANDOM(void); extern unsigned int getENTRYLO(void); extern unsigned int getBADVADDR(void); extern unsigned int getENTRYHI(void); extern unsigned int getSTATUS(void); extern unsigned int getCAUSE(void); extern unsigned int getEPC(void); extern unsigned int getPRID(void); extern unsigned int getTIMER(void); /* Only some of CP0 register are R/W: handling requires care. * All functions return the value in register after write */ extern unsigned int setINDEX(unsigned int index); extern unsigned int setENTRYLO(unsigned int entry); extern unsigned int setENTRYHI(unsigned int entry); extern unsigned int setSTATUS(unsigned int entry); extern unsigned int setCAUSE(unsigned int cause); extern unsigned int setTIMER(unsigned int timer); /* these functions produce a program trap if executed in user mode * without CPU0 bit _set_ */ extern void TLBWR(void); extern void TLBWI(void); extern void TLBP(void); extern void TLBR(void); extern void TLBCLR(void); extern void WAIT(void); /* This function allows a current process to change its operating mode, * turning on/off interrupt masks, turning on user mode, and at the same time * changing the location of execution. * It is available only in kernel mode, thru a BIOS routine * (otherwise it causes a break). * It updates processor status, PC and stack pointer _completely_, * in one atomic operation. * It has no meaningful return value. */ extern unsigned int LDCXT (unsigned int stackPtr, unsigned int status, unsigned int pc); /* This function may be called from kernel or from user mode with CPU 0 * STATUS bit _on_: otherwise, it will cause a trap */ /* This function stores processor state to memory. It intentionally leaves * the PC field set to 0; putting a meaningful value there is programmer's * task. * Return value is PC value for the instruction immediately following * the call. * This too is NOT an atomic operation: the processor state is saved * register by register to memory. So, this call is interruptible (in a * clean way) or cause a trap (for example, an memory access error if * pointer is not correctly set). * If called from user state, it will trap ONLY if CPU 0 bit of STATUS CP0 * register is NOT set, and only when access to CP0 register (STATUS, ENTRYHI, * CAUSE) is requested (if no other errors intervene). * However, trying it does not harm system security a bit. */ extern unsigned int STST(STATE_PTR statep); /* This function may be used to restart an interrupted/blocked process, * reloading it from a physical address passed as argument. * It is available only in kernel mode, thru a BIOS routine * (otherwise it causes a break). * It updates processor status _completely_, in one atomic operation. * It has no meaningful return value: $2 (v0) register is used for * BIOS call, but it is reloaded too. * Remember that it is programmer's task to increment PC where * needed (e.g. syscall handling) */ extern unsigned int LDST(STATE_PTR statep); /* This function stops the system printing a warning message on terminal 0 */ extern void PANIC(void); /* This function halts the system printing a regular shutdown message on * terminal 0 */ extern void HALT(void); extern void INITCPU(unsigned int cpuid, STATE_PTR start_state); extern int CAS(volatile unsigned int *atomic, unsigned int oldval, unsigned int newval); #endif /* !defined(UMPS_LIBUMPS_H) */ umps3-3.0.5/src/support/libumps/types.h000066400000000000000000000065711435132321600200720ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2004 Mauro Morsiani * Copyright (C) 2020 Mikey Goldweber * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /**************************************************************************** * * This header file contains utility types definitions. * ****************************************************************************/ #ifndef UMPS_TYPES_H #define UMPS_TYPES_H /* Device register type for disks, flash devices and printers (dtp) */ typedef struct dtpreg { unsigned int status; unsigned int command; unsigned int data0; unsigned int data1; } dtpreg_t; /* Device register type for terminals */ typedef struct termreg { unsigned int recv_status; unsigned int recv_command; unsigned int transm_status; unsigned int transm_command; } termreg_t; typedef union devreg { dtpreg_t dtp; termreg_t term; } devreg_t; /* Bus register area */ typedef struct devregarea { unsigned int rambase; unsigned int ramsize; unsigned int execbase; unsigned int execsize; unsigned int bootbase; unsigned int bootsize; unsigned int todhi; unsigned int todlo; unsigned int intervaltimer; unsigned int timescale; unsigned int tlb_floor_addr; unsigned int inst_dev[5]; unsigned int interrupt_dev[5]; devreg_t devreg[5][8]; } devregarea_t; /* Pass Up Vector */ typedef struct passupvector { unsigned int tlb_refill_handler; unsigned int tlb_refill_stackPtr; unsigned int exception_handler; unsigned int exception_stackPtr; } passupvector_t; #define STATE_GPR_LEN 29 /* Processor state */ typedef struct state { unsigned int entry_hi; unsigned int cause; unsigned int status; unsigned int pc_epc; unsigned int gpr[STATE_GPR_LEN]; unsigned int hi; unsigned int lo; } state_t; #define reg_at gpr[0] #define reg_v0 gpr[1] #define reg_v1 gpr[2] #define reg_a0 gpr[3] #define reg_a1 gpr[4] #define reg_a2 gpr[5] #define reg_a3 gpr[6] #define reg_t0 gpr[7] #define reg_t1 gpr[8] #define reg_t2 gpr[9] #define reg_t3 gpr[10] #define reg_t4 gpr[11] #define reg_t5 gpr[12] #define reg_t6 gpr[13] #define reg_t7 gpr[14] #define reg_s0 gpr[15] #define reg_s1 gpr[16] #define reg_s2 gpr[17] #define reg_s3 gpr[18] #define reg_s4 gpr[19] #define reg_s5 gpr[20] #define reg_s6 gpr[21] #define reg_s7 gpr[22] #define reg_t8 gpr[23] #define reg_t9 gpr[24] #define reg_gp gpr[25] #define reg_sp gpr[26] #define reg_fp gpr[27] #define reg_ra gpr[28] #define reg_HI gpr[29] #define reg_LO gpr[30] #define ETH_ALEN 6 #define ETH_PAYLOAD 1500 typedef struct packet { unsigned char dest[ETH_ALEN]; unsigned char src[ETH_ALEN]; unsigned char proto[2]; unsigned char data[ETH_PAYLOAD]; unsigned char dummy[2]; } packet_t; #endif /* !defined(UMPS_TYPES_H) */ umps3-3.0.5/src/tests/000077500000000000000000000000001435132321600145175ustar00rootroot00000000000000umps3-3.0.5/src/tests/CMakeLists.txt000066400000000000000000000003631435132321600172610ustar00rootroot00000000000000add_executable(test_json_serialize test_json_serialize.cc) add_dependencies(test_json_serialize base) target_link_libraries(test_json_serialize base) target_include_directories(test_json_serialize PRIVATE ${PROJECT_SOURCE_DIR}/src) umps3-3.0.5/src/tests/test_json_serialize.cc000066400000000000000000000032141435132321600211050ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include "base/json.h" int main(int argc, char** argv) { JsonObject object; object.Set("favorite-drink", "Pan-Galactic Gargle Blaster"); object.Set("favorite-number", 42); JsonArray* array = new JsonArray; object.Set("friends", array); array->Add(new JsonString("Ford Prefect")); array->Add(new JsonString("Trillian")); JsonObject* o1 = new JsonObject; o1->Set("name", "Marvin"); o1->Set("manufacturer", "Sirius Cybernetics Corporation"); array->Add(o1); JsonObject* nestedObject = new JsonObject; nestedObject->Set("true", true); nestedObject->Set("false", false); object.Set("tautologies", nestedObject); object.Set("What do we use to represent an empty set in JSON?", new JsonArray()); object.Set("What does an empty object look like?", new JsonObject()); std::string buffer; object.Serialize(buffer, true); std::cout << buffer; return 0; } umps3-3.0.5/src/umps/000077500000000000000000000000001435132321600143415ustar00rootroot00000000000000umps3-3.0.5/src/umps/CMakeLists.txt000066400000000000000000000035361435132321600171100ustar00rootroot00000000000000add_library(umps STATIC blockdev.h blockdev.cc blockdev_params.h const.h device.h device.cc disassemble.h disassemble.cc error.h event.h event.cc machine_config.h machine_config.cc machine.h machine.cc memspace.h memspace.cc mp_controller.h mp_controller.cc mpic.h mpic.cc processor.h processor.cc processor_defs.h stoppoint.h stoppoint.cc symbol_table.h symbol_table.cc systembus.h systembus.cc time_stamp.h time_stamp.cc types.h utility.h utility.cc vde_network.h vde_network.cc libvdeplug_dyn.h) add_dependencies(umps base) target_include_directories(umps PRIVATE ${PROJECT_BINARY_DIR} ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/src/include) target_compile_options(umps PRIVATE ${SIGCPP_CFLAGS}) target_compile_definitions(umps PRIVATE -DPACKAGE_DATA_DIR="${UMPS_DATA_DIR}") target_link_libraries(umps PRIVATE base) add_executable(umps3-elf2umps elf2umps.cc) target_include_directories(umps3-elf2umps PRIVATE ${PROJECT_BINARY_DIR} ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/src/include) target_link_libraries(umps3-elf2umps ${LIBELF}) add_executable(umps3-mkdev mkdev.cc) target_include_directories(umps3-mkdev PRIVATE ${PROJECT_BINARY_DIR} ${PROJECT_SOURCE_DIR}/src) add_executable(umps3-objdump disassemble.cc objdump.cc) target_include_directories(umps3-objdump PRIVATE ${PROJECT_BINARY_DIR} ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/src/include) install(TARGETS umps3-elf2umps umps3-mkdev umps3-objdump RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) umps3-3.0.5/src/umps/blockdev.cc000066400000000000000000000130451435132321600164440ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2004 Mauro Morsiani * Copyright (C) 2020 Mattia Biondi * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /**************************************************************************** * * This module provides some utility classes for block devices handling. * They are: Block for block devices sectors/flash device blocks representation; * DiskParams for simulated disk devices performance parameters; * FlashParams for simulated flash devices performance parameters. * ****************************************************************************/ #include #include #include "umps/types.h" #include "umps/blockdev_params.h" #include "umps/utility.h" #include "umps/blockdev.h" // This method returns an empty (unitialized) 4096 byte Block Block::Block() { } // This method fills a Block with file contents starting at "offset" // bytes from file start, as computed by caller. // Returns TRUE if read does not succeed, FALSE otherwise bool Block::ReadBlock(FILE * blkFile, SWord offset) { if (fseek(blkFile, offset, SEEK_SET) == EOF) // already at EOF return(true); else if (fread((void *)blkBuf, WORDLEN, BLOCKSIZE, blkFile) != BLOCKSIZE) // file too short return(true); else // all OK return(false); } // This method writes Block contents in a file, starting at "offset" bytes // from file start, as computed by caller. Returns TRUE if write does not // succeed, FALSE otherwise bool Block::WriteBlock(FILE * blkFile, SWord offset) { if (fseek(blkFile, offset, SEEK_SET) == EOF) // already at EOF return(true); else if (fwrite((void *)blkBuf, WORDLEN, BLOCKSIZE, blkFile) != BLOCKSIZE) // some error occurred return(true); else // all OK fflush(blkFile); return(false); } // This method returns the Word contained in the Block at ofs (Word items) // offset, range [0..BLOCKSIZE - 1]. Warning: in-bounds checking is leaved // to caller Word Block::getWord(unsigned int ofs) { if (ofs < BLOCKSIZE) return(blkBuf[ofs]); else return(MAXWORDVAL); } // This method fills with "value" the Word contained in the Block at ofs // (Word items) offset, range [0..BLOCKSIZE - 1]. Warning: in-bounds // checking is leaved to caller void Block::setWord(unsigned int ofs, Word value) { if (ofs < BLOCKSIZE) blkBuf[ofs] = value; } /****************************************************************************/ // This method reads disk parameters from file header, builds a // DiskParams object, and returns the disk sectors start offset: this // allows to modify the parameters' size without changing the caller. // If fileOfs returned is 0, something has gone wrong; file is rewound after // use DiskParams::DiskParams(FILE * diskFile, SWord * fileOfs) { SWord ret; unsigned int i; Block * blk = new Block(); rewind(diskFile); if (blk->ReadBlock(diskFile, 0) || blk->getWord(0) != DISKFILEID) // errors in file reading or disk file magic number missing ret = 0; else { // if DISKFILEID is present all parameters should be correct; // fills the object for (i = 0; i < DISKPNUM; i++) parms[i] = (unsigned int) blk->getWord(i + 1); rewind(diskFile); // sets the disk contents start position ret = DISKPNUM + 1; } delete blk; *fileOfs = ret; } // All the following methods return the corresponding geometry or // performance figure unsigned int DiskParams::getCylNum(void) { return(parms[CYLNUM]); } unsigned int DiskParams::getHeadNum(void) { return(parms[HEADNUM]); } unsigned int DiskParams::getSectNum(void) { return(parms[SECTNUM]); } unsigned int DiskParams::getRotTime(void) { return(parms[ROTTIME]); } unsigned int DiskParams::getSeekTime(void) { return(parms[SEEKTIME]); } unsigned int DiskParams::getDataSect(void) { return(parms[DATASECT]); } // This method reads flash device parameters from file header, builds a // FlashParams object, and returns the flash device blocks start offset: this // allows to modify the parameters' size without changing the caller. // If fileOfs returned is 0, something has gone wrong; file is rewound after // use FlashParams::FlashParams(FILE * flashFile, SWord * fileOfs) { SWord ret; unsigned int i; Block * blk = new Block(); rewind(flashFile); if (blk->ReadBlock(flashFile, 0) || blk->getWord(0) != FLASHFILEID) // errors in file reading or flash device file magic number missing ret = 0; else { // if FLASHFILEID is present all parameters should be correct; // fills the object for (i = 0; i < FLASHPNUM; i++) parms[i] = (unsigned int) blk->getWord(i + 1); rewind(flashFile); // sets the flash device contents start position ret = FLASHPNUM + 1; } delete blk; *fileOfs = ret; } // All the following methods return the corresponding geometry or // performance figure unsigned int FlashParams::getBlocksNum(void) { return(parms[BLOCKSNUM]); } unsigned int FlashParams::getWTime(void) { return(parms[WTIME]); } umps3-3.0.5/src/umps/blockdev.h000066400000000000000000000110751435132321600163070ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2004 Mauro Morsiani * Copyright (C) 2020 Mattia Biondi * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // This class implements the block devices' 4096 byte sectors/flash devices blocks. // Each object contains a single buffer; methods are provided to read/write // these blocks from/to real files and to access to the word-sized contents. // This class is provided primarily to make DMA transfer easier and to // standardize block handling. class Block { public: // This method returns an empty (unitialized) 4096 byte Block Block(); // Object deletion is done by default handler // This method fills a Block with file contents starting at "offset" // bytes from file start, as computed by caller. // Returns TRUE if read does not succeed, FALSE otherwise bool ReadBlock(FILE * blkFile, SWord offset); // This method writes Block contents in a file, starting at "offset" // bytes from file start, as computed by caller. Returns TRUE if // write does not succeed, FALSE otherwise bool WriteBlock(FILE * blkFile, SWord offset); // This method returns the Word contained in the Block at ofs (Word // items) offset, range [0..BLOCKSIZE - 1]. Warning: in-bounds // checking is leaved to caller Word getWord(unsigned int ofs); // This method fills with "value" the Word contained in the Block at // ofs (Word items) offset, range [0..BLOCKSIZE - 1]. Warning: // in-bounds checking is leaved to caller void setWord(unsigned int ofs, Word value); private: // Block contents Word blkBuf[BLOCKSIZE]; }; /****************************************************************************/ // This class contains the simulated disk drive geometry and performance // parameters. They are filled by mkdev utility and used by DiskDevice class // for detailed disk performance simulation. // Position, min, max and default values, where applicable, are defined in // h/blockdev.h header file. // // Parameters are: // number of cylinders; // number of heads; // number of sectors per track; // disk rotation time in microseconds; // average track-to-track seek time in microseconds; // data % of sector (to compute inter-sector gap) class DiskParams { public: // This method reads disk parameters from file header, builds a // DiskParams object, and returns the disk sectors start offset: // this allows to modify the parameters' size without changing the // caller. If fileOfs returned is 0, something has gone wrong; file // is rewound after use DiskParams(FILE * diskFile, SWord * fileOfs); // Object deletion is done by default handler // These methods return the corresponding geometry or performance // figure unsigned int getCylNum(void); unsigned int getHeadNum(void); unsigned int getSectNum(void); unsigned int getRotTime(void); unsigned int getSeekTime(void); unsigned int getDataSect(void); private: // parameter buffer unsigned int parms[DISKPNUM]; }; // This class contains the simulated flash device geometry and performance // parameters. They are filled by mkdev utility and used by FlashDevice class // for detailed flash device performance simulation. // Position, min, max and default values, where applicable, are defined in // h/blockdev.h header file. // // Parameters are: // number of blocks; // average read/write time in microseconds; class FlashParams { public: // This method reads flash device parameters from file header, builds a // FlashParams object, and returns the flash device blocks start offset: // this allows to modify the parameters' size without changing the // caller. If fileOfs returned is 0, something has gone wrong; file // is rewound after use FlashParams(FILE * flashFile, SWord * fileOfs); // Object deletion is done by default handler // These methods return the corresponding geometry or performance // figure unsigned int getBlocksNum(void); unsigned int getWTime(void); private: // parameter buffer unsigned int parms[FLASHPNUM]; }; umps3-3.0.5/src/umps/blockdev_params.h000066400000000000000000000053401435132321600176500ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2004 Mauro Morsiani * Copyright (C) 2020 Mattia Biondi * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /**************************************************************************** * * This module contains constant & macro definitions useful for block devices * handling and for file identification. * ****************************************************************************/ // file recognition tags (magic numbers) #define DISKFILEID 0x0053504D #define FLASHFILEID 0x0153504D #define BIOSFILEID 0x0253504D #define COREFILEID 0x0353504D #define AOUTFILEID 0x0453504D #define STABFILEID 0x4153504D // DiskParams class items constants: position, min, max and default (DFL) // values (where applicable) are given for each: see class definition // number of parameters #define DISKPNUM 6 // number of cylinders: 2 bytes (64 K) #define CYLNUM 0 #define MAXCYL 0xFFFF #define DFLCYL 32 // number of heads: 1 byte #define HEADNUM 1 #define MAXHEAD 0xFF #define DFLHEAD 2 // number of sectors: 1 byte #define SECTNUM 2 #define MAXSECT 0xFF #define DFLSECT 8 // 1 full rotation (in microseconds): here min and max values are given #define ROTTIME 3 #define MINRPM 360 #define MAXRPM 10800 #define DFLROTTIME 16666 // average track-to-track seek time in microseconds #define SEEKTIME 4 #define MAXSEEKTIME 10000 #define DFLSEEKTIME 100 // data percentage of sector: 1-100 (min and max values given) #define DATASECT 5 #define MINDATAS 10 #define MAXDATAS 90 #define DFLDATAS 80 // FlashParams class items constants: position, min, max and default (DFL) // values (where applicable) are given for each: see class definition // number of parameters #define FLASHPNUM 2 // number of blocks: 3 bytes #define BLOCKSNUM 0 #define MAXBLOCKS 0xFFFFFF #define DFLBLOCKS 512 // average write time in microseconds; // read time will always be READRATIO to write time #define WTIME 1 #define MAXWTIME MAXSEEKTIME * 10 #define DFLWTIME DFLSEEKTIME * 10 #define READRATIO 3/4 umps3-3.0.5/src/umps/const.h000066400000000000000000000113411435132321600156400ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2004 Mauro Morsiani * Copyright (C) 2020 Mattia Biondi * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * This header file contains the global constant & macro definitions. */ #include // general configuration constants #define MPSFILETYPE ".umps" #define AOUTFILETYPE ".aout.umps" #define BIOSFILETYPE ".rom.umps" #define COREFILETYPE ".core.umps" #define STABFILETYPE ".stab.umps" // maximum area size for trace ranges: a little more than 4KB // to avoid troubles in browser refresh if area is too large #define MAXTRACESIZE (FRAMESIZE + 1) /***************************************************************************/ // no more user-serviceable parts below this line // some utility constants #define HIDDEN static #define EOS '\0' #define EMPTYSTR "" #define EXIT_FAILURE 1 #define EXIT_SUCCESS 0 // host specific constants #ifdef WORDS_BIGENDIAN #define BIGENDIANCPU 1 #else #define BIGENDIANCPU 0 #endif // hardware constants // physical memory page frame size (in words) #define FRAMESIZE 1024 // KB per frame #define FRAMEKB 4 // block device size in words #define BLOCKSIZE FRAMESIZE // eth packet size #define PACKETSIZE 1514 // DMA transfer time #define DMATICKS BLOCKSIZE // miscellaneous MIPS alignment and size definitions needed by modules // other by processor.cc // number of ASIDs #define MAXASID 64 // MIPS NOP instruction #define NOP 0UL // word length in bytes, byte length in bits, sign masks, etc. #define WORDLEN 4 #define BYTELEN 8 #define WORDSHIFT 2 #define MINWORDVAL 0x00000000UL #define MAXWORDVAL 0xFFFFFFFFUL #define MAXSWORDVAL 0x7FFFFFFFUL #define SIGNMASK 0x80000000UL #define BYTEMASK 0x000000FFUL // immediate/lower halfword part mask #define IMMMASK 0x0000FFFFUL // word alignment mask #define ALIGNMASK 0x00000003UL // halfword bit length #define HWORDLEN 16 // exception type constants (simulator internal coding) #define NOEXCEPTION 0 #define INTEXCEPTION 1 #define MODEXCEPTION 2 #define UTLBLEXCEPTION 3 #define TLBLEXCEPTION 4 #define UTLBSEXCEPTION 5 #define TLBSEXCEPTION 6 #define ADELEXCEPTION 7 #define ADESEXCEPTION 8 #define DBEXCEPTION 9 #define IBEXCEPTION 10 #define SYSEXCEPTION 11 #define BPEXCEPTION 12 #define RIEXCEPTION 13 #define CPUEXCEPTION 14 #define OVEXCEPTION 15 // interrupt handling related constants // timer interrupt line #define TIMERINT 2 // device starting interrupt line #define DEVINTBASE 3 // device register length #define DEVREGLEN 4 // interrupts available for registers #define DEVINTUSED 5 // devices per interrupt line #define DEVPERINT 8 // segments base addresses #define KSEG0BASE 0x00000000UL #define KSEG0TOP 0x20000000UL #define KUSEGBASE 0x80000000UL // bus memory mapping constants (BIOS/BIOS Data Page/device registers/BOOT/RAM) #define BIOSBASE 0x00000000UL #define BIOSDATABASE 0x0FFFF000UL #define DEVBASE 0x10000000UL #define BOOTBASE 0x1FC00000UL #define RAMBASE 0x20000000UL // size of bios data page (in words) #define BIOSDATASIZE ((DEVBASE - BIOSDATABASE) / WORDLEN) // Processor structure register numbers #define CPUREGNUM 34 #define CPUGPRNUM 32 #define CP0REGNUM 10 // device type codes #define NULLDEV 0 #define DISKDEV 1 #define FLASHDEV 2 #define ETHDEV 3 #define PRNTDEV 4 #define TERMDEV 5 // interrupt line offset used for terminals // (lots of code must be modified if this changes) #define TERMINT 4 // memory access types for brkpt/susp/trace ranges in watch.cc and appforms.cc // modules #define READWRITE 0x6 #define READ 0x4 #define WRITE 0x2 #define EXEC 0x1 #define EMPTY 0x0 // some useful macros // recognizes bad (unaligned) virtual address #define BADADDR(w) ((w & ALIGNMASK) != 0UL) // returns the sign bit of a word #define SIGNBIT(w) (w & SIGNMASK) // returns 1 if the two strings are equal, 0 otherwise #define SAMESTRING(s,t) (strcmp(s,t) == 0) // returns 1 if a is in open-ended interval [b, c[, 0 otherwise #define INBOUNDS(a,b,c) (a >= b && a < c) umps3-3.0.5/src/umps/device.cc000066400000000000000000001371431435132321600161200ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2004 Mauro Morsiani * Copyright (C) 2010 Tomislav Jonjic * Copyright (C) 2020 Mattia Biondi * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /**************************************************************************** * * This module contains definitions for Device class and sub-classes. Each * Device sub-class represents a device type inside the simulation; each * object is a single device. Device class iself defines interface for all * devices, and represents the "uninstalled" device. All devices are * created and controlled by a SystemBus object. Each Device is identified * by a (interrupt line, device number) pair. See external documentation * for individual devices capabilities and command/error codes. * ****************************************************************************/ #include #include #include #include #include #include "umps/types.h" #include "umps/blockdev_params.h" #include "umps/blockdev.h" #include "umps/systembus.h" #include "umps/utility.h" #include "umps/device.h" #include "umps/machine_config.h" #include "umps/time_stamp.h" #include "umps/error.h" #include "umps/vde_network.h" #include "umps/machine.h" using namespace boost::placeholders; // last operation result description HIDDEN const char* const opResult[2] = { "UNSUCCESSFUL", "SUCCESSFUL" }; // static buffers definition and costants #define SMALLBUFSIZE 32 #define STRBUFSIZE 256 HIDDEN char strbuf[STRBUFSIZE]; // common device register definitions #define STATUS 0 #define COMMAND 1 #define DATA0 2 #define DATA1 3 // common status conditions #define READY 1 #define ILOPERR 2 #define BUSY 3 // common command values #define RESET 0 #define ACK 1 // // PrinterDevice specific commands, error codes, completion times (in microseconds) // #define PRNTCHR 2 #define PRNTERR 4 #define PRNTRESETTIME 40 #define PRNTCHRTIME 8 // this means a throughput of 125 KB/s // TerminalDevice specific definitions, commands, status codes, completion // times // terminal register names #define RECVSTATUS 0 #define RECVCOMMAND 1 #define TRANSTATUS 2 #define TRANCOMMAND 3 // specific terminal commands #define TRANCHR 2 #define RECVCHR 2 // specific terminal status conditions #define TRANERR 4 #define RECVERR 4 #define RECVD 5 #define TRANSMD 5 // terminal op completion times (in microseconds) #define TERMRESETTIME 400 #define TRANCHRTIME 80 #define RECVCHRTIME 80 // this means a throughput of about 12.5 KB/s // DiskDevice specific commands / status codes // controller reset time (microsecs) #define DISKRESETTIME 400 // other performance figures are loaded from file // controller commands #define DSEEKCYL 2 #define DREADBLK 3 #define DWRITEBLK 4 // specific error codes #define DSEEKERR 4 #define DREADERR 5 #define DWRITERR 6 #define DDMAERR 7 // FlashDevice specific commands / status codes // controller reset time (microsecs) #define FLASHRESETTIME 400 // other performance figures are loaded from file // controller commands #define FREADBLK 2 #define FWRITEBLK 3 // specific error codes #define FREADERR 4 #define FWRITERR 5 #define FDMAERR 6 // EthDevice specific commands / status codes // eth commands #define READCONF 2 #define READNET 3 #define WRITENET 4 #define CONFIGURE 5 // configuration constants #define SETMAC 0x80 // already defined in network.e // #define PROMISQ 0x4 // #define INTERRUPT 0x2 // #define NAMED 0x1 #define READPENDING 0x80 #define READPENDINGMASK 0x7F // specific error codes // READERR WRITERR DMAERR as for DiskDevice // eth op completion times (microsecs) #define ETHRESETTIME 200 #define READNETTIME 1220 #define WRITENETTIME READNETTIME #define CONFNETTIME 40 #define POLLNETTIME (READNETTIME / 2) // // local functions // // This function decodes device STATUS field and tells if previous operation // has been successful or not HIDDEN const char * isSuccess(unsigned int devType, Word regVal); /****************************************************************************/ /* Definitions to be exported. */ /****************************************************************************/ // Device class defines the interface to all device types, and represents // the "uninstalled device" (NULLDEV) itself. Device objects are created and // controlled by a SystemBus object, but also may be inspected by Watch if // needed // This method creates a Device object with "coordinates" (interrupt line, // device number) clears device register, and links it to bus Device::Device(SystemBus * busl, unsigned int intl, unsigned int dnum) { intL = intl; devNum = dnum; dType = NULLDEV; for (unsigned int i = 0; i < DEVREGLEN; i++) reg[i] = 0UL; bus = busl; complTime = UINT64_C(0); // a NULLDEV never works isWorking = false; } // No operation for "uninstalled" devices Device::~Device() { } // This method is invoked by SystemBus when the required operation scheduled // on the device (using Event objects and queue) should be completed: the // default NULLDEV device has nothing to do, but others do unsigned int Device::CompleteDevOp() { return 0; } // This method allows SystemBus to write into device register for device: // NULLDEV device register write has no effects, but other devices will // start performing required operations if COMMAND register is written with // proper codes void Device::WriteDevReg(unsigned int dummynum, Word dummydata) { } // This method returns the static buffer contained in each device describing // the current device status (operation performed, etc.). NULLDEV devices // are not operational const char* Device::getDevSStr() { return "Not operational"; } // This method returns the current value for device register field indexed // by regnum Word Device::ReadDevReg(unsigned int regnum) { if (regnum < DEVREGLEN) { return(reg[regnum]); } else { Panic("Unknown register index in Device::ReadDevReg()"); // never returns return MAXWORDVAL; } } // This method gets the current operational status for the device, as set by // user inside the simulation; a "not-working" device fails all operations // requested and reports proper error codes; a NULLDEV always fail bool Device::getDevNotWorking() { return !isWorking; } /* * Return a human-readable expression for completion time of the * current device operation. */ std::string Device::getCTimeInfo() const { if (isBusy()) return TimeStamp::toString(complTime); else return ""; } // This method sets the operational status for the device inside the // simulation as user wishes; a "not-working" device fails all operations // requested and reports proper error codes; a NULLDEV always fails. bool Device::setDevNotWorking(bool cond) { if (dType == NULLDEV) isWorking = false; else isWorking = !cond; return !isWorking; } void Device::setCondition(bool working) { if (dType != NULLDEV && working != isWorking) { isWorking = working; SignalConditionChanged.emit(isWorking); } } // This method allows to copy inputstr contents inside TerminalDevice // receiver buffer: not operational for all other devices (NULLDEV included) // and produces a panic message void Device::Input(const char * inputstr) { Panic("Input directed to a non-Terminal device in Device::Input()"); } bool Device::isBusy() const { return reg[STATUS] == BUSY; } uint64_t Device::scheduleIOEvent(uint64_t delay) { return bus->scheduleEvent(delay, boost::bind(&Device::CompleteDevOp, this)); } /****************************************************************************/ // PrinterDevice class allows to emulate parallel character printer // currently in use (see performance figures shown before). It uses the same // interface as Device, redefining only a few methods' implementation: refer // to it for individual methods descriptions. // It adds to Device data structure: // a pointer to SetupInfo object containing printer log file name; // a static buffer for device operation & status description; // a FILE structure for log file access. // // See Device class methods description for interface // PrinterDevice::PrinterDevice(SystemBus* bus, const MachineConfig* cfg, unsigned int il, unsigned int devNo) : Device(bus, il, devNo) , config(cfg) { dType = PRNTDEV; isWorking = true; reg[STATUS] = READY; sprintf(statStr, "Idle"); if ((prntFile = fopen(config->getDeviceFile(il, devNo).c_str(), "w")) == NULL) { sprintf(strbuf, "Cannot open printer %u file : %s", devNum, strerror(errno)); Panic(strbuf); } } PrinterDevice::~PrinterDevice() { // tries to close log file if (fclose(prntFile) == EOF) { sprintf(strbuf, "Cannot close printer file %u : %s", devNum, strerror(errno)); Panic(strbuf); } } void PrinterDevice::WriteDevReg(unsigned int regnum, Word data) { // Only COMMAND and DATA0 registers are writable, and only when // device is not busy. if (reg[STATUS] == BUSY) return; switch (regnum) { case COMMAND: reg[COMMAND] = data; // decode operation requested: for each, acknowledges a // previous interrupt if pending, sets the device registers, // and inserts an Event in SystemBus mantained queue switch (data) { case RESET: bus->IntAck(intL, devNum); complTime = scheduleIOEvent(PRNTRESETTIME * config->getClockRate()); sprintf(statStr, "Resetting (last op: %s)", isSuccess(dType, reg[STATUS])); reg[STATUS] = BUSY; break; case ACK: bus->IntAck(intL, devNum); sprintf(statStr, "Idle (last op: %s)", isSuccess(dType, reg[STATUS])); reg[STATUS] = READY; break; case PRNTCHR: bus->IntAck(intL, devNum); sprintf(statStr, "Printing char 0x%.2X (last op: %s)", (unsigned char) reg[DATA0], isSuccess(dType, reg[STATUS])); complTime = scheduleIOEvent(PRNTCHRTIME * config->getClockRate()); reg[STATUS] = BUSY; break; default: sprintf(statStr, "Unknown command (last op: %s)", isSuccess(dType, reg[STATUS])); reg[STATUS] = ILOPERR; bus->IntReq(intL, devNum); break; } // Status has changed (almost certanly, that is -- we don't // worry about spurious status change notifications as they // are harmless). SignalStatusChanged(getDevSStr()); break; case DATA0: reg[DATA0] = data; break; default: break; } } const char* PrinterDevice::getDevSStr() { return statStr; } unsigned int PrinterDevice::CompleteDevOp() { // checks which operation must be completed: for each, sets device // register, performs requested operation and produces an interrupt // request switch (reg[COMMAND]) { case RESET: // a reset always works, even if isWorking == FALSE sprintf(statStr, "Reset completed : waiting for ACK"); reg[STATUS] = READY; break; case PRNTCHR: if (isWorking) { // normal operation if (fputc((unsigned char) reg[DATA0], prntFile) == EOF) { sprintf(strbuf, "Error writing printer %u file : %s", devNum, strerror(errno)); Panic(strbuf); } fflush(prntFile); sprintf(statStr, "Printed char 0x%.2X : waiting for ACK", (unsigned char) reg[DATA0]); reg[STATUS] = READY; } else { // no operation & error simulation sprintf(statStr, "Error printing char 0x%.2X : waiting for ACK", (unsigned char) reg[DATA0]); reg[STATUS] = PRNTERR; } break; default: Panic("Unknown operation in PrinterDevice::CompleteDevOp()"); break; } SignalStatusChanged(getDevSStr()); bus->IntReq(intL, devNum); return STATUS; } // TerminalDevice class allows to emulate serial "dumb" terminal (see // performance figures shown before). TerminalDevice may be split up into // two independent sub-devices: "transmitter" and "receiver". // It uses the same interface as Device, redefining only a few methods' // implementation: refer to it for individual methods descriptions. // It adds to Device data structure: // a pointer to SetupInfo object containing terminal log file name; // a static buffer for device operation & status description; // a FILE structure for log file access; // some structures for handling terminal transmitter and receiver. // // See Device class methods description for interface // TerminalDevice::TerminalDevice(SystemBus* bus, const MachineConfig* cfg, unsigned int il, unsigned int devNo) : Device(bus, il, devNo), config(cfg) { dType = TERMDEV; isWorking = true; recvBuf = NULL; recvBp = 0; reg[RECVSTATUS] = READY; reg[TRANSTATUS] = READY; sprintf(recvStatStr, "Idle"); sprintf(tranStatStr, "Idle"); recvCTime = UINT64_C(0); tranCTime = UINT64_C(0); recvIntPend = false; tranIntPend = false; // tries to open log file if ((termFile = fopen(config->getDeviceFile(il, devNo).c_str(), "w")) == NULL) { sprintf(strbuf, "Cannot open terminal %u file : %s", devNum, strerror(errno)); Panic(strbuf); } // else file has been open with success: set it to no buffering for quick // terminal screen update setvbuf(termFile, (char *) NULL, _IONBF, 0); } TerminalDevice::~TerminalDevice() { if (fclose(termFile) == EOF) { sprintf(strbuf, "Cannot close terminal file %u : %s", devNum, strerror(errno)); Panic(strbuf); } } void TerminalDevice::WriteDevReg(unsigned int regnum, Word data) { // only COMMAND registers are writable, and only when device is not busy // format is NNNN NNNN CHAR COMM switch (regnum) { case RECVCOMMAND: // decode operation requested: for each, acknowledges a // previous interrupt if pending, sets the device registers, // and inserts an Event in SystemBus mantained queue if (reg[RECVSTATUS] != BUSY) { reg[RECVCOMMAND] = data; switch (data) { case RESET: if (!tranIntPend) bus->IntAck(intL, devNum); recvIntPend = false; recvCTime = scheduleIOEvent(TERMRESETTIME * config->getClockRate()); sprintf(recvStatStr, "Resetting (last op: %s)", isSuccess(dType, reg[RECVSTATUS] & BYTEMASK)); reg[RECVSTATUS] = BUSY; break; case ACK: if (!tranIntPend) bus->IntAck(intL, devNum); recvIntPend = false; sprintf(recvStatStr, "Idle (last op: %s)", isSuccess(dType, reg[RECVSTATUS] & BYTEMASK)); reg[RECVSTATUS] = READY; break; case RECVCHR: if (!tranIntPend) bus->IntAck(intL, devNum); recvIntPend = false; sprintf(recvStatStr, "Receiving (last op: %s)", isSuccess(dType, reg[RECVSTATUS] & BYTEMASK)); recvCTime = scheduleIOEvent(RECVCHRTIME * config->getClockRate()); reg[RECVSTATUS] = BUSY; break; default: sprintf(recvStatStr, "Unknown command (last op: %s)", isSuccess(dType, reg[RECVSTATUS] & BYTEMASK)); reg[RECVSTATUS] = ILOPERR; bus->IntReq(intL, devNum); recvIntPend = true; break; } SignalStatusChanged.emit(getDevSStr()); } break; case TRANCOMMAND: // decode operation requested: for each, acknowledges a // previous interrupt if pending, sets the device registers, // and inserts an Event in SystemBus mantained queue if (reg[TRANSTATUS] != BUSY) { reg[TRANCOMMAND] = data; // to extract command switch (data & BYTEMASK) { case RESET: if (!recvIntPend) bus->IntAck(intL, devNum); tranIntPend = false; tranCTime = scheduleIOEvent(TERMRESETTIME * config->getClockRate()); sprintf(tranStatStr, "Resetting (last op: %s)", isSuccess(dType, reg[TRANSTATUS] & BYTEMASK)); reg[TRANSTATUS] = BUSY; break; case ACK: if (!recvIntPend) bus->IntAck(intL, devNum); tranIntPend = false; sprintf(tranStatStr, "Idle (last op: %s)", isSuccess(dType, reg[TRANSTATUS] & BYTEMASK)); reg[TRANSTATUS] = READY; break; case TRANCHR: if (!recvIntPend) bus->IntAck(intL, devNum); tranIntPend = false; sprintf(tranStatStr, "Transm. char 0x%.2X (last op: %s)", (unsigned char) ((data >> BYTELEN) & BYTEMASK), isSuccess(dType, reg[TRANSTATUS] & BYTEMASK)); tranCTime = scheduleIOEvent(TRANCHRTIME * config->getClockRate()); reg[TRANSTATUS] = BUSY; break; default: sprintf(tranStatStr, "Unknown command (last op: %s)", isSuccess(dType, reg[TRANSTATUS] & BYTEMASK)); reg[TRANSTATUS] = ILOPERR; bus->IntReq(intL, devNum); tranIntPend = true; break; } SignalStatusChanged.emit(getDevSStr()); } break; case RECVSTATUS: case TRANSTATUS: default: break; } } const char* TerminalDevice::getDevSStr() { sprintf(strbuf, "%s\n%s", recvStatStr, tranStatStr); return strbuf; } const char* TerminalDevice::getTXStatus() const { return tranStatStr; } const char* TerminalDevice::getRXStatus() const { return recvStatStr; } std::string TerminalDevice::getCTimeInfo() const { return getRXCTimeInfo() + "\n" + getTXCTimeInfo(); } std::string TerminalDevice::getTXCTimeInfo() const { if (reg[TRANSTATUS] == BUSY) return TimeStamp::toString(tranCTime); else return ""; } std::string TerminalDevice::getRXCTimeInfo() const { if (reg[RECVSTATUS] == BUSY) return TimeStamp::toString(recvCTime); else return ""; } unsigned int TerminalDevice::CompleteDevOp() { // only one sub-device should complete its op: which one? bool doRecv; unsigned int devMod; // determines which operation must be completed if (reg[RECVSTATUS] == BUSY && reg[TRANSTATUS] == BUSY) { // Both sub-devices are working, so tie breaking depends on // timestamps: lower is first. If they are equal, this doesn't // matter because there will be another CompleteDevOp // following, and one sub-device will have already completed // its op or posponed it (recv). if (recvCTime <= tranCTime) doRecv = true; else doRecv = false; } else { // Surely one or other sub-device is idle doRecv = (reg[RECVSTATUS] == BUSY); } if (doRecv) { // recv sub-device operation completion // // Checks which operation must be completed: for each, sets // device register, performs requested operation and produces // an interrupt request. switch (reg[RECVCOMMAND]) { case RESET: // a reset always works, even if isWorking == FALSE sprintf(recvStatStr, "Reset completed : waiting for ACK"); reg[RECVSTATUS] = READY; recvIntPend = true; bus->IntReq(intL, devNum); break; case RECVCHR: if (recvBuf == NULL || recvBuf[recvBp] == EOS) { // no char in input: wait another receiver cycle recvCTime = scheduleIOEvent(RECVCHRTIME * config->getClockRate()); } else { // buffer is not empty if (isWorking) { sprintf(recvStatStr, "Received char 0x%.2X : waiting for ACK", recvBuf[recvBp]); reg[RECVSTATUS] = (((Word) recvBuf[recvBp]) << BYTELEN) | RECVD; recvBp++; } else { // no operation & error simulation sprintf(recvStatStr, "Error receiving char : waiting for ACK"); reg[RECVSTATUS] = RECVERR; } // interrupt request recvIntPend = true; bus->IntReq(intL, devNum); } break; default: Panic("Unknown operation in TerminalDevice::CompleteDevOp()"); break; } // even if there is no char to receive, the RECVSTATUS register is // "rewritten" by the receiver and CTime changed devMod = RECVSTATUS; } else { // Transmitter sub-device operation completion: // // Checks which operation must be completed: for each, sets // device register, performs requested operation and produces // an interrupt request. switch (reg[TRANCOMMAND] & BYTEMASK) { case RESET: // a reset always works, even if isWorking == FALSE sprintf(tranStatStr, "Reset completed : waiting for ACK"); reg[TRANSTATUS] = READY; break; case TRANCHR: if (isWorking) { if (fputc((unsigned char) ((reg[TRANCOMMAND] >> BYTELEN) & BYTEMASK), termFile) == EOF) { sprintf(strbuf, "Error writing terminal %u file : %s", devNum, strerror(errno)); Panic(strbuf); } // else operation is successful: fflush(termFile); SignalTransmitted.emit((unsigned char) ((reg[TRANCOMMAND] >> BYTELEN) & BYTEMASK)); sprintf(tranStatStr, "Transm. char 0x%.2lX : waiting for ACK", (reg[TRANCOMMAND] >> BYTELEN) & BYTEMASK); reg[TRANSTATUS] = (reg[TRANCOMMAND] & (BYTEMASK << BYTELEN)) | TRANSMD; } else { // no operation & error simulation sprintf(tranStatStr, "Error transm. char 0x%.2lX : waiting for ACK", (reg[TRANCOMMAND] >> BYTELEN) & BYTEMASK); reg[TRANSTATUS] = (reg[TRANCOMMAND] & (BYTEMASK << BYTELEN)) | TRANERR; } break; default: Panic("Unknown operation in TerminalDevice::CompleteDevOp()"); break; } // interrupt generation bus->IntReq(intL, devNum); tranIntPend = true; devMod = TRANSTATUS; } SignalStatusChanged.emit(getDevSStr()); bus->getMachine()->HandleBusAccess(DEV_REG_ADDR(intL, devNum) + devMod * WS, WRITE, NULL); return devMod; } void TerminalDevice::Input(const char* inputstr) { char* strp; if (recvBuf != NULL && recvBuf[recvBp] == EOS) { // buffer exausted: delete it delete recvBuf; recvBuf = NULL; } if (recvBuf == NULL) { // simply copies inputstr into it, adding a trailing '\n' recvBuf = new char[strlen(inputstr) + 2]; sprintf(recvBuf, "%s\n", inputstr); } else { // copies the unreceived buffer part plus the new input into // a new buffer strp = new char [(strlen(recvBuf) - recvBp) + strlen(inputstr) + 2]; sprintf(strp, "%s%s\n",&(recvBuf[recvBp]), inputstr); delete recvBuf; recvBuf = strp; } recvBp = 0; // writes input to log file if (fprintf(termFile, "%s\n", inputstr) < 0) { sprintf(strbuf, "Error writing terminal %u file : %s", devNum, strerror(errno)); Panic(strbuf); } } // DiskDevice class allows to emulate a disk drive: each 4096 byte sector it // contains is identified by (cyl, head, sect) set of disk coordinates; // (geometry and performance figures are loaded from disk image file). // Operations on sectors (R/W) require previous seek on the desired cylinder. // It also contains a sector buffer of one sector to speed up operations. // // It uses the same interface as Device, redefining only a few methods' // implementation: refer to it for individual methods descriptions. // // It adds to Device data structure: // a pointer to SetupInfo object containing disk image file name; // a static buffer for device operation & status description; // a FILE structure for disk image file access; // a set of disk parameters (read from disk image file header); // a Block object for file handling; // some items for performance computation. DiskDevice::DiskDevice(SystemBus* bus, const MachineConfig* cfg, unsigned int line, unsigned int devNo) : Device(bus, line, devNo) , config(cfg) { // adds to a Device object DiskDevice-specific fields dType = DISKDEV; isWorking = true; reg[STATUS] = READY; sprintf(statStr, "Idle"); diskBuf = new Block(); // tries to access disk image file if ((diskFile = fopen(config->getDeviceFile(intL, devNum).c_str(), "r+")) == NULL) { sprintf(strbuf, "Cannot open disk %u file : %s", devNum, strerror(errno)); Panic(strbuf); } // else file has been open with success: tests if it is a valid disk file diskP = new DiskParams(diskFile, &diskOfs); if (diskOfs == 0) { // file is not a valid disk file sprintf(strbuf, "Cannot open disk %u file : invalid/corrupted file", devNum); Panic(strbuf); } // DATA1 format == drive geometry: CYL CYL HEAD SECT reg[DATA1] = (diskP->getCylNum() << HWORDLEN) | (diskP->getHeadNum() << BYTELEN) | diskP->getSectNum(); currCyl = 0; sectTicks = (diskP->getRotTime() * config->getClockRate()) / diskP->getSectNum(); cylBuf = headBuf = sectBuf = MAXWORDVAL; } DiskDevice::~DiskDevice() { delete diskBuf; delete diskP; if (fclose(diskFile) == EOF) { sprintf(strbuf, "Cannot close disk file %u : %s", devNum, strerror(errno)); Panic(strbuf); } } // Disk device register write: only COMMAND and DATA0 registers are // writable, and only when device is not busy. void DiskDevice::WriteDevReg(unsigned int regnum, Word data) { if (reg[STATUS] == BUSY) return; Word timeOfs; unsigned int cyl, head, sect, currSect; switch (regnum) { case COMMAND: reg[COMMAND] = data; // Decode operation requested: for each, acknowledges a // previous interrupt if pending, sets the device registers, // and inserts an Event in SystemBus mantained queue. switch (data & BYTEMASK) { case RESET: bus->IntAck(intL, devNum); // controller reset & cylinder recalibration timeOfs = (DISKRESETTIME + (diskP->getSeekTime() * currCyl)) * config->getClockRate(); complTime = scheduleIOEvent(timeOfs); sprintf(statStr, "Resetting (last op: %s)", isSuccess(dType, reg[STATUS])); reg[STATUS] = BUSY; break; case ACK: bus->IntAck(intL, devNum); sprintf(statStr, "Idle (last op: %s)", isSuccess(dType, reg[STATUS])); reg[STATUS] = READY; break; case DSEEKCYL: bus->IntAck(intL, devNum); cyl = (data >> BYTELEN) & IMMMASK; if (cyl < diskP->getCylNum()) { bus->IntAck(intL, devNum); sprintf(statStr, "Seeking Cyl 0x%.4X (last op: %s)", cyl, isSuccess(dType, reg[STATUS])); // compute movement offset if (cyl < currCyl) cyl = currCyl - cyl; else cyl = cyl - currCyl; complTime = scheduleIOEvent((diskP->getSeekTime() * cyl * config->getClockRate()) + 1); reg[STATUS] = BUSY; } else { // cyl out of range sprintf(statStr, "Cyl 0x%.4X out of range : waiting for ACK", cyl); reg[STATUS] = DSEEKERR; bus->IntReq(intL, devNum); } break; case DREADBLK: bus->IntAck(intL, devNum); // computes target coordinates head = (data >> HWORDLEN) & BYTEMASK; sect = (data >> BYTELEN) & BYTEMASK; if (head < diskP->getHeadNum() && sect < diskP->getSectNum()) { sprintf(statStr, "Reading C/H/S 0x%.4X/0x%.2X/0x%.2X (last op: %s)", currCyl, head, sect, isSuccess(dType, reg[STATUS])); if (currCyl == cylBuf && head == headBuf && sect == sectBuf) { // sector is already in disk buffer timeOfs = DMATICKS; } else { // invalidate current buffer cylBuf = headBuf = sectBuf = MAXWORDVAL; // compute op completion time // use only TodLO for easier computation currSect = (bus->getToDLO() / sectTicks) % diskP->getSectNum(); // remaining time for current sector timeOfs = bus->getToDLO() % sectTicks; // compute sector offset if (sect > currSect) sect = (sect - currSect) - 1; else sect = (diskP->getSectNum() - 1) - (currSect - sect); // completion time is = current sect rem. time + // sectors-in-between time + sector data read + // DMA transfer time timeOfs += (sectTicks * sect) + ((sectTicks * diskP->getDataSect()) / 100) + DMATICKS; } complTime = scheduleIOEvent(timeOfs); reg[STATUS] = BUSY; } else { // head/sector out of range sprintf(statStr, "Head/sect 0x%.2X/0x%.2X out of range : waiting for ACK", head, sect); reg[STATUS] = DREADERR; bus->IntReq(intL, devNum); } break; case DWRITEBLK: bus->IntAck(intL, devNum); // computes target coordinates head = (data >> HWORDLEN) & BYTEMASK; sect = (data >> BYTELEN) & BYTEMASK; if (head < diskP->getHeadNum() && sect < diskP->getSectNum()) { sprintf(statStr, "Writing C/H/S 0x%.4X/0x%.2X/0x%.2X (last op: %s)", currCyl, head, sect, isSuccess(dType, reg[STATUS])); // DMA transfer from memory if (bus->DMATransfer(diskBuf, reg[DATA0], false)) { // DMA transfer error: invalidate current buffer cylBuf = headBuf = sectBuf = MAXWORDVAL; timeOfs = DMATICKS; } else { // disk sector in buffer from memory cylBuf = currCyl; headBuf = head; sectBuf = sect; // compute op completion time // use only TodLO for easier computation // disk spins during DMA transfer currSect = ((bus->getToDLO() + DMATICKS) / sectTicks) % diskP->getSectNum(); // remaining time for DMA + current sector timeOfs = DMATICKS + ((bus->getToDLO() + DMATICKS) % sectTicks); // compute sector offset if (sect > currSect) sect = (sect - currSect) - 1; else sect = (diskP->getSectNum() - 1) - (currSect - sect); // completion time is = DMA time + current sect rem. time + // sectors-in-between time + sector data write timeOfs += (sectTicks * sect) + ((sectTicks * diskP->getDataSect()) / 100); } complTime = scheduleIOEvent(timeOfs); reg[STATUS] = BUSY; } else { // head/sector out of range sprintf(statStr, "Head/sect 0x%.2X/0x%.2X out of range : waiting for ACK", head, sect); reg[STATUS] = DWRITERR; bus->IntReq(intL, devNum); } break; default: sprintf(statStr, "Unknown command (last op: %s)", isSuccess(dType, reg[STATUS])); reg[STATUS] = ILOPERR; bus->IntReq(intL, devNum); break; } SignalStatusChanged(getDevSStr()); break; case DATA0: // physical address for R/W buffer in memory reg[DATA0] = data; break; default: break; } } const char* DiskDevice::getDevSStr() { return statStr; } unsigned int DiskDevice::CompleteDevOp() { // for file access SWord blkOfs; unsigned int head, sect; // checks which operation must be completed: for each, sets device // register, performs requested operation and produces an interrupt // request switch (reg[COMMAND] & BYTEMASK) { case RESET: // a reset always works, even if isWorking == FALSE // it invalidates the sector buffer sprintf(statStr, "Reset completed : waiting for ACK"); reg[STATUS] = READY; cylBuf = headBuf = sectBuf = MAXWORDVAL; break; case DSEEKCYL: if (isWorking) { currCyl = (reg[COMMAND] >> BYTELEN) & IMMMASK; sprintf(statStr, "Cyl 0x%.4X reached : waiting for ACK", currCyl); reg[STATUS] = READY; } else { // error simulation: currCyl is between seek start & end currCyl = (((reg[COMMAND] >> BYTELEN) & IMMMASK) + currCyl) / 2; sprintf(statStr, "Cyl 0x%.4X seek error : waiting for ACK", currCyl); reg[STATUS] = DSEEKERR; } break; case DREADBLK: // locates target coordinates head = (reg[COMMAND] >> HWORDLEN) & BYTEMASK; sect = (reg[COMMAND] >> BYTELEN) & BYTEMASK; if (isWorking) { blkOfs = (diskOfs + ((currCyl * diskP->getHeadNum() * diskP->getSectNum()) + (head * diskP->getSectNum()) + sect) * BLOCKSIZE) * WORDLEN; if (cylBuf != MAXWORDVAL || !diskBuf->ReadBlock(diskFile, blkOfs)) { // Wanted sector is already in buffer or has been read correctly cylBuf = currCyl; headBuf = head; sectBuf = sect; if (bus->DMATransfer(diskBuf, reg[DATA0], true)) { // DMA transfer error reg[STATUS] = DDMAERR; sprintf(statStr, "DMA error reading C/H/S 0x%.4X/0x%.2X/0x%.2X : waiting for ACK", currCyl, head, sect); } else { // all ok sprintf(statStr, "C/H/S 0x%.4X/0x%.2X/0x%.2X block read: waiting for ACK", currCyl, head, sect); reg[STATUS] = READY; } } else { // ReadBlock() has failed for sure sprintf(strbuf, "Unable to read disk %u file : invalid/corrupted file", devNum); Panic(strbuf); } } else { // error simulation sprintf(statStr, "Error reading C/H/S 0x%.4X/0x%.2X/0x%.2X : waiting for ACK", currCyl, head, sect); // buffer invalidation cylBuf = headBuf = sectBuf = MAXWORDVAL; reg[STATUS] = DREADERR; } break; case DWRITEBLK: // locates target coordinates head = (reg[COMMAND] >> HWORDLEN) & BYTEMASK; sect = (reg[COMMAND] >> BYTELEN) & BYTEMASK; if (isWorking) { blkOfs = (diskOfs + ((currCyl * diskP->getHeadNum() * diskP->getSectNum()) + (head * diskP->getSectNum()) + sect) * BLOCKSIZE) * WORDLEN; if (diskBuf->WriteBlock(diskFile, blkOfs)) { // error writing block to disk file sprintf(strbuf, "Unable to write disk %u file : invalid/corrupted file", devNum); Panic(strbuf); } // else all is ok: buffer is still valid sprintf(statStr, "C/H/S 0x%.4X/0x%.2X/0x%.2X block written : waiting for ACK", currCyl, head, sect); reg[STATUS] = READY; } else { // error simulation & buffer invalidation cylBuf = headBuf = sectBuf = MAXWORDVAL; sprintf(statStr, "Error writing C/H/S 0x%.4X/0x%.2X/0x%.2X : waiting for ACK", currCyl, head, sect); reg[STATUS] = DWRITERR; } break; default: Panic("Unknown operation in DiskDevice::CompleteDevOp()"); break; } SignalStatusChanged(getDevSStr()); bus->IntReq(intL, devNum); return STATUS; } // FlashDevice class allows to emulate a flash drive: each 4096 byte block // is identified by one flash device coordinate; // (geometry and performance figures are loaded from flash device image file). // It also contains a block buffer of one block to speed up operations. // // FlashDevice uses the same interface as Device, redefining only a few methods' // implementation: refer to it for individual methods descriptions. // // It adds to Device data structure: // a pointer to SetupInfo object containing flash device log file name; // a static buffer for device operation & status description; // a FILE structure for flash device image file access; // a Block object for file handling; // some items for performance computation. FlashDevice::FlashDevice(SystemBus* bus, const MachineConfig* cfg, unsigned int line, unsigned int devNo) : Device(bus, line, devNo) , config(cfg) { // adds to a Device object FlashDevice-specific fields dType = FLASHDEV; isWorking = true; reg[STATUS] = READY; sprintf(statStr, "Idle"); flashBuf = new Block(); // tries to access flash device image file if ((flashFile = fopen(config->getDeviceFile(intL, devNum).c_str(), "r+")) == NULL) { sprintf(strbuf, "Cannot open flash device %u file : %s", devNum, strerror(errno)); Panic(strbuf); } // else file has been open with success: tests if it is a valid flash device file flashP = new FlashParams(flashFile, &flashOfs); if (flashOfs == 0) { // file is not a valid flash device file sprintf(strbuf, "Cannot open flash device %u file : invalid/corrupted file", devNum); Panic(strbuf); } // DATA1 format == drive geometry: BLOCKS reg[DATA1] = flashP->getBlocksNum(); blockBuf = MAXWORDVAL; } FlashDevice::~FlashDevice() { delete flashBuf; delete flashP; if (fclose(flashFile) == EOF) { sprintf(strbuf, "Cannot close flash device file %u : %s", devNum, strerror(errno)); Panic(strbuf); } } // Flash device register write: only COMMAND and DATA0 registers are // writable, and only when device is not busy. void FlashDevice::WriteDevReg(unsigned int regnum, Word data) { if (reg[STATUS] == BUSY) return; Word timeOfs; unsigned int block; switch (regnum) { case COMMAND: reg[COMMAND] = data; // Decode operation requested: for each, acknowledges a // previous interrupt if pending, sets the device registers, // and inserts an Event in SystemBus mantained queue switch (data & BYTEMASK) { case RESET: bus->IntAck(intL, devNum); timeOfs = (FLASHRESETTIME + flashP->getWTime()) * config->getClockRate(); complTime = scheduleIOEvent(timeOfs); sprintf(statStr, "Resetting (last op: %s)", isSuccess(dType, reg[STATUS])); reg[STATUS] = BUSY; break; case ACK: bus->IntAck(intL, devNum); sprintf(statStr, "Idle (last op: %s)", isSuccess(dType, reg[STATUS])); reg[STATUS] = READY; break; case FREADBLK: bus->IntAck(intL, devNum); // computes target coordinates block = (data >> BYTELEN) & MAXBLOCKS; if (block < flashP->getBlocksNum()) { sprintf(statStr, "Reading block 0x%.6X (last op: %s)", block, isSuccess(dType, reg[STATUS])); if (block == blockBuf) { // block is already in flash device buffer timeOfs = DMATICKS; } else { // invalidate current buffer blockBuf = MAXWORDVAL; // completion time is = block data read + DMA transfer time timeOfs = ((flashP->getWTime() * READRATIO) * config->getClockRate()) + DMATICKS; } complTime = scheduleIOEvent(timeOfs); reg[STATUS] = BUSY; } else { // block out of range sprintf(statStr, "Block 0x%.6X out of range : waiting for ACK", block); reg[STATUS] = FREADERR; bus->IntReq(intL, devNum); } break; case FWRITEBLK: bus->IntAck(intL, devNum); // computes target coordinates block = (data >> BYTELEN) & MAXBLOCKS; if (block < flashP->getBlocksNum()) { sprintf(statStr, "Writing block 0x%.6X (last op: %s)", block, isSuccess(dType, reg[STATUS])); // DMA transfer from memory if (bus->DMATransfer(flashBuf, reg[DATA0], false)) { // DMA transfer error: invalidate current buffer blockBuf = MAXWORDVAL; timeOfs = DMATICKS; } else { // flash device block in buffer from memory blockBuf = block; // completion time is = block data write + DMA transfer time timeOfs = ((flashP->getWTime()) * config->getClockRate()) + DMATICKS; } complTime = scheduleIOEvent(timeOfs); reg[STATUS] = BUSY; } else { // block out of range sprintf(statStr, "Block 0x%.6X out of range : waiting for ACK", block); reg[STATUS] = FWRITERR; bus->IntReq(intL, devNum); } break; default: sprintf(statStr, "Unknown command (last op: %s)", isSuccess(dType, reg[STATUS])); reg[STATUS] = ILOPERR; bus->IntReq(intL, devNum); break; } SignalStatusChanged(getDevSStr()); break; case DATA0: // physical address for R/W buffer in memory reg[DATA0] = data; break; default: break; } } const char* FlashDevice::getDevSStr() { return statStr; } unsigned int FlashDevice::CompleteDevOp() { // for file access SWord blkOfs; unsigned int block; // checks which operation must be completed: for each, sets device // register, performs requested operation and produces an interrupt // request switch (reg[COMMAND] & BYTEMASK) { case RESET: // a reset always works, even if isWorking == FALSE // it invalidates the block buffer sprintf(statStr, "Reset completed : waiting for ACK"); reg[STATUS] = READY; blockBuf = MAXWORDVAL; break; case FREADBLK: // locates target coordinates block = (reg[COMMAND] >> BYTELEN) & MAXBLOCKS; if (isWorking) { blkOfs = (flashOfs + (block * BLOCKSIZE)) * WORDLEN; if (blockBuf != MAXWORDVAL || !flashBuf->ReadBlock(flashFile, blkOfs)) { // Wanted block is already in buffer or has been read correctly blockBuf = block; if (bus->DMATransfer(flashBuf, reg[DATA0], true)) { // DMA transfer error reg[STATUS] = FDMAERR; sprintf(statStr, "DMA error reading block 0x%.6X : waiting for ACK", block); } else { // all ok sprintf(statStr, "Block 0x%.6X read: waiting for ACK", block); reg[STATUS] = READY; } } else { // ReadBlock() has failed for sure sprintf(strbuf, "Unable to read flash device %u file : invalid/corrupted file", devNum); Panic(strbuf); } } else { // error simulation sprintf(statStr, "Error reading block 0x%.6X : waiting for ACK", block); // buffer invalidation blockBuf = MAXWORDVAL; reg[STATUS] = FREADERR; } break; case FWRITEBLK: // locates target coordinates block = (reg[COMMAND] >> BYTELEN) & MAXBLOCKS; if (isWorking) { blkOfs = (flashOfs + (block * BLOCKSIZE)) * WORDLEN; if (flashBuf->WriteBlock(flashFile, blkOfs)) { // error writing block to flash device file sprintf(strbuf, "Unable to write flash device %u file : invalid/corrupted file", devNum); Panic(strbuf); } // else all is ok: buffer is still valid sprintf(statStr, "Block 0x%.6X written : waiting for ACK", block); reg[STATUS] = READY; } else { // error simulation & buffer invalidation blockBuf = MAXWORDVAL; sprintf(statStr, "Error writing block 0x%.6X : waiting for ACK", block); reg[STATUS] = FWRITERR; } break; default: Panic("Unknown operation in FlashDevice::CompleteDevOp()"); break; } SignalStatusChanged(getDevSStr()); bus->IntReq(intL, devNum); return STATUS; } /****************************************************************************/ /* Definitions strictly local to the module. */ /****************************************************************************/ // This function decodes device STATUS field and tells if previous operation // has been successful or not HIDDEN const char * isSuccess(unsigned int devType, Word regVal) { const char * result = NULL; switch (devType) { case PRNTDEV: case DISKDEV: case FLASHDEV: case ETHDEV: if (regVal == READY) result = opResult[true]; else result = opResult[false]; break; case TERMDEV: if (regVal == READY || regVal == RECVD || regVal == TRANSMD) result = opResult[true]; else result = opResult[false]; break; default: Panic("Unknown device in device module::isSuccess()"); break; } return(result); } // EthDevice class allows to emulate an ethernet interface EthDevice::EthDevice(SystemBus* bus, const MachineConfig* cfg, unsigned int line, unsigned int devNo) : Device(bus, line, devNo), config(cfg) { // adds to a Device object EthDevice-specific fields dType = ETHDEV; isWorking = true; reg[STATUS] = READY; readbuf = new Block(); writebuf = new Block(); sprintf(statStr, "Idle"); // FIXME: we should make this much better (and hairy...) if (!testnetinterface(config->getDeviceFile(intL, devNum).c_str())) throw EthError(devNo); /* open the net */ netint = new netinterface(config->getDeviceFile(intL, devNum).c_str(), (const char*) config->getMACId(devNum), devNum); if (netint->getmode() & INTERRUPT) { scheduleIOEvent(POLLNETTIME * config->getClockRate()); polling = true; } else { polling = false; } } EthDevice::~EthDevice() { delete netint; delete readbuf; delete writebuf; } void EthDevice::WriteDevReg(unsigned int regnum, Word data) { int rp = reg[STATUS] & READPENDING; int err = 0; if ((reg[STATUS] & READPENDINGMASK) != BUSY) { switch (regnum) { case COMMAND: // decode operation requested: for each, acknowledges a // previous interrupt if pending, sets the device registers, // and inserts an Event in SystemBus mantained queue reg[COMMAND] = data; switch (data) { case RESET: bus->IntAck(intL, devNum); sprintf(statStr, "Reset requested : waiting for ACK"); reg[STATUS] = BUSY; complTime = scheduleIOEvent(ETHRESETTIME * config->getClockRate()); break; case ACK: bus->IntAck(intL, devNum); sprintf(statStr, "Idle (last op: %s)", isSuccess(dType, reg[STATUS] & READPENDINGMASK)); reg[STATUS] = READY; break; case READCONF: bus->IntAck(intL, devNum); reg[STATUS] = BUSY; sprintf(statStr, "Reading Interface Configuration"); complTime = scheduleIOEvent(CONFNETTIME * config->getClockRate()); break; case CONFIGURE: bus->IntAck(intL, devNum); reg[STATUS] = BUSY; sprintf(statStr, "Writing Interface Configuration"); complTime = scheduleIOEvent(CONFNETTIME * config->getClockRate()); break; case READNET: bus->IntAck(intL, devNum); reg[STATUS] = BUSY; complTime = scheduleIOEvent(READNETTIME * config->getClockRate()); sprintf(statStr, "Receiving Data"); break; case WRITENET: bus->IntAck(intL, devNum); if (bus->DMAVarTransfer(writebuf, reg[DATA0], reg[DATA1], false)) { reg[STATUS] = DDMAERR; sprintf(statStr, "DMA error on netwrite: waiting for ACK"); err=1; } else { complTime = scheduleIOEvent(WRITENETTIME * config->getClockRate()); reg[STATUS] = BUSY; sprintf(statStr, "Sending Data"); } break; default: sprintf(statStr, "Unknown command (last op: %s)", isSuccess(dType, reg[STATUS] & READPENDINGMASK)); reg[STATUS] = ILOPERR; err=1; break; } reg[STATUS] |= rp; if (err) bus->IntReq(intL, devNum); SignalStatusChanged(getDevSStr()); break; case DATA0: reg[DATA0] = data; break; case DATA1: reg[DATA1] = data; break; default: break; } } } const char* EthDevice::getDevSStr() { return statStr; } unsigned int EthDevice::CompleteDevOp() { int rp = reg[STATUS] & READPENDING; const bool busy = (reg[STATUS] & READPENDINGMASK) == BUSY; if (polling && !busy) { /* polling with no pending ops */ polling = false; if (!rp) { /* process has not been informed yet */ if (netint->polling()) { /* there are waiting packets */ reg[STATUS] = reg[STATUS] | READPENDING; SignalStatusChanged(getDevSStr()); bus->IntReq(intL, devNum); } else { /* there are no waiting packets; continue polling if the user hasn't changed her mind */ if (netint->getmode() & INTERRUPT) { scheduleIOEvent(POLLNETTIME * config->getClockRate()); polling = true; } } } } else { //Real operation switch (reg[COMMAND]) { case RESET: // a reset always works, even if isWorking == FALSE sprintf(statStr, "Reset completed : waiting for ACK"); reg[STATUS] = READY; break; case READCONF: // readconf always works even if isWorking == FALSE { char macaddr[6]; sprintf(statStr, "Interface Configuration Read : waiting for ACK"); netint->getaddr(macaddr); reg[DATA0]=(((Word) netint->getmode()) <<16) | (((Word) macaddr[0])<<8) | ((Word) macaddr[1]); reg[DATA1]=((Word) macaddr[2])<<24 | ((Word) macaddr[3])<<16 | ((Word) macaddr[4]) <<8 | ((Word)macaddr[5]); } reg[STATUS] = READY; break; case CONFIGURE: // configure always works even if isWorking == FALSE { char macaddr[6]; int newmode=reg[DATA0]>>16; if ((newmode & SETMAC) != 0) { macaddr[0]=reg[DATA0]>>8 & 0xff; macaddr[1]=reg[DATA0] & 0xff; macaddr[2]=reg[DATA1]>>24 & 0xff; macaddr[3]=reg[DATA1]>>16 & 0xff; macaddr[4]=reg[DATA1]>>8 & 0xff; macaddr[5]=reg[DATA1] & 0xff; netint->setaddr(macaddr); } newmode &= ~SETMAC; sprintf(statStr, "Interface Reconfigured: waiting for ACK"); netint->setmode(newmode); } reg[STATUS] = READY; break; case READNET: if (isWorking) { if ((reg[DATA1]=netint->readdata((char *) readbuf, PACKETSIZE)) < 0) { sprintf(statStr, "Net reading error: waiting for ACK"); reg[STATUS] = DREADERR; } else if (reg[DATA1] == 0) { sprintf(statStr, "No pending packet for read: waiting for ACK"); reg[STATUS] = READY; } else { if (bus->DMAVarTransfer(readbuf, reg[DATA0], reg[DATA1], true)) { reg[STATUS] = FDMAERR; sprintf(statStr, "DMA error on netread: waiting for ACK"); } else { sprintf(statStr, "Packet received: waiting for ACK"); reg[STATUS] = READY; } } rp=netint->polling(); } else { // no operation & error simulation sprintf(statStr, "Net reading error : waiting for ACK"); reg[STATUS] = DREADERR; } break; case WRITENET: if (isWorking) { if (reg[DATA1] == netint->writedata((char *)writebuf, reg[DATA1])) { sprintf(statStr, "Packet Sent: waiting for ACK"); reg[STATUS] = READY; } else { sprintf(statStr, "Net writing error: waiting for ACK"); reg[STATUS] = DWRITERR; } } else { // no operation & error simulation sprintf(statStr, "Net writing error : waiting for ACK"); reg[STATUS] = DWRITERR; } break; } SignalStatusChanged(getDevSStr()); reg[STATUS] |= rp; bus->IntReq(intL, devNum); // If user wants interrupts, we are not already polling, and // there are no pending read requests, schedule another poll // event. if (netint->getmode() & INTERRUPT && !polling && !rp) { scheduleIOEvent(POLLNETTIME * config->getClockRate()); polling = true; } } return STATUS; } bool EthDevice::isBusy() const { return (reg[STATUS] & READPENDINGMASK) == BUSY; } umps3-3.0.5/src/umps/device.h000066400000000000000000000255621435132321600157630ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2004 Mauro Morsiani * Copyright (C) 2020 Mattia Biondi * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef UMPS_DEVICE_H #define UMPS_DEVICE_H #include "umps/types.h" #include "umps/const.h" #include enum DeviceType { DT_NULL = 0, DT_DISK, DT_FLASH, DT_ETH, DT_PRINTER, DT_TERMINAL, N_DEVICES }; #define PRNTBUFSIZE 128 #define TERMBUFSIZE 128 #define DISKBUFSIZE 128 #define FLASHBUFSIZE 128 #define ETHBUFSIZE 128 class SystemBus; class Block; class DiskParams; class FlashParams; class netinterface; class MachineConfig; // Device class defines the interface to all device types, and represents // the "uninstalled device" (NULLDEV) itself. Device objects are created and // controlled by a SystemBus object, but also may be inspected by Watch if // needed class Device { public: // This method creates a Device object with "coordinates" (interrupt // line, device number) clears device register, and links it to bus Device(SystemBus * busl, unsigned int intl, unsigned int dnum); virtual ~Device(); unsigned int getInterruptLine() const { return intL; } unsigned int getNumber() const { return devNum; } // This method returns device type ID unsigned int Type() const { return dType; } // This method is invoked by SystemBus when the required operation // scheduled on the device (using Event objects and queue) should be // completed: the default NULLDEV device has nothing to do, but // others do virtual unsigned int CompleteDevOp(); // This method allows SystemBus to write into device register for // device: NULLDEV device register write has no effects, but other // devices will start performing required operations if COMMAND // register is written with proper codes virtual void WriteDevReg(unsigned int regnum, Word data); // This method returns the static buffer contained in each device // describing the current device status (operation performed, etc.). // NULLDEV devices are not operational virtual const char* getDevSStr(); /* * Return a human-readable expression for completion time of the * current device operation. */ virtual std::string getCTimeInfo() const; // This method allows to copy inputstr contents inside // TerminalDevice receiver buffer: not operational for all other // devices (NULLDEV included) and produces a panic message virtual void Input(const char* inputstr); // This method returns the current value for device register field // indexed by regnum Word ReadDevReg(unsigned int regnum); // This method gets the current operational status for the device, // as set by user inside the simulation; a "not-working" device // fails all operations requested and reports proper error codes; a // NULLDEV always fails. bool getDevNotWorking(); // This method sets the operational status for the device inside the // simulation as user wishes; a "not-working" device fails all operations // requested and reports proper error codes; a NULLDEV always fail bool setDevNotWorking(bool cond); void setCondition(bool working); bool getCondition() const { return isWorking; } sigc::signal SignalStatusChanged; sigc::signal SignalConditionChanged; protected: virtual bool isBusy() const; uint64_t scheduleIOEvent(uint64_t delay); // Interrupt line and device number unsigned int intL; unsigned int devNum; // device register structure Word reg[DEVREGLEN]; // device type ID (see h/const.h) unsigned int dType; // controlling SystemBus object SystemBus* bus; // Completion time for current operation (if any) uint64_t complTime; // device operational status bool isWorking; }; /**************************************************************************/ // PrinterDevice class allows to emulate parallel character printer // currently in use (see performance figures shown before). It uses the same // interface as Device, redefining only a few methods' implementation: refer // to it for individual methods descriptions. // It adds to Device data structure: // a pointer to SetupInfo object containing printer log file name; // a static buffer for device operation & status description; // a FILE structure for log file access. class PrinterDevice: public Device { public: PrinterDevice(SystemBus* busl, const MachineConfig* config, unsigned int intl, unsigned int dnum); virtual ~PrinterDevice(); virtual void WriteDevReg(unsigned int regnum, Word data); virtual unsigned int CompleteDevOp(); virtual const char* getDevSStr(); private: const MachineConfig* const config; // log file handling FILE * prntFile; char statStr[PRNTBUFSIZE]; }; /**************************************************************************/ // TerminalDevice class allows to emulate serial "dumb" terminal (see // performance figures shown before). TerminalDevice may be split up into // two independent sub-devices: "transmitter" and "receiver". // It uses the same interface as Device, redefining only a few methods' // implementation: refer to it for individual methods descriptions. // It adds to Device data structure: // a pointer to SetupInfo object containing terminal log file name; // a static buffer for device operation & status description; // a FILE structure for log file access; // some structures for handling terminal transmitter and receiver. class TerminalDevice: public Device { public: TerminalDevice(SystemBus* bus, const MachineConfig* cfg, unsigned int il, unsigned int devNo); virtual ~TerminalDevice(); virtual void WriteDevReg(unsigned int regnum, Word data); virtual unsigned int CompleteDevOp(); virtual const char* getDevSStr(); const char* getTXStatus() const; const char* getRXStatus() const; std::string getTXCTimeInfo() const; std::string getRXCTimeInfo() const; virtual std::string getCTimeInfo() const; virtual void Input(const char * inputstr); sigc::signal SignalTransmitted; private: const MachineConfig* const config; // for log file handling FILE * termFile; // receiver buffer and pointer to first character to receive from it char * recvBuf; unsigned int recvBp; // static buffer for receiver char recvStatStr[TERMBUFSIZE]; // static buffer for transmitter char tranStatStr[TERMBUFSIZE]; // Completion time for current receiver operation (if any) uint64_t recvCTime; // Completion time for current transmitter operation (if any) uint64_t tranCTime; // receiver operation pending flag bool recvIntPend; // transmitter operation pending flag bool tranIntPend; }; /**************************************************************************/ // DiskDevice class allows to emulate a disk drive: each 4096 byte sector // is identified by (cyl, head, sect) set of disk coordinates; // (geometry and performance figures are loaded from disk image file). // Operations on sectors (R/W) require previous seek on the desired cylinder. // It also contains a sector buffer of one sector to speed up operations. // // It uses the same interface as Device, redefining only a few methods' // implementation: refer to it for individual methods descriptions. // // It adds to Device data structure: // a pointer to SetupInfo object containing disk log file name; // a static buffer for device operation & status description; // a FILE structure for disk image file access; // a set of disk parameters (read from disk image file header); // a Block object for file handling; // some items for performance computation. class DiskDevice: public Device { public: DiskDevice(SystemBus* bus, const MachineConfig* cfg, unsigned int line, unsigned int devNo); virtual ~DiskDevice(); virtual void WriteDevReg(unsigned int regnum, Word data); virtual unsigned int CompleteDevOp(); virtual const char * getDevSStr(); private: const MachineConfig* const config; // to handle it FILE * diskFile; // static buffer char statStr[DISKBUFSIZE]; // sector buffer and coordinates on disk (cyl, head, sect) Block * diskBuf; unsigned int cylBuf, headBuf, sectBuf; // start of disk image inside file (after header) SWord diskOfs; // disk performance parameters DiskParams * diskP; // sector underhead time in ticks Word sectTicks; // current cylinder unsigned int currCyl; }; /**************************************************************************/ // FlashDevice class allows to emulate a flash drive: each 4096 byte block // is identified by one flash device coordinate; // (geometry and performance figures are loaded from flash device image file). // It also contains a block buffer of one block to speed up operations. // // FlashDevice uses the same interface as Device, redefining only a few methods' // implementation: refer to it for individual methods descriptions. // // It adds to Device data structure: // a pointer to SetupInfo object containing flash device log file name; // a static buffer for device operation & status description; // a FILE structure for flash device image file access; // a Block object for file handling; // some items for performance computation. class FlashDevice: public Device { public: FlashDevice(SystemBus* bus, const MachineConfig* cfg, unsigned int line, unsigned int devNo); virtual ~FlashDevice(); virtual void WriteDevReg(unsigned int regnum, Word data); virtual unsigned int CompleteDevOp(); virtual const char * getDevSStr(); private: const MachineConfig* const config; // to handle it FILE * flashFile; // static buffer char statStr[FLASHBUFSIZE]; // block buffer and coordinates on flash device (block) Block * flashBuf; unsigned int blockBuf; // start of flash device image inside file (after header) SWord flashOfs; // flash device performance parameters FlashParams * flashP; }; /**************************************************************************/ // EthDevice class allows to emulate an ethernet interface class EthDevice: public Device { public: EthDevice(SystemBus* bus, const MachineConfig* config, unsigned int line, unsigned int devNo); virtual ~EthDevice(); virtual void WriteDevReg(unsigned int regnum, Word data); virtual unsigned int CompleteDevOp(); virtual const char* getDevSStr(); protected: virtual bool isBusy() const; private: const MachineConfig* const config; Block *readbuf; Block *writebuf; // static buffer char statStr[ETHBUFSIZE]; bool polling; netinterface *netint; }; #endif // UMPS_DEVICE_H umps3-3.0.5/src/umps/disassemble.cc000066400000000000000000000367351435132321600171610ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2004 Mauro Morsiani * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /**************************************************************************** * * This support module contains some functions used to convert MIPS * instructions in human-readable form. They are used by objdump utility * and Watch class. * It also contains some utility functions for instruction decoding and sign * extension of halfword quantities. * ****************************************************************************/ #include #include #include "umps/const.h" #include "umps/types.h" #include "umps/cp0.h" #include "umps/processor_defs.h" #include "umps/arch.h" // register names HIDDEN const char* const regName[CPUREGNUM] = { "00", "at", "v0", "v1", "a0", "a1", "a2", "a3", "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra", "HI", "LO" }; // CP0 register names HIDDEN const char* const cp0RegName[CP0REGNUM] = { "Index", "Random", "EntryLo", "BadVAddr", "Timer", "EntryHi", "Status", "Cause", "EPC", "PRId" }; // instruction mnemonic names split by type: array is indexed by opcode // (this explains the empty spaces) // register instructions HIDDEN const char* const regIName[] = { "sll", "", "srl", "sra", "sllv", "", "srlv", "srav", "jr", "jalr", "", "cas", "syscall", "break", "", "", "mfhi", "mthi", "mflo", "mtlo", "", "", "", "", "mult", "multu", "div", "divu", "", "", "", "", "add", "addu", "sub", "subu", "and", "or", "xor", "nor", "", "", "slt", "sltu", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "nop", "ri/ill (r-type)" }; // position for NOP (since its opcode clashes with SLL) #define NOPINAME 62 // reserved instruction string position for regIName array #define RIREGINAME 63 // immediate/branch/load/store instructions HIDDEN const char* const iName[] = { "bltz", "bgez", "j", "jal", "beq", "bne", "blez", "bgtz", "addi", "addiu", "slti", "sltiu", "andi", "ori", "xori", "lui", "bltzal", "bgezal", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "lb", "lh", "lwl", "lw", "lbu", "lhu", "lwr", "", "sb", "sh", "swl", "sw", "", "", "swr", "", "", "", "", "", "", "", "", "", "", "", "", "ri/ill (unknown)", "ri/ill (R4000)", "ri/ill (load/store)", "ri/ill (branch type)", "ri/ill (immediate)" }; // string positions for illegal opcodes by type in previous array #define RIIMMINAME 63 #define RIBRANCHINAME 62 #define RILSINAME 61 #define RIR40INAME 60 #define RIUNKINAME 59 // coprocessor instructions HIDDEN const char* const cp0IName[] = { "mfc0", "tlbr", "tlbwi", "bc0f", "mtc0", "bc0t", "tlbwr", "rfe", "tlbp", "ri/ill/unimp" }; // string positions for peculiar istructions (decoding for them is peculiar) #define RFEINAME 7 #define BC0FINAME 3 #define BC0TINAME 5 #define CPUILLINAME 9 // static string buffer size and definition #define STRBUFSIZE 64 HIDDEN char strbuf[STRBUFSIZE]; // utility functions for instruction decoding HIDDEN void strRegInstr(Word instr); HIDDEN void strImmInstr(Word instr); HIDDEN void strBranchInstr(Word instr); HIDDEN void strLSInstr(Word instr); HIDDEN void strCopInstr(Word instr); /****************************************************************************/ /* Definitions to be exported. */ /****************************************************************************/ // This function decodes and returns the opcode type for an instruction unsigned int OpType(Word instr) { if (OPCODE(instr) == 0UL) // SPECIAL field is 0: istruction type is REGTYPE return(REGTYPE); else return((instr & OPTYPEMASK) >> OPCODEOFFS); } // This function sign-extends an halfword quantity to a full word for // 2-complement arithmetics SWord SignExtImm(Word instr) { // computes sign bit value if (((instr >> IMMSIGNPOS) & 0x1UL)) return(instr | ~(IMMMASK)); else return(ZEXTIMM(instr)); } // This function detects invalid formats for register type instructions, // checking if fields are set appropriately to zero where needed. // Returns FALSE if instruction is valid, and a non-zero value otherwise bool InvalidRegInstr(Word instr) { bool invalid = false; switch (FUNCT(instr)) { case SFN_ADD: case SFN_ADDU: case SFN_AND: case SFN_NOR: case SFN_OR: case SFN_SLLV: case SFN_SLT: case SFN_SLTU: case SFN_SRAV: case SFN_SRLV: case SFN_SUB: case SFN_SUBU: case SFN_XOR: case SFN_CAS: invalid = SHAMT(instr); break; case SFN_SLL: case SFN_SRA: case SFN_SRL: invalid = RS(instr); break; case SFN_DIV: case SFN_DIVU: case SFN_MULT: case SFN_MULTU: invalid = SHAMT(instr) || RD(instr); break; case SFN_JALR: invalid = SHAMT(instr) || RT(instr); break; case SFN_MFHI: case SFN_MFLO: invalid = SHAMT(instr) || RT(instr) || RS(instr); break; case SFN_JR: case SFN_MTHI: case SFN_MTLO: invalid = SHAMT(instr) || RT(instr) || RD(instr); break; case SFN_BREAK: case SFN_SYSCALL: default: break; } return invalid; } // This function maps the MIPS R2/3000 CP0 register codes to simulator // internal codes and returns TRUE if the register is implemented, // FALSE otherwise bool ValidCP0Reg(unsigned int regnum, unsigned int* cpnum) { bool ret = true; switch(regnum) { case CP0_Index: *cpnum = INDEX; break; case CP0_Random: *cpnum = RANDOM; break; case CP0_EntryLo: *cpnum = ENTRYLO; break; case CP0_BadVAddr: *cpnum = BADVADDR; break; case CP0_Timer: *cpnum = CP0REG_TIMER; break; case CP0_EntryHi: *cpnum = ENTRYHI; break; case CP0_Status: *cpnum = STATUS; break; case CP0_Cause: *cpnum = CAUSE; break; case CP0_EPC: *cpnum = EPC; break; case CP0_PRID: *cpnum = PRID; break; default: // invalid register ret = false; *cpnum = PRID; // this way, bad function use doesn't touch critical CP0 reg break; } return ret; } // This function returns main processor's register name indexed by position const char* RegName(unsigned int index) { if (index < CPUREGNUM) return regName[index]; else return EMPTYSTR; } // This function returns CP0 register name indexed by position const char* CP0RegName(unsigned int index) { if (index < CP0REGNUM) return cp0RegName[index]; else return ""; } // this function returns the pointer to a static buffer which contains // the instruction translation into readable form const char* StrInstr(Word instr) { switch (OpType(instr)) { case REGTYPE: strRegInstr(instr); break; case IMMTYPE: strImmInstr(instr); break; case BRANCHTYPE: strBranchInstr(instr); break; case LOADCOPTYPE: case STORECOPTYPE: case COPTYPE: strCopInstr(instr); break; case LOADTYPE: case STORETYPE: strLSInstr(instr); break; default: // unknown instruction (generic) sprintf(strbuf, "%s", iName[RIUNKINAME]); break; } return strbuf; } /****************************************************************************/ /* Definitions strictly local to the module. */ /****************************************************************************/ // This function decodes and puts into static buffer the human // readable form for MIPS register type instructions HIDDEN void strRegInstr(Word instr) { if (InvalidRegInstr(instr)) sprintf(strbuf, "%s", regIName[RIREGINAME]); else { // instruction format is correct switch(FUNCT(instr)) { case SFN_ADD: case SFN_ADDU: case SFN_AND: case SFN_NOR: case SFN_OR: case SFN_SLLV: case SFN_SLT: case SFN_SLTU: case SFN_SRAV: case SFN_SRLV: case SFN_SUB: case SFN_SUBU: case SFN_XOR: case SFN_CAS: sprintf(strbuf, "%s\t$%s, $%s, $%s", regIName[FUNCT(instr)], regName[RD(instr)], regName[RS(instr)], regName[RT(instr)]); break; case SFN_SLL: case SFN_SRA: case SFN_SRL: if (instr == NOP) sprintf(strbuf, "%s", regIName[NOPINAME]); else sprintf(strbuf, "%s\t$%s, $%s, %.2lu", regIName[FUNCT(instr)], regName[RD(instr)], regName[RT(instr)], SHAMT(instr)); break; case SFN_DIV: case SFN_DIVU: case SFN_MULT: case SFN_MULTU: sprintf(strbuf, "%s\t$%s, $%s", regIName[FUNCT(instr)], regName[RS(instr)], regName[RT(instr)]); break; case SFN_JALR: sprintf(strbuf, "%s\t$%s, $%s", regIName[FUNCT(instr)], regName[RD(instr)], regName[RS(instr)]); break; case SFN_MFHI: case SFN_MFLO: sprintf(strbuf, "%s\t$%s", regIName[FUNCT(instr)], regName[RD(instr)]); break; case SFN_JR: case SFN_MTHI: case SFN_MTLO: sprintf(strbuf, "%s\t$%s", regIName[FUNCT(instr)], regName[RS(instr)]); break; case SFN_BREAK: case SFN_SYSCALL: sprintf(strbuf, "%s\t(%lu)", regIName[FUNCT(instr)], CALLVAL(instr)); break; default: // unknown instruction sprintf(strbuf, "%s", regIName[RIREGINAME]); break; } } } // This function decodes and puts into static buffer the human readable form // for MIPS immediate type instructions HIDDEN void strImmInstr(Word instr) { switch (OPCODE(instr)) { case ADDI: case ADDIU: case SLTI: case SLTIU: sprintf(strbuf, "%s\t$%s, $%s, %+ld", iName[OPCODE(instr)], regName[RT(instr)], regName[RS(instr)], (long int) SignExtImm(instr)); break; case ANDI: case ORI: case XORI: sprintf(strbuf, "%s\t$%s, $%s, 0x%.8lX", iName[OPCODE(instr)], regName[RT(instr)], regName[RS(instr)], ZEXTIMM(instr)); break; case LUI: if (!RS(instr)) sprintf(strbuf, "%s\t$%s, 0x%.4lX", iName[OPCODE(instr)], regName[RT(instr)], ZEXTIMM(instr)); else // instruction is ill-formed sprintf(strbuf, "%s", iName[RIIMMINAME]); break; default: sprintf(strbuf, "%s", iName[RIIMMINAME]); break; } } // This function decodes and puts into static buffer the human readable form // for MIPS branch type instructions HIDDEN void strBranchInstr(Word instr) { switch (OPCODE(instr)) { case BEQ: case BNE: sprintf(strbuf, "%s\t$%s, $%s, %+ld", iName[OPCODE(instr)], regName[RS(instr)], regName[RT(instr)], (long int) (SignExtImm(instr) << WORDSHIFT)); break; case BGL: // uses RT field to choose which branch type is requested switch (RT(instr)) { case BGEZ: case BGEZAL: case BLTZ: case BLTZAL: sprintf(strbuf, "%s\t$%s, %+ld", iName[RT(instr)], regName[RS(instr)], (long int) (SignExtImm(instr) << WORDSHIFT)); break; default: // unknown instruction of this type sprintf(strbuf, "%s", iName[RIBRANCHINAME]); break; } break; case BLEZ: case BGTZ: if (!RT(instr)) // instruction is well formed sprintf(strbuf, "%s\t$%s, %+ld", iName[OPCODE(instr)], regName[RS(instr)], (long int) (SignExtImm(instr) << WORDSHIFT)); else // istruction is ill-formed sprintf(strbuf, "%s", iName[RIBRANCHINAME]); break; case J: case JAL: sprintf(strbuf, "%s\t(4 bit PC)+0x%.7lX", iName[OPCODE(instr)], (instr & ~(OPCODEMASK)) << WORDSHIFT); break; default: sprintf(strbuf, "%s", iName[RIBRANCHINAME]); break; } } // This function decodes and puts into static buffer the human readable form // for MIPS load/store type instructions HIDDEN void strLSInstr(Word instr) { switch (OPCODE(instr)) { case LB: case LBU: case LH: case LHU: case LW: case LWL: case LWR: case SB: case SH: case SW: case SWL: case SWR: sprintf(strbuf, "%s\t$%s, %+ld($%s)", iName[OPCODE(instr)], regName[RT(instr)], (long int) SignExtImm(instr), regName[RS(instr)]); break; default: sprintf(strbuf, "%s", iName[RILSINAME]); break; } } // This function decodes and puts into static buffer the human readable form // for MIPS coprocessor handling instructions HIDDEN void strCopInstr(Word instr) { unsigned int cp0Num; if (OPCODE(instr) == COP0SEL) { // COPOPTYPE corresponds to RS field switch(COPOPTYPE(instr)) { case CO0: // coprocessor 0 operations if (RT(instr) || RD(instr) || SHAMT(instr)) // instruction is ill-formed sprintf(strbuf, "CP%.1lu %s", COPNUM(instr), cp0IName[CPUILLINAME]); else switch(FUNCT(instr)) { case RFE: sprintf(strbuf, "%s", cp0IName[RFEINAME]); break; case TLBP: case TLBR: case TLBWI: case TLBWR: sprintf(strbuf, "%s", cp0IName[FUNCT(instr)]); break; case COFUN_WAIT: std::strcpy(strbuf, "wait"); break; default: // unknown coprocessor 0 operation requested sprintf(strbuf, "CP%.1lu %s", COPNUM(instr), cp0IName[CPUILLINAME]); break; } break; case BC0: switch(COPOPCODE(instr)) { case BC0F: sprintf(strbuf, "%s\t%+ld", cp0IName[BC0FINAME], (long int) (SignExtImm(instr) << WORDSHIFT)); break; case BC0T: sprintf(strbuf, "%s\t%+ld", cp0IName[BC0TINAME], (long int) (SignExtImm(instr) << WORDSHIFT)); break; default: // traps BC0FL R4000 instructions // and the like... sprintf(strbuf, "CP%.1lu %s", COPNUM(instr), cp0IName[CPUILLINAME]); break; } break; case MFC0: // valid instruction has SHAMT and FUNCT fields // set to 0, and refers to a valid CP0 register if (ValidCP0Reg(RD(instr), &cp0Num) && !SHAMT(instr) && !FUNCT(instr)) // istruction is valid sprintf(strbuf, "%s\t$%s, $%s", cp0IName[COPOPTYPE(instr)], regName[RT(instr)], cp0RegName[cp0Num]); else // invalid instruction format or CP0 reg sprintf(strbuf, "CP%.1lu %s", COPNUM(instr), cp0IName[CPUILLINAME]); break; case MTC0: // valid instruction has SHAMT and FUNCT fields // set to 0, and refers to a valid CP0 register if (ValidCP0Reg(RD(instr), &cp0Num) && !SHAMT(instr) && !FUNCT(instr)) // valid instr. sprintf(strbuf, "%s\t$%s, $%s", cp0IName[COPOPTYPE(instr)], regName[RT(instr)], cp0RegName[cp0Num]); else // TLBCLR backpatch if (RD(instr) == CONTEXTREG) sprintf(strbuf, "TLBCLR"); else // invalid instr. sprintf(strbuf, "CP%.1lu %s", COPNUM(instr), cp0IName[CPUILLINAME]); break; default: // unknown and CFC0, CTC0, COP0, LWC0 generic instructions sprintf(strbuf, "CP%.1lu %s", COPNUM(instr), cp0IName[CPUILLINAME]); break; } } else { if (((instr >> DWCOPBITPOS) & 0x1UL)) // LDC, SDC, BEQL reserved instructions handling sprintf(strbuf, "%s", iName[RIR40INAME]); else // LWC, SWC instr. handling: // these ops are invalid for CP0 and // other coprocessors are unavailable sprintf(strbuf, "CP%.1lu %s", COPNUM(instr), cp0IName[CPUILLINAME]); } } // FIXME: not _quite_ complete const char* InstructionMnemonic(Word ins) { if (OpType(ins) == REGTYPE) return regIName[FUNCT(ins)]; else return iName[OPCODE(ins)]; } umps3-3.0.5/src/umps/disassemble.h000066400000000000000000000037641435132321600170170ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2004 Mauro Morsiani * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef UMPS_DISASSEMBLE_H #define UMPS_DISASSEMBLE_H // This function decodes and returns the opcode type for an instruction unsigned int OpType(Word instr); // This function sign-extends an halfword quantity to a full word for // 2-complement arithmetics SWord SignExtImm(Word instr); // This function detects invalid formats for register type instructions, // checking if fields are set appropriately to zero where needed. // Returns FALSE if instruction is valid, and a non-zero value otherwise bool InvalidRegInstr(Word instr); // This function maps the MIPS R2/3000 CP0 register codes to simulator internal // codes and returns TRUE if the register is implemented, FALSE otherwise bool ValidCP0Reg(unsigned int regnum, unsigned int * cpnum); // This function returns main processor's register name indexed by position const char * RegName(unsigned int index); // This function returns CP0 register name indexed by position const char * CP0RegName(unsigned int index); // this function returns the pointer to a static buffer which contains // the instruction translation into readable form const char * StrInstr(Word instr); const char* InstructionMnemonic(Word ins); #endif // UMPS_DISASSEMBLE_H umps3-3.0.5/src/umps/elf2umps.cc000066400000000000000000000371631435132321600164170ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2004 Mauro Morsiani * Copyright (C) 2011 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * This is a stand-alone program which converts an ELF object or executable * file into one of MPS project file formats: .core, .rom or .aout. * See external documentation for format details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "base/bit_tricks.h" #include "umps/aout.h" #include "umps/blockdev_params.h" /* * Functions throughtout this module close over this global! */ static Elf* elf; static Elf32_Ehdr* elfHeader; static const size_t kBlockSize = 4096; #define forEachSection(sd) \ for (sd = elf_nextscn(elf, NULL); sd != NULL; sd = elf_nextscn(elf, sd)) #define forEachSectionData(sd, data) \ for (data = elf_getdata(sd, NULL); data != NULL; data = elf_getdata(sd, data)) struct Symbol { Symbol(const std::string& name, Elf32_Sym* details) : name(name), details(details) { } std::string name; Elf32_Sym* details; }; /* * Iterator class that factors out the logic behind the iteration * through an ELF symbol table. */ class SymbolTableIterator : public std::iterator { public: explicit SymbolTableIterator(Elf* elf); SymbolTableIterator(); SymbolTableIterator& operator++(); SymbolTableIterator operator++(int); Symbol operator*() const; bool operator==(const SymbolTableIterator& other) { return current == other.current; } bool operator!=(const SymbolTableIterator& other) { return current != other.current; } private: Elf_Scn* const stSec; Elf32_Shdr* const stSecHeader; Elf_Data* data; Elf32_Sym* current; }; static void fatalError(const char *format, ...); static void elfError(); static void printHelp(); uint32_t toTargetEndian(uint32_t x); static Elf_Scn* getSectionByType(Elf32_Word type); static Elf32_Addr getGPValue(); static void elf2aout(bool isCore); static void createSymbolTable(); static void elf2bios(); const char* programName; // Input file name const char* fileName = NULL; // Be verbose static bool verbose = false; int main(int argc, char** argv) { programName = argv[0]; if (elf_version(EV_CURRENT) == EV_NONE) fatalError("ELF library out of date"); uint32_t fileId = 0; bool createMap = false; // Parse args, lamely int i = 1; while (i < argc && fileName == NULL) { if (!strcmp("-v", argv[i])) { verbose = true; } else if (!strcmp("-m", argv[i])) { createMap = true; } else if (!strcmp("-k", argv[i])) { fileId = COREFILEID; if (i + 1 < argc) fileName = argv[i + 1]; } else if (!strcmp("-b", argv[i])) { fileId = BIOSFILEID; if (i + 1 < argc) fileName = argv[i + 1]; } else if (!strcmp("-a", argv[i])) { fileId = AOUTFILEID; if (i + 1 < argc) fileName = argv[i + 1]; } i++; } if (fileName == NULL) { fprintf(stderr, "%s : Wrong/unknown argument(s)\n", programName); printHelp(); exit(1); } int fd = open(fileName, O_RDONLY); if (fd == -1) fatalError("Cannot access %s: %s", fileName, strerror(errno)); elf = elf_begin(fd, ELF_C_READ, NULL); if (elf == NULL) elfError(); if (elf_kind(elf) != ELF_K_ELF) fatalError("`%s' is not an ELF file", fileName); // Check ELF file version and arch elfHeader = elf32_getehdr(elf); if (elfHeader == NULL) elfError(); if (elfHeader->e_version != EV_CURRENT) fatalError("%s: unsupported ELF file version", fileName); if (elfHeader->e_machine != EM_MIPS) fatalError("%s: invalid machine type (MIPS expected)", fileName); switch (fileId) { case BIOSFILEID: elf2bios(); break; case AOUTFILEID: elf2aout(false); break; case COREFILEID: elf2aout(true); break; default: // Assert not reached assert(0); } if (fileId == COREFILEID || createMap) createSymbolTable(); return 0; } static void fatalError(const char *format, ...) { fprintf(stderr, "%s: ", programName); va_list ap; va_start(ap, format); vfprintf(stderr, format, ap); va_end(ap); fputc('\n', stderr); exit(1); } static void elfError() { fatalError("ELF error: %s", elf_errmsg(-1)); } static void printHelp() { fprintf(stderr, "%s syntax : %s [-v] [-m] {-k | -b | -a} \n" "\n" "where:\n\t-v\tverbose operation\n\t-m\tmake map file .stab.umps\n" "\n" "\t-k\tmake kernel core file .core.umps + map file\n" "\t-b\tmake BIOS file .rom.umps\n\t-a\tmake a.out file .aout.umps\n\n", programName, programName); } inline uint32_t toTargetEndian(uint32_t x) { #ifdef WORDS_BIGENDIAN return (elfHeader->e_ident[EI_DATA] == ELFDATA2LSB) ? SwapEndian32(x) : x; #else return (elfHeader->e_ident[EI_DATA] == ELFDATA2LSB) ? x : SwapEndian32(x); #endif } SymbolTableIterator::SymbolTableIterator(Elf* elf) : stSec(getSectionByType(SHT_SYMTAB)), stSecHeader(elf32_getshdr(stSec)) { current = NULL; data = NULL; while ((data = elf_getdata(stSec, data)) != NULL) if (data->d_type == ELF_T_SYM && data->d_size > 0) break; if (data != NULL) current = (Elf32_Sym*) data->d_buf; } SymbolTableIterator::SymbolTableIterator() : stSec(NULL), stSecHeader(NULL), current(NULL) { } Symbol SymbolTableIterator::operator*() const { assert(current != NULL); const char* name = elf_strptr(elf, stSecHeader->sh_link, current->st_name); assert(name != NULL); return Symbol(name, current); } SymbolTableIterator& SymbolTableIterator::operator++() { if (current == NULL) return *this; ++current; if (current >= (Elf32_Sym*) data->d_buf + data->d_size / sizeof(Elf32_Sym)) { while ((data = elf_getdata(stSec, data)) != NULL) if (data->d_type == ELF_T_SYM && data->d_size > 0) break; if (data == NULL) current = NULL; else current = (Elf32_Sym*) data->d_buf; } return *this; } SymbolTableIterator SymbolTableIterator::operator++(int) { SymbolTableIterator result = *this; ++(*this); return result; } static Elf_Scn* getSectionByType(Elf32_Word type) { Elf_Scn* sd; forEachSection(sd) { Elf32_Shdr* sh = elf32_getshdr(sd); if (sh == NULL) elfError(); if (sh->sh_type == type) return sd; } return NULL; } /* * Retreive the starting $gp value */ static Elf32_Addr getGPValue() { SymbolTableIterator it(elf), end; for (; it != end; ++it) if ((*it).name == "_gp") return (*it).details->st_value; return (Elf32_Addr) -1; } /* * Convert an ELF executable into a uMPS a.out or core executable. */ static void elf2aout(bool isCore) { // Check ELF object type if (elfHeader->e_type != ET_EXEC) fatalError("ELF object file is not executable"); uint32_t header[N_AOUT_HDR_ENT]; std::fill_n(header, N_AOUT_HDR_ENT, 0); header[AOUT_HE_TAG] = AOUTFILEID; // Set program entry header[AOUT_HE_ENTRY] = elfHeader->e_entry; // Set initial $gp entry header[AOUT_HE_GP_VALUE] = getGPValue(); if (header[AOUT_HE_GP_VALUE] == (Elf32_Addr) -1) fatalError("Cannot obtain initial $gp value"); // Obtain the program header table Elf32_Phdr* pht = elf32_getphdr(elf); if (pht == NULL) elfError(); size_t phtSize; if (elf_getphdrnum(elf, &phtSize)) elfError(); // Obtain segment info bool foundDataSeg = false; uint8_t* dataBuf = NULL; bool foundTextSeg = false; uint8_t* textBuf = NULL; for (size_t i = 0; i < phtSize; i++) { if (pht[i].p_type != PT_LOAD) continue; if (pht[i].p_flags == (PF_R | PF_W)) { if (foundDataSeg) fatalError("Redundant .data program header table entry %u", (unsigned int) i); foundDataSeg = true; header[AOUT_HE_DATA_MEMSZ] = pht[i].p_memsz; header[AOUT_HE_DATA_VADDR] = pht[i].p_vaddr; uint32_t size = isCore ? pht[i].p_memsz : pht[i].p_filesz; header[AOUT_HE_DATA_FILESZ] = (size / kBlockSize) * kBlockSize; if (header[AOUT_HE_DATA_FILESZ] < size) header[AOUT_HE_DATA_FILESZ] += kBlockSize; if (header[AOUT_HE_DATA_FILESZ] > 0) { dataBuf = new uint8_t[header[AOUT_HE_DATA_FILESZ]]; std::fill_n(dataBuf, header[AOUT_HE_DATA_FILESZ], 0); } else { dataBuf = NULL; } } else if (pht[i].p_flags == (PF_R | PF_X)) { if (foundTextSeg) fatalError("Redundant .text program header table entry %u", (unsigned int) i); if (pht[i].p_memsz == 0) fatalError("Empty .text segment"); foundTextSeg = true; header[AOUT_HE_TEXT_MEMSZ] = pht[i].p_memsz; header[AOUT_HE_TEXT_VADDR] = pht[i].p_vaddr; header[AOUT_HE_TEXT_FILESZ] = (pht[i].p_memsz / kBlockSize) * kBlockSize; if (header[AOUT_HE_TEXT_FILESZ] < pht[i].p_memsz) header[AOUT_HE_TEXT_FILESZ] += kBlockSize; textBuf = new uint8_t[header[AOUT_HE_TEXT_FILESZ]]; std::fill_n(textBuf, header[AOUT_HE_TEXT_FILESZ], 0); } else { fprintf(stderr, "Warning: unknown program header table entry %u\n", (unsigned int) i); } } if (!foundTextSeg) fatalError("Missing .text program header"); if (!foundDataSeg) { header[AOUT_HE_DATA_MEMSZ] = header[AOUT_HE_DATA_FILESZ] = 0; header[AOUT_HE_DATA_VADDR] = header[AOUT_HE_TEXT_VADDR] + header[AOUT_HE_TEXT_FILESZ]; dataBuf = NULL; } header[AOUT_HE_TEXT_OFFSET] = 0; header[AOUT_HE_DATA_OFFSET] = header[AOUT_HE_TEXT_FILESZ]; // Scan sections and copy data to a.out segments Elf_Scn* sd; forEachSection(sd) { Elf32_Shdr* sh = elf32_getshdr(sd); if (sh == NULL) elfError(); if (sh->sh_type == SHT_PROGBITS && (sh->sh_flags & SHF_ALLOC)) { uint8_t* buf; if (sh->sh_addr >= header[AOUT_HE_DATA_VADDR]) buf = dataBuf + (sh->sh_addr - header[AOUT_HE_DATA_VADDR]); else buf = textBuf + (sh->sh_addr - header[AOUT_HE_TEXT_VADDR]); Elf_Data* data; forEachSectionData(sd, data) { std::memcpy(buf, data->d_buf, data->d_size); buf += data->d_size; } } } // Write out the .aout header uint32_t* headerBuf = (uint32_t*) textBuf; for (size_t i = 0; i < N_AOUT_HDR_ENT; i++) { if (headerBuf[i]) fatalError("No space for a.out header"); headerBuf[i] = toTargetEndian(header[i]); } std::string outName = fileName; if (isCore) outName += ".core.umps"; else outName += ".aout.umps"; FILE* file = fopen(outName.c_str(), "w"); if (file == NULL) fatalError("Cannot create a.out file `%s'", outName.c_str()); // If it's a core file, write the RRF padding first if (isCore) { uint32_t tag = toTargetEndian(COREFILEID); if (fwrite(&tag, sizeof(tag), 1, file) != 1) fatalError("Error writing a.out file `%s'", outName.c_str()); uint32_t pad = 0; for (size_t i = 0; i < 1024; i++) if (fwrite(&pad, sizeof(pad), 1, file) != 1) fatalError("Error writing a.out file `%s'", outName.c_str()); } // Write the segments, finally. if (fwrite(textBuf, 1, header[AOUT_HE_TEXT_FILESZ], file) != header[AOUT_HE_TEXT_FILESZ]) fatalError("Error writing a.out file `%s'", outName.c_str()); if (dataBuf != NULL) { if (fwrite(dataBuf, 1, header[AOUT_HE_DATA_FILESZ], file) != header[AOUT_HE_DATA_FILESZ]) fatalError("Error writing a.out file `%s'", outName.c_str()); } fclose(file); delete dataBuf; delete textBuf; } /* * Extract the symbol table from an ELF object file (ET_EXEC or * ET_REL) and create a symbol table in uMPS .stab format. */ static void createSymbolTable() { static const char* const typeName[] = { "", "OBJ", "FUN" }; static const char* const bindingName[] = { "LOC", "GLB", "WK " }; std::string outName = fileName; outName += ".stab.umps"; FILE* file = fopen(outName.c_str(), "w"); if (file == NULL) fatalError("Cannot create symbol table file `%s': %s", outName.c_str(), strerror(errno)); uint32_t tag = toTargetEndian(STABFILEID); if (fwrite(&tag, sizeof(tag), 1, file) != 1) fatalError("Error writing symbol table file `%s': %s", outName.c_str(), strerror(errno)); // Write a header template, to be patched later. if (fprintf(file, "%.8X %.8X\n", 0, 0) < 0) fatalError("Error writing symbol table file `%s': %s", outName.c_str(), strerror(errno)); // Write symbol records uint32_t funCount = 0, objCount = 0; SymbolTableIterator it(elf), end; int rc; for (; it != end; ++it) { Symbol s = *it; unsigned char type = ELF32_ST_TYPE(s.details->st_info); unsigned char binding = ELF32_ST_BIND(s.details->st_info); if (!s.name.empty() && (type == STT_FUNC || type == STT_OBJECT) && (s.name[0] != '_' || s.name == "__start")) { rc = fprintf(file, "%-32.32s :%s:0x%.8lX:0x%.8lX:%s\n", s.name.c_str(), typeName[type], (unsigned long) s.details->st_value, (unsigned long) s.details->st_size, bindingName[binding]); if (rc < 0) fatalError("Error writing symbol table file `%s': %s", outName.c_str(), strerror(errno)); if (type == STT_FUNC) funCount++; else objCount++; } } // Write symbol counts if (fseek(file, sizeof(tag), SEEK_SET) || fprintf(file, "%.8X %.8X", (unsigned int) funCount, (unsigned int) objCount) < 0) { fatalError("Error writing symbol table file `%s': %s", outName.c_str(), strerror(errno)); } fclose(file); } /* * Convert an ELF relocatable object file into an uMPS ROM (BIOS) file. */ static void elf2bios() { // Check ELF object type if (elfHeader->e_type != ET_REL) fatalError("`%s' is not a relocatable ELF object file", fileName); // Find .text section Elf_Scn* sd; forEachSection(sd) { Elf32_Shdr* sh = elf32_getshdr(sd); if (sh == NULL) elfError(); if ((sh->sh_type == SHT_PROGBITS) && (sh->sh_flags & SHF_ALLOC) && (sh->sh_flags & SHF_EXECINSTR)) { break; } } if (sd == NULL) fatalError("Could not find .text section"); std::string outName = fileName; outName += ".rom.umps"; FILE* file = fopen(outName.c_str(), "w"); if (file == NULL) fatalError("Cannot create BIOS file `%s'", outName.c_str()); Elf_Data* data; // Write header uint32_t tag = toTargetEndian(BIOSFILEID); uint32_t size = 0; forEachSectionData(sd, data) if (data->d_type == ELF_T_BYTE) size += data->d_size / 4; size = toTargetEndian(size); if (fwrite(&tag, sizeof(tag), 1, file) != 1 || fwrite(&size, sizeof(size), 1, file) != 1) { fatalError("Error writing BIOS file `%s': %s", outName.c_str(), strerror(errno)); } // Copy .text into file forEachSectionData(sd, data) { if (data->d_type == ELF_T_BYTE) { if (fwrite(data->d_buf, 1, data->d_size, file) != data->d_size) fatalError("Error writing BIOS file `%s': %s", outName.c_str(), strerror(errno)); } } fclose(file); // Gratuitous sanity check size_t relocs = 0; forEachSection(sd) { Elf32_Shdr* sh = elf32_getshdr(sd); if (sh == NULL) elfError(); if (sh->sh_type == SHT_REL || sh->sh_type == SHT_RELA) { forEachSectionData(sd, data) { switch (data->d_type) { case ELF_T_REL: relocs += data->d_size / sizeof(Elf32_Rel); break; case ELF_T_RELA: relocs += data->d_size / sizeof(Elf32_Rela); break; default: break; } } } } if (relocs) { fprintf(stderr, "%s: Warning: BIOS code may contain %u unresolved relocations\n", programName, (unsigned int) relocs); } } umps3-3.0.5/src/umps/error.h000066400000000000000000000044501435132321600156460ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef UMPS_ERROR_H #define UMPS_ERROR_H #include #include class Error : public std::runtime_error { public: explicit Error(const std::string& what) throw() : std::runtime_error(what) { } virtual ~Error() throw() { } }; class FileError : public Error { public: explicit FileError(const std::string& fileName) throw() : Error("Error accessing `" + fileName + "'"), fileName(fileName) { } virtual ~FileError() throw() { } const std::string fileName; }; class InvalidFileFormatError : public Error { public: InvalidFileFormatError(const std::string& fileName, const std::string& what) throw() : Error(what), fileName(fileName) { } virtual ~InvalidFileFormatError() throw() { } const std::string fileName; }; class InvalidCoreFileError : public InvalidFileFormatError { public: InvalidCoreFileError(const std::string& fileName, const std::string& what) throw() : InvalidFileFormatError(fileName, what) { } virtual ~InvalidCoreFileError() throw() { } }; class ReadingError : public Error { public: explicit ReadingError() throw() : Error("Reading error") { } virtual ~ReadingError() throw() { } }; class CoreFileOverflow : public Error { public: explicit CoreFileOverflow() throw() : Error("Core file too large") { } virtual ~CoreFileOverflow() throw() { } }; class EthError : public Error { public: EthError(unsigned int devNo) throw() : Error("Ethernet device error"), devNo(devNo) { } virtual ~EthError() throw() { } const unsigned int devNo; }; // Error hook void Panic(const char* message); #endif // UMPS_ERROR_H umps3-3.0.5/src/umps/event.cc000066400000000000000000000072351435132321600160000ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2004 Mauro Morsiani * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /**************************************************************************** * * This module implements some classes used from the SystemBus for the * scheduling of device events (such as device operations completion and * interrupts generation). They are: Event class, to keep track of single * events, as required by devices; and EventQueue class, to organize the * Events into a time-ordered queue. * ***************************************************************************/ #include "umps/event.h" #include #include #include #include "umps/const.h" #include "umps/utility.h" #include "umps/time_stamp.h" Event::Event(uint64_t ts, Word inc, Callback callback) : deadline(ts + inc), callback(callback), next(NULL) { } // This method links an Event to its successor in a structure void Event::AddBefore(Event * ev) { next = ev; } // This method inserts an Event after another, linking the former to the // successor of the latter void Event::InsAfter(Event * ev) { next = ev->next; ev->next = this; } // This method returns the pointer to the successor of an Event Event *Event::Next() { return (next); } // This method creates a new (empty) queue EventQueue::EventQueue() { head = NULL; lastIns = NULL; } // This method deletes the queue and its associated structures EventQueue::~EventQueue() { Event *p, *q; p = head; q = NULL; while (p != NULL) { q = p->Next(); delete p; p = q; } } uint64_t EventQueue::nextDeadline() const { assert(!IsEmpty()); return head->getDeadline(); } Event::Callback EventQueue::nextCallback() const { assert(!IsEmpty()); return head->getCallback(); } // This method creates a new Event object and inserts it in the // EventQueue; EventQueue is sorted on ascending time order uint64_t EventQueue::InsertQ(uint64_t tod, Word delay, Event::Callback callback) { Event *ins, *p, *q; ins = new Event(tod, delay, callback); if (IsEmpty()) { head = ins; } else if (ins->getDeadline() <= head->getDeadline()) { // "ins" has to happen before that at the head of the queue; // should be put before it ins->AddBefore(head); head = ins; } else { // should find place in queue: check lastIns to shorten search time if (lastIns != NULL && !(ins->getDeadline() <= lastIns->getDeadline())) // can start from lastIns p = lastIns; else // must start from the head p = head; q = p; while (p != NULL && p->getDeadline() <= ins->getDeadline()) { q = p; p = p->Next(); } // place found: insert after q and before p ins->InsAfter(q); } lastIns = ins; return ins->getDeadline(); } // This method removes the head of a (not empty) queue and sets it to the // following Event void EventQueue::RemoveHead() { Event *p; if (!IsEmpty()) { p = head; head = head->Next(); if (p == lastIns) // reposition lastIns to the new head lastIns = head; delete p; } } umps3-3.0.5/src/umps/event.h000066400000000000000000000056121435132321600156370ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2004 Mauro Morsiani * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef UMPS_EVENT_H #define UMPS_EVENT_H #include #include "umps/types.h" // Event class is used to keep track of the external events of the // system: device operations and interrupt generation. // Every object contains a device number saying what device will complete // its operation and generate a interrupt, a TimeStamp saying when it // will happen, and a pointer to the next event in a structure, if needed // (set to NULL otherwise) class Event { public: typedef boost::function Callback; Event(uint64_t ts, Word inc, Callback callback); uint64_t getDeadline() const { return deadline; } Callback getCallback() const { return callback; } // This method links an Event to its successor in a structure void AddBefore(Event* ev); // This method inserts an Event after another, linking the former to the // successor of the latter void InsAfter(Event* ev); // This method returns the pointer to the successor of an Event Event *Next(); private: // Event verification time uint64_t deadline; // Event handler Callback callback; Event *next; }; // This class implements a NULL-terminated sorted queue of Event objects, // used to schedule the device events in the system. // An object contains a pointer to the head of the queue, and a pointer to // the last inserted Event (to speed up insertion). // The queue is sorted on ascending timestamp order class EventQueue { public: // This method creates a new (empty) queue EventQueue(); ~EventQueue(); // This method returns TRUE if the queue is empty, FALSE otherwise bool IsEmpty() const { return head == NULL; } uint64_t nextDeadline() const; Event::Callback nextCallback() const; // This method creates a new Event object and inserts it in the // EventQueue; EventQueue is sorted on ascending time order uint64_t InsertQ(uint64_t tod, Word delay, Event::Callback callback); // This method removes the head of a (not empty) queue and sets it to the // following Event void RemoveHead(); private: // head of the queue Event* head; // last Event inserted Event* lastIns; }; #endif // UMPS_EVENT_H umps3-3.0.5/src/umps/libvdeplug_dyn.h000066400000000000000000000103331435132321600175210ustar00rootroot00000000000000/* * libvdeplug - A library to connect to a VDE Switch. * dynamic loading version (requires libdl). * * Copyright (C) 2006, 2007 Renzo Davoli, University of Bologna * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation version 2.1 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 Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ /* Use this include file when you need to write an application that can * benefit from vde when available. * Linking libvdeplug to your programs you force your application users * to have the library installed (otherway the dynamic linker complies * and the program does not start). * * * usage: * define a struct vdepluglib variable; * eg: * struct vdepluglib vdeplug; * * test the availability of the library and load it: * * libvdeplug_dynopen(vdeplug); * if vdeplug.dl_handle is not NULL the library is ready otherwise it is * not available in the target system. * * if libvdeplug does exist the library function can be called * in this way: * vdeplug.vde_open(....) * vdeplug.vde_read(....) * vdeplug.vde_open(....) * vdeplug.vde_recv(....) * vdeplug.vde_send(....) * vdeplug.vde_datafd(....) * vdeplug.vde_ctlfd(....) * vdeplug.vde_close(....) * libvdeplug_dynclose(vdeplug) can be used to deallocate the dynamic library * when needed. *************************************************/ #ifndef _VDEDYNLIB_H #define _VDEDYNLIB_H #include #include #define LIBVDEPLUG_INTERFACE_VERSION 1 struct vdeconn; typedef struct vdeconn VDECONN; /* Open a VDE connection. * vde_open_options: * port: connect to a specific port of the switch (0=any) * group: change the ownership of the communication port to a specific group * (NULL=no change) * mode: set communication port mode (if 0 standard socket mode applies) */ struct vde_open_args { int port; char *group; mode_t mode; }; /* vde_open args: * vde_switch: switch id (path) * descr: description (it will appear in the port description on the switch) */ #define vde_open(vde_switch,descr,open_args) \ vde_open_real((vde_switch),(descr),LIBVDEPLUG_INTERFACE_VERSION,(open_args)) struct vdepluglib { void *dl_handle; VDECONN * (*vde_open_real)(const char *vde_switch,char *descr,int interface_version, struct vde_open_args *open_args); size_t (* vde_recv)(VDECONN *conn,void *buf,size_t len,int flags); size_t (* vde_send)(VDECONN *conn,const void *buf,size_t len,int flags); int (* vde_datafd)(VDECONN *conn); int (* vde_ctlfd)(VDECONN *conn); int (* vde_close)(VDECONN *conn); }; typedef VDECONN * (* VDE_OPEN_REAL_T)(const char *vde_switch,char *descr,int interface_version, struct vde_open_args *open_args); typedef size_t (* VDE_RECV_T)(VDECONN *conn,void *buf,size_t len,int flags); typedef size_t (* VDE_SEND_T)(VDECONN *conn,const void *buf,size_t len,int flags); typedef int (* VDE_INT_FUN)(VDECONN *conn); #define libvdeplug_dynopen(x) do { \ (x).dl_handle=dlopen("libvdeplug.so",RTLD_NOW); \ if ((x).dl_handle) { \ (x).vde_open_real=(VDE_OPEN_REAL_T) dlsym((x).dl_handle,"vde_open_real"); \ (x).vde_recv=(VDE_RECV_T) dlsym((x).dl_handle,"vde_recv"); \ (x).vde_send=(VDE_SEND_T) dlsym((x).dl_handle,"vde_send"); \ (x).vde_datafd=(VDE_INT_FUN) dlsym((x).dl_handle,"vde_datafd"); \ (x).vde_ctlfd=(VDE_INT_FUN) dlsym((x).dl_handle,"vde_ctlfd"); \ (x).vde_close=(VDE_INT_FUN) dlsym((x).dl_handle,"vde_close"); \ } else { \ (x).vde_open_real=NULL; \ (x).vde_send= NULL; \ (x).vde_recv= NULL; \ (x).vde_datafd= (x).vde_ctlfd= (x).vde_close= NULL; \ } \ } while (0); #define libvdeplug_dynclose(x) ({ \ dlclose((x).dl_handle); \ }) #endif umps3-3.0.5/src/umps/machine.cc000066400000000000000000000143651435132321600162650ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "umps/machine.h" #include #include #include "base/lang.h" #include "umps/types.h" #include "umps/const.h" #include "umps/processor.h" #include "umps/machine_config.h" #include "umps/stoppoint.h" #include "umps/systembus.h" Machine::Machine(const MachineConfig* config, StoppointSet* breakpoints, StoppointSet* suspects, StoppointSet* tracepoints) : stopMask(0), config(config), halted(false), breakpoints(breakpoints), suspects(suspects), tracepoints(tracepoints) { assert(config->Validate(NULL)); bus.reset(new SystemBus(config, this)); for (unsigned int i = 0; i < config->getNumProcessors(); i++) { Processor* cpu = new Processor(config, i, this, bus.get()); cpu->SignalException.connect( sigc::bind(sigc::mem_fun(this, &Machine::onCpuException), cpu) ); cpu->StatusChanged.connect( sigc::bind(sigc::mem_fun(this, &Machine::onCpuStatusChanged), cpu) ); pd[i].stopCause = 0; cpus.push_back(cpu); } cpus[0]->Reset(MCTL_DEFAULT_BOOT_PC, MCTL_DEFAULT_BOOT_SP); } Machine::~Machine() { for (Processor* p : cpus) delete p; } void Machine::step(unsigned int steps, unsigned int* stepped, bool* stopped) { stopRequested = pauseRequested = false; for (Processor* cpu : cpus) pd[cpu->Id()].stopCause = 0; unsigned int i; for (i = 0; !halted && i < steps && !stopRequested && !pauseRequested; ++i) { bus->ClockTick(); for (CpuVector::iterator it = cpus.begin(); it != cpus.end(); ++it) (*it)->Cycle(); } if (stepped) *stepped = i; if (stopped) *stopped = stopRequested; } void Machine::step(bool* stopped) { step(1, NULL, stopped); } uint32_t Machine::idleCycles() const { uint32_t c; if ((c = bus->IdleCycles()) == 0) return 0; for (Processor* cpu : cpus) { c = std::min(c, cpu->IdleCycles()); if (c == 0) return 0; } return c; } void Machine::skip(uint32_t cycles) { bus->Skip(cycles); for (Processor* cpu : cpus) { if (!cpu->isHalted()) cpu->Skip(cycles); } } void Machine::Halt() { halted = true; } void Machine::onCpuException(unsigned int excCode, Processor* cpu) { bool utlbExc = (excCode == UTLBLEXCEPTION || excCode == UTLBSEXCEPTION); if (((stopMask & SC_EXCEPTION) && !utlbExc) || ((stopMask & SC_UTLB_USER) && utlbExc && cpu->InUserMode()) || ((stopMask & SC_UTLB_KERNEL) && utlbExc && cpu->InKernelMode())) { pd[cpu->getId()].stopCause |= SC_EXCEPTION; stopRequested = true; } } void Machine::onCpuStatusChanged(const Processor* cpu) { // Whenever a cpu goes to sleep, give the client a chance to // detect idle machine states. if (cpu->isIdle()) pauseRequested = true; } void Machine::HandleBusAccess(Word pAddr, Word access, Processor* cpu) { // Check for breakpoints and suspects switch (access) { case READ: case WRITE: if (stopMask & SC_SUSPECT) { Stoppoint* suspect = suspects->Probe(MAXASID, pAddr, (access == READ) ? AM_READ : AM_WRITE, cpu); if (suspect != NULL) { pd[cpu->getId()].stopCause |= SC_SUSPECT; pd[cpu->getId()].suspectId = suspect->getId(); stopRequested = true; } } break; case EXEC: if (stopMask & SC_BREAKPOINT) { Stoppoint* breakpoint = breakpoints->Probe(MAXASID, pAddr, AM_EXEC, cpu); if (breakpoint != NULL) { pd[cpu->getId()].stopCause |= SC_BREAKPOINT; pd[cpu->getId()].breakpointId = breakpoint->getId(); stopRequested = true; } } break; default: // Assert not reached assert(0); } // Check for traced ranges if (access == WRITE) { Stoppoint* tracepoint = tracepoints->Probe(MAXASID, pAddr, AM_WRITE, cpu); (void) tracepoint; } } void Machine::HandleVMAccess(Word asid, Word vaddr, Word access, Processor* cpu) { switch (access) { case READ: case WRITE: if (stopMask & SC_SUSPECT) { Stoppoint* suspect = suspects->Probe(asid, vaddr, (access == READ) ? AM_READ : AM_WRITE, cpu); if (suspect != NULL) { pd[cpu->Id()].stopCause |= SC_SUSPECT; pd[cpu->Id()].suspectId = suspect->getId(); stopRequested = true; } } break; case EXEC: if (stopMask & SC_BREAKPOINT) { Stoppoint* breakpoint = breakpoints->Probe(asid, vaddr, AM_EXEC, cpu); if (breakpoint != NULL) { pd[cpu->Id()].stopCause |= SC_BREAKPOINT; pd[cpu->Id()].breakpointId = breakpoint->getId(); stopRequested = true; } } break; default: // Assert not reached assert(0); } } Processor* Machine::getProcessor(unsigned int cpuId) { return cpus[cpuId]; } Device* Machine::getDevice(unsigned int line, unsigned int devNo) { return bus->getDev(line, devNo); } SystemBus* Machine::getBus() { return bus.get(); } void Machine::setStopMask(unsigned int mask) { stopMask = mask; } unsigned int Machine::getStopMask() const { return stopMask; } unsigned int Machine::getStopCause(unsigned int cpuId) const { assert(cpuId < config->getNumProcessors()); return pd[cpuId].stopCause; } unsigned int Machine::getActiveBreakpoint(unsigned int cpuId) const { assert(cpuId < config->getNumProcessors()); return pd[cpuId].breakpointId; } unsigned int Machine::getActiveSuspect(unsigned int cpuId) const { assert(cpuId < config->getNumProcessors()); return pd[cpuId].suspectId; } bool Machine::ReadMemory(Word physAddr, Word* data) { return bus->WatchRead(physAddr, data); } bool Machine::WriteMemory(Word paddr, Word data) { return bus->WatchWrite(paddr, data); } umps3-3.0.5/src/umps/machine.h000066400000000000000000000054071435132321600161240ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef UMPS_MACHINE_H #define UMPS_MACHINE_H #include #include "base/lang.h" #include "umps/machine_config.h" enum StopCause { SC_USER = 1 << 0, SC_BREAKPOINT = 1 << 1, SC_SUSPECT = 1 << 2, SC_EXCEPTION = 1 << 3, SC_UTLB_KERNEL = 1 << 4, SC_UTLB_USER = 1 << 5 }; class Processor; class SystemBus; class Device; class StoppointSet; class Machine { public: Machine(const MachineConfig* config, StoppointSet* breakpoints, StoppointSet* suspects, StoppointSet* tracepoints); ~Machine(); void step(bool* stopped = NULL); void step(unsigned int steps, unsigned int* stepped = NULL, bool* stopped = NULL); uint32_t idleCycles() const; void skip(uint32_t cycles); void Halt(); bool IsHalted() const { return halted; } Processor* getProcessor(unsigned int cpuId); Device* getDevice(unsigned int line, unsigned int devNo); SystemBus* getBus(); void setStopMask(unsigned int mask); unsigned int getStopMask() const; unsigned int getStopCause(unsigned int cpuId) const; unsigned int getActiveBreakpoint(unsigned int cpuId) const; unsigned int getActiveSuspect(unsigned int cpuId) const; bool ReadMemory(Word physAddr, Word* data); bool WriteMemory(Word paddr, Word data); void HandleBusAccess(Word pAddr, Word access, Processor* cpu); void HandleVMAccess(Word asid, Word vaddr, Word access, Processor* cpu); private: struct ProcessorData { unsigned int stopCause; unsigned int breakpointId; unsigned int suspectId; }; void onCpuStatusChanged(const Processor* cpu); void onCpuException(unsigned int, Processor* cpu); unsigned int stopMask; const MachineConfig* const config; scoped_ptr bus; typedef std::vector CpuVector; std::vector cpus; ProcessorData pd[MachineConfig::MAX_CPUS]; bool halted; bool stopRequested; bool pauseRequested; StoppointSet* breakpoints; StoppointSet* suspects; StoppointSet* tracepoints; }; #endif // UMPS_MACHINE_H umps3-3.0.5/src/umps/machine_config.cc000066400000000000000000000243371435132321600176120ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * Copyright (C) 2020 Mattia Biondi * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "umps/machine_config.h" #include #include #include #include #include #include #include #include "base/json.h" #include "umps/const.h" #include "umps/error.h" #include "umps/utility.h" template static T bumpProperty(T minValue, T value, T maxValue) { if (value < minValue) return minValue; else if (value > maxValue) return maxValue; else return value; } const char* const MachineConfig::deviceKeyPrefix[N_EXT_IL] = { "disk", "flash", "eth", "printer", "terminal" }; MachineConfig* MachineConfig::LoadFromFile(const std::string& fileName, std::string& error) { std::ifstream inputStream(fileName.c_str()); if (inputStream.fail()) { error = boost::str(boost::format("Unable to open file `%s'") %fileName); return NULL; } std::unique_ptr root; try { JsonParser parser; JsonNode* node = parser.Parse(inputStream); if (!node->Holds(JSON_OBJECT)) { error = "Invalid machine configuration file (object expected)"; return NULL; } root.reset(node->AsObject()); } catch (JsonParser::SyntaxError& e) { error = "Invalid machine configuration file (erroneous JSON syntax)"; return NULL; } std::unique_ptr config(new MachineConfig(fileName)); try { if (root->HasMember("num-processors")) config->setNumProcessors(root->Get("num-processors")->AsNumber()); if (root->HasMember("clock-rate")) config->setClockRate(root->Get("clock-rate")->AsNumber()); if (root->HasMember("tlb-size")) config->setTLBSize(root->Get("tlb-size")->AsNumber()); if (root->HasMember("tlb-floor-address")) config->setTLBFloorAddress(stoul((root->Get("tlb-floor-address")->AsString()).erase(0, 2), 0, 16)); if (root->HasMember("num-ram-frames")) config->setRamSize(root->Get("num-ram-frames")->AsNumber()); if (root->HasMember("boot")) { JsonObject* bootOpt = root->Get("boot")->AsObject(); config->setLoadCoreEnabled(bootOpt->Get("load-core-file")->AsBool()); config->setROM(ROM_TYPE_CORE, bootOpt->Get("core-file")->AsString()); } if (root->HasMember("bootstrap-rom")) config->setROM(ROM_TYPE_BOOT, root->Get("bootstrap-rom")->AsString()); if (root->HasMember("execution-rom")) config->setROM(ROM_TYPE_BIOS, root->Get("execution-rom")->AsString()); if (root->HasMember("symbol-table")) { JsonObject* stab = root->Get("symbol-table")->AsObject(); config->setROM(ROM_TYPE_STAB, stab->Get("file")->AsString()); config->setSymbolTableASID(stab->Get("asid")->AsNumber()); } if (root->HasMember("devices")) { JsonObject* devices = root->Get("devices")->AsObject(); for (unsigned int il = 0; il < N_EXT_IL; il++) { for (unsigned int devNo = 0; devNo < N_DEV_PER_IL; devNo++) { std::string key = boost::str(boost::format("%s%u") %deviceKeyPrefix[il] %devNo); if (devices->HasMember(key)) { JsonObject* devObj = devices->Get(key)->AsObject(); config->setDeviceEnabled(il, devNo, devObj->Get("enabled")->AsBool()); config->setDeviceFile(il, devNo, devObj->Get("file")->AsString()); if (il == EXT_IL_INDEX(IL_ETHERNET) && devObj->HasMember("address")) { uint8_t macId[6]; if (ParseMACId(devObj->Get("address")->AsString(), macId)) config->setMACId(devNo, macId); } } } } } } catch (JsonNode::JsonError& e) { error = "Invalid machine configuration file"; return NULL; } return config.release(); } MachineConfig* MachineConfig::Create(const std::string& fileName) { std::unique_ptr config(new MachineConfig(fileName)); // The constructor initializes all the basic fields to sane // initial values; in addition, we enable a terminal device for // newly created configs. config->setDeviceFile(EXT_IL_INDEX(IL_TERMINAL), 0, "term0.umps"); config->setDeviceEnabled(EXT_IL_INDEX(IL_TERMINAL), 0, true); config->Save(); return config.release(); } void MachineConfig::Save() { scoped_ptr root(new JsonObject); root->Set("num-processors", (int) getNumProcessors()); root->Set("clock-rate", (int) getClockRate()); root->Set("tlb-size", (int) getTLBSize()); root->Set("tlb-floor-address", IntToHexString(getTLBFloorAddress())); root->Set("num-ram-frames", (int) getRamSize()); JsonObject* bootOpt = new JsonObject; bootOpt->Set("load-core-file", isLoadCoreEnabled()); bootOpt->Set("core-file", getROM(ROM_TYPE_CORE)); root->Set("boot", bootOpt); root->Set("bootstrap-rom", getROM(ROM_TYPE_BOOT)); root->Set("execution-rom", getROM(ROM_TYPE_BIOS)); JsonObject* stabObject = new JsonObject; stabObject->Set("file", romFiles[ROM_TYPE_STAB]); stabObject->Set("asid", (int) symbolTableASID); root->Set("symbol-table", stabObject); JsonObject* devicesObject = new JsonObject; for (unsigned int il = 0; il < N_EXT_IL; il++) { for (unsigned int devNo = 0; devNo < N_DEV_PER_IL; devNo++) { if (!devFiles[il][devNo].empty()) { JsonObject* object = new JsonObject; object->Set("enabled", devEnabled[il][devNo]); object->Set("file", devFiles[il][devNo]); if (il == EXT_IL_INDEX(IL_ETHERNET) && getMACId(devNo)) object->Set("address", MACIdToString(getMACId(devNo))); std::string key = boost::str(boost::format("%s%u") %deviceKeyPrefix[il] %devNo); devicesObject->Set(key, object); } } } root->Set("devices", devicesObject); std::string buf; root->Serialize(buf, true); std::ofstream file(fileName.c_str(), std::ios_base::trunc | std::ios_base::out); if (file.fail() || !(file << buf)) throw FileError(fileName); file.flush(); } MachineConfig::MachineConfig(const std::string& fn) : fileName(fn) { resetToFactorySettings(); } bool MachineConfig::Validate(std::list* errors) const { bool isValid = true; if (romFiles[ROM_TYPE_BOOT].empty()) { if (errors) errors->push_back("Bootstrap BIOS file not set"); isValid = false; } if (romFiles[ROM_TYPE_BIOS].empty()) { if (errors) errors->push_back("Execution BIOS file not set"); isValid = false; } if (romFiles[ROM_TYPE_STAB].empty()) { if (errors) errors->push_back("Symbol table file not set"); isValid = false; } return isValid; } void MachineConfig::setRamSize(Word size) { ramSize = bumpProperty(MIN_RAM, size, MAX_RAM); } void MachineConfig::setNumProcessors(unsigned int value) { cpus = bumpProperty(MIN_CPUS, value, MAX_CPUS); } void MachineConfig::setClockRate(unsigned int value) { clockRate = bumpProperty(MIN_CLOCK_RATE, value, MAX_CLOCK_RATE); } void MachineConfig::setTLBSize(Word size) { tlbSize = bumpProperty(MIN_TLB, size, MAX_TLB); } void MachineConfig::setTLBFloorAddress(Word addr) { if (addr == MINWORDVAL) tlbFloorAddress = RAMBASE + (getRamSize() * FRAMESIZE * FRAMEKB); else tlbFloorAddress = addr; } void MachineConfig::setROM(ROMType type, const std::string& fileName) { romFiles[type] = fileName; } const std::string& MachineConfig::getROM(ROMType type) const { return romFiles[type]; } void MachineConfig::setSymbolTableASID(Word asid) { symbolTableASID = bumpProperty(MIN_ASID, asid, MAX_ASID); } unsigned int MachineConfig::getDeviceType(unsigned int il, unsigned int devNo) const { assert(il < N_EXT_IL && devNo < N_DEV_PER_IL); static unsigned int types[] = { DISKDEV, FLASHDEV, ETHDEV, PRNTDEV, TERMDEV }; if (getDeviceEnabled(il, devNo) && !getDeviceFile(il, devNo).empty()) return types[il]; else return NULLDEV; } bool MachineConfig::getDeviceEnabled(unsigned int il, unsigned int devNo) const { assert(il < N_EXT_IL && devNo < N_DEV_PER_IL); return devEnabled[il][devNo]; } void MachineConfig::setDeviceEnabled(unsigned int il, unsigned int devNo, bool setting) { assert(il < N_EXT_IL && devNo < N_DEV_PER_IL); devEnabled[il][devNo] = setting; } void MachineConfig::setDeviceFile(unsigned int il, unsigned int devNo, const std::string& fileName) { assert(il < N_EXT_IL && devNo < N_DEV_PER_IL); devFiles[il][devNo] = fileName; } const std::string& MachineConfig::getDeviceFile(unsigned int il, unsigned int devNo) const { assert(il < N_EXT_IL && devNo < N_DEV_PER_IL); return devFiles[il][devNo]; } const uint8_t* MachineConfig::getMACId(unsigned int devNo) const { assert(devNo < N_DEV_PER_IL); return macId[devNo].get(); } void MachineConfig::setMACId(unsigned int devNo, const uint8_t* value) { assert(devNo < N_DEV_PER_IL); if (value != NULL) { macId[devNo].reset(new uint8_t[6]); std::copy(value, value + 6, macId[devNo].get()); } else { macId[devNo].reset(); } } void MachineConfig::resetToFactorySettings() { setNumProcessors(DEFAULT_NUM_CPUS); setClockRate(DEFAULT_CLOCK_RATE); setTLBSize(DEFAULT_TLB_SIZE); setTLBFloorAddress(DEFAULT_TLB_FLOOR_ADDRESS); setRamSize(DEFAUlT_RAM_SIZE); std::string dataDir = PACKAGE_DATA_DIR; setROM(ROM_TYPE_BOOT, dataDir + "/coreboot.rom.umps"); setROM(ROM_TYPE_BIOS, dataDir + "/exec.rom.umps"); setLoadCoreEnabled(true); setROM(ROM_TYPE_CORE, "kernel.core.umps"); setROM(ROM_TYPE_STAB, "kernel.stab.umps"); setSymbolTableASID(MAX_ASID); for (unsigned int i = 0; i < N_EXT_IL; ++i) for (unsigned int j = 0; j < N_DEV_PER_IL; ++j) devEnabled[i][j] = false; } bool MachineConfig::validFileMagic(Word tag, const char* fName) { bool valid; FILE* file = NULL; if (tag == 0UL) { file = fopen(fName, "a"); valid = (file != NULL); } else { file = fopen(fName, "r"); Word fileTag; valid = ((file != NULL) && (fread((void*) &fileTag, WS, 1, file) == 1) && (fileTag == tag)); } if (file != NULL) fclose(file); return valid; } umps3-3.0.5/src/umps/machine_config.h000066400000000000000000000100011435132321600174330ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * Copyright (C) 2020 Mattia Biondi * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef UMPS_MACHINE_CONFIG_H #define UMPS_MACHINE_CONFIG_H #include #include #include "base/lang.h" #include "base/basic_types.h" #include "umps/arch.h" #include "umps/const.h" #include "umps/types.h" #include "umps/utility.h" enum ROMType { ROM_TYPE_BOOT, ROM_TYPE_BIOS, ROM_TYPE_CORE, ROM_TYPE_STAB, N_ROM_TYPES }; class MachineConfig { public: static const Word MIN_RAM = 8; static const Word MAX_RAM = 512; static const Word DEFAUlT_RAM_SIZE = 64; static const unsigned int MIN_CPUS = 1; static const unsigned int MAX_CPUS = 16; static const unsigned int DEFAULT_NUM_CPUS = 1; static const unsigned int MIN_CLOCK_RATE = 1; static const unsigned int MAX_CLOCK_RATE = 99; static const unsigned int DEFAULT_CLOCK_RATE = 1; static const Word MIN_TLB = 4; static const Word MAX_TLB = 64; static const Word DEFAULT_TLB_SIZE = 16; static constexpr Word TLB_FLOOR_ADDRESS[4] = { MINWORDVAL, 0x40000000, 0x80000000, MAXWORDVAL }; static const Word DEFAULT_TLB_FLOOR_ADDRESS = MAXWORDVAL; static const Word MIN_ASID = 0; static const Word MAX_ASID = 64; static MachineConfig* LoadFromFile(const std::string& fileName, std::string& error); static MachineConfig* Create(const std::string& fileName); const std::string& getFileName() const { return fileName; } void Save(); bool Validate(std::list* errors) const; void setLoadCoreEnabled(bool setting) { loadCoreFile = setting; } bool isLoadCoreEnabled() const { return loadCoreFile; } void setRamSize(Word size); Word getRamSize() const { return ramSize; } void setNumProcessors(unsigned int value); unsigned int getNumProcessors() const { return cpus; } void setClockRate(unsigned int value); unsigned int getClockRate() const { return clockRate; } void setTLBSize(Word size); Word getTLBSize() const { return tlbSize; } void setTLBFloorAddress(Word addr); Word getTLBFloorAddress() const { return tlbFloorAddress; } void setROM(ROMType type, const std::string& fileName); const std::string& getROM(ROMType type) const; void setSymbolTableASID(Word asid); Word getSymbolTableASID() const { return symbolTableASID; } unsigned int getDeviceType(unsigned int il, unsigned int devNo) const; bool getDeviceEnabled(unsigned int il, unsigned int devNo) const; void setDeviceEnabled(unsigned int il, unsigned int devNo, bool setting); void setDeviceFile(unsigned int il, unsigned int devNo, const std::string& fileName); const std::string& getDeviceFile(unsigned int il, unsigned int devNo) const; const uint8_t* getMACId(unsigned int devNo) const; void setMACId(unsigned int devNo, const uint8_t* value); private: MachineConfig(const std::string& fileName); void resetToFactorySettings(); bool validFileMagic(Word tag, const char* fName); std::string fileName; bool loadCoreFile; Word ramSize; unsigned int cpus; unsigned int clockRate; Word tlbSize; Word tlbFloorAddress; std::string romFiles[N_ROM_TYPES]; Word symbolTableASID; std::string devFiles[N_EXT_IL][N_DEV_PER_IL]; bool devEnabled[N_EXT_IL][N_DEV_PER_IL]; scoped_array macId[N_DEV_PER_IL]; static const char* const deviceKeyPrefix[N_EXT_IL]; }; #endif // UMPS_MACHINE_CONFIG_H umps3-3.0.5/src/umps/memspace.cc000066400000000000000000000063551435132321600164530ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2004 Mauro Morsiani * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /**************************************************************************** * * This module implements the classes which represents memory spaces: * RamSpace (for RAM) and BiosSpace (for ROMs), completely under control of * SystemBus. * ****************************************************************************/ #include "umps/memspace.h" #include #include #include #include "umps/arch.h" #include "umps/const.h" #include "umps/blockdev_params.h" #include "umps/error.h" // This method creates a RamSpace object of a given size (in words) and // fills it with core file contents if needed RamSpace::RamSpace(Word size_, const char* fName) : ram(new Word[size_]), size(size_) { if (fName != NULL && *fName) { FILE* cFile; if ((cFile = fopen(fName, "r")) == NULL) throw FileError(fName); // Check validity Word tag; if (fread((void *) &tag, WORDLEN, 1, cFile) != 1 || tag != COREFILEID) { fclose(cFile); throw InvalidCoreFileError(fName, "Invalid core file"); } if (fread((void *) ram.get(), WORDLEN, size, cFile) != size) if (ferror(cFile)) { fclose(cFile); throw ReadingError(); } if (!feof(cFile)) { fclose(cFile); throw CoreFileOverflow(); } fclose(cFile); } } bool RamSpace::CompareAndSet(Word index, Word oldval, Word newval) { if (ram[index] == oldval) { ram[index] = newval; return true; } else { return false; } } /****************************************************************************/ // This method creates a BiosSpace object, filling with .rom file contents BiosSpace::BiosSpace(const char* fileName) { assert(fileName != NULL && *fileName); FILE* file; if ((file = fopen(fileName, "r")) == NULL) throw FileError(fileName); Word tag; if ((fread((void *) &tag, WS, 1, file) != 1) || (tag != BIOSFILEID) || (fread((void *) &size, WS, 1, file) != 1)) { fclose(file); throw InvalidFileFormatError(fileName, "ROM file expected"); } memPtr.reset(new Word[size]); if (fread((void*) memPtr.get(), WS, size, file) != size) { fclose(file); throw InvalidFileFormatError(fileName, "Wrong ROM file size"); } fclose(file); } // This method returns the value of Word at ofs address // (SystemBus must assure that ofs is in range) Word BiosSpace::MemRead(Word ofs) { assert(ofs < size); return memPtr[ofs]; } // This method returns BiosSpace size in bytes Word BiosSpace::Size() { return size * WORDLEN; } umps3-3.0.5/src/umps/memspace.h000066400000000000000000000052371435132321600163130ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2004 Mauro Morsiani * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef UMPS_MEMSPACE_H #define UMPS_MEMSPACE_H #include "base/lang.h" #include "umps/types.h" // This class implements the RAM device. Any object allows reads and // writes with random access to word-sized items using appropriate // methods. Contents may be loaded from file at creation. SystemBus // must do all bounds checking and address conversion for access class RamSpace { public: // This method creates a RamSpace object of a given size (in words) // and fills it with file contents if needed RamSpace(Word size_, const char* fName); // This method returns the value of Word at index Word MemRead(Word index) const { return ram[index]; } // This method allows to write data to a specified address (as word // offset). SystemBus must check address validity and make // byte-to-word address conversion) void MemWrite(Word index, Word data) { ram[index] = data; } bool CompareAndSet(Word index, Word oldval, Word newval); // This method returns RamSpace size in bytes Word Size() const { return size << 2; } private: scoped_array ram; // size of structure in words (C style addressing: [0..size - 1]) Word size; }; // This class implements ROM devices. The BIOS or Bootstrap ROM is read // from a disk file, whose name is specified during object creation. // Access is read-only, to word-sized items in it. SystemBus must do all // bounds checking and address conversion for access class BiosSpace { public: // This method creates a BiosSpace object, filling with .rom file // contents BiosSpace(const char *name); // This method returns the value of Word at ofs address // (SystemBus must assure that ofs is in range) Word MemRead(Word ofs); // This method returns BiosSpace size in bytes Word Size(); private: scoped_array memPtr; // size of structure in Words (C style addressing: [0..size - 1]) Word size; }; #endif // UMPS_MEMSPACE_H umps3-3.0.5/src/umps/mkdev.cc000066400000000000000000000344731435132321600157710ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* * uMPS - A general purpose computer system simulator * * Copyright (C) 2004 Mauro Morsiani * Copyright (C) 2020 Mattia Biondi * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /**************************************************************************** * * This is a stand-alone program which produces "empty" disk image files * with specified performance figures and geometry, or assembles existing * data files into a single flash device image file. Disk image files are * used to emulate disk devices; flash device image files are used to emulate * flash drive devices. * ****************************************************************************/ #include #include #include #include #include #include #include "umps/types.h" #include "umps/blockdev_params.h" /****************************************************************************/ /* Declarations strictly local to the module. */ /****************************************************************************/ // default disk image file name HIDDEN char diskDflFName[] = "disk0"; HIDDEN char flashDflFName[] = "flash0"; // default disk header parameters (see h/blockdev.h) HIDDEN unsigned int diskDfl[DISKPNUM] = { DFLCYL, DFLHEAD, DFLSECT, DFLROTTIME, DFLSEEKTIME, DFLDATAS }; // default flash device header parameters (see h/blockdev.h) HIDDEN unsigned int flashDfl[FLASHPNUM] = { DFLBLOCKS, DFLWTIME, }; // // Program functions // HIDDEN void showHelp(const char * prgName); HIDDEN int mkDisk(int argc, char * argv[]); HIDDEN int mkFlash(int argc, char * argv[]); HIDDEN bool decodeDiskP(int idx, unsigned int * par, const char * str); HIDDEN bool decodeFlashP(int idx, unsigned int * par, const char * str); HIDDEN int writeDisk(const char * prg, const char * fname); HIDDEN int writeFlash(const char * prg, const char * fname, const char * file); HIDDEN void testForCore(FILE * rfile); // StrToWord is duplicated here from utility.cc to avoid full utility.o linking HIDDEN bool StrToWord(const char * str, Word * value); /****************************************************************************/ /* Definitions to be exported. */ /****************************************************************************/ // This function scans the line arguments; if no error is found, the // required file is produced, or a warning/help message is printed. // See showHelp() for argument format. // Returns an EXIT_SUCCESS/FAILURE code int main(int argc, char* argv[]) { int ret = EXIT_SUCCESS; if (argc == 1) showHelp(argv[0]); else if (SAMESTRING("-d", argv[1])) ret = mkDisk(argc, argv); else if (SAMESTRING("-f", argv[1])) ret = mkFlash(argc, argv); else { fprintf(stderr, "%s : Unknown argument(s)\n", argv[0]); showHelp(argv[0]); ret = EXIT_FAILURE; } return ret; } /****************************************************************************/ /* Definitions strictly local to the module. */ /****************************************************************************/ // This function prints a warning/help message on standard error HIDDEN void showHelp(const char * prgName) { fprintf(stderr, "%s syntax : %s {-d | -f} [parameters..]\n\n", prgName, prgName); fprintf(stderr, "%s -d %s [cyl [head [sect [rpm [seekt [datas]]]]]]\n",prgName, MPSFILETYPE); fprintf(stderr, "where:\n\tcyl = no. of cylinders\t\t\t[1..%u]\t(default = %u)\n", MAXCYL, diskDfl[CYLNUM]); fprintf(stderr, "\thead = no. of heads\t\t\t[1..%u]\t(default = %u)\n", MAXHEAD, diskDfl[HEADNUM]); fprintf(stderr, "\tsect = no. of sectors\t\t\t[1..%u]\t(default = %u)\n", MAXSECT, diskDfl[SECTNUM]); fprintf(stderr, "\trpm = disk rotations per min.\t\t[%u..%u]\t(default = %.0f)\n", MINRPM, MAXRPM, 6E7F / diskDfl[ROTTIME]); fprintf(stderr, "\tseekt = avg. cyl2cyl time (microsecs.)\t[1..%u]\t(default = %u)\n", MAXSEEKTIME, diskDfl[SEEKTIME]); fprintf(stderr, "\tdatas = sector data occupation %%\t[%u%%..%u%%]\t(default = %u%%)\n", MINDATAS, MAXDATAS, diskDfl[DATASECT]); fprintf(stderr, "\t = disk image file name\t\t\t(example = %s%s)\n", diskDflFName, MPSFILETYPE); fprintf(stderr, "\n%s -f %s [blocks [wt]]\n", prgName, MPSFILETYPE); fprintf(stderr, "where:\n\tblocks = no. of blocks\t\t\t[1..0x%.6X]\t(default = %u)\n", MAXBLOCKS, flashDfl[BLOCKSNUM]); fprintf(stderr, "\twt = avg. write time (microsecs.)\t[1..%u]\t(default = %u)\n", MAXWTIME, flashDfl[WTIME]); fprintf(stderr, "\t = flash dev. image file name\t\t(example = %s%s)\n", flashDflFName, MPSFILETYPE); fprintf(stderr, "\t = file to be written\n"); fprintf(stderr, "\tnote: use /dev/null as to create an empty image file\n\n"); } // This function builds an empty disk image file, putting geometry and // performance figures (by default or passed as command line arguments) // in file header. // Returns an EXIT_SUCCESS/FAILURE code HIDDEN int mkDisk(int argc, char * argv[]) { int i; bool error = false; int ret = EXIT_SUCCESS; if (argc < 3 || argc > 9 || strstr(argv[2], MPSFILETYPE) == NULL) { // too many or too few args fprintf(stderr, "%s : disk image file parameters wrong/missing\n", argv[0]); ret = EXIT_FAILURE; } else { // start argument decoding if (argc == 3) // all by default: build file image ret = writeDisk(argv[0], argv[2]); else { // scan args and places them in diskDfl[] for (i = 0; i < argc - 3 && !error; i++) error = decodeDiskP(i, &(diskDfl[i]), argv[i + 3]); if (!error) // build file images ret = writeDisk(argv[0], argv[2]); else { fprintf(stderr, "%s : disk image file parameters wrong/missing\n", argv[0]); ret = EXIT_FAILURE; } } } return(ret); } // This function builds a flash device image file from a data file passed as // command line argument, putting geometry and performance figures (by default // or passed as command line arguments) in file header. The data file is split // into BLOCKSIZE blocks. // Returns an EXIT_SUCCESS/FAILURE code HIDDEN int mkFlash(int argc, char * argv[]) { int i; bool error = false; int ret = EXIT_SUCCESS; if (argc < 4 || argc > 6 || strstr(argv[2], MPSFILETYPE) == NULL) { // too many or too few args fprintf(stderr, "%s : flash device image file parameters wrong/missing\n", argv[0]); ret = EXIT_FAILURE; } else { // start argument decoding if (argc == 4) // all by default: build file image ret = writeFlash(argv[0], argv[2], argv[3]); else { // scan args and places them in flashDfl[] for (i = 0; i < argc - 4 && !error; i++) error = decodeFlashP(i, &(flashDfl[i]), argv[i + 4]); if (!error) // build file images ret = writeFlash(argv[0], argv[2], argv[3]); else { fprintf(stderr, "%s : flash device image file parameters wrong/missing\n", argv[0]); ret = EXIT_FAILURE; } } } return(ret); } // This function decodes disk parameters contained in string str by // argument position idx on the command line. // The decoded parameter returns thru par pointer, while decoding success // is signaled returning TRUE if an error occurred, and FALSE otherwise HIDDEN bool decodeDiskP(int idx, unsigned int * par, const char * str) { Word temp; bool error = false; if (!StrToWord(str, &temp)) // error decoding parameter error = true; else switch (idx) { // argument decoded by position on command line // min and max values are checked if needed case CYLNUM: if (INBOUNDS(temp, 1, MAXCYL + 1)) *par = (unsigned int) temp; else error = true; break; case HEADNUM: if (INBOUNDS(temp, 1, MAXHEAD + 1)) *par = (unsigned int) temp; else error = true; break; case SECTNUM: if (INBOUNDS(temp, 1, MAXSECT + 1)) *par = (unsigned int) temp; else error = true; break; case ROTTIME: if (INBOUNDS(temp, MINRPM, MAXRPM + 1)) *par = (unsigned int) (6E7F / temp); else error = true; break; case SEEKTIME: if (INBOUNDS(temp, 1, MAXSEEKTIME + 1)) *par = (unsigned int) temp; else error = true; break; case DATASECT: if (INBOUNDS(temp, MINDATAS, MAXDATAS + 1)) *par = (unsigned int) temp; else error = true; break; default: // unknown parameter error = true; } return(error); } // This function decodes flash device parameters contained in string str by // argument position idx on the command line. // The decoded parameter returns thru par pointer, while decoding success // is signaled returning TRUE if an error occurred, and FALSE otherwise HIDDEN bool decodeFlashP(int idx, unsigned int * par, const char * str) { Word temp; bool error = false; if (!StrToWord(str, &temp)) // error decoding parameter error = true; else switch (idx) { // argument decoded by position on command line // min and max values are checked if needed case BLOCKSNUM: if (INBOUNDS(temp, 1, MAXBLOCKS + 1)) *par = (unsigned int) temp; else error = true; break; case WTIME: if (INBOUNDS(temp, 1, MAXWTIME + 1)) *par = (unsigned int) temp; else error = true; break; default: // unknown parameter error = true; } return(error); } // This function creates the disk image file on the disk, prepending it with // a header containing geometry and performance figures. // A number of 4096-byte empty blocks is created, depending on disk geometry. // Returns an EXIT_SUCCESS/FAILURE code HIDDEN int writeDisk(const char * prg, const char * fname) { FILE * dfile = NULL; int ret = EXIT_SUCCESS; unsigned int i; unsigned int dfsize = diskDfl[CYLNUM] * diskDfl[HEADNUM] * diskDfl[SECTNUM]; Word blk[BLOCKSIZE]; Word diskid = DISKFILEID; // clear block for (i = 0; i < BLOCKSIZE; i++) blk[i] = 0; // try to open image file and write header if ((dfile = fopen(fname, "w")) == NULL || \ fwrite((void *) &diskid, WORDLEN, 1, dfile) != 1 || \ fwrite((void *) diskDfl, sizeof(unsigned int), DISKPNUM, dfile) != DISKPNUM) ret = EXIT_FAILURE; else { // write empty blocks for (i = 0; i < dfsize && ret != EXIT_FAILURE; i++) if (fwrite((void *) blk, WORDLEN, BLOCKSIZE, dfile) != BLOCKSIZE) ret = EXIT_FAILURE; if (fclose(dfile) != 0) ret = EXIT_FAILURE; } if (ret == EXIT_FAILURE) fprintf(stderr, "%s : error writing disk file image %s : %s\n", prg, fname, strerror(errno)); return(ret); } // This function creates the flash device image file on the disk, prepending it with // a header containing geometry and performance figures. // Returns an EXIT_SUCCESS/FAILURE code HIDDEN int writeFlash(const char * prg, const char * fname, const char * file) { FILE * ffile = NULL; FILE * rfile = NULL; int ret = EXIT_SUCCESS; unsigned int i, j; unsigned int ffsize = flashDfl[BLOCKSNUM]; Word blk[BLOCKSIZE]; Word flashid = FLASHFILEID; // clear block for (i = 0; i < BLOCKSIZE; i++) blk[i] = 0UL; // tries to open image file and write header if ((ffile = fopen(fname, "w")) == NULL || \ fwrite((void *) &flashid, WORDLEN, 1, ffile) != 1 || \ fwrite((void *) flashDfl, sizeof(unsigned int), FLASHPNUM, ffile) != FLASHPNUM) ret = EXIT_FAILURE; else { // tries to process data file if((rfile = fopen(file, "r")) == NULL) ret = EXIT_FAILURE; else { // file exists and is readable: process it // .core files should be stripped of magic file tag for // .alignment reasons testForCore(rfile); for (j = 0; j < ffsize && ret != EXIT_FAILURE; j++) { // split file into blocks inside the flash device image if (!feof(rfile)) { // read block from input file if (fread((void *) blk, WORDLEN, BLOCKSIZE, rfile) != BLOCKSIZE) if (ferror(rfile)) ret = EXIT_FAILURE; // write block to output file if (fwrite((void *) blk, WORDLEN, BLOCKSIZE, ffile) != BLOCKSIZE) ret = EXIT_FAILURE; // clear block for (i = 0; i < BLOCKSIZE; i++) blk[i] = 0UL; } else { // write empty block if (fwrite((void *) blk, WORDLEN, BLOCKSIZE, ffile) != BLOCKSIZE) ret = EXIT_FAILURE; } } if (!feof(rfile)) fprintf(stderr, "%s : error writing flash device file image %s : file %s truncated\n", prg, fname, file); fclose(rfile); } // try to close flash device image file if (fclose(ffile) != 0) ret = EXIT_FAILURE; } if (ret == EXIT_FAILURE) fprintf(stderr, "%s : error writing flash device file image %s : %s\n", prg, fname, strerror(errno)); return(ret); } // This function tests if file to be inserted is a .core file; if so the // magic file tag should be skipped for alignment reasons, else file should // be inserted as-is HIDDEN void testForCore(FILE * rfile) { Word tag; // file existence have been tested by caller if (fread((void *) &tag, WORDLEN, 1, rfile) == 0 || tag != COREFILEID) // file is empty or not .core rewind(rfile); // else file is .core: leave file position as it is to skip tag } // This function converts a string to a Word (typically, an address) value. // Returns TRUE if conversion was successful, FALSE otherwise HIDDEN bool StrToWord(const char * str, Word * value) { char * endp; bool valid = true; // try to convert the string into a unsigned long *value = strtoul(str, &endp, 0); if (endp != NULL) { // there may be some garbage while (*endp != EOS && valid) { if (!isspace(*endp)) valid = false; endp++; } } return(valid); } umps3-3.0.5/src/umps/mp_controller.cc000066400000000000000000000050721435132321600175330ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "umps/mp_controller.h" #include #include "base/lang.h" #include "umps/machine_config.h" #include "umps/machine.h" #include "umps/processor.h" #include "umps/systembus.h" #include "umps/arch.h" using namespace boost::placeholders; MPController::MPController(const MachineConfig* config, Machine* machine) : config(config), machine(machine), bootPC(MCTL_DEFAULT_BOOT_PC), bootSP(MCTL_DEFAULT_BOOT_SP) { } Word MPController::Read(Word addr, const Processor* cpu) const { UNUSED_ARG(cpu); switch (addr) { case MCTL_NCPUS: return config->getNumProcessors(); case MCTL_BOOT_PC: return bootPC; case MCTL_BOOT_SP: return bootSP; default: return 0; } } void MPController::Write(Word addr, Word data, const Processor* cpu) { UNUSED_ARG(cpu); Word cpuId; switch (addr) { case MCTL_RESET_CPU: cpuId = data & MCTL_RESET_CPU_CPUID_MASK; if (cpuId < config->getNumProcessors()) machine->getBus()->scheduleEvent(kCpuResetDelay * config->getClockRate(), boost::bind(&Processor::Reset, machine->getProcessor(cpuId), bootPC, bootSP)); break; case MCTL_BOOT_PC: bootPC = data; break; case MCTL_BOOT_SP: bootSP = data; break; case MCTL_HALT_CPU: cpuId = data & MCTL_RESET_CPU_CPUID_MASK; if (cpuId < config->getNumProcessors()) machine->getBus()->scheduleEvent(kCpuHaltDelay * config->getClockRate(), boost::bind(&Processor::Halt, machine->getProcessor(cpuId))); break; case MCTL_POWER: if (data == 0x0FF) machine->getBus()->scheduleEvent(kPoweroffDelay * config->getClockRate(), boost::bind(&Machine::Halt, machine)); break; default: break; } } umps3-3.0.5/src/umps/mp_controller.h000066400000000000000000000026021435132321600173710ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef UMPS_MP_CONTROLLER_H #define UMPS_MP_CONTROLLER_H #include "umps/types.h" class MachineConfig; class Machine; class SystemBus; class Processor; class MPController { public: MPController(const MachineConfig* config, Machine* machine); Word Read(Word addr, const Processor* cpu) const; void Write(Word addr, Word data, const Processor* cpu); private: static const unsigned int kCpuResetDelay = 50; static const unsigned int kCpuHaltDelay = 50; static const unsigned int kPoweroffDelay = 1000; const MachineConfig* const config; Machine* const machine; Word bootPC; Word bootSP; }; #endif // UMPS_MP_CONTROLLER_H umps3-3.0.5/src/umps/mpic.cc000066400000000000000000000134041435132321600156020ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2011 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "umps/mpic.h" #include #include #include "umps/machine_config.h" #include "umps/systembus.h" #include "umps/processor.h" using namespace boost::placeholders; InterruptController::InterruptController(const MachineConfig* config, SystemBus* bus) : config(config), bus(bus), arbiter(0), cpuData(config->getNumProcessors()) { } void InterruptController::StartIRQ(unsigned int il, unsigned int devNo) { il -= kBaseIL; assert(il >= kSharedILBase || !devNo); // Obtain source routing info Source& source = sources[il][devNo]; Word target = kInvalidCpuId; if (source.route.policy == IRT_POLICY_FIXED) { if (source.route.destination < cpuData.size()) target = source.route.destination; } else { for (size_t i = 0; i < cpuData.size(); i++) { Word id = (arbiter + i) % cpuData.size(); if (source.route.destination & (1U << id)) { if (target == kInvalidCpuId || cpuData[id].taskPriority > cpuData[target].taskPriority) target = id; } } if (target != kInvalidCpuId) arbiter = (target + 1) % cpuData.size(); } // No further work to do if no valid target cpu was found; // interrupt is lost forever. if (target == kInvalidCpuId) return; source.lastTarget = target; cpuData[target].ipMask |= 1U << (kBaseIL + il); // For shared int. lines, also set the appropriate bit in the // interrupting devices bitmap if (il >= kSharedILBase) cpuData[target].idb[il - kSharedILBase] |= 1U << devNo; bus->AssertIRQ(kBaseIL + il, target); } void InterruptController::EndIRQ(unsigned int il, unsigned int devNo) { il -= kBaseIL; assert(il >= kSharedILBase || !devNo); // This might be a "spurious" acknowledge message in case the // interrupt wasn't delivered to any core. Word target = sources[il][devNo].lastTarget; if (target == kInvalidCpuId) return; // Deassert IP signals and IDB bits if (il >= kSharedILBase) { cpuData[target].idb[il - kSharedILBase] &= ~(1U << devNo); if (!cpuData[target].idb[il - kSharedILBase]) { cpuData[target].ipMask &= ~(1U << (kBaseIL + il)); bus->DeassertIRQ(kBaseIL + il, target); } } else { cpuData[target].ipMask &= ~(1U << (kBaseIL + il)); bus->DeassertIRQ(kBaseIL + il, target); } sources[il][devNo].lastTarget = kInvalidCpuId; } Word InterruptController::Read(Word addr, const Processor* cpu) const { if (CDEV_BITMAP_BASE <= addr && addr < CDEV_BITMAP_END) return cpuData[cpu->Id()].idb[(addr - CDEV_BITMAP_BASE) >> 2]; if (IRT_BASE <= addr && addr < IRT_END) { unsigned int offset = (addr - IRT_BASE) >> 2; unsigned int il = offset / N_DEV_PER_IL; unsigned int slot = offset % N_DEV_PER_IL; const Source& s = sources[il][slot]; return s.route.destination | (s.route.policy << IRT_ENTRY_POLICY_BIT); } if (CPUCTL_BASE <= addr && addr < CPUCTL_END) { const CpuData& cd = cpuData[cpu->Id()]; switch (addr) { case CPUCTL_INBOX: if (!cd.ipiInbox.empty()) { IpiMessage ipi = cd.ipiInbox.front(); return ipi.msg | (ipi.origin << CPUCTL_INBOX_ORIGIN_BIT); } else { return 0; } case CPUCTL_TPR: return cd.taskPriority; case CPUCTL_BIOS_RES_0: return cd.biosReserved[0]; case CPUCTL_BIOS_RES_1: return cd.biosReserved[1]; default: return 0; } } // Assert not reached assert(0); return 0; } void InterruptController::Write(Word addr, Word data, const Processor* cpu) { if (IRT_BASE <= addr && addr < IRT_END) { unsigned int offset = (addr - IRT_BASE) >> 2; unsigned int il = offset / N_DEV_PER_IL; unsigned int slot = offset % N_DEV_PER_IL; Source& s = sources[il][slot]; s.route.destination = IRT_ENTRY_GET_DEST(data); s.route.policy = IRT_ENTRY_GET_POLICY(data); } else if (CPUCTL_BASE <= addr && addr < CPUCTL_END) { CpuData& cd = cpuData[cpu->Id()]; switch (addr) { case CPUCTL_INBOX: if (!cd.ipiInbox.empty()) { cd.ipiInbox.pop_front(); if (cd.ipiInbox.empty()) { cd.ipMask &= ~(1U << IL_IPI); bus->DeassertIRQ(IL_IPI, cpu->Id()); } } break; case CPUCTL_OUTBOX: bus->scheduleEvent(kIpiLatency * config->getClockRate(), boost::bind(&InterruptController::deliverIPI, this, cpu->Id(), data)); break; case CPUCTL_TPR: cd.taskPriority = data & CPUCTL_TPR_PRIORITY_MASK; break; case CPUCTL_BIOS_RES_0: cd.biosReserved[0] = data; break; case CPUCTL_BIOS_RES_1: cd.biosReserved[1] = data; break; default: break; } } } void InterruptController::deliverIPI(unsigned int origin, Word outbox) { Word recipients = CPUCTL_OUTBOX_GET_RECIP(outbox); for (unsigned int i = 0; i < config->getNumProcessors(); i++) { if (recipients & (1U << i)) { bool hasSlot = true; for (const IpiMessage& ipi : cpuData[i].ipiInbox) { if (ipi.origin == origin) { hasSlot = false; break; } } if (hasSlot) { IpiMessage ipi; ipi.origin = origin; ipi.msg = CPUCTL_OUTBOX_GET_MSG(outbox); cpuData[i].ipiInbox.push_back(ipi); cpuData[i].ipMask |= 1U << IL_IPI; bus->AssertIRQ(IL_IPI, i); } } } } umps3-3.0.5/src/umps/mpic.h000066400000000000000000000052451435132321600154500ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2011 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef UMPS_MPIC_H #define UMPS_MPIC_H #include #include #include "umps/machine_config.h" #include "umps/cp0.h" class SystemBus; class Processor; class InterruptController { public: static const Word kIpiLatency = 20; InterruptController(const MachineConfig* config, SystemBus* bus); void StartIRQ(unsigned int il, unsigned int devNo = 0); void EndIRQ(unsigned int il, unsigned int devNo = 0); Word Read(Word addr, const Processor* cpu) const; void Write(Word addr, Word data, const Processor* cpu); Word GetIP(Word cpuId) const { return cpuData[cpuId].ipMask << CAUSE_IP_BIT(0); } private: static const unsigned int kBaseIL = 2; static const unsigned int kSharedILBase = 1; static const Word kInvalidCpuId = ~0U; struct Source { Source() : lastTarget(kInvalidCpuId) { route.destination = 0; route.policy = IRT_POLICY_FIXED; } // Core the last interrupt from this source was delivered to, needed // for ack messages. We have to keep this because the routing info can // change at any time. Word lastTarget; // IRT entry fields struct { unsigned destination : MachineConfig::MAX_CPUS; unsigned policy : 1; } route; }; struct IpiMessage { unsigned origin : 4; unsigned msg : 8; }; struct CpuData { CpuData() { ipMask = 0; for (Word& data : idb) data = 0; taskPriority = CPUCTL_TPR_PRIORITY_MASK; } Word ipMask; Word idb[N_EXT_IL]; std::deque ipiInbox; unsigned int taskPriority; Word biosReserved[2]; }; void deliverIPI(unsigned int origin, Word outbox); const MachineConfig* const config; SystemBus* const bus; // Simple rotating index used to break ties between cpu // destinations with equal task priorities unsigned int arbiter; // Incoming int. sources Source sources[N_EXT_IL + 1][N_DEV_PER_IL]; // Int. controller cpu interface, for each core std::vector cpuData; }; #endif // UMPS_MPIC_H umps3-3.0.5/src/umps/objdump.cc000066400000000000000000000331711435132321600163150ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2004 Mauro Morsiani * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /**************************************************************************** * * This s a stand-alone program which recognizes .core, .aout and .rom file * formats and prints format and contents information on the standard output. * The following information may be retrieved: * * - header contents for .core/.aout files; * - machine code listing for all file types; * - hexadecimal word dump of file contents; * - byte dump of file contents. * * Both byte and word dumps are provided since, while word dump is almost * the same for code compiled on big/little endian machines, byte dump * differs. * * All zero-filled spaces are skipped, code lines are numbered with proper * (virtual) address, and byte/word dumps are tagged with file offsets. * ****************************************************************************/ #include #include #include #include #include "umps/types.h" #include "umps/blockdev_params.h" #include "umps/aout.h" #include "umps/disassemble.h" static const size_t AOUTENTNUM = 10; // number of NOPS, empty words and characters before skip in displaying #define NOPSMIN 2 #define EMPTYWMIN 3 #define EMPTYCMIN 3 // character and word buffer sizes for file read #define WBUFSIZE 4 #define CBUFSIZE 16 // .aout header field names (copied from elf2umps.cc) HIDDEN const char* const aoutName[] = { "", "Program (virtual) starting address", ".text (virtual) start address", ".text memory size", ".text file start offset", ".text file size", ".data (virtual) start address", ".data memory size", ".data file start offset", ".data file size" }; // // Program functions // HIDDEN void showHelp(const char * prgName); HIDDEN int hdrDump(const char * prgName, const char * fileName); HIDDEN int disAsm(const char * prgName, const char * fileName); HIDDEN int asmPrint(const char * prg, const char * fname, FILE * inF, Word asmStart, Word asmSize); HIDDEN int xDump(const char * prgName, const char * fileName); HIDDEN int bDump(const char * prgName, const char * fileName); /****************************************************************************/ /* Definitions to be exported. */ /****************************************************************************/ // This function scans the line arguments; if no error is found, the // required analysis is performed, or a warning/help message is printed. // Returns an EXIT_SUCCESS/FAILURE code int main(int argc, char * argv[]) { // required analysis possible bool hdr = false; bool disasm = false; bool xdump = false; bool bdump = false; int ret = EXIT_SUCCESS; int i; if (argc == 1) showHelp(argv[0]); else { // scan line arguments for (i = 1; i < argc - 1 && ret != EXIT_FAILURE; i++) { if (SAMESTRING("-h", argv[i])) hdr = true; else if (SAMESTRING("-d", argv[i])) disasm = true; else if (SAMESTRING("-x", argv[i])) xdump = true; else if (SAMESTRING("-b", argv[i])) bdump = true; else if (SAMESTRING("-a", argv[i])) { hdr = true; disasm = true; xdump = true; bdump = true; } else // unrecognized option ret = EXIT_FAILURE; } if (ret != EXIT_FAILURE && strstr(argv[argc - 1], MPSFILETYPE) != NULL) { if (hdr == true) ret = hdrDump(argv[0], argv[argc - 1]); if (disasm == true && ret != EXIT_FAILURE) ret = disAsm(argv[0], argv[argc - 1]); if (xdump == true && ret != EXIT_FAILURE) ret = xDump(argv[0], argv[argc - 1]); if (bdump == true && ret != EXIT_FAILURE) ret = bDump(argv[0], argv[argc - 1]); } else { fprintf(stderr, "%s : Wrong/unknown argument(s)\n", argv[0]); showHelp(argv[0]); ret = EXIT_FAILURE; } } return(ret); } /****************************************************************************/ /* Definitions strictly local to the module. */ /****************************************************************************/ // This function prints a warning/help message on standard error HIDDEN void showHelp(const char * prgName) { fprintf(stderr, "%s syntax : %s [-h] [-d] [-x] [-b] [-a] %s\n\n", prgName, prgName, MPSFILETYPE); fprintf(stderr, "where:\n\t-h\tshow file header\n\t-d\tdisassemble .text area\n"); fprintf(stderr, "\t-b\tfull byte dump\n\t-x\tfull word dump\n\t-a\tall of the above\n\n"); } // This function locates and prints the header contents of a .aout/.core file; // Returns an EXIT_SUCCESS/FAILURE code HIDDEN int hdrDump(const char * prgName, const char * fileName) { int ret = EXIT_SUCCESS; FILE * inFile = NULL; Word tag; Word aoutHdr[AOUTENTNUM]; unsigned int i; SWord offs; if ((inFile = fopen(fileName, "r")) == NULL) { fprintf(stderr, "%s : Error opening file %s : %s\n", prgName, fileName, strerror(errno)); ret = EXIT_FAILURE; } else if (fread((void *) &tag, WORDLEN, 1, inFile) != 1 || !(tag == AOUTFILEID || tag == COREFILEID || tag == BIOSFILEID)) { fprintf(stderr, "%s : Error opening file %s : invalid/corrupted file\n", prgName, fileName); ret = EXIT_FAILURE; } else { // file is of correct type if (tag == BIOSFILEID) printf("%s : ROM file type : it has no a.out header\n\n", fileName); else { if (tag == AOUTFILEID) { offs = 0L; printf("%s : a.out file type\n\n", fileName); } else { offs = CORE_HDR_SIZE * WORDLEN; printf("%s : core file type\n\n", fileName); } // load header if (fseek(inFile, offs, SEEK_SET) == EOF || \ fread((void *) aoutHdr, WORDLEN, AOUTENTNUM, inFile) != AOUTENTNUM || \ fclose(inFile) == EOF) { fprintf(stderr, "%s : Error opening file %s : invalid/corrupted file\n", prgName, fileName); ret = EXIT_FAILURE; } else { // print header for (i = 1; i < AOUTENTNUM; i++) printf("%-35.35s: 0x%.8X\n", aoutName[i], aoutHdr[i]); printf("\n"); } } } return(ret); } // This function locates and prints a machine code listing for executable // instructions contained in a .rom or .aout/.core file. // Returns an EXIT_SUCCESS/FAILURE code HIDDEN int disAsm(const char * prgName, const char * fileName) { int ret = EXIT_SUCCESS; FILE * inFile = NULL; Word tag, size; Word aoutHdr[AOUTENTNUM]; SWord offs; if ((inFile = fopen(fileName, "r")) == NULL) { fprintf(stderr, "%s : Error opening file %s : %s\n", prgName, fileName, strerror(errno)); ret = EXIT_FAILURE; } else { // file exists if (fread((void *) &tag, WORDLEN, 1, inFile) != 1 || !(tag == AOUTFILEID || tag == COREFILEID || tag == BIOSFILEID)) { fprintf(stderr, "%s : Error opening file %s : invalid/corrupted file\n", prgName, fileName); ret = EXIT_FAILURE; } else { // file is of correct type if (tag == BIOSFILEID) { if (fread((void *) &size, WORDLEN, 1, inFile) == 1) // size read correctly ret = asmPrint(prgName, fileName, inFile, 0, size * WORDLEN); else ret = EXIT_FAILURE; } else { if (tag == AOUTFILEID) offs = 0L; else offs = CORE_HDR_SIZE * WORDLEN; // load header if (fseek(inFile, offs, SEEK_SET) == EOF || \ fread((void *) aoutHdr, WORDLEN, AOUTENTNUM, inFile) != AOUTENTNUM || \ fseek(inFile, (SWord) ((aoutHdr[AOUT_HE_ENTRY] - aoutHdr[AOUT_HE_TEXT_VADDR]) + aoutHdr[AOUT_HE_TEXT_OFFSET]) + offs, SEEK_SET) == EOF) { fprintf(stderr, "%s : Error reading file %s : invalid/corrupted file\n", prgName, fileName); ret = EXIT_FAILURE; } else ret = asmPrint(prgName, fileName, inFile, aoutHdr[AOUT_HE_ENTRY], aoutHdr[AOUT_HE_TEXT_MEMSZ]); } } fclose(inFile); } return(ret); } // This function loads the executable code contained in the file inF // from current position, and prints a readable machine listing for // instructions contained there (up to asmSize) numbering them by beginning // with asmStart. // It skips blocks of NOPs after finding NOPSMIN ones. // Returns an EXIT_SUCCESS/FAILURE code HIDDEN int asmPrint(const char * prg, const char * fname, FILE * inF, Word asmStart, Word asmSize) { Word instr; unsigned int nops = 0; for (; asmSize > 0 && !feof(inF) && !ferror(inF); asmStart += WORDLEN, asmSize -= WORDLEN) { // read one instruction if (fread((void *) &instr, WORDLEN, 1, inF) == 1) { if (instr == NOP) // count NOPs for skipping nops++; else nops = 0; if (nops < NOPSMIN) printf("0x%.8X : %s\n", asmStart, StrInstr(instr)); else if (nops == NOPSMIN) // tries to skip a NOPs block printf("*\n"); } } if (ferror(inF)) { fprintf(stderr, "%s : Error disassembling file %s : %s\n", prg, fname, strerror(errno)); return(EXIT_FAILURE); } else { printf("\n"); return(EXIT_SUCCESS); } } // This function loads the file contents of fileName, and prints a // hexadecimal word dump of them. It skips magic file number for .core // and .rom file types, since it is not "seen" inside simulation. // It skips blocks of zero-filled words after finding EMPTYWMIN ones. // Returns an EXIT_SUCCESS/FAILURE code HIDDEN int xDump(const char * prgName, const char * fileName) { int ret = EXIT_SUCCESS; FILE * inFile = NULL; Word tag, size; Word buf[WBUFSIZE]; unsigned int i, words; bool empty; unsigned int emptyl = 0; unsigned int idx = 0; if ((inFile = fopen(fileName, "r")) == NULL) { fprintf(stderr, "%s : Error opening file %s : %s\n", prgName, fileName, strerror(errno)); ret = EXIT_FAILURE; } else { // identifies file type if (fread((void *) &tag, WORDLEN, 1, inFile) != 1 || !(tag == AOUTFILEID || tag == COREFILEID || tag == BIOSFILEID)) { fprintf(stderr, "%s : Error opening file %s : invalid/corrupted file\n", prgName, fileName); ret = EXIT_FAILURE; } else { // file is of correct type if (tag == AOUTFILEID) rewind(inFile); // core and bios file tag should be skipped, since it is not "seen" // inside mps simulations else if (tag == BIOSFILEID) // skip size header too if (fread((void *) &size, WORDLEN, 1, inFile) != 1) if (ferror(inFile)) { fprintf(stderr, "%s : Error reading file %s : %s\n", prgName, fileName, strerror(errno)); ret = EXIT_FAILURE; } // else all is ok while (!ferror(inFile) && !feof(inFile)) // scans file if ((words = fread((void *) buf, WORDLEN, WBUFSIZE, inFile)) > 0) { for (i = 0, empty = true; i < words && empty; i++) if (buf[i] != 0UL) empty = false; // count empty lines if (empty) emptyl++; else emptyl = 0; if (emptyl < EMPTYWMIN) { printf ("0x%.8X : ", idx); for (i = 0; i < words; i++) printf("0x%.8X ", buf[i]); printf("\n"); } else if (emptyl == EMPTYWMIN) // skips empty lines printf("*\n"); idx += (words * WORDLEN); } printf("\n"); } fclose (inFile); } return(ret); } // This function loads the file contents of fileName, and prints a // byte dump of them. It skips magic file number for .core // and .rom file types, since it is not "seen" inside simulation. // It skips blocks of zero-filled bytes after finding EMPTYBMIN ones. // Returns an EXIT_SUCCESS/FAILURE code HIDDEN int bDump(const char * prgName, const char * fileName) { int ret = EXIT_SUCCESS; FILE * inFile = NULL; Word tag, size; unsigned char buf[CBUFSIZE]; unsigned int i, chars; bool empty; unsigned int emptyl = 0; unsigned int idx = 0; if ((inFile = fopen(fileName, "r")) == NULL) { fprintf(stderr, "%s : Error opening file %s : %s\n", prgName, fileName, strerror(errno)); ret = EXIT_FAILURE; } else { // tries file recognition if (fread((void *) &tag, WORDLEN, 1, inFile) != 1 || !(tag == AOUTFILEID || tag == COREFILEID || tag == BIOSFILEID)) { fprintf(stderr, "%s : Error opening file %s : invalid/corrupted file\n", prgName, fileName); ret = EXIT_FAILURE; } else { // file is of correct type if (tag == AOUTFILEID) rewind(inFile); // core and bios file tag should be skipped, since it is not // "seen" inside mps simulations else if (tag == BIOSFILEID) // skip size header too if (fread((void *) &size, WORDLEN, 1, inFile) != 1) if (ferror(inFile)) { fprintf(stderr, "%s : Error reading file %s : %s\n", prgName, fileName, strerror(errno)); ret = EXIT_FAILURE; } // else all is ok while (!ferror(inFile) && !feof(inFile)) if ((chars = fread((void *) buf, sizeof(unsigned char), CBUFSIZE, inFile)) > 0) { for (i = 0, empty = true; i < chars && empty; i++) if (buf[i] != 0) empty = false; if (empty) // counts empty lines emptyl++; else emptyl = 0; if (emptyl < EMPTYCMIN) { printf ("0x%.8X : ", idx); for (i = 0; i < chars; i++) printf("%.2X ", buf[i]); printf("\n"); } else if (emptyl == EMPTYCMIN) // skips empty lines printf("*\n"); idx += chars; } printf("\n"); } fclose (inFile); } return(ret); } umps3-3.0.5/src/umps/processor.cc000066400000000000000000001320641435132321600166750ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2004 Mauro Morsiani * Copyright (C) 2020 Mattia Biondi * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * This module implements the Processor class. A Processor object emulates * MIPS R2/3000 processor features almost perfectly. It is linked to a * SystemBus object for physical memory accesses. * * MIPS processor features are too complex to be fully descripted * here: refer to external documentation. * * This module also contains TLBEntry class definition: it is used to * streamline TLB build and handling in Processor. */ #include "umps/processor.h" #include #include "umps/const.h" #include "umps/cp0.h" #include "umps/processor_defs.h" #include "umps/machine.h" #include "umps/systembus.h" #include "umps/utility.h" #include "umps/machine_config.h" #include "umps/error.h" #include "umps/disassemble.h" // Names of exceptions HIDDEN const char* const excName[] = { "NO EXCEPTION", "INT", "MOD", "TLBL Refill", "TLBL", "TLBS Refill", "TLBS", "ADEL", "ADES", "DBE", "IBE", "SYS", "BP", "RI", "CPU", "OV" }; // exception code table (each corresponding to an exception cause); // each exception cause is mapped to one exception type expressed in // CAUSE register field format HIDDEN const Word excCode[] = { 0UL, 0UL, 1UL, 2UL, 2UL, 3UL, 3UL, 4UL, 5UL, 6UL, 7UL, 8UL, 9UL, 10UL, 11UL, 12UL }; // Each TLBEntry object represents a single entry in the TLB contained in // the CP0 coprocessor part of a real MIPS processor. // Each one is a 64-bit field split in two parts (HI and LO), with special // fields and control bits (see external documentation for more details). class TLBEntry { public: // This method builds an entry and sets its initial contents TLBEntry(Word entHI = 0, Word entLO = 0); // This method returns the HI 32-bit part of the entry Word getHI() const { return tlbHI; } // This method returns the LO 32-bit part of the entry Word getLO() const { return tlbLO; } // This method sets the entry HI part (leaving the zero-filled // field untouched) void setHI(Word entHI); // This method sets the entry LO part (leaving the zero-filled field // untouched) void setLO(Word entLO); // This method compares the entry contents with the VPN part of a // virtual address and returns TRUE if a match is found, FALSE // otherwise bool VPNMatch(Word vaddr); // This method compares the entry contents with the ASID field in a // CP0 special register and returns TRUE if a match is found, FALSE // otherwise bool ASIDMatch(Word cpreg); // the following methods return the bit value for the corresponding // access control bit bool IsG(); bool IsV(); bool IsD(); private: // contains the VPN + ASID fields, and a zero-filled field Word tlbHI; // contains the PFN field, some access control bits and a // zero-filled field Word tlbLO; }; // This method builds an entry and sets its initial contents TLBEntry::TLBEntry(Word entHI, Word entLO) { tlbHI = entHI; tlbLO = entLO; } // This method sets the entry HI part (leaving the zero-filled field untouched) void TLBEntry::setHI(Word entHI) { tlbHI = (entHI & (VPNMASK | ASIDMASK)); } // This method sets the entry LO part (leaving the zero-filled field untouched) void TLBEntry::setLO(Word entLO) { tlbLO = (entLO & ENTRYLOMASK); } // This method compares the entry contents with the VPN part of a virtual // address and returns TRUE if a match is found, FALSE otherwise bool TLBEntry::VPNMatch(Word vaddr) { return VPN(tlbHI) == VPN(vaddr); } // This method compares the entry contents with the ASID field in a CP0 // special register and returns TRUE if a match is found, FALSE otherwise bool TLBEntry::ASIDMatch(Word cpreg) { return ASID(tlbHI) == ASID(cpreg); } // This method returns the value of G (Global) access control bit in an // entry LO part bool TLBEntry::IsG() { return BitVal(tlbLO, GBITPOS); } // This method returns the value of V (Valid) access control bit in an // entry LO part bool TLBEntry::IsV() { return BitVal(tlbLO, VBITPOS); } // This method returns the value of D (Dirty) access control bit in an // entry LO part bool TLBEntry::IsD() { return BitVal(tlbLO, DBITPOS); } Processor::Processor(const MachineConfig* config, Word cpuId, Machine* machine, SystemBus* bus) : id(cpuId), machine(machine), bus(bus), status(PS_HALTED), tlbSize(config->getTLBSize()), tlb(new TLBEntry[tlbSize]), tlbFloorAddress(config->getTLBFloorAddress()) { } Processor::~Processor() { } void Processor::setStatus(ProcessorStatus newStatus) { if (status != newStatus) { status = newStatus; StatusChanged.emit(); } } // This method puts Processor in startup state. This is done following the // MIPS conventions on register fields to be set; it also pre-loads the // first instruction since Cycle() goes on with execute-load loop void Processor::Reset(Word pc, Word sp) { unsigned int i; // first instruction is not in a branch delay slot isBranchD = false; // no exception pending at start excCause = NOEXCEPTION; copENum = 0; // no loads pending at start loadPending = LOAD_TARGET_NONE; loadReg = 0; loadVal = MAXSWORDVAL; // clear general purpose registers for (i = 0; i < CPUREGNUM; i++) gpr[i] = 0; gpr[29] = sp; // no previous instruction is available prevPC = MAXWORDVAL; prevPhysPC = MAXWORDVAL; prevInstr = NOP; // clears CP0 registers and then sets them for (i = 0; i < CP0REGNUM; i++) cpreg[i] = 0UL; // first instruction is already loaded cpreg[RANDOM] = ((tlbSize - 1UL) << RNDIDXOFFS) - RANDOMSTEP; cpreg[STATUS] = STATUSRESET; cpreg[PRID] = id; currPC = pc; // maps PC to physical address space and fetches first instruction // mapVirtual and SystemBus cannot signal TRUE on this call if (mapVirtual(currPC, &currPhysPC, EXEC) || bus->InstrRead(currPhysPC, &currInstr, this)) Panic("Illegal memory access in Processor::Reset"); // sets values for following PCs nextPC = currPC + WORDLEN; succPC = nextPC + WORDLEN; setStatus(PS_RUNNING); } void Processor::Halt() { setStatus(PS_HALTED); } // This method makes Processor execute a single instruction. // For simulation purposes, it differs from traditional processor cycle: // the first instruction after a reset is pre-loaded, and cycle is // execute - fetch instead of fetch - execute. // This way, it is possible to know what instruction will be executed // before its execution happens // // Method works as follows: // the instruction currently loaded is executed; if no exception occurs, // interrupt status is checked to see if an INT exception has to be // processed; if no interrupts are pending, next instruction is located and // fetched, so Processor is ready for another Cycle(). If an exception // occurs, first instruction at vector address is loaded, so a new Cycle() // may start. // Other MIPS-specific minor tasks are performed at proper points. void Processor::Cycle() { // Nothing to do if the cpu is halted if (isHalted()) return; // Update internal timer if (cpreg[STATUS] & STATUS_TE) { if (cpreg[CP0REG_TIMER] == 0) AssertIRQ(IL_CPUTIMER); cpreg[CP0REG_TIMER]--; } else { DeassertIRQ(IL_CPUTIMER); } // In low-power state, only the per-cpu timer keeps running if (isIdle()) return; // Instruction decode & exec if (execInstr(currInstr)) handleExc(); // Check if we entered sleep mode as a result of the last // instruction; if so, we effectively stall the pipeline. if (isIdle()) return; // PC saving for book-keeping purposes prevPC = currPC; prevPhysPC = currPhysPC; prevInstr = currInstr; // RANDOM register is decremented randomRegTick(); // currPC is loaded so a new cycle fetch may start: this "PC stack" is // used to emulate delayed branch slots currPC = nextPC; nextPC = succPC; succPC += WORDLEN; // Check for interrupt exception; note that this will _not_ // trigger another exception if we're already in "exception mode". if (checkForInt()) handleExc(); // processor cycle fetch part if (mapVirtual(currPC, &currPhysPC, EXEC)) { // TLB or Address exception caused: current instruction is nullified currInstr = NOP; handleExc(); } else if (bus->InstrRead(currPhysPC, &currInstr, this)) { // IBE exception caused: current instruction is nullified currInstr = NOP; handleExc(); } } uint32_t Processor::IdleCycles() const { if (isHalted()) return (uint32_t) -1; else if (isIdle()) return (cpreg[STATUS] & STATUS_TE) ? cpreg[CP0REG_TIMER] : (uint32_t) -1; else return 0; } void Processor::Skip(uint32_t cycles) { assert(isIdle() && cycles <= IdleCycles()); if (cpreg[STATUS] & STATUS_TE) cpreg[CP0REG_TIMER] -= cycles; } // This method allows SystemBus and Processor itself to signal Processor // when an exception happens. SystemBus signal IBE/DBE exceptions; Processor // itself signal all other kinds of exception. // // Method works as follows: // the exception internal code is put into proper Processor private // variable(s), and is signaled to Watch (so Watch may stop simulation if // needed). // Exception processing is done by Processor private method handleExc() void Processor::SignalExc(unsigned int exc, Word cpuNum) { excCause = exc; SignalException.emit(excCause); // used only for CPUEXCEPTION handling copENum = cpuNum; } void Processor::AssertIRQ(unsigned int il) { cpreg[CAUSE] |= CAUSE_IP(il); // If in standby mode, go back to being a power hog. if (isIdle()) setStatus(PS_RUNNING); } void Processor::DeassertIRQ(unsigned int il) { cpreg[CAUSE] &= ~CAUSE_IP(il); } // This method allows to get critical information on Processor current // internal status. Parameters are self-explanatory: they are extracted from // proper places inside Processor itself void Processor::getCurrStatus(Word * asid, Word * pc, Word * instr, bool * isLD, bool * isBD) { *asid = (ASID(cpreg[ENTRYHI])) >> ASIDOFFS; *pc = currPC; *instr = currInstr; *isLD = (loadPending != LOAD_TARGET_NONE); *isBD = isBranchD; } Word Processor::getASID() const { return ASID(cpreg[ENTRYHI]) >> ASIDOFFS; } bool Processor::InUserMode() const { return BitVal(cpreg[STATUS], KUCBITPOS); } bool Processor::InKernelMode() const { return (!BitVal(cpreg[STATUS], KUCBITPOS)); } // This method allows to get Processor previously executed instruction and // location void Processor::getPrevStatus(Word * pc, Word * instr) { *pc = prevPC; *instr = prevInstr; } // This method allows to get a human-readable mnemonic expression for the last // exception happened (thanks to excName[] array) const char* Processor::getExcCauseStr() { // 0 means no exception if (excCause) return excName[excCause]; else return (EMPTYSTR); } // This method allows to get the physical location of instruction executed // in the previous Processor Cycle() Word Processor::getPrevPPC() { return(prevPhysPC); } // This method allows to get the physical location of instruction executed // in the current Processor Cycle() Word Processor::getCurrPPC() { return(currPhysPC); } // This method allows to get the virtual location of instruction that will // be executed in the next Processor Cycle() Word Processor::getNextPC() { return(nextPC); } // This method allows to get the virtual location of instruction that will // be executed in the Processor Cycle() _after_ the next Word Processor::getSuccPC() { return(succPC); } // This method allows to get the value of the general purpose register // indexed by num (HI and LO are the last ones in the array) SWord Processor::getGPR(unsigned int num) { return(gpr[num]); } // This method allows to get the value of the CP0 special register indexed // by num. num coding itself is internal (see h/processor.h for mapping) Word Processor::getCP0Reg(unsigned int num) { return(cpreg[num]); } void Processor::getTLB(unsigned int index, Word* hi, Word* lo) const { *hi = tlb[index].getHI(); *lo = tlb[index].getLO(); } Word Processor::getTLBHi(unsigned int index) const { return tlb[index].getHI(); } Word Processor::getTLBLo(unsigned int index) const { return tlb[index].getLO(); } // This method allows to modify the current value of a general purpose // register (HI and LO are the last ones in the array) void Processor::setGPR(unsigned int num, SWord val) { if (num > 0) // register $0 is read-only gpr[num] = val; } // This method allows to modify the current value of a CP0 special // register. num coding itself is internal (see h/processor.h for mapping) void Processor::setCP0Reg(unsigned int num, Word val) { if (num < CP0REGNUM) cpreg[num] = val; } // This method allows to modify the current value of nextPC to force sudden // branch: it violates delayed branch slot conventions void Processor::setNextPC(Word npc) { nextPC = npc; } // This method allows to modify the current value of succPC to force sudden // branch: it does not violate branch slot conventions, if used to change // the target of a branch already taken void Processor::setSuccPC(Word spc) { succPC = spc; } // This method allows to modify the current value of a specified TLB entry void Processor::setTLB(unsigned int index, Word hi, Word lo) { if (index < tlbSize) { tlb[index].setHI(hi); tlb[index].setLO(lo); SignalTLBChanged(index); } else { Panic("Unknown TLB entry in Processor::setTLB()"); } } void Processor::setTLBHi(unsigned int index, Word value) { assert(index < tlbSize); tlb[index].setHI(value); SignalTLBChanged(index); } void Processor::setTLBLo(unsigned int index, Word value) { assert(index < tlbSize); tlb[index].setLO(value); SignalTLBChanged(index); } // // Processor private methods start here // // This method advances CP0 RANDOM register, following MIPS conventions; it // cycles from RANDOMTOP to RANDOMBASE, one STEP less for each clock tick void Processor::randomRegTick() { cpreg[RANDOM] = (cpreg[RANDOM] - RANDOMSTEP) & (((tlbSize - 1UL) << RNDIDXOFFS)); if (cpreg[RANDOM] < RANDOMBASE) cpreg[RANDOM] = ((tlbSize - 1UL) << RNDIDXOFFS); } // This method pushes the KU/IE bit stacks in CP0 STATUS register to start // exception handling void Processor::pushKUIEStack() { unsigned int bitp; // push the KUIE stack for (bitp = KUOBITPOS; bitp > KUCBITPOS; bitp--) if (BitVal(cpreg[STATUS], bitp - 2)) cpreg[STATUS] = SetBit(cpreg[STATUS], bitp); else cpreg[STATUS] = ResetBit(cpreg[STATUS], bitp); // sets to 0 current KU IE bits cpreg[STATUS] = ResetBit(cpreg[STATUS], KUCBITPOS); cpreg[STATUS] = ResetBit(cpreg[STATUS], IECBITPOS); } // This method pops the KU/IE bit stacks in CP0 STATUS register to end // exception handling. It is invoked on RFE instruction execution void Processor::popKUIEStack() { unsigned int bitp; for (bitp = IECBITPOS; bitp < IEOBITPOS; bitp++) { if (BitVal(cpreg[STATUS], bitp + 2)) cpreg[STATUS] = SetBit(cpreg[STATUS], bitp); else cpreg[STATUS] = ResetBit(cpreg[STATUS], bitp); } } // This method test for pending interrupts, checking global abilitation (IEc // bit in STATUS register) and comparing interrupt mask with interrupts // pending on bus; returns TRUE if an INT exception must be raised, FALSE // otherwise, and sets CP0 registers if needed bool Processor::checkForInt() { if ((cpreg[STATUS] & STATUS_IEc) && (cpreg[CAUSE] & cpreg[STATUS] & CAUSE_IP_MASK)) { SignalExc(INTEXCEPTION); // Clear all Cause fields except IP cpreg[CAUSE] &= CAUSE_IP_MASK; return true; } else { // No interrupt on this cycle return false; } } /** * Try to enter standby mode */ void Processor::suspend() { if (!(cpreg[CAUSE] & CAUSE_IP_MASK)) setStatus(PS_IDLE); } // This method sets the appropriate CP0 registers at exception // raising, following the MIPS conventions; it also set the PC value // to point the appropriate exception handler vector. void Processor::handleExc() { // If there is a load pending, it is completed while the processor // prepares for exception handling (a small bubble...). completeLoad(); // set the excCode into CAUSE reg cpreg[CAUSE] = IM(cpreg[CAUSE]) | (excCode[excCause] << CAUSE_EXCCODE_BIT); if (isBranchD) { // previous instr. is branch/jump: must restart from it cpreg[CAUSE] = SetBit(cpreg[CAUSE], CAUSE_BD_BIT); cpreg[EPC] = prevPC; // first instruction in exception handler itself is not in a BD slot isBranchD = false; } else { // BD is already set to 0 by CAUSE masking with IM; sets only EPC cpreg[EPC] = currPC; } // Set coprocessor unusable number in CAUSE register for // `Coprocessor Unusable' exceptions if (excCause == CPUEXCEPTION) cpreg[CAUSE] = cpreg[CAUSE] | (copENum << COPEOFFSET); // Set PC to the right handler Word excVector; if (BitVal(cpreg[STATUS], STATUS_BEV_BIT)) { // Machine bootstrap exception handler excVector = BOOTEXCBASE; } else { excVector = KSEG0BASE; } pushKUIEStack(); if (excCause == UTLBLEXCEPTION || excCause == UTLBSEXCEPTION) excVector += TLBREFOFFS; else excVector += OTHEREXCOFFS; if (excCause == INTEXCEPTION) { // interrupt: test is before istruction fetch, so handling // could start immediately currPC = excVector; nextPC = currPC + WORDLEN; succPC = nextPC + WORDLEN; } else { // other exception, at instruction fetch or later: handling // could be done only in next processor cycle (current // instruction has been nullified) nextPC = excVector; succPC = nextPC + WORDLEN; } } // This method zeroes out the TLB void Processor::zapTLB() { // Leave out the first entry ([0]) for (size_t i = 1; i < tlbSize; ++i) { tlb[i].setHI(0); tlb[i].setLO(0); SignalTLBChanged(i); } } // This method allows to handle the delayed load slot: it provides to load // the target register with the needed value during the execution of other // instructions, when invoked at the appropriate point in the "pipeline" void Processor::completeLoad() { // loadPending tells whether a general purpose or a CP0 special register // is the target switch (loadPending) { case LOAD_TARGET_GPREG: if (loadReg != 0) gpr[loadReg] = loadVal; loadPending = LOAD_TARGET_NONE; break; case LOAD_TARGET_CPREG: // CP0 registers have some zero-filled fields or are read-only // so individual handling is needed switch(loadReg) { case INDEX: // loadable part is index field only, and P bit // remain unchanged cpreg[INDEX] = (cpreg[INDEX] & SIGNMASK) | (((Word) loadVal) & ((tlbSize - 1UL) << RNDIDXOFFS)); break; case ENTRYLO: // loadable part is PFN and status bits only cpreg[ENTRYLO] = ((Word) loadVal) & ENTRYLOMASK; break; case CP0REG_TIMER: cpreg[CP0REG_TIMER] = (Word) loadVal; DeassertIRQ(IL_CPUTIMER); break; case ENTRYHI: // loadable parts are VPN and ASID fields cpreg[ENTRYHI] = ((Word) loadVal) & (VPNMASK | ASIDMASK); break; case STATUS: // loadable parts are CU0 bit, TE bit, BEV bit in DS, IM mask and // KUIE bit stack cpreg[STATUS] = ((Word) loadVal) & STATUSMASK; break; case EPC: case PRID: case RANDOM: case BADVADDR: default: // read-only regs: writes have no effects break; } loadPending = LOAD_TARGET_NONE; break; case LOAD_TARGET_NONE: default: break; } } // This method maps the virtual addresses to physical ones following the // complex mapping algorithm and TLB used by MIPS (see external doc). // It returns TRUE if conversion was not possible (this implies an exception // have been raised) and FALSE if conversion has taken place: physical value // for address conversion is returned thru paddr pointer. // AccType details memory access type (READ/WRITE/EXECUTE) bool Processor::mapVirtual(Word vaddr, Word * paddr, Word accType) { // SignalProcVAccess() is always done so it is possible // to track accesses which produce exceptions machine->HandleVMAccess(ENTRYHI_GET_ASID(cpreg[ENTRYHI]), vaddr, accType, this); // address validity and bounds check if (BADADDR(vaddr) || (InUserMode() && (INBOUNDS(vaddr, KSEG0BASE, KUSEGBASE)))) { // bad offset or kernel segment access from user mode *paddr = MAXWORDVAL; // the bad virtual address is put into BADVADDR reg cpreg[BADVADDR] = vaddr; if (accType == WRITE) SignalExc(ADESEXCEPTION); else SignalExc(ADELEXCEPTION); return true; } else if (INBOUNDS(vaddr, KSEG0BASE, tlbFloorAddress)) { // no bad offset; if vaddr < KUSEGBASE the processor is surely // in kernelMode // valid access to KSEG0 area *paddr = vaddr; return false; } // The access is in user mode to user space, or in kernel mode // to KSEG0 or KUSEG spaces. unsigned int index; if (probeTLB(&index, cpreg[ENTRYHI], vaddr)) { if (tlb[index].IsV()) { if (accType != WRITE || tlb[index].IsD()) { // All OK *paddr = PHADDR(vaddr, tlb[index].getLO()); return false; } else { // write operation on frame with D bit set to 0 *paddr = MAXWORDVAL; setTLBRegs(vaddr); SignalExc(MODEXCEPTION); return true; } } else { // invalid access to frame with V bit set to 0 *paddr = MAXWORDVAL; setTLBRegs(vaddr); if (accType == WRITE) SignalExc(TLBSEXCEPTION); else SignalExc(TLBLEXCEPTION); return true; } } else { // bad or missing VPN match: Refill event required *paddr = MAXWORDVAL; setTLBRegs(vaddr); if (accType == WRITE) SignalExc(UTLBSEXCEPTION); else SignalExc(UTLBLEXCEPTION); return true; } } // This method sets the CP0 special registers on exceptions forced by TLB // handling (see mapVirtual() for invocation/specific cases). void Processor::setTLBRegs(Word vaddr) { // Note that ENTRYLO is left undefined! cpreg[BADVADDR] = vaddr; cpreg[ENTRYHI] = VPN(vaddr) | ASID(cpreg[ENTRYHI]); } // This method make Processor execute a single MIPS instruction, emulating // pipeline constraints and load delay slots (see external doc). bool Processor::execInstr(Word instr) { Word temp; unsigned int i, cp0Num; bool error = false; bool isValidBranch = false; switch (OpType(instr)) { case REGTYPE: // MIPS register-type instruction execution error = execRegInstr(&temp, instr, &isValidBranch); // delayed load is completed _after_ istruction execution, but // _before_ instruction result is moved to target register completeLoad(); if (!error && RD(instr)) // no errors & target register != r0: put instruction result // into target register gpr[RD(instr)] = (SWord) temp; break; case IMMTYPE: // MIPS immediate-type instruction error = execImmInstr(&temp, instr); // delayed load is completed _after_ istruction execution, but // _before_ instruction result is moved to target register completeLoad(); if (!error && RT(instr)) // no errors & target register != r0: put instruction result // into target register gpr[RT(instr)] = (SWord) temp; break; case BRANCHTYPE: // MIPS branch-type instruction error = execBranchInstr(instr, &isValidBranch); // delayed load is completed just after instruction execution completeLoad(); break; case COPTYPE: // MIPS coprocessor-type instruction // Some simulation issues: // CP0 is built-in and its Cp0Cond condition line is always // FALSE; other coprocessors are hard-wired to // non-availability // detects coprocessor referred if (OPCODE(instr) == COP0SEL && cp0Usable()) { // COPOPTYPE corresponds to RS field switch(COPOPTYPE(instr)) { case CO0: // coprocessor 0 operations if (RT(instr) || RD(instr) || SHAMT(instr)) { // instruction is ill-formed: exception signaled // is CPU to help detection cause SignalExc(CPUEXCEPTION, 0); error = true; } else { // all instructions follow MIPS guidelines switch(FUNCT(instr)) { case RFE: popKUIEStack(); break; case TLBP: // solution "by the book" cpreg[INDEX] = SIGNMASK; if (probeTLB(&i, cpreg[ENTRYHI], cpreg[ENTRYHI])) cpreg[INDEX] = (i << RNDIDXOFFS); break; case TLBR: cpreg[ENTRYHI] = tlb[RNDIDX(cpreg[INDEX])].getHI(); cpreg[ENTRYLO] = tlb[RNDIDX(cpreg[INDEX])].getLO(); break; case TLBWI: tlb[RNDIDX(cpreg[INDEX])].setHI(cpreg[ENTRYHI]); tlb[RNDIDX(cpreg[INDEX])].setLO(cpreg[ENTRYLO]); SignalTLBChanged(RNDIDX(cpreg[INDEX])); break; case TLBWR: tlb[RNDIDX(cpreg[RANDOM])].setHI(cpreg[ENTRYHI]); tlb[RNDIDX(cpreg[RANDOM])].setLO(cpreg[ENTRYLO]); SignalTLBChanged(RNDIDX(cpreg[INDEX])); break; case COFUN_WAIT: suspend(); break; default: // unknown coprocessor 0 operation requested SignalExc(CPUEXCEPTION, 0); error = true; break; } } // delayed load is completed after instruction execution completeLoad(); break; case BC0: switch(COPOPCODE(instr)) { case BC0F: // condition line for CP0 is always FALSE succPC = nextPC + (SignExtImm(instr) << WORDSHIFT); isValidBranch = true; break; case BC0T: // condition line for CP0 is always FALSE // so this is a nop instruction isValidBranch = true; break; default: // traps BC0FL R4000 instructions // and the like... SignalExc(CPUEXCEPTION, 0); error = true; break; } completeLoad(); break; case MFC0: // delayed load is completed _before_ istruction // execution since instruction itself produces a // delayed load completeLoad(); // valid instruction has SHAMT and FUNCT fields // set to 0, and refers to a valid CP0 register if (ValidCP0Reg(RD(instr), &cp0Num) && !SHAMT(instr) && !FUNCT(instr)) { setLoad(LOAD_TARGET_GPREG, RT(instr), (SWord) cpreg[cp0Num]); } else { // invalid instruction format or CP0 reg SignalExc(CPUEXCEPTION, 0); error = true; } break; case MTC0: // delayed load is completed _before_ istruction // execution since instruction itself produces a // delayed load completeLoad(); // valid instruction has SHAMT and FUNCT fields // set to 0, and refers to a valid CP0 register if (ValidCP0Reg(RD(instr), &cp0Num) && !SHAMT(instr) && !FUNCT(instr)) setLoad(LOAD_TARGET_CPREG, cp0Num, gpr[RT(instr)]); else { // check if it is TLBCLR backpatch if (RD(instr) == CONTEXTREG && !SHAMT(instr) && !FUNCT(instr)) zapTLB(); else { // invalid instruction SignalExc(CPUEXCEPTION, 0); error = true; } } break; default: // unknown and CFC0, CTC0, COP0, LWC0 generic instructions SignalExc(CPUEXCEPTION, 0); error = true; break; } } else { // coprocessor 0 (or other) unusable SignalExc(CPUEXCEPTION, COPNUM(instr)); error = true; } break; case LOADTYPE: // MIPS load instruction // delayed load is completed _before_ istruction execution since // instruction itself produces a delayed load completeLoad(); error = execLoadInstr(instr); break; case STORETYPE: // MIPS store instruction // delayed load is completed _before_ istruction execution // since it happens "logically" so in the pipeline completeLoad(); error = execStoreInstr(instr); break; case LOADCOPTYPE: case STORECOPTYPE: if (BitVal(instr, DWCOPBITPOS)) // LDC, SDC reserved instructions handling SignalExc(RIEXCEPTION); else SignalExc(CPUEXCEPTION, COPNUM(instr)); error = true; break; default: // unknown instruction (generic) SignalExc(RIEXCEPTION); error = true; break; } // Branch delay slot handling: if the instruction generated an // exception, isBranchD is _not_ modified, since the exception // handler needs it; otherwise, the next instruction is a BD slot // if the current instruction is a valid branch. if (!error) isBranchD = isValidBranch; return error; } // This method tests for CP0 availability (as set in STATUS register and in // MIPS conventions) bool Processor::cp0Usable() { // CP0 is usable only when marked in user mode, and always in kernel mode return BitVal(cpreg[STATUS], STATUS_CU0_BIT) || !BitVal(cpreg[STATUS], STATUS_KUc_BIT); } // This method scans the TLB looking for a entry that matches ASID/VPN pair; // scan algorithm follows MIPS specifications, and returns the _highest_ // entry that matches bool Processor::probeTLB(unsigned int* index, Word asid, Word vpn) { bool found = false; for (unsigned int i = 0; i < tlbSize; i++) { if (tlb[i].VPNMatch(vpn) && (tlb[i].IsG() || tlb[i].ASIDMatch(asid))) { found = true; *index = i; } } return found; } // This method sets delayed load handling variables when needed by // instruction execution void Processor::setLoad(LoadTargetType loadCode, unsigned int regNum, SWord regVal) { loadPending = loadCode; loadReg = regNum; loadVal = regVal; } // This method returns a sign-extended byte from inside a word following // MIPS conventions about big and little endianness; it is requested by LB // instruction execution SWord Processor::signExtByte(Word val, unsigned int bytep) { if (BIGENDIANCPU) // byte to be extended is on the other "side" of the word bytep = (WORDLEN - 1) - bytep; // shifts byte into first position val = val >> (BYTELEN * bytep); if (BitVal(val, BYTELEN - 1)) // must sign-extend with 1 return(val | ~(BYTESIGNMASK)); else return(val & BYTESIGNMASK); } // This method returns a zero-extended byte from inside a word following // MIPS conventions about big and little endianness; requested by LBU // instruction Word Processor::zExtByte(Word val, unsigned int bytep) { if (BIGENDIANCPU) // byte is on the other "side" of the word bytep = (WORDLEN - 1) - bytep; // shifts byte into first position and zero-extends it return((val >> (BYTELEN * bytep)) & BYTEMASK); } // This method returns a word with one byte overwritten, taken from another // word, and following MIPS conventions about big and little endianness: // requested by SB instruction Word Processor::mergeByte(Word dest, Word src, unsigned int bytep) { if (BIGENDIANCPU) bytep = (WORDLEN - 1) - bytep; // shifts least significant byte of src into position and clears around it src = (src & BYTEMASK) << (bytep * BYTELEN); //clears specified byte and overwrites it with src return((dest & ~(BYTEMASK << (bytep * BYTELEN))) | src); } // This method returns the sign-extended halfword taken from a word, following // MIPS conventions about big and little endianness (LH instruction) SWord Processor::signExtHWord(Word val, unsigned int hwp) { if (BIGENDIANCPU) hwp = 1 - hwp; // shifts halfword into first position val = val >> (HWORDLEN * hwp); return(SignExtImm(val)); } // This method returns the zero-extended halfword taken from a word, following // MIPS conventions about big and little endianness (LHU instruction) Word Processor::zExtHWord(Word val, unsigned int hwp) { if (BIGENDIANCPU) hwp = 1 - hwp; // shifts halfword into first position val = val >> (HWORDLEN * hwp); return(ZEXTIMM(val)); } // this method returns a word partially overwritten with another, following // MIPS conventions about big and little endianness (SH instruction) Word Processor::mergeHWord(Word dest, Word src, unsigned int hwp) { if (BIGENDIANCPU) hwp = 1 - hwp; // shifts least significant halfword of src into position and clears around it src = (src & IMMMASK) << (hwp * HWORDLEN); // clears specified halfword and overwrites it with src return((dest & ~(IMMMASK << (hwp * HWORDLEN))) | src); } // This method copies into dest some bytes from src (starting from bytep // position in src), beginning the copy from the left or right side of dest. // It computes the partial overlaps needed the LWL/LWR and SWL/SWR. // instructions following the MIPS conventions on big and little endianness Word Processor::merge(Word dest, Word src, unsigned int bytep, bool loadBig, bool startLeft) { if (loadBig) // LWL/LWR with BIGENDIANCPU == 1 or SWR/SWL with BIGENDIANCPU == 0 bytep = (WORDLEN - 1) - bytep; // else bytep is already correct: // LWL/LWR with BIGENDIANCPU == 0 or SWR/SWL with BIGENDIANCPU = 1 if (startLeft) { // starts from left end of dest: // shifts src part into position and clear right of it src = src << (((WORDLEN - 1) - bytep) * BYTELEN); // clear the left part of dest and merges it with src dest = (dest & ~(MAXWORDVAL << (((WORDLEN - 1) - bytep) * BYTELEN))) | src; } else { // starts from right end of dest: shifts src part into position // and clears left of it (because src is unsigned) src = src >> (bytep * BYTELEN); // clears the right part of dest and merges it with src dest = (dest & ~(MAXWORDVAL >> (bytep * BYTELEN))) | src; } return(dest); } // This method executes a MIPS register-type instruction, following MIPS // guidelines; returns instruction result thru res pointer, and branch delay // slot indication if needed (due to JR/JALR presence). It also returns TRUE // if an exception occurred, FALSE otherwise bool Processor::execRegInstr(Word * res, Word instr, bool * isBD) { bool error = false; Word paddr; bool atomic; *isBD = false; error = InvalidRegInstr(instr); if (!error) { // instruction format is correct switch (FUNCT(instr)) { case SFN_ADD: if (SignAdd(res, gpr[RS(instr)], gpr[RT(instr)])) { SignalExc(OVEXCEPTION); error = true; } break; case SFN_ADDU: *res = gpr[RS(instr)] + gpr[RT(instr)]; break; case SFN_AND: *res = gpr[RS(instr)] & gpr[RT(instr)]; break; case SFN_BREAK: SignalExc(BPEXCEPTION); error = true; break; case SFN_DIV: if (gpr[RT(instr)] != 0) { gpr[LO] = gpr[RS(instr)] / gpr[RT(instr)]; gpr[HI] = gpr[RS(instr)] % gpr[RT(instr)]; } else { // divisor is zero gpr[LO] = MAXSWORDVAL; gpr[HI] = 0; } break; case SFN_DIVU: if (gpr[RT(instr)] != 0) { gpr[LO] = ((Word) gpr[RS(instr)]) / ((Word) gpr[RT(instr)]); gpr[HI] = ((Word) gpr[RS(instr)]) % ((Word) gpr[RT(instr)]); } else { // divisor is zero gpr[LO] = MAXSWORDVAL; gpr[HI] = 0; } break; case SFN_JALR: // solution "by the book" succPC = gpr[RS(instr)]; *res = currPC + (2 * WORDLEN); *isBD = true; // alternative: *res = succPC; succPC = gpr[RS(instr)] break; case SFN_JR: succPC = gpr[RS(instr)]; *isBD = true; break; case SFN_MFHI: *res = gpr[HI]; break; case SFN_MFLO: *res = gpr[LO]; break; case SFN_MTHI: gpr[HI] = gpr[RS(instr)]; break; case SFN_MTLO: gpr[LO] = gpr[RS(instr)]; break; case SFN_MULT: SignMult(gpr[RS(instr)], gpr[RT(instr)], &(gpr[HI]), &(gpr[LO])); break; case SFN_MULTU: UnsMult((Word) gpr[RS(instr)], (Word) gpr[RT(instr)], (Word *)&(gpr[HI]), (Word *)&(gpr[LO])); break; case SFN_NOR: *res = ~(gpr[RS(instr)] | gpr[RT(instr)]); break; case SFN_OR: *res = gpr[RS(instr)] | gpr[RT(instr)]; break; case SFN_SLL: *res = gpr[RT(instr)] << SHAMT(instr); break; case SFN_SLLV: *res = gpr[RT(instr)] << REGSHAMT(gpr[RS(instr)]); break; case SFN_SLT: if (gpr[RS(instr)] < gpr[RT(instr)]) *res = 1UL; else *res = 0UL; break; case SFN_SLTU: if (((Word) gpr[RS(instr)]) < ((Word) gpr[RT(instr)])) *res = 1UL; else *res = 0UL; break; case SFN_SRA: *res = (gpr[RT(instr)] >> SHAMT(instr)); break; case SFN_SRAV: *res = (gpr[RT(instr)] >> REGSHAMT(gpr[RS(instr)])); break; case SFN_SRL: *res = (((Word) gpr[RT(instr)]) >> SHAMT(instr)); break; case SFN_SRLV: *res = (((Word) gpr[RT(instr)]) >> REGSHAMT(gpr[RS(instr)])); break; case SFN_SUB: if (SignSub(res, gpr[RS(instr)], gpr[RT(instr)])) { SignalExc(OVEXCEPTION); error = true; } break; case SFN_SUBU: *res = gpr[RS(instr)] - gpr[RT(instr)]; break; case SFN_SYSCALL: SignalExc(SYSEXCEPTION); error = true; break; case SFN_XOR: *res = gpr[RS(instr)] ^ gpr[RT(instr)]; break; case SFN_CAS: if (mapVirtual(gpr[RS(instr)], &paddr, WRITE) || bus->CompareAndSet(paddr, gpr[RT(instr)], gpr[RD(instr)], &atomic, this)) error = true; else *res = atomic; break; default: // unknown instruction SignalExc(RIEXCEPTION); error = true; } } else { // istruction is ill-formed SignalExc(RIEXCEPTION); } return error; } // This method executes a MIPS immediate-type instruction, following MIPS // guidelines; returns instruction result thru res pointer. It also returns // TRUE if an exception occurred, FALSE otherwise bool Processor::execImmInstr(Word * res, Word instr) { bool error = false; switch(OPCODE(instr)) { case ADDI: if (SignAdd(res, gpr[RS(instr)], SignExtImm(instr))) { SignalExc(OVEXCEPTION); error = true; } break; case ADDIU: *res = gpr[RS(instr)] + SignExtImm(instr); break; case ANDI: *res = gpr[RS(instr)] & ZEXTIMM(instr); break; case LUI: if (!RS(instr)) *res = (ZEXTIMM(instr) << HWORDLEN); else { // instruction is ill-formed SignalExc(RIEXCEPTION); error = true; } break; case ORI: *res = gpr[RS(instr)] | ZEXTIMM(instr); break; case SLTI: if (gpr[RS(instr)] < SignExtImm(instr)) *res = 1UL; else *res = 0UL; break; case SLTIU: if (((Word) gpr[RS(instr)]) < ((Word) SignExtImm(instr))) *res = 1UL; else *res = 0UL; break; case XORI: *res = gpr[RS(instr)] ^ ZEXTIMM(instr); break; default: SignalExc(RIEXCEPTION); error = true; break; } return(error); } // This method executes a MIPS branch-type instruction, following MIPS // guidelines; returns instruction result thru res pointer, and branch delay // slot indication if needed. It also returns TRUE if an exception occurred, // FALSE otherwise bool Processor::execBranchInstr(Word instr, bool* isBD) { bool error = false; switch (OPCODE(instr)) { case BEQ: if (gpr[RS(instr)] == gpr[RT(instr)]) succPC = nextPC + (SignExtImm(instr) << WORDSHIFT); break; case BGL: // uses RT field to choose which branch type is requested switch (RT(instr)) { case BGEZ: if (!SIGNBIT(gpr[RS(instr)])) succPC = nextPC + (SignExtImm(instr) << WORDSHIFT); break; case BGEZAL: // solution "by the book"; alternative: gpr[..] = succPC gpr[LINKREG] = currPC + (2 * WORDLEN); if (!SIGNBIT(gpr[RS(instr)])) succPC = nextPC + (SignExtImm(instr) << WORDSHIFT); break; case BLTZ: if (SIGNBIT(gpr[RS(instr)])) succPC = nextPC + (SignExtImm(instr) << WORDSHIFT); break; case BLTZAL: gpr[LINKREG] = currPC + (2 * WORDLEN); if (SIGNBIT(gpr[RS(instr)])) succPC = nextPC + (SignExtImm(instr) << WORDSHIFT); break; default: // unknown instruction SignalExc(RIEXCEPTION); error = true; break; } break; case BGTZ: if (!RT(instr)) { // instruction is well formed if (gpr[RS(instr)] > 0) succPC = nextPC + (SignExtImm(instr) << WORDSHIFT); } else { // istruction is ill-formed SignalExc(RIEXCEPTION); error = true; } break; case BLEZ: if (!RT(instr)) { // instruction is well formed if (gpr[RS(instr)] <= 0) succPC = nextPC + (SignExtImm(instr) << WORDSHIFT); } else { // istruction is ill-formed SignalExc(RIEXCEPTION); error = true; } break; case BNE: if (gpr[RS(instr)] != gpr[RT(instr)]) succPC = nextPC + (SignExtImm(instr) << WORDSHIFT); break; case J: succPC = JUMPTO(nextPC, instr); break; case JAL: // solution "by the book": alt. gpr[..] = succPC gpr[LINKREG] = currPC + (2 * WORDLEN); succPC = JUMPTO(nextPC, instr); break; default: SignalExc(RIEXCEPTION); error = true; break; } // Next instruction is BD slot, unless an exception occurred *isBD = !error; return error; } // This method executes a MIPS load-type instruction, following MIPS // guidelines. It returns TRUE if an exception occurred, FALSE // otherwise. bool Processor::execLoadInstr(Word instr) { Word paddr, vaddr, temp; bool error = false; switch (OPCODE(instr)) { case LB: vaddr = gpr[RS(instr)] + SignExtImm(instr); // reads the full word from bus and then extracts the byte if (!mapVirtual(ALIGN(vaddr), &paddr, READ) && !bus->DataRead(paddr, &temp, this)) setLoad(LOAD_TARGET_GPREG, RT(instr), signExtByte(temp, BYTEPOS(vaddr))); else // exception signaled: rt not loadable error = true; break; case LBU: vaddr = gpr[RS(instr)] + SignExtImm(instr); // reads the full word from bus and then extracts the byte if (!mapVirtual(ALIGN(vaddr), &paddr, READ) && !bus->DataRead(paddr, &temp, this)) setLoad(LOAD_TARGET_GPREG, RT(instr), (SWord) zExtByte(temp, BYTEPOS(vaddr))); else // exception signaled: rt not loadable error = true; break; case LH: vaddr = gpr[RS(instr)] + SignExtImm(instr); if (BitVal(vaddr, 0)) { // unaligned halfword SignalExc(ADELEXCEPTION); error = true; } else // reads the full word from bus and then extracts the halfword if (!mapVirtual(ALIGN(vaddr), &paddr, READ) && !bus->DataRead(paddr, &temp, this)) setLoad(LOAD_TARGET_GPREG, RT(instr), signExtHWord(temp, HWORDPOS(vaddr))); else // exception signaled: rt not loadable error = true; break; case LHU: vaddr = gpr[RS(instr)] + SignExtImm(instr); if (BitVal(vaddr, 0)) { // unaligned halfword SignalExc(ADELEXCEPTION); error = true; } else // reads the full word from bus and then extracts the halfword if (!mapVirtual(ALIGN(vaddr), &paddr, READ) && !bus->DataRead(paddr, &temp, this)) setLoad(LOAD_TARGET_GPREG, RT(instr), (SWord) zExtHWord(temp, HWORDPOS(vaddr))); else // exception signaled: rt not loadable error = true; break; case LW: vaddr = gpr[RS(instr)] + SignExtImm(instr); if (!mapVirtual(vaddr, &paddr, READ) && !bus->DataRead(paddr, &temp, this)) setLoad(LOAD_TARGET_GPREG, RT(instr), (SWord) temp); else // exception signaled: rt not loadable error = true; break; case LWL: vaddr = gpr[RS(instr)] + SignExtImm(instr); // reads the full word from bus and then extracts the desired part if (!mapVirtual(ALIGN(vaddr), &paddr, READ) && !bus->DataRead(paddr, &temp, this)) { temp = merge((Word) gpr[RT(instr)], temp, BYTEPOS(vaddr), BIGENDIANCPU, true); setLoad(LOAD_TARGET_GPREG, RT(instr), temp); } else // exception signaled: rt not loadable error = true; break; case LWR: vaddr = gpr[RS(instr)] + SignExtImm(instr); // reads the full word from bus and then extracts the desired part if (!mapVirtual(ALIGN(vaddr), &paddr, READ) && !bus->DataRead(paddr, &temp, this)) { temp = merge((Word) gpr[RT(instr)], temp, BYTEPOS(vaddr), BIGENDIANCPU, false); setLoad(LOAD_TARGET_GPREG, RT(instr), temp); } else // exception signaled: rt not loadable error = true; break; default: SignalExc(RIEXCEPTION); error = true; break; } return(error); } // This method executes a MIPS store-type instruction, following MIPS // guidelines; It returns TRUE if an exception occurred, FALSE // otherwise. bool Processor::execStoreInstr(Word instr) { Word paddr, vaddr, temp; bool error = false; switch(OPCODE(instr)) { case SB: // here things are a little dirty: instead of writing // the byte directly into memory, it reads the full word, // modifies the byte as needed, and writes the word back. // This works because there could be read-only memory but // not write-only... vaddr = gpr[RS(instr)] + SignExtImm(instr); if (!mapVirtual(ALIGN(vaddr), &paddr, WRITE) && !bus->DataRead(paddr, &temp, this)) { temp = mergeByte(temp, (Word) gpr[RT(instr)], BYTEPOS(vaddr)); if (bus->DataWrite(paddr, temp, this)) // bus exception signaled error = true; } else // address or bus exception signaled error = true; break; case SH: vaddr = gpr[RS(instr)] + SignExtImm(instr); if (BitVal(vaddr, 0)) { // unaligned halfword SignalExc(ADESEXCEPTION); error = true; } else // the same "dirty" thing here... if (!mapVirtual(ALIGN(vaddr), &paddr, WRITE) && !bus->DataRead(paddr, &temp, this)) { temp = mergeHWord(temp, (Word) gpr[RT(instr)], HWORDPOS(vaddr)); if (bus->DataWrite(paddr, temp, this)) // bus exception signaled error = true; } else // address or bus exception signaled error = true; break; case SW: vaddr = gpr[RS(instr)] + SignExtImm(instr); if (mapVirtual(vaddr, &paddr, WRITE) || bus->DataWrite(paddr, (Word) gpr[RT(instr)], this)) // address or bus exception signaled error = true; break; case SWL: vaddr = gpr[RS(instr)] + SignExtImm(instr); if (!mapVirtual(ALIGN(vaddr), &paddr, WRITE) && !bus->DataRead(paddr, &temp, this)) { temp = merge(temp, (Word) gpr[RT(instr)], BYTEPOS(vaddr), !(BIGENDIANCPU), false); if(bus->DataWrite(paddr, temp, this)) // bus exception error = true; } else // address or bus exception signaled error = true; break; case SWR: vaddr = gpr[RS(instr)] + SignExtImm(instr); if (!mapVirtual(ALIGN(vaddr), &paddr, WRITE) && !bus->DataRead(paddr, &temp, this)) { temp = merge(temp, (Word) gpr[RT(instr)], BYTEPOS(vaddr), !(BIGENDIANCPU), true); if (bus->DataWrite(paddr, temp, this)) // bus exception signaled error = true; } else // addresss or bus exception signaled error = true; break; default: SignalExc(RIEXCEPTION); error = true; break; } return(error); } umps3-3.0.5/src/umps/processor.h000066400000000000000000000156111435132321600165350ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2004 Mauro Morsiani * Copyright (C) 2020 Mattia Biondi * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef UMPS_PROCESSOR_H #define UMPS_PROCESSOR_H #include #include "base/lang.h" #include "umps/types.h" #include "umps/const.h" class MachineConfig; class Machine; class SystemBus; class TLBEntry; enum ProcessorStatus { PS_HALTED, PS_RUNNING, PS_IDLE }; class Processor { public: // Register file size: static const unsigned int kNumCPURegisters = 34; static const unsigned int kNumCP0Registers = 9; Processor(const MachineConfig* config, Word id, Machine* machine, SystemBus* bus); virtual ~Processor(); Word getId() const { return id; } Word Id() const { return id; } ProcessorStatus getStatus() const { return status; } bool isHalted() const { return status == PS_HALTED; } bool isRunning() const { return status == PS_RUNNING; } bool isIdle() const { return status == PS_IDLE; } void Reset(Word pc, Word sp); void Halt(); // This method makes Processor execute a single instruction. For // simulation purposes, it differs from traditional processor cycle: // the first instruction after a reset is pre-loaded, and cycle is // execute - fetch instead of fetch - execute. This way, it is // possible to know what instruction will be executed before its // execution happens void Cycle(); uint32_t IdleCycles() const; void Skip(uint32_t cycles); // This method allows SystemBus and Processor itself to signal // Processor when an exception happens. SystemBus signal IBE/DBE // exceptions; Processor itself signal all other kinds of exception. void SignalExc(unsigned int exc, Word cpuNum = 0UL); void AssertIRQ(unsigned int il); void DeassertIRQ(unsigned int il); // The following methods allow inspection of Processor internal // status. Name & parameters are self-explanatory: remember that // all addresses are _virtual_ when not marked Phys/P/phys (for // physical ones), and all index checking must be performed from // caller. void getCurrStatus(Word * asid, Word * pc, Word * instr, bool * isLD, bool * isBD); Word getASID() const; Word getPC() const { return currPC; } Word getInstruction() const { return currInstr; } bool InUserMode() const; bool InKernelMode() const; void getPrevStatus(Word * pc, Word * instr); const char* getExcCauseStr(); Word getNextPC(void); Word getSuccPC(void); Word getPrevPPC(void); Word getCurrPPC(void); SWord getGPR(unsigned int num); Word getCP0Reg(unsigned int num); void getTLB(unsigned int index, Word * hi, Word * lo) const; Word getTLBHi(unsigned int index) const; Word getTLBLo(unsigned int index) const; // The following methods allow to change Processor internal status // Name & parameters are almost self-explanatory: remember that // all addresses are _virtual_ when not marked Phys/P/phys (for // physical ones), and all index checking must be performed from // caller. Processor status modification is allowed during // debugging inside the simulation. void setGPR(unsigned int num, SWord val); void setCP0Reg(unsigned int num, Word val); void setNextPC(Word npc); void setSuccPC(Word spc); void setTLB(unsigned int index, Word hi, Word lo); void setTLBHi(unsigned int index, Word value); void setTLBLo(unsigned int index, Word value); // Signals sigc::signal StatusChanged; sigc::signal SignalException; sigc::signal SignalTLBChanged; private: enum MultiplierPorts { HI = 32, LO = 33 }; // Type of destination register for a pending load enum LoadTargetType { LOAD_TARGET_GPREG, LOAD_TARGET_CPREG, LOAD_TARGET_NONE }; const Word id; // object references for memory access (bus) and for virtual address // accessing (watch) Machine* machine; SystemBus* bus; ProcessorStatus status; // last exception cause: an internal format is used (see excName[] // for mnemonic code) and it is mapped to CAUSE register format by // excCode[] array unsigned int excCause; // for CPUEXCEPTION, coprocessor unusable number Word copENum; // tracks branch delay slots bool isBranchD; // delayed load handling variables: // indicates if a delayed load is pending LoadTargetType loadPending; // register target unsigned int loadReg; // value to be loaded into register SWord loadVal; // general purpose registers, together with HI and LO registers SWord gpr[kNumCPURegisters]; // instruction to be executed Word currInstr; // previous virtual and physical addresses for PC, and previous // instruction executed; for book-keeping purposes and for handling // exceptions in BD slot Word prevPC; Word prevPhysPC; Word prevInstr; // current virtual and physical addresses for PC Word currPC; Word currPhysPC; // virtual values for PC after current one: they are needed to // emulate branch delay slot; no physical values are available since // conversion is needed (and sometimes possible) only for PC current // value Word nextPC; Word succPC; // CP0 components: special registers and the TLB Word cpreg[CP0REGNUM]; size_t tlbSize; scoped_array tlb; Word tlbFloorAddress; // private methods void setStatus(ProcessorStatus newStatus); void handleExc(); void zapTLB(void); bool execInstr(Word instr); bool execRegInstr(Word * res, Word instr, bool * isBD); bool execImmInstr(Word * res, Word instr); bool execBranchInstr(Word instr, bool * isBD); bool execLoadInstr(Word instr); bool execStoreInstr(Word instr); bool execLoadCopInstr(Word instr); bool execStoreCopInstr(Word instr); bool mapVirtual(Word vaddr, Word * paddr, Word accType); bool probeTLB(unsigned int * index, Word asid, Word vpn); void completeLoad(void); void randomRegTick(void); void pushKUIEStack(void); void popKUIEStack(void); void setTLBRegs(Word vaddr); bool checkForInt(); void suspend(); bool cp0Usable(void); void setLoad(LoadTargetType loadCode, unsigned int regNum, SWord regVal); SWord signExtByte(Word val, unsigned int bytep); Word zExtByte(Word val, unsigned int bytep); SWord signExtHWord(Word val, unsigned int hwp); Word zExtHWord(Word val, unsigned int hwp); Word mergeByte(Word dest, Word src, unsigned int bytep); Word mergeHWord(Word dest, Word src, unsigned int hwp); Word merge(Word dest, Word src, unsigned int bytep, bool loadBig, bool startLeft); DISABLE_COPY_AND_ASSIGNMENT(Processor); }; #endif // UMPS_PROCESSOR_H umps3-3.0.5/src/umps/processor_defs.h000066400000000000000000000162631435132321600175420ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2004 Mauro Morsiani * Copyright (C) 2020 Mattia Biondi * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /**************************************************************************** * * This header file contains constant & macro definitions for processor.cc * and disassemble.cc program modules. * * All names, bit positions and masks follow MIPS register formats: see * documentation. * * Instruction opcodes too may be retrieved from appropriate documentation: * codes are in octal format for easier coding by programmer. * ****************************************************************************/ // virtual exception handling base address #define BOOTEXCBASE (BOOTBASE + 0x100UL) // exception virtual offsets #define TLBREFOFFS 0x000UL #define OTHEREXCOFFS 0x080UL // CPUEXCEPTION handling constant #define COPEOFFSET 27 // used to decode TLBCLR instruction #define CONTEXTREG 4 // and corresponding register index in Processor structure enum { INDEX, RANDOM, ENTRYLO, BADVADDR, CP0REG_TIMER, ENTRYHI, STATUS, CAUSE, EPC, PRID }; // STATUS and CAUSE interrupt area mask #define INTMASK 0x0000FF00UL // TLB EntryHI handling masks and constants #define VPNMASK 0xFFFFF000UL #define ASIDMASK 0x00000FC0UL #define ASIDOFFS 6 #define OFFSETMASK (~(VPNMASK)) // TLB EntryLO bit positions and mask #define GBITPOS 8 #define VBITPOS 9 #define DBITPOS 10 #define ENTRYLOMASK 0xFFFFFF00UL // RANDOM and INDEX register handling constants #define RNDIDXOFFS 8 #define RANDOMBASE (1UL << RNDIDXOFFS) #define RANDOMSTEP (1UL << RNDIDXOFFS) // STATUS register handling constants: // at reset, are set to 1: cop0 (bit 28), BEV (bit 22) // all other bits are set to 0 (interrupts disabled, etc.) #define STATUSRESET 0x10400000UL // this mask forbids changes to some bits (CU3->1 usable bits, all DS field // except BEV bit, 0 fixed parts) #define STATUSMASK 0x1F40FF3FUL // STATUS kernel/user, interrupt enable #define KUOBITPOS 5 #define IEOBITPOS 4 #define KUCBITPOS 1 #define IECBITPOS 0 // instruction decode field masks and constants #define OPTYPEMASK 0xE0000000UL #define OPCODEMASK 0xFC000000UL #define FUNCTMASK 0x0000003FUL #define RSMASK 0x03E00000UL #define RTMASK 0x001F0000UL #define RDMASK 0x0000F800UL #define SHAMTMASK 0x000007C0UL #define PCUPMASK 0xF0000000UL #define COPTYPEMASK 0x03E00000UL #define COPCODEMASK 0x03FF0000UL #define COPNUMMASK 0x0C000000UL #define CALLMASK 0x03FFFFC0UL #define IMMSIGNPOS 15 #define DWCOPBITPOS 28 #define LINKREG 31 #define OPCODEOFFS 26 #define RSOFFSET 21 #define RTOFFSET 16 #define RDOFFSET 11 #define SHAMTOFFS 6 #define COPTYPEOFFS 21 #define COPCODEOFFS 16 // instruction main opcodes #define REGTYPE 077 #define IMMTYPE 010 #define COPTYPE 020 #define BRANCHTYPE 000 #define LOADTYPE 040 #define LOADCOPTYPE 060 #define STORECOPTYPE 070 #define STORETYPE 050 // REGTYPE (Special) function opcodes #define SFN_ADD 040 #define SFN_ADDU 041 #define SFN_AND 044 #define SFN_BREAK 015 #define SFN_DIV 032 #define SFN_DIVU 033 #define SFN_JALR 011 #define SFN_JR 010 #define SFN_MFHI 020 #define SFN_MFLO 022 #define SFN_MTHI 021 #define SFN_MTLO 023 #define SFN_MULT 030 #define SFN_MULTU 031 #define SFN_NOR 047 #define SFN_OR 045 #define SFN_SLL 000 #define SFN_SLLV 004 #define SFN_SLT 052 #define SFN_SLTU 053 #define SFN_SRA 003 #define SFN_SRAV 007 #define SFN_SRL 002 #define SFN_SRLV 006 #define SFN_SUB 042 #define SFN_SUBU 043 #define SFN_SYSCALL 014 #define SFN_XOR 046 #define SFN_CAS 013 // IMMCOMPTYPE opcodes #define ADDI 010 #define ADDIU 011 #define ANDI 014 #define LUI 017 #define ORI 015 #define SLTI 012 #define SLTIU 013 #define XORI 016 // BRANCHTYPE opcodes #define BEQ 004 #define BGL 001 #define BGEZ 001 #define BGEZAL 021 #define BLTZ 000 #define BLTZAL 020 #define BGTZ 007 #define BLEZ 006 #define BNE 005 #define J 002 #define JAL 003 // COPTYPE opcodes // detects if instruction refers to CP0 #define COP0SEL 020 #define CO0 020 #define RFE 020 #define TLBP 010 #define TLBR 001 #define TLBWI 002 #define TLBWR 006 #define COFUN_WAIT 040 #define BC0 010 #define BC0F 0400 #define BC0T 0401 #define MFC0 0 #define MTC0 04 // LOADTYPE opcodes #define LB 040 #define LBU 044 #define LH 041 #define LHU 045 #define LW 043 #define LWL 042 #define LWR 046 //STORETYPE opcodes #define SB 050 #define SH 051 #define SW 053 #define SWL 052 #define SWR 056 // LOADCOPTYPE opcodes #define LWC0 060 // STORECOPTYPE opcodes #define SWC0 070 // byte sign extension mask #define BYTESIGNMASK 0x0000007FUL // useful macros // extracts VPN from address #define VPN(w) ((w & VPNMASK)) // extracts ASID from address #define ASID(w) ((w & ASIDMASK)) // applies interrupt mask to STATUS/CAUSE register #define IM(r) ((r & INTMASK)) // aligns a virtual address (clears lowest bits) #define ALIGN(va) (va & ~(ALIGNMASK)) // computes physical address from virtual address and PFN field #define PHADDR(va, pa) ((va & OFFSETMASK) | (pa & VPNMASK)) // detects which byte is referred by a LB/LBU/LWL/LWR/SB/SWL/SWR instruction #define BYTEPOS(va) (va & ALIGNMASK) // detects which halfword is referred by a LH/LHU/SH instruction #define HWORDPOS(va) ((va & ALIGNMASK) >> 1) // instruction fields decoding macros #define OPCODE(i) ((i & OPCODEMASK) >> OPCODEOFFS) #define FUNCT(i) (i & FUNCTMASK) #define SHAMT(i) ((i & SHAMTMASK) >> SHAMTOFFS) #define REGSHAMT(r) (r & (SHAMTMASK >> SHAMTOFFS)) #define RS(i) ((i & RSMASK) >> RSOFFSET) #define RT(i) ((i & RTMASK) >> RTOFFSET) #define RD(i) ((i & RDMASK) >> RDOFFSET) // coprocessor instructions decoding macros #define COPOPCODE(i) ((i & COPCODEMASK) >> COPCODEOFFS) #define COPOPTYPE(i) ((i & COPTYPEMASK) >> COPTYPEOFFS) #define COPNUM(i) ((i & COPNUMMASK) >> OPCODEOFFS) // computes jump target from PC and J/JAL instruction itself #define JUMPTO(pc, i) ((pc & PCUPMASK) | ((i & ~(OPCODEMASK)) << WORDSHIFT)) // zero-extends a immediate (halfword) quantity #define ZEXTIMM(i) (i & IMMMASK) // computes index value from INDEX register format: // SIGNMASK is used to clear bit 31, which marks a failed TLB probe #define RNDIDX(id) ((id & ~(SIGNMASK)) >> RNDIDXOFFS) // decodes BREAK/SYSCALL argument inside instruction itself #define CALLVAL(i) ((i & CALLMASK) >> SHAMTOFFS) umps3-3.0.5/src/umps/stoppoint.cc000066400000000000000000000076531435132321600167220ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "umps/stoppoint.h" #include #include std::string Stoppoint::ToString() const { static const char* fmtStr = ""; return boost::str(boost::format(fmtStr) %id %enabled %accessMode %range.getASID() %range.getStart() %range.getEnd()); } StoppointSet::~StoppointSet() { } Stoppoint* StoppointSet::Find(Word asid, Word addr) { AddressRange r(asid, addr, addr); StoppointMap::iterator it = addressMap.find(r); return (it != addressMap.end()) ? it->second : NULL; } bool StoppointSet::CanInsert(const AddressRange& range) const { for (Stoppoint::Ptr p : points) if (p->getRange().Overlaps(range)) return false; return true; } bool StoppointSet::Add(const AddressRange& range, AccessMode mode) { return Add(range, mode, nextId()); } bool StoppointSet::Add(const AddressRange& range, AccessMode mode, unsigned int id, bool enabled) { // Check for collisions if (!CanInsert(range)) return false; // No overlap: safe to add Stoppoint* p = new Stoppoint(std::max(id, nextId()), range, mode); p->SetEnabled(enabled); points.push_back(Stoppoint::Ptr(p)); addressMap[p->getRange()] = p; SignalStoppointInserted(); return true; } void StoppointSet::Remove(size_t index) { assert(index < Size()); Stoppoint::Ptr p = points[index]; StoppointMap::iterator it = addressMap.find(p->getRange()); assert(it != addressMap.end()); addressMap.erase(it); points.erase(points.begin() + index); SignalStoppointRemoved(index); } void StoppointSet::Clear() { addressMap.clear(); points.clear(); } void StoppointSet::SetEnabled(size_t index, bool setting) { assert(index <= Size()); if (points[index]->IsEnabled() != setting) { points[index]->SetEnabled(setting); SignalEnabledChanged(index); } } Stoppoint* StoppointSet::Probe(Word asid, Word addr, AccessMode mode, const Processor* cpu) const { if (IsEmpty()) return NULL; AddressRange range(asid, addr, addr); StoppointMap::const_iterator it = addressMap.lower_bound(range); if (it == addressMap.end() || (range < it->first && it != addressMap.begin())) { --it; } Stoppoint* p = it->second; if (p->Matches(asid, addr, mode)) { size_t index = 0; for (index = 0; index < points.size(); ++index) if (points[index].get() == p) break; assert(index < points.size()); SignalHit.emit(index, p, addr, cpu); return p; } else { return NULL; } } std::string StoppointSet::ToString(bool sorted) const { std::string result = "["; bool first = true; if (sorted) { StoppointMap::const_iterator it; for (it = addressMap.begin(); it != addressMap.end(); ++it) { if (!first) result.append(",\n "); first = false; result.append(it->second->ToString()); } } else { for (const Stoppoint::Ptr& p : points) { if (!first) result.append(",\n "); first = false; result.append(p->ToString()); } } return result.append("]"); } unsigned int StoppointSet::nextId() const { unsigned int id = 0; for (Stoppoint::Ptr p : points) id = std::max(id, p->getId() + 1); return id; } umps3-3.0.5/src/umps/stoppoint.h000066400000000000000000000115751435132321600165620ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2010 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef UMPS_STOPPOINT_H #define UMPS_STOPPOINT_H #include #include #include #include #include #include "umps/types.h" #include "base/lang.h" class Processor; enum AccessMode { AM_EXEC = 1 << 0, AM_WRITE = 1 << 1, AM_READ = 1 << 2, AM_READ_WRITE = AM_WRITE | AM_READ }; class AddressRange { public: AddressRange(Word asid, Word start, Word end) : asid(asid), start(start), end(end) { assert(start <= end); } Word getASID() const { return asid; } Word getStart() const { return start; } Word getEnd() const { return end; } bool LessThan(const AddressRange& other) const { return (asid < other.asid) || (asid == other.asid && start < other.start); } bool operator<(const AddressRange& other) const { return LessThan(other); } bool Overlaps(const AddressRange& r) const { return !(asid != r.asid || r.end < start || r.start > end); } bool Contains(Word asid, Word addr) const { return asid == this->asid && start <= addr && addr <= end; } private: Word asid, start, end; }; class Stoppoint: public enable_shared_from_this { public: typedef shared_ptr Ptr; typedef shared_ptr ConstPtr; Stoppoint(unsigned int id, const AddressRange& range, AccessMode mode) : id(id), enabled(true), range(range), accessMode(mode) { } unsigned int getId() const { return id; } void SetEnabled(bool setting) { enabled = setting; } bool IsEnabled() const { return enabled; } const AddressRange& getRange() const { return range; } void setAccessMode(AccessMode mode) { accessMode = mode; } AccessMode getAccessMode() const { return accessMode; } bool Matches(Word asid, Word addr, AccessMode mode) const { return (enabled && range.Contains(asid, addr) && (accessMode & mode)); } std::string ToString() const; private: unsigned int id; bool enabled; AddressRange range; AccessMode accessMode; }; class StoppointSet { public: virtual ~StoppointSet(); size_t Size() const { return points.size(); } bool IsEmpty() const { return points.empty(); } Stoppoint* Get(size_t index); const Stoppoint* Get(size_t index) const; Stoppoint* Find(Word asid, Word addr); bool CanInsert(const AddressRange& range) const; bool Add(const AddressRange& range, AccessMode mode); bool Add(const AddressRange& range, AccessMode mode, unsigned int id, bool enabled = true); void Remove(size_t index); void Clear(); void SetEnabled(size_t index, bool setting); Stoppoint* Probe(Word asid, Word addr, AccessMode mode, const Processor* cpu) const; template void GetStoppointsInRange(Word asid, Word start, Word end, OutputIterator out); std::string ToString(bool sorted = false) const; sigc::signal SignalStoppointInserted; sigc::signal SignalStoppointRemoved; sigc::signal SignalEnabledChanged; sigc::signal SignalHit; private: unsigned int nextId() const; typedef std::vector StoppointVector; StoppointVector points; typedef std::map StoppointMap; StoppointMap addressMap; public: typedef StoppointVector::const_iterator const_iterator; typedef const_iterator iterator; const_iterator begin() const { return points.begin(); } const_iterator end() const { return points.end(); } }; inline Stoppoint* StoppointSet::Get(size_t index) { assert(index < Size()); return points[index].get(); } inline const Stoppoint* StoppointSet::Get(size_t index) const { assert(index < Size()); return points[index].get(); } template void StoppointSet::GetStoppointsInRange(Word asid, Word start, Word end, OutputIterator out) { AddressRange r1(asid, start, start); AddressRange r2(asid, end, end); StoppointMap::const_iterator it = addressMap.lower_bound(r1); for (it = addressMap.lower_bound(r1); it != addressMap.end() && (it->first < r2 || !(r2 < it->first)); ++it) { *out++ = it->second; } } #endif // UMPS_STOPPOINT_H umps3-3.0.5/src/umps/symbol_table.cc000066400000000000000000000173361435132321600173360ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2004 Mauro Morsiani * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "umps/symbol_table.h" #include #include #include #include #include #include #include #include #include "umps/const.h" #include "umps/utility.h" #include "umps/blockdev_params.h" #include "umps/disassemble.h" #include "umps/error.h" static const unsigned int STRBUFSIZE = 64; #define NOTFOUND -1 using namespace boost::placeholders; // This method builds a Symbol object, given its description as produced by // elf2umps utility; no format check, since it's fixed Symbol::Symbol(const char* sName, const char* sInfo) { sNamep = new char [strlen(sName) + 1]; strcpy(sNamep, sName); if (sInfo[0] == 'F') sType = TYPE_FUNCTION; else sType = TYPE_OBJECT; // get start and size const char * nump; nump = strchr(sInfo, ':') + 1; StrToWord(nump, &sStart); nump = strchr(nump, ':') + 1; StrToWord(nump, &sSize); // backpatch for gcc 3.3.3 "size == 0" bug // and for very small objects if (sSize < WORDLEN) sSize = WORDLEN; } // This method deletes a Symbol object and contained items Symbol::~Symbol() { delete[] sNamep; } // This method defines a lexicographic order on Symbols, based on start // address; returns TRUE if "this" object is less than Symb, and FALSE // otherwise bool Symbol::LessThan(const Symbol* other) { return sStart < other->sStart; } // Given pos virtual address, this method returns -1 if pos is // out lower bound, 0 if in, 1 if out upper bound int Symbol::Contains(Word pos) const { if (sStart > pos) return -1; else if (pos >= (sStart + sSize)) return 1; else return 0; } SWord Symbol::Offset(Word pos) const { return (SWord) (pos - sStart); } void Symbol::setSize(Word size) { sSize = size; } // This method builds a symbol table from .stab file fName produced by // elf2umps utility SymbolTable::SymbolTable(Word asid, const char* fName) : asid(asid) { assert(fName != NULL); FILE* file; unsigned int numF, numO, i; char sName[STRBUFSIZE]; char sType[STRBUFSIZE]; fTable = NULL; ftSize = 0; oTable = NULL; otSize = 0; if ((file = fopen(fName, "r")) == NULL) { throw FileError(fName); } else { Word tag; if (fread((void *) &tag, WS, 1, file) != 1) { fclose(file); throw FileError(fName); } if (tag != STABFILEID || fscanf(file, "%X %X ", &numF, &numO) != 2) { fclose(file); throw InvalidFileFormatError(fName, "Invalid symbol table file format"); } } // inits symbol table structures ftSize = numF; otSize = numO; if (ftSize > 0) { fTable = new Symbol* [ftSize]; for (i = 0; i < ftSize; i++) fTable[i] = NULL; } if (otSize > 0) { oTable = new Symbol* [otSize]; for (i = 0; i < otSize; i++) oTable[i] = NULL; } // scans symbol table file and builds objects bool error = false; for (i = 0, numF = 0, numO = 0; i < (ftSize + otSize) && !error; i++) { if (fscanf(file, "%s :%s ", sName, sType) != 2) { error = true; } else if (sType[0] == 'F') { fTable[numF] = new Symbol(sName, sType); numF++; } else { // sType[0] is 'O' oTable[numO] = new Symbol(sName, sType); numO++; } } if (error) { // Clean up first if (ftSize > 0) { for (i = 0; i < ftSize; i++) if (fTable[i] != NULL) delete fTable[i]; delete fTable; } if (otSize > 0) { for (i = 0; i < otSize; i++) if (oTable[i] != NULL) delete oTable[i]; delete oTable; } throw InvalidFileFormatError(fName, "Invalid symbol table file format"); } // sort tables if (ftSize > 1) { sortTable(fTable, ftSize); // backpatch FUN part of symbol table for gcc bug for (i = 0; i < ftSize - 1; i++) if (fTable[i]->getEnd() == fTable[i]->getStart()) // backpatch needed fTable[i]->setSize(fTable[i+1]->getStart() - fTable[i]->getStart()); } if (otSize > 1) sortTable(oTable, otSize); for (unsigned int i = 0; i < Size(); i++) { const Symbol* s = Get(i); map[s->getName()].push_back(s); } fclose(file); } SymbolTable::~SymbolTable() { unsigned int i; if (fTable != NULL) { for (i = 0; i < ftSize; i++) delete fTable[i]; delete[] fTable; } if (oTable != NULL) { for (i = 0; i < otSize; i++) delete oTable[i]; delete[] oTable; } } // This method probes the table, given a complete address (asid + // pos): it probes both tables if fullSearch is TRUE, and only // function table otherwise. It returns symbol name and offset of pos // inside it, or NULL if no match is found const char* SymbolTable::Probe(Word asid, Word pos, bool fullSearch, SWord* offsetp) const { if (asid != this->asid) return NULL; int idx; if ((idx = search(fTable, ftSize, pos)) != NOTFOUND) { *offsetp = fTable[idx]->Offset(pos); return fTable[idx]->getName(); } else if (fullSearch && (idx = search(oTable, otSize, pos)) != NOTFOUND) { *offsetp = oTable[idx]->Offset(pos); return oTable[idx]->getName(); } else return NULL; } const Symbol* SymbolTable::Probe(Word asid, Word addr, bool fullSearch) const { if (asid != this->asid) return NULL; int i; if ((i = search(fTable, ftSize, addr)) != NOTFOUND) return fTable[i]; else if (fullSearch && (i = search(oTable, otSize, addr)) != NOTFOUND) return oTable[i]; else return NULL; } // This method returns the total number of symbols unsigned int SymbolTable::Size() const { return otSize + ftSize; } const Symbol* SymbolTable::Get(unsigned int index) const { if (index < ftSize) return fTable[index]; else return oTable[index - ftSize]; } std::list SymbolTable::Lookup(const char* name) const { SymbolMap::const_iterator it = map.find(name); if (it != map.end()) return it->second; else return std::list(); } std::list SymbolTable::Lookup(const char* name, Symbol::Type type) const { std::list symbols = Lookup(name); std::list subset; std::remove_copy_if(symbols.begin(), symbols.end(), std::back_inserter(subset), boost::bind(std::not_equal_to(), boost::bind(&Symbol::getType, _1), type)); return subset; } // This method scans the specified table looking for a Symbol range // containing it; returns NOTFOUND if not found, 0..size -1 if found (index // into table) // Almost classical binary search int SymbolTable::search(Symbol** table, unsigned int size, Word pos) const { if (table == NULL) return NOTFOUND; int a = 0; int b; int c = size - 1; int res; bool found = false; do { b = (a + c) / 2; if ((res = (table[b])->Contains(pos)) == 0) found = true; else // res is > 0 if pos is (possibly) in an area with // start address greater than current, < 0 otherwise if (res > 0) a = b + 1; else c = b - 1; } while (!found && a <= c); return found ? b : NOTFOUND; } void SymbolTable::sortTable(Symbol** table, size_t size) { struct Compare { static bool InOrder(Symbol* s1, Symbol* s2) { return s1->LessThan(s2); } }; std::sort(table, table + size, Compare::InOrder); } umps3-3.0.5/src/umps/symbol_table.h000066400000000000000000000102431435132321600171660ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2004 Mauro Morsiani * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef UMPS_SYMBOL_TABLE_H #define UMPS_SYMBOL_TABLE_H #include #include #include #include "base/basic_types.h" #include "umps/arch.h" #include "umps/types.h" // A symbol table entry. It represents a "named" address range, // associated to the ASID of the containing symbol table. class Symbol { public: enum Type { TYPE_FUNCTION, TYPE_OBJECT }; // This method builds a Symbol object, given its description as produced by // elf2umps utility; no format check, since it's fixed Symbol(const char* sName, const char* sInfo); ~Symbol(); // This method defines a lexicographic order on Symbols, based on // start address; returns TRUE if "this" object is less than Symb, // and FALSE otherwise bool LessThan(const Symbol* other); // Given pos virtual address, this method returns -1 if pos is // out lower bound, 0 if in, 1 if out upper bound int Contains(Word pos) const; const char* getName() const { return sNamep; } Word getStart() const { return sStart; } Word getEnd() const { return sStart + sSize - WS; } Type getType() const { return sType; } SWord Offset(Word pos) const; // this method allows to set symbol size (backpatch for gcc bug) // [FIXME: What? Which GCC bug?] void setSize(Word size); private: // Symbol name string char* sNamep; // Symbol starting address Word sStart; // Symbol size Word sSize; Type sType; }; // SymbolTable class defines an object containing a complete symbol table. It // is loaded from file, and is associated to a particular ASID, which comes // from the configuration. // For performance reasons, symbol table is split into two parts: one for // functions and one for memory objects, since functions table wiil be // accessed more often. Both SymbolTable object tables are lexicographically // ordered for fast probing, function table is accessed for any instruction // reference; both are inspected for mapping breakpoint/suspect/trace ranges // to mnemonic names. class SymbolTable { public: // This method builds a symbol table from .stab file fName produced by // elf2umps utility SymbolTable(Word asid, const char * fName); ~SymbolTable(); Word getASID() const { return asid; } unsigned int Size() const; // This method probes the table, given a complete address (asid + // pos): it probes both tables if fullSearch is TRUE, and only // function table otherwise. It returns symbol name and offset // inside it, or NULL if no match is found const char* Probe(Word asid, Word pos, bool fullSearch, SWord* offsetp) const; const Symbol* Probe(Word asid, Word addr, bool fullSearch) const; const Symbol* Get(unsigned int index) const; std::list Lookup(const char* name) const; std::list Lookup(const char* name, Symbol::Type type) const; private: static void sortTable(Symbol** table, size_t size); // This method scans the specified table looking for a Symbol range // containing it; returns NOTFOUND if not found, 0..size -1 if found // (index into table) int search(Symbol** table, unsigned int size, Word pos) const; // Symbol table ASID const Word asid; // Number of function/object symbols unsigned int ftSize; unsigned int otSize; // Symbol tables: one for functions, other for memory object symbols Symbol** fTable; Symbol** oTable; typedef std::map< std::string, std::list> SymbolMap; SymbolMap map; }; #endif // UMPS_SYMBOL_TABLE_H umps3-3.0.5/src/umps/systembus.cc000066400000000000000000000406461435132321600167200ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2004 Mauro Morsiani * Copyright (C) 2020 Mattia Biondi * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /**************************************************************************** * * This module implements the SystemBus object class. * * A SystemBus connects a Processor object to memory and devices. * It satisfies the Processor requests to read and write memory positions, * and simulates interrupts generation and other external exceptions. * SystemBus defines the physical memory map of the system, mapping RamSpace * and BiosSpace objects and Device registers to "physical" addresses in * memory. * It also implements the system clock and timer, and keeps track of some * important system constants. All this information is mapped in the "bus * device register area", at the start of the device register area. * * On creation, it initializes the device and memory subsystems * as set in the simulator configuration (contained in the SetupInfo * object); on destruction, it removes them. * * SystemBus notifies all changes to memory location to Watch controlling * object, to track memory accesses for simulation brkpt/susp/trace handling * ****************************************************************************/ #include "umps/systembus.h" #include #include "umps/const.h" #include "umps/blockdev_params.h" #include "umps/utility.h" #include "umps/device.h" #include "umps/blockdev.h" #include "umps/processor.h" #include "umps/types.h" #include "umps/arch.h" #include "umps/machine_config.h" #include "umps/machine.h" #include "umps/mp_controller.h" #include "umps/time_stamp.h" #include "umps/error.h" #include "umps/memspace.h" #include "umps/event.h" #include "umps/mpic.h" // This macro converts a byte address into a word address (minus offset) #define CONVERT(ad, bs) ((ad - bs) >> WORDSHIFT) class DeviceAreaAddress { public: DeviceAreaAddress(Word paddr) : pa(paddr) { assert(MMIO_BASE <= paddr && paddr < MMIO_END); } DeviceAreaAddress(unsigned int line, unsigned int device, unsigned int field) : pa(DEV_REG_ADDR(line + DEV_IL_START, device) + field * WS) { assert(line < N_EXT_IL && device < N_DEV_PER_IL && field < DEV_REG_SIZE_W); } Word address() const { return pa; } unsigned int regIndex() const { return wordIndex() / DEV_REG_SIZE_W; } unsigned int line() const { return regIndex() / N_DEV_PER_IL; } unsigned int device() const { return regIndex() % N_DEV_PER_IL; } unsigned int field() const { return wordIndex() % DEV_REG_SIZE_W; } private: unsigned int wordIndex() const { return (pa - DEV_REG_START) >> 2; } Word pa; }; SystemBus::SystemBus(const MachineConfig* conf, Machine* machine) : config(conf), machine(machine), pic(new InterruptController(conf, this)), mpController(new MPController(conf, machine)) { tod = UINT64_C(0); timer = MAXWORDVAL; eventQ = new EventQueue(); const char *coreFile = NULL; if (config->isLoadCoreEnabled()) coreFile = config->getROM(ROM_TYPE_CORE).c_str(); ram = new RamSpace(config->getRamSize() * FRAMESIZE, coreFile); biosdata = new RamSpace(BIOSDATASIZE, NULL); bios = new BiosSpace(config->getROM(ROM_TYPE_BIOS).c_str()); boot = new BiosSpace(config->getROM(ROM_TYPE_BOOT).c_str()); // Create devices and initialize registers used for interrupt // handling. intPendMask = 0UL; for (unsigned intl = 0; intl < N_EXT_IL; intl++) { instDevTable[intl] = 0UL; for (unsigned int devNo = 0; devNo < N_DEV_PER_IL; devNo++) { devTable[intl][devNo] = makeDev(intl, devNo); if (devTable[intl][devNo]->Type() != NULLDEV) instDevTable[intl] = SetBit(instDevTable[intl], devNo); } } } // This method deletes a SystemBus object and all related structures SystemBus::~SystemBus() { delete eventQ; delete ram; delete biosdata; delete bios; delete boot; for (unsigned int intl = 0; intl < DEVINTUSED; intl++) for (unsigned int dnum = 0; dnum < DEVPERINT; dnum++) delete devTable[intl][dnum]; } // This method increments system clock and decrements interval timer; // on timer underflow (0 -> FFFFFFFF transition) a interrupt is // generated. Event queue is checked against the current clock value // and device operations are completed if needed; all memory changes // are notified to Watch control object void SystemBus::ClockTick() { tod++; // both registers signal "change" because they are conceptually one machine->HandleBusAccess(BUS_REG_TOD_HI, WRITE, NULL); machine->HandleBusAccess(BUS_REG_TOD_LO, WRITE, NULL); // Update interval timer if (UnsSub(&timer, timer, 1)) pic->StartIRQ(IL_TIMER); machine->HandleBusAccess(BUS_REG_TIMER, WRITE, NULL); // Scan the event queue while (!eventQ->IsEmpty() && eventQ->nextDeadline() <= tod) { (eventQ->nextCallback())(); eventQ->RemoveHead(); } } uint32_t SystemBus::IdleCycles() const { if (eventQ->IsEmpty()) return timer; const uint64_t et = eventQ->nextDeadline(); if (et > tod) return std::min(timer, (uint32_t) (et - tod - 1)); else return 0; } void SystemBus::Skip(uint32_t cycles) { tod += cycles; machine->HandleBusAccess(BUS_REG_TOD_HI, WRITE, NULL); machine->HandleBusAccess(BUS_REG_TOD_LO, WRITE, NULL); timer -= cycles; machine->HandleBusAccess(BUS_REG_TIMER, WRITE, NULL); } void SystemBus::setToDHI(Word hi) { TimeStamp::setHi(tod, hi); } void SystemBus::setToDLO(Word lo) { TimeStamp::setLo(tod, lo); } void SystemBus::setTimer(Word time) { timer = time; } // This method reads a data word from memory at address addr, returning it // thru datap pointer. It also returns TRUE if the address was invalid and // an exception was caused, FALSE otherwise, and signals memory access to // Watch control object bool SystemBus::DataRead(Word addr, Word* datap, Processor* cpu) { machine->HandleBusAccess(addr, READ, cpu); if (busRead(addr, datap, cpu)) { // address invalid: signal exception to processor cpu->SignalExc(DBEXCEPTION); return true; } return false; } // // These methods allow Watch to inspect or modify single memory locations; // they return TRUE if address is invalid or memory cannot be altered, and // FALSE otherwise // bool SystemBus::WatchRead(Word addr, Word* datap) { return busRead(addr, datap, machine->getProcessor(0)); } bool SystemBus::WatchWrite(Word addr, Word data) { return busWrite(addr, data, machine->getProcessor(0)); } // This method writes the data word at physical addr in RAM memory or device // register area. Writes to BIOS or BOOT areas cause a DBEXCEPTION (no // writes allowed). It returns TRUE if an exception was caused, FALSE // otherwise, and notifies access to Watch control object bool SystemBus::DataWrite(Word addr, Word data, Processor* proc) { machine->HandleBusAccess(addr, WRITE, proc); if (busWrite(addr, data, proc)) { // data write is out of valid write bounds proc->SignalExc(DBEXCEPTION); return true; } else return false; } bool SystemBus::CompareAndSet(Word addr, Word oldval, Word newval, bool* result, Processor* cpu) { // The CAS read-modify-write operation, as specified by the uMPS // ISA, is required to fail for I/O locations. if (RAMBASE <= addr && addr < RAMBASE + ram->Size()) { *result = ram->CompareAndSet((addr - RAMBASE) >> 2, oldval, newval); return false; } else if (MMIO_BASE <= addr && addr < MMIO_END) { *result = false; return false; } else { cpu->SignalExc(DBEXCEPTION); return true; } } // This method transfers a block from or to memory, starting with address // startAddr; it returns TRUE is transfer was not successful (non-existent // memory, read-only memory, unaligned addresses), FALSE otherwise. // It notifies too the memory accesses to Watch control object bool SystemBus::DMATransfer(Block * blk, Word startAddr, bool toMemory) { if (BADADDR(startAddr)) return true; bool error = false; if (toMemory) { for (Word ofs = 0; ofs < BLOCKSIZE && !error; ofs++) { error = busWrite(startAddr + (ofs * WORDLEN), blk->getWord(ofs)); machine->HandleBusAccess(startAddr + (ofs * WORDLEN), WRITE, NULL); } } else { Word val; for (Word ofs = 0; ofs < BLOCKSIZE && !error; ofs++) { error = busRead(startAddr + (ofs * WORDLEN), &val); machine->HandleBusAccess(startAddr + (ofs * WORDLEN), READ, NULL); blk->setWord(ofs, val); } } return error; } // This method transfers a partial block from or to memory, starting with address // startAddr; it returns TRUE is transfer was not successful (non-existent // memory, read-only memory, unaligned addresses), FALSE otherwise. // It notifies too the memory accesses to Watch control object bool SystemBus::DMAVarTransfer(Block* blk, Word startAddr, Word byteLength, bool toMemory) { // fit bytes into words Word length; if (byteLength % WORDLEN) length = (byteLength / WORDLEN) + 1; else length = byteLength / WORDLEN; if (BADADDR(startAddr) || length > BLOCKSIZE) return true; bool error = false; if (toMemory) { for (Word ofs = 0; ofs < length && !error; ofs++) { error = busWrite(startAddr + (ofs * WORDLEN), blk->getWord(ofs)); machine->HandleBusAccess(startAddr + (ofs * WORDLEN), WRITE, NULL); } } else { Word val; for (Word ofs = 0; ofs < length && !error; ofs++) { error = busRead(startAddr + (ofs * WORDLEN), &val); machine->HandleBusAccess(startAddr + (ofs * WORDLEN), READ, NULL); blk->setWord(ofs, val); } } return error; } // This method reads a istruction from memory at address addr, returning // it thru istrp pointer. It also returns TRUE if the address was invalid and // an exception was caused, FALSE otherwise, and notifies Watch bool SystemBus::InstrRead(Word addr, Word* instrp, Processor* proc) { machine->HandleBusAccess(addr, EXEC, proc); if (busRead(addr, instrp)) { // address invalid: signal exception to processor proc->SignalExc(IBEXCEPTION); return true; } else { // address was valid return false; } } // This method inserts in the eventQ a event that must happen // at (current system time) + delay uint64_t SystemBus::scheduleEvent(uint64_t delay, Event::Callback callback) { return eventQ->InsertQ(tod, delay, callback); } void SystemBus::IntReq(unsigned int intl, unsigned int devNum) { pic->StartIRQ(DEV_IL_START + intl, devNum); } void SystemBus::IntAck(unsigned int intl, unsigned int devNum) { pic->EndIRQ(DEV_IL_START + intl, devNum); } // This method returns the current interrupt line status Word SystemBus::getPendingInt(const Processor* cpu) { return pic->GetIP(cpu->Id()); } void SystemBus::AssertIRQ(unsigned int il, unsigned int target) { machine->getProcessor(target)->AssertIRQ(il); } void SystemBus::DeassertIRQ(unsigned int il, unsigned int target) { machine->getProcessor(target)->DeassertIRQ(il); } // This method returns the Device object with given "coordinates" Device * SystemBus::getDev(unsigned int intL, unsigned int dNum) { if (intL < DEVINTUSED && dNum < DEVPERINT) return(devTable[intL][dNum]); else { Panic("Unknown device specified in SystemBus::getDev()"); // never returns return NULL; } } /****************************************************************************/ /* Definitions strictly local to the module. */ /****************************************************************************/ // This method reads the data at the address addr, and passes it back thru // the datap pointer. It also return FALSE if the addr is valid, and TRUE // otherwise bool SystemBus::busRead(Word addr, Word* datap, Processor* cpu) { if (INBOUNDS(addr, RAMBASE, RAMBASE + ram->Size())) *datap = ram->MemRead(CONVERT(addr, RAMBASE)); else if (INBOUNDS(addr, BIOSDATABASE, BIOSDATABASE + biosdata->Size())) *datap = biosdata->MemRead(CONVERT(addr, BIOSDATABASE)); else if (INBOUNDS(addr, BIOSBASE, BIOSBASE + bios->Size())) *datap = bios->MemRead(CONVERT(addr,BIOSBASE)); else if (INBOUNDS(addr, BOOTBASE, BOOTBASE + boot->Size())) *datap = boot->MemRead(CONVERT(addr, BOOTBASE)); else if (INBOUNDS(addr, MMIO_BASE, MMIO_END)) *datap = busRegRead(addr, cpu); else { // address invalid: data read is out of bounds *datap = MAXWORDVAL; return true; } // address was valid return false; } // This method returns the value for the device field addressed in the "bus // register area" Word SystemBus::busRegRead(Word addr, Processor* cpu) { Word data; if (INBOUNDS(addr, DEV_REG_START, DEV_REG_END)) { // We're in the device register space DeviceAreaAddress da(addr); Device* device = devTable[da.line()][da.device()]; data = device->ReadDevReg(da.field()); } else if (INBOUNDS(addr, IDEV_BITMAP_BASE, IDEV_BITMAP_END)) { // We're in the "installed-devices bitmap" structure space unsigned int wordIndex = CONVERT(addr, IDEV_BITMAP_BASE); data = instDevTable[wordIndex]; } else if (INBOUNDS(addr, CDEV_BITMAP_BASE, CDEV_BITMAP_END) || INBOUNDS(addr, IRT_BASE, IRT_END) || INBOUNDS(addr, CPUCTL_BASE, CPUCTL_END)) { data = pic->Read(addr, cpu); } else if (MCTL_BASE <= addr && addr < MCTL_END) { data = mpController->Read(addr, cpu); } else { // We're in the low "bus register area" space switch (addr) { case BUS_REG_TIME_SCALE: // number of ticks for a microsecond data = config->getClockRate(); break; case BUS_REG_TOD_HI: data = getToDHI(); break; case BUS_REG_TOD_LO: data = getToDLO(); break; case BUS_REG_TIMER: data = timer; break; case BUS_REG_RAM_BASE: data = RAMBASE; break; case BUS_REG_RAM_SIZE: data = ram->Size(); break; case BUS_REG_BIOS_BASE: data = BIOSBASE; break; case BUS_REG_BIOS_SIZE: data = bios->Size(); break; case BUS_REG_BOOT_BASE: data = BOOTBASE; break; case BUS_REG_BOOT_SIZE: data = boot->Size(); break; case TLB_FLOOR_ADDR: data = config->getTLBFloorAddress(); break; default: // unmapped bus device register area: // read give 0, write has no effects data = 0UL; break; } } return data; } // This method accesses the system configuration and constructs // the devices needed, linking them to SystemBus object Device* SystemBus::makeDev(unsigned int intl, unsigned int dnum) { unsigned int devt; Device * dev; devt = config->getDeviceType(intl, dnum); switch(devt) { case PRNTDEV: dev = new PrinterDevice(this, config, intl, dnum); break; case TERMDEV: dev = new TerminalDevice(this, config, intl, dnum); break; case ETHDEV: dev = new EthDevice(this, config, intl, dnum); break; case DISKDEV: dev = new DiskDevice(this, config, intl, dnum); break; case FLASHDEV: dev = new FlashDevice(this, config, intl, dnum); break; default: dev = new Device(this, intl, dnum); break; } return dev; } // This method writes the data at the physical address addr, and passes it // back thru the datap pointer. It also return FALSE if the addr is valid // and writable, and TRUE otherwise bool SystemBus::busWrite(Word addr, Word data, Processor* cpu) { if (INBOUNDS(addr, RAMBASE, RAMBASE + ram->Size())) { ram->MemWrite(CONVERT(addr, RAMBASE), data); } else if (INBOUNDS(addr, BIOSDATABASE, BIOSDATABASE + biosdata->Size())) { biosdata->MemWrite(CONVERT(addr, BIOSDATABASE), data); } else if (INBOUNDS(addr, MMIO_BASE, MMIO_END)) { if (DEV_REG_START <= addr && addr < DEV_REG_END) { DeviceAreaAddress dva(addr); Device* device = devTable[dva.line()][dva.device()]; device->WriteDevReg(dva.field(), data); } else if (INBOUNDS(addr, IRT_BASE, IRT_END) || INBOUNDS(addr, CPUCTL_BASE, CPUCTL_END)) { pic->Write(addr, data, cpu); } else if (MCTL_BASE <= addr && addr < MCTL_END) { mpController->Write(addr, data, NULL); } else { // data write is in bus registers area if (addr == BUS_REG_TIMER) { // update the interval timer and reset its interrupt line timer = data; pic->EndIRQ(IL_TIMER); } // else data write is on a read only bus register, and // has no harmful effects } } else { // Address out of valid write bounds return(true); } return(false); } umps3-3.0.5/src/umps/systembus.h000066400000000000000000000143501435132321600165530ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2004 Mauro Morsiani * Copyright (C) 2020 Mattia Biondi * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef UMPS_SYSTEMBUS_H #define UMPS_SYSTEMBUS_H #include "base/lang.h" #include "base/basic_types.h" #include "umps/event.h" #include "umps/const.h" #include "umps/time_stamp.h" class Machine; class MachineConfig; class Device; class Processor; class RamSpace; class BiosSpace; class Block; class MPController; class InterruptController; class SystemBus { public: SystemBus(const MachineConfig* config, Machine* machine); ~SystemBus(); // This method increments system clock and decrements interval // timer; on timer underflow (0 -> FFFFFFFF transition) a interrupt // is generated. Event queue is checked against the current clock // value and device operations are completed if needed; all memory // changes are notified to Watch control object void ClockTick(); uint32_t IdleCycles() const; void Skip(uint32_t cycles); // This method reads a data word from memory at physical address // addr, returning it thru datap pointer. It also returns TRUE if // the address was invalid and an exception was caused, FALSE // otherwise, and signals memory access to Watch control object bool DataRead(Word addr, Word* datap, Processor* cpu); // This method writes the data word at physical addr in RAM memory or device // register area. Writes to BIOS or BOOT areas cause a DBEXCEPTION (no // writes allowed). It returns TRUE if an exception was caused, FALSE // otherwise, and notifies access to Watch control object bool DataWrite(Word addr, Word data, Processor* proc); bool CompareAndSet(Word addr, Word oldval, Word newval, bool* result, Processor* cpu); // This method reads a istruction from memory at physical address addr, // returning it thru istrp pointer. It also returns TRUE if the // address was invalid and an exception was caused, FALSE otherwise, // and notifies Watch bool InstrRead(Word addr, Word* instrp, Processor* proc); // This method transfers a block from or to memory, starting with // address startAddr; it returns TRUE is transfer was not successful // (non-existent memory, read-only memory, unaligned addresses), // FALSE otherwise. It notifies too the memory accesses to Watch // control object bool DMATransfer(Block * blk, Word startAddr, bool toMemory); // This method transfers a partial block from or to memory, starting with // address startAddr; it returns TRUE is transfer was not successful // (non-existent memory, read-only memory, unaligned addresses), // FALSE otherwise. It notifies too the memory accesses to Watch // control object bool DMAVarTransfer(Block * blk, Word startAddr, Word byteLength, bool toMemory); uint64_t scheduleEvent(uint64_t delay, Event::Callback callback); // This method sets the appropriate bits into intCauseDev[] and // IntPendMask to signal device interrupt pending; it notifies // memory changes to Watch too void IntReq(unsigned int intNum, unsigned int devNum); // This method resets the appropriate bits into intCauseDev[] and // IntPendMask to signal device interrupt acknowlege; it notifies // memory changes to Watch too void IntAck(unsigned int intNum, unsigned int devNum); // This method returns the current interrupt line status Word getPendingInt(const Processor* cpu); void AssertIRQ(unsigned int il, unsigned int target); void DeassertIRQ(unsigned int il, unsigned int target); Machine* getMachine() { return machine; } // This method returns the Device object with given "coordinates" Device * getDev(unsigned int intL, unsigned int dNum); // These methods allow to inspect or modify TimeofDay Clock and // Interval Timer (typically for simulation reasons) Word getToDLO() const { return TimeStamp::getLo(tod); } Word getToDHI() const { return TimeStamp::getHi(tod); } Word getTimer() const { return timer; } void setToDHI(Word hi); void setToDLO(Word lo); void setTimer(Word time); // These methods allow Watch to inspect or modify single memory // locations; they return TRUE if address is invalid or cannot be // changed, and FALSE otherwise bool WatchRead(Word addr, Word * datap); bool WatchWrite(Word addr, Word data); private: const MachineConfig* const config; Machine* const machine; scoped_ptr pic; scoped_ptr mpController; // system clock & interval timer uint64_t tod; Word timer; // device events queue EventQueue * eventQ; // physical memory spaces RamSpace * ram; RamSpace * biosdata; BiosSpace * bios; BiosSpace * boot; // device handling & interrupt generation tables Device* devTable[DEVINTUSED][DEVPERINT]; Word instDevTable[DEVINTUSED]; // pending interrupts on lines: this word is packed into MIPS Cause // Register IP field format for easy masking Word intPendMask; // This method read the data at physical address addr, and // passes it back thru the datap pointer. It also return FALSE if // the addr is valid, and TRUE otherwise bool busRead(Word addr, Word* datap, Processor* cpu = 0); // This method returns the value for the device field addressed in // the "bus register area" Word busRegRead(Word addr, Processor* cpu); // This method writes the data at physical address addr, and // passes it back thru the datap pointer. It also return FALSE if // the addr is valid and writable, and TRUE otherwise bool busWrite(Word addr, Word data, Processor* cpu = 0); // This method accesses the system configuration and constructs // the devices needed, linking them to SystemBus object Device * makeDev(unsigned int intl, unsigned int dnum); }; #endif // UMPS_SYSTEMBUS_H umps3-3.0.5/src/umps/time_stamp.cc000066400000000000000000000021641435132321600170150ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2004 Mauro Morsiani * Copyright (C) 2011 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "time_stamp.h" #include namespace TimeStamp { std::string toString(uint64_t ts) { using boost::format; using boost::str; return str(format("0x%08lx.%08lx") %((unsigned long) getHi(ts)) %((unsigned long) getLo(ts))); } } // namespace TimeStamp umps3-3.0.5/src/umps/time_stamp.h000066400000000000000000000025751435132321600166650ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2004 Mauro Morsiani * Copyright (C) 2011 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef UMPS_TIME_STAMP_H #define UMPS_TIME_STAMP_H #include #include "base/basic_types.h" namespace TimeStamp { inline uint32_t getHi(uint64_t ts) { return ts >> 32; } inline uint32_t getLo(uint64_t ts) { return (uint32_t) ts; } inline void setHi(uint64_t& ts, uint32_t value) { ts = (uint64_t) getLo(ts) | (uint64_t) value << 32; } inline void setLo(uint64_t& ts, uint32_t value) { ts = (uint64_t) getHi(ts) | (uint64_t) value; } std::string toString(uint64_t ts); } // namespace TimeStamp #endif // UMPS_TIME_STAMP_H umps3-3.0.5/src/umps/types.h000066400000000000000000000027401435132321600156610ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2004 Mauro Morsiani * Copyright (C) 2011 Tomislav Jonjic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /**************************************************************************** * * This header file contains some type definitions needed. * * "Word" (unsigned word) and "SWord" (signed word) represent MIPS * registers, and were introduced to allow a better debugging; by using them * appropriately, it was possible to detect where possibly incorrect * manipulation of register values and format were done. * ****************************************************************************/ #ifndef UMPS_TYPES_H #define UMPS_TYPES_H #include "base/basic_types.h" typedef uint32_t Word; typedef int32_t SWord; #endif // UMPS_TYPES_H umps3-3.0.5/src/umps/utility.cc000066400000000000000000000144471435132321600163650ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2004 Mauro Morsiani * Copyright (C) 2020 Mattia Biondi * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /**************************************************************************** * * This module contains some utility functions (bit manipulation, * etc) used in other modules. * ****************************************************************************/ #include "umps/utility.h" #include #include "umps/types.h" #include #include #include #include #include #include // This function sets to 1 the (bitPos % 32) bit of the word w Word SetBit (Word w, unsigned bitPos) { return (w | (1 << bitPos)); } // This function resets to 0 the (bitPos % 32) bit of the word w Word ResetBit (Word w, unsigned bitPos) { return(w & ((~0) ^ SetBit(0, bitPos))); } // This function returns the bitPos bit value in w bool BitVal(Word w, unsigned int bitPos) { return (w >> bitPos) & 1UL; } // This function adds the _unsigned_ quantities a1 and a2, // puts result into dest, and returns TRUE if a overflow occurred, // FALSE otherwise bool UnsAdd(Word *dest, Word a1, Word a2) { *dest = a1 + a2; return ~a1 < a2; } // This function subtacts the _unsigned_ quantity s2 from s1, // puts result into dest, and returns TRUE if a underflow occurred, // FALSE otherwise bool UnsSub(Word *dest, Word s1, Word s2) { *dest = s1 - s2; return s1 < s2; } // This function adds the _signed_ quantities a1 and a2, puts result into // dest (casting it to unsigned), and returns TRUE if a overflow occurred, // FALSE otherwise bool SignAdd(Word *dest, SWord a1, SWord a2) { *dest = (Word) (a1 + a2); return (SIGNBIT(a1) == SIGNBIT(a2) && SIGNBIT(*dest) != SIGNBIT(a1)); } // This function subtracts the _signed_ quantity s2 from s1, puts result // into dest (casting it to unsigned), and returns TRUE if a underflow // occurred, FALSE otherwise bool SignSub(Word *dest, SWord s1, SWord s2) { *dest = (Word) (s1 - s2); return (SIGNBIT(s1) != SIGNBIT(s2) && SIGNBIT(*dest) != SIGNBIT(s1)); } // This function multiplies the _unsigned_ quantities m1 and m2, // returning back the high and low part of the unsigned 64 bit result via // hip and lop pointers // Algorithm used is "classical": // given the 32 bit quantities AB and CD, divided into high and low 16 bit // parts A, B, C and D // // AB x // CD = // ------ // AD.BD + // AC.BC.0 // ---------- // // where AD, BD etc. are the 32 bit results of the multiplication of A by D, // etc., and X.Y means (X << 16) + Y to allow the addition of the intermediate // results. // This chunk of code (C) J. Larus (SPIM, 1990) (with minor modifications) void UnsMult(Word m1, Word m2, Word * hip, Word * lop) { Word a, b, c, d, x, y; a = (m1 & ~(IMMMASK)) >> HWORDLEN; b = (m1 & IMMMASK); c = (m2 & ~(IMMMASK)) >> HWORDLEN; d = (m2 & IMMMASK); *lop = b * d; x = (a * d) + (b * c); y = (((*lop) >> HWORDLEN) & IMMMASK) + x; *lop = ((*lop) & IMMMASK) | ((y & IMMMASK) << HWORDLEN); *hip = ((y >> HWORDLEN) & IMMMASK) + (a * c); } // This function multiplies the _signed_ quantities m1 and m2, // returning back the high and low part of the signed 64 bit result // via hip and lop pointers // This too (C) J. Larus (SPIM, 1990) void SignMult(SWord m1, SWord m2, SWord * hip, SWord * lop) { bool negResult = false; // convert negative numbers to positive for unsigned multipl. // and keep track of result sign if (m1 < 0) { negResult = !negResult; m1 = -m1; } if (m2 < 0) { negResult = !negResult; m2 = -m2; } UnsMult((Word) m1, (Word) m2, (Word *) hip, (Word *) lop); if (negResult) { // must 2-complement result (and keep count of *lop -> *hip carry) // 1-complement *hip = ~(*hip); *lop = ~(*lop); // add 1 to lower word to get 2-complement and check for carry if (UnsAdd((Word *) lop, (Word) (*lop), 1UL)) // overflow occurred: carry out to hip (*hip)++; } } // This function prints a variable list of arguments to the standard // error, and waits for an input to continue. Used for debugging void trace(char *format, ...) { va_list args; va_start(args, format); vfprintf(stderr, format, args); va_end(args); getchar(); } // This function converts a string to a Word (typically, an address) value. // Returns TRUE if conversion was successful, FALSE otherwise bool StrToWord(const char * str, Word * value) { char * endp; bool valid = true; // tries to convert the string into a unsigned long *value = strtoul(str, &endp, 0); if (endp != NULL) { // there may be some garbage while (*endp != EOS && valid) { if (!isspace(*endp)) valid = false; endp++; } } return(valid); } uint8_t* ParseMACId(const std::string& input, uint8_t* id) { unsigned int groups[6]; if (sscanf(input.c_str(), "%02x:%02x:%02x:%02x:%02x:%02x", &groups[0], &groups[1], &groups[2], &groups[3], &groups[4], &groups[5]) != 6) return NULL; if (groups[0] % 2) return NULL; std::copy(groups, groups + 6, id); return id; } std::string MACIdToString(const uint8_t* id) { char buf[6*3 + 1]; sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", (unsigned int) id[0], (unsigned int) id[1], (unsigned int) id[2], (unsigned int) id[3], (unsigned int) id[4], (unsigned int) id[5]); return std::string(buf); } std::string IntToHexString(Word addr) { std::stringstream stream; stream << "0x" << std::setfill ('0') << std::setw(sizeof(Word)*2) << std::hex << addr; return stream.str(); } umps3-3.0.5/src/umps/utility.h000066400000000000000000000060051435132321600162160ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2004 Mauro Morsiani * Copyright (C) 2020 Mattia Biondi * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef UMPS_UTILITY_H #define UMPS_UTILITY_H #include #include #include "umps/types.h" // This function sets to 1 the (bitPos % 32) bit of the word w Word SetBit (Word w, unsigned int bitPos); // This function resets to 0 the (bitPos % 32) bit of the word w Word ResetBit (Word w, unsigned int bitPos); // This function returns the bitPos bit value in w bool BitVal(Word w, unsigned int bitPos); // This function adds the _unsigned_ quantities a1 and a2, // puts result into dest, and returns TRUE if a overflow occurred, // FALSE otherwise bool UnsAdd(Word *dest, Word a1, Word a2); // This function subtacts the _unsigned_ quantity s2 from s1, // puts result into dest, and returns TRUE if a underflow occurred, // FALSE otherwise bool UnsSub(Word *dest, Word s1, Word s2); // This function adds the _signed_ quantities a1 and a2, puts result into // dest (casting it to unsigned), and returns TRUE if a overflow occurred, // FALSE otherwise bool SignAdd(Word *dest, SWord a1, SWord a2); // This function subtracts the _signed_ quantity s2 from s1, puts result // into dest (casting it to unsigned), and returns TRUE if a underflow // occurred, FALSE otherwise bool SignSub(Word *dest, SWord s1, SWord s2); // This function multiplies the _unsigned_ quantities m1 and m2, // returning back the high and low part of the unsigned 64 bit result via // hip and lop pointers void SignMult(SWord m1, SWord m2, SWord * hip, SWord * lop); // This function multiplies the _signed_ quantities m1 and m2, // returning back the high and low part of the signed 64 bit result // via hip and lop pointers void UnsMult(Word m1, Word m2, Word * hip, Word * lop); // This function prints a variable list of arguments to the standard // error, and waits for an input to continue. Used for debugging void trace(char *format, ...); // This function converts a string to a Word (typically, an address) value. // Returns TRUE if conversion was successful, FALSE otherwise bool StrToWord(const char * str, Word * value); uint8_t* ParseMACId(const std::string& input, uint8_t* id); std::string MACIdToString(const uint8_t* id); std::string IntToHexString(Word addr); #endif // UMPS_UTILITY_H umps3-3.0.5/src/umps/vde_network.cc000066400000000000000000000145621435132321600172070ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2004 Renzo Davoli * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "umps/vde_network.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "umps/const.h" #include "umps/types.h" #include "umps/blockdev_params.h" #include "umps/utility.h" #include "umps/error.h" enum request_type { REQ_NEW_CONTROL }; // FIXME: what is this for? struct request_v3 { uint32_t magic; uint32_t version; enum request_type type; struct sockaddr_un sock; }; #define SWITCH_MAGIC 0xfeedface #define STRBUFLEN 128 #define MAXNETQUEUE 16 #define MAXPACKETLEN 1536 HIDDEN struct vdepluglib vdepluglib; HIDDEN char strbuf[STRBUFLEN]; HIDDEN char packbuf[MAXPACKETLEN]; class netblock { public: netblock(const char *icontent,int ilen); ~netblock(void); class netblock *getNext(); void setNext(class netblock *p); char * getContent(); int getLen(); private: char *content; int len; struct netblock *next; }; class netblockq { public: netblockq(int imaxelem); ~netblockq(); int enqueue(const char *content,int len); int empty(); int dequeue(char *pcontent, int len); private: class netblock *head,*tail; int maxelem,nelem; }; unsigned int testnetinterface(const char *name) { char name2[1024]; int size; if (!vdepluglib.dl_handle) libvdeplug_dynopen(vdepluglib); /* vde lib does not exist */ if (vdepluglib.dl_handle == NULL) return 0; if ((size = readlink(name,name2,1023)) > 0) { name2[size]=0; name=name2; } /* file does not exist or read error */ if (access(name,R_OK) != 0) return 0; return 1; } netinterface::netinterface(const char *name, const char *addr, int intnum) { char name2[1024]; int size; if ((size=readlink(name,name2,1023)) > 0) { name2[size]=0; name=name2; } vdeconn = vdepluglib.vde_open(name, (char*) "uMPS", NULL); queue=NULL; polldata.fd = vdepluglib.vde_datafd(vdeconn); polldata.events = POLLIN | POLLOUT | POLLERR | POLLHUP | POLLNVAL; if (addr != NULL) { for (int i=0; i<6; i++) ethaddr[i]=addr[i]; } else { char tempaddr[7]; snprintf(tempaddr,7," %5d",getpid()); ethaddr[0]=intnum*2; for (int i=1; i<6; i++) ethaddr[i]=tempaddr[i]; } mode = PROMISQ | NAMED; queue = new netblockq(MAXNETQUEUE); } netinterface::~netinterface(void) { vdepluglib.vde_close(vdeconn); if (queue != NULL) delete queue; } unsigned int netinterface::readdata(char *buf, int len) { if (queue->empty() && !this->polling()) return 0; else return queue->dequeue(buf, len); } unsigned int netinterface::writedata(char *buf, int len) { int retval,pollout; if ((pollout=poll(&polldata,1,0)) < 0) { sprintf(strbuf,"poll: %s",strerror(errno)); Panic(strbuf); return 0; // -1 ?? } else { if (!(polldata.revents & POLLOUT)) retval=0; else { if (len >= 12 && (mode & NAMED) != 0) memcpy(buf+6,ethaddr,6); retval=vdepluglib.vde_send(vdeconn,buf,len,0); } } return retval; } unsigned int netinterface::polling() { int len; //socklen_t datainsize; //struct sockaddr_un datain; do { if ((poll(&polldata,1,0)) < 0) { sprintf(strbuf,"poll: %s",strerror(errno)); Panic(strbuf); } else if (polldata.revents & POLLIN) { /* We don't store sender address to avoid EINVAL in recvfrom */ len=vdepluglib.vde_recv(vdeconn,packbuf,MAXPACKETLEN,0); if (mode & PROMISQ //promiquous mode: receive everything || (len > 12 // header okay and && (memcmp(packbuf,ethaddr,6)==0 //it is sent to this interface || (packbuf[0] & 1)))) //or it's a broadcast queue->enqueue(packbuf,len); } } while (polldata.revents & POLLIN); return (!queue->empty()); } void netinterface::setaddr(char *iethaddr) { register int i; for (i=0; i<6; i++) ethaddr[i]=iethaddr[i]; // for (int i = 0; i < 6; i++) // printf("%x:",ethaddr[i]); // printf("\n"); } void netinterface::getaddr(char *pethaddr) { register int i; for (i=0; i<6; i++) pethaddr[i]=ethaddr[i]; } void netinterface::setmode(int imode) { mode=imode; // printf("mode %x\n", mode); } unsigned int netinterface::getmode() { return mode; } netblock::netblock(const char *icontent,int ilen) { if (ilen>0) { content=new char [ilen]; memcpy(content,icontent,ilen); } else content=NULL; len=ilen; } netblock::~netblock(void) { if (content != NULL) delete(content); } class netblock *netblock::getNext() { return next; } void netblock::setNext(class netblock *p) { next=p; } char *netblock::getContent() { return content; } int netblock::getLen() { return len; } netblockq::netblockq(int imaxelem) { maxelem=imaxelem; head=tail=NULL; nelem=0; } netblockq::~netblockq() { class netblock *next; while (head != NULL) { next=head->getNext(); delete (head); head=next; } } int netblockq::enqueue(const char *content,int len) { if (nelem >= maxelem) return 0; else { class netblock *oldtail=tail; tail=new netblock(content,len); if (oldtail!=NULL) oldtail->setNext(tail); tail->setNext(NULL); if (head==NULL) head=tail; nelem++; return 1; } } int netblockq::empty() { return head==NULL; } int netblockq::dequeue(char *pcontent, int len) { if (head==NULL) return 0; else { class netblock *oldhead=head; int packlen; head=oldhead->getNext(); if (head == NULL) tail = NULL; packlen=oldhead->getLen(); if (len < packlen) packlen=len; memcpy(pcontent,oldhead->getContent(),packlen); delete oldhead; nelem--; return packlen; } } umps3-3.0.5/src/umps/vde_network.h000066400000000000000000000030551435132321600170440ustar00rootroot00000000000000/* * uMPS - A general purpose computer system simulator * * Copyright (C) 2004 Renzo Davoli * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef UMPS_VDE_NETWORK_H #define UMPS_VDE_NETWORK_H #include #include #include #include "umps/libvdeplug_dyn.h" class netblockq; #define PROMISQ 0x4 #define INTERRUPT 0x2 #define NAMED 0x1 unsigned int testnetinterface(const char *name); class netinterface { public: netinterface(const char *name, const char *addr, int intnum); ~netinterface(void); unsigned int readdata(char *buf, int len); unsigned int writedata(char *buf, int len); unsigned int polling(); void setaddr(char *iethaddr); void getaddr(char *pethaddr); void setmode(int imode); unsigned int getmode(); private: VDECONN *vdeconn; char ethaddr[6]; char mode; struct pollfd polldata; class netblockq *queue; }; #endif // UMPS_VDE_NETWORK_H