commit 2e5b2022fd032dc03a3aaa4e504fb09ab8e79a36 Author: Folkert van Heusden Date: Tue Mar 1 13:57:57 2022 +0100 KEK diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..0ad25db --- /dev/null +++ b/LICENSE @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 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 Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are 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. + + 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. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + 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 Affero 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. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + 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 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 work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero 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 Affero 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 Affero 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 Affero 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 Affero 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + 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 AGPL, see +. diff --git a/README.md b/README.md new file mode 100644 index 0000000..d25b807 --- /dev/null +++ b/README.md @@ -0,0 +1,30 @@ +KEK +Kek might (I work occasionally on it so don't hold your breath) become a DEC PDP-11 emulator capable of running UNIX-v7. + +Run: + make all +to build. + +To run the test-cases: + + ./kek -m tc + +... it should end in "ALL FINE". + + +To run a disk image: + + ./kek -R filename.rk -p offset 2> /dev/null + +... where offset is a decimal(!) address to start (optional). + + +To run a tape image: + + ./kek -T filename.bin -p offset 2> /dev/null + +... where offset is a decimal(!) address to start (optional). + + +Released in 2018 under AGPL v3.0 +Folkert van Heusden diff --git a/bus.cpp b/bus.cpp new file mode 100644 index 0000000..79f796b --- /dev/null +++ b/bus.cpp @@ -0,0 +1,448 @@ +// (C) 2018 by Folkert van Heusden +// Released under AGPL v3.0 +#include +#include + +#include "bus.h" +#include "gen.h" +#include "cpu.h" +#include "memory.h" +#include "tm-11.h" +#include "tty.h" + +bus::bus() : c(nullptr), tm11(nullptr), rk05_(nullptr), rx02_(nullptr), tty_(nullptr) +{ + m = new memory(16 * 8192); + + for(int i=0; i<16; i++) { + pages[i].par = (i & 7) * 8192 / 64; + pages[i].pdr = (3 << 1) | (0 << 4) | (0 << 6) | ((8192 / (32 * 2)) << 8); + } + + CPUERR = MMR2 = MMR3 = PIR = CSR = 0; +} + +bus::~bus() +{ + delete c; + delete tm11; + delete rk05_; + delete rx02_; + delete tty_; + delete m; +} + +void bus::clearmem() +{ + m -> reset(); +} + +uint16_t bus::read(const uint16_t a, const bool word_mode, const bool use_prev) +{ + uint16_t temp = 0; + + if (a >= 0160000) { + fprintf(stderr, "read%c I/O %o\n", word_mode ? 'b' : ' ', a); + + if (a == 0177750) { // MAINT + fprintf(stderr, "read MAINT\n"); + return 1; // POWER OK + } + + if (a == 0177570) { // console switch & display register + fprintf(stderr, "read console switch\n"); + return 128; // educated guess + } + + if (a == 0172540) { // KW11P programmable clock + fprintf(stderr, "read programmable clock\n"); + return 128; + } + + if (a == 0177772) { // PIR + fprintf(stderr, "read PIT\n"); + return PIR; + } + + if (a == 0177546) { // line frequency clock and status register + fprintf(stderr, "read line freq clock\n"); + return CSR; + } + + if (a == 0177514) { // printer, CSR register, LP11 + fprintf(stderr, "read LP11 CSR\n"); + return 0x80; + } + + /// MMU /// + if (a >= 0172300 && a < 0172320) { + uint16_t t = pages[((a & 017) >> 1)].pdr; + fprintf(stderr, "read PDR for %d: %o\n", (a & 017) >> 1, t); + return t; + } + + if (a >= 0172340 && a < 0172360) { + uint16_t t = pages[((a & 017) >> 1)].par; + fprintf(stderr, "read PAR for %d: %o\n", (a & 017) >> 1, t); + return t; + } + + if (a >= 0177600 && a < 0177620) { + uint16_t t = pages[((a & 017) >> 1) + 8].pdr; + fprintf(stderr, "read PDR for %d: %o\n", ((a & 017) >> 1) + 8, t); + return t; + } + + if (a >= 0177640 && a < 0177660) { + uint16_t t = pages[((a & 017) >> 1) + 8].par; + fprintf(stderr, "read PAR for %d: %o\n", ((a & 017) >> 1) + 8, t); + return t; + } + + if (a == 0177572) { + uint16_t t = ((c -> getRunMode() ? 0b11 : 0b00) << 5) | // kernel == 00 + ((c -> getRegister(7) >> 13) << 1) | // page nr + 0 // MMU enabled + ; + fprintf(stderr, "read MMU SR0 %o\n", t); + return t; + } + /////////// + + if (word_mode) { + if (a == 0177776) { // PSW + fprintf(stderr, "readb PSW LSB\n"); + return c -> getPSW() & 255; + } + + if (a == 0177777) { + fprintf(stderr, "readb PSW MSB\n"); + return c -> getPSW() >> 8; + } + + if (a == 0177774) { // stack limit register + fprintf(stderr, "readb stack limit register\n"); + return c -> getStackLimitRegister() & 0xff; + } + if (a == 0177775) { // stack limit register + fprintf(stderr, "readb stack limit register\n"); + return c -> getStackLimitRegister() >> 8; + } + + if (a >= 0177700 && a <= 0177705) { // kernel R0-R5 + fprintf(stderr, "readb kernel R%d\n", a - 0177700); + return c -> getRegister(false, a - 0177700) & 0xff; + } + if (a >= 0177710 && a <= 0177715) { // user R0-R5 + fprintf(stderr, "readb user R%d\n", a - 0177710); + return c -> getRegister(true, a - 0177710) & 0xff; + } + if (a == 0177706) { // kernel SP + fprintf(stderr, "readb kernel sp\n"); + return c -> getStackPointer(0) & 0xff; + } + if (a == 0177707) { // PC + fprintf(stderr, "readb pc\n"); + return c -> getPC() & 0xff; + } + if (a == 0177716) { // supervisor SP + fprintf(stderr, "readb supervisor sp\n"); + return c -> getStackPointer(1) & 0xff; + } + if (a == 0177717) { // user SP + fprintf(stderr, "readb user sp\n"); + return c -> getStackPointer(3) & 0xff; + } + + if (a == 0177766) { // cpu error register + fprintf(stderr, "readb cpuerr\n"); + return CPUERR & 0xff; + } + } + else { + if (a == 0177576) { // MMR2 + fprintf(stderr, "read MMR2\n"); + return MMR2; + } + + if (a == 0172516) { // MMR3 + fprintf(stderr, "read MMR3\n"); + return MMR3; + } + + if (a == 0177776) { // PSW + fprintf(stderr, "read PSW\n"); + return c -> getPSW(); + } + + if (a == 0177774) { // stack limit register + return c -> getStackLimitRegister(); + } + + if (a >= 0177700 && a <= 0177705) { // kernel R0-R5 + fprintf(stderr, "read kernel R%d\n", a - 0177700); + return c -> getRegister(false, a - 0177700); + } + if (a >= 0177710 && a <= 0177715) { // user R0-R5 + fprintf(stderr, "read user R%d\n", a - 0177710); + return c -> getRegister(true, a - 0177710); + } + if (a == 0177706) { // kernel SP + fprintf(stderr, "read kernel sp\n"); + return c -> getStackPointer(0); + } + if (a == 0177707) { // PC + fprintf(stderr, "read pc\n"); + return c -> getPC(); + } + if (a == 0177716) { // supervisor SP + fprintf(stderr, "read supervisor sp\n"); + return c -> getStackPointer(1); + } + if (a == 0177717) { // user SP + fprintf(stderr, "read user sp\n"); + return c -> getStackPointer(3); + } + + if (a == 0177766) { // cpu error register + fprintf(stderr, "read CPUERR\n"); + return CPUERR; + } + + } + + if (tm11 && a >= TM_11_BASE && a < TM_11_END) + return word_mode ? tm11 -> readByte(a) : tm11 -> readWord(a); + + if (rk05_ && a >= RK05_BASE && a < RK05_END) + return word_mode ? rk05_ -> readByte(a) : rk05_ -> readWord(a); + + if (tty_ && a >= PDP11TTY_BASE && a < PDP11TTY_END) + return word_mode ? tty_ -> readByte(a) : tty_ -> readWord(a); + + if (a & 1) + D(fprintf(stderr, "bus::readWord: odd address UNHANDLED %o\n", a);) + D(fprintf(stderr, "UNHANDLED read %o(%c)\n", a, word_mode ? 'B' : ' ');) + + c -> busError(); + + return -1; + } + + const uint8_t apf = a >> 13; // active page field + bool is_user = use_prev ? (c -> getBitPSW(12) && c -> getBitPSW(13)) : (c -> getBitPSW(14) && c -> getBitPSW(15)); + fprintf(stderr, "READ: is_user %d, offset %d\n", is_user, apf + is_user * 8); + uint32_t m_offset = pages[apf + is_user * 8].par * 64; + + if ((a & 1) && word_mode == 0) + fprintf(stderr, "odd addressing\n"); + + fprintf(stderr, "READ FROM %o\n", m_offset); + if (!word_mode) + temp = m -> readWord(m_offset + (a & 8191)); + else + temp = m -> readByte(m_offset + (a & 8191)); + + // D(fprintf(stderr, "read bus %o(%d): %o\n", a, word_mode, temp);) + + return temp; +} + +uint16_t bus::write(const uint16_t a, const bool word_mode, uint16_t value, const bool use_prev) +{ + //D(fprintf(stderr, "write bus %o(%d): %o\n", a, word_mode, value);) + + assert(word_mode == 0 || value < 256); + + if (a >= 0160000) { + fprintf(stderr, "write%c %o to I/O %o\n", word_mode ? 'b' : ' ', value, a); + + if (word_mode) { + if (a == 0177776 || a == 0177777) { // PSW + D(fprintf(stderr, "writeb PSW %s\n", a & 1 ? "MSB" : "LSB");) + assert(value < 256); + uint16_t vtemp = c -> getPSW(); + + if (a & 1) + vtemp = (vtemp & 0x00ff) | (value << 8); + else + vtemp = (vtemp & 0xff00) | value; + + c -> setPSW(vtemp); + + return value; + } + + if (a == 0177774 || a == 0177775) { // stack limit register + D(fprintf(stderr, "writeb Set stack limit register to %o\n", value);) + uint16_t v = c -> getStackLimitRegister(); + + if (a & 1) + v = (v & 0xff00) | value; + else + v = (v & 0x00ff) | (value << 8); + + c -> setStackLimitRegister(v); + return v; + } + } + else { + if (a == 0177776) { // PSW + D(fprintf(stderr, "write PSW %o\n", value);) + c -> setPSW(value); + return value; + } + + if (a == 0177774) { // stack limit register + D(fprintf(stderr, "write Set stack limit register to %o\n", value);) + c -> setStackLimitRegister(value); + return value; + } + + if (a >= 0177700 && a <= 0177705) { // kernel R0-R5 + fprintf(stderr, "write kernel R%d to %o\n", a - 01777700, value); + c -> setRegister(false, a - 0177700, value); + return value; + } + if (a >= 0177710 && a <= 0177715) { // user R0-R5 + fprintf(stderr, "write user R%d to %o\n", a - 01777710, value); + c -> setRegister(true, a - 0177710, value); + return value; + } + if (a == 0177706) { // kernel SP + fprintf(stderr, "write kernel SP to %o\n", value); + c -> setStackPointer(0, value); + return value; + } + if (a == 0177707) { // PC + fprintf(stderr, "write PC to %o\n", value); + c -> setPC(value); + return value; + } + if (a == 0177716) { // supervisor SP + fprintf(stderr, "write supervisor sp to %o\n", value); + c -> setStackPointer(1, value); + return value; + } + if (a == 0177717) { // user SP + fprintf(stderr, "write user sp to %o\n", value); + c -> setStackPointer(3, value); + return value; + } + } + + if (a == 0177766) { // cpu error register + fprintf(stderr, "write CPUERR %o\n", value); + CPUERR = 0; + return CPUERR; + } + + if (a == 0172516) { // MMR3 + fprintf(stderr, "write set MMR3 to %o\n", value); + MMR3 = value; + return MMR3; + } + + if (a == 0177772) { // PIR + fprintf(stderr, "write set PIR to %o\n", value); + PIR = value; // FIXME + return PIR; + } + + if (a == 0177546) { // line frequency clock and status register + fprintf(stderr, "write set LFC/SR to %o\n", value); + CSR = value; + return CSR; + } + + if (tm11 && a >= TM_11_BASE && a < TM_11_END) { + word_mode ? tm11 -> writeByte(a, value) : tm11 -> writeWord(a, value); + return value; + } + + if (rk05_ && a >= RK05_BASE && a < RK05_END) { + word_mode ? rk05_ -> writeByte(a, value) : rk05_ -> writeWord(a, value); + return value; + } + + if (tty_ && a >= PDP11TTY_BASE && a < PDP11TTY_END) { + word_mode ? tty_ -> writeByte(a, value) : tty_ -> writeWord(a, value); + return value; + } + + /// MMU /// + if (a >= 0172300 && a < 0172320) { + fprintf(stderr, "write set PDR for %d to %o\n", (a & 017) >> 1, value); + pages[((a & 017) >> 1)].pdr = value; + return value; + } + + if (a >= 0172340 && a < 0172360) { + fprintf(stderr, "write set PAR for %d to %o\n", (a & 017) >> 1, value); + pages[((a & 017) >> 1)].par = value; + return value; + } + + if (a >= 0117600 && a < 0117620) { + fprintf(stderr, "write set PDR for %d to %o\n", ((a & 017) >> 1) + 8, value); + pages[((a & 017) >> 1) + 8].pdr = value; + return value; + } + + if (a >= 0117640 && a < 0177660) { + fprintf(stderr, "write set PAR for %d to %o\n", ((a & 017) >> 1) + 8, value); + pages[((a & 017) >> 1) + 8].par = value; + return value; + } + + if (a == 0177746) { // cache control register + // FIXME + return value; + } + + /////////// + + if (a == 0177374) { // FIXME + fprintf(stderr, "char: %c\n", value & 127); + return 128; + } + + if (a & 1) + D(fprintf(stderr, "bus::writeWord: odd address UNHANDLED\n");) + D(fprintf(stderr, "UNHANDLED write %o(%c): %o\n", a, word_mode ? 'B' : ' ', value);) + + c -> busError(); + + return value; + } + + const uint8_t apf = a >> 13; // active page field + bool is_user = use_prev ? (c -> getBitPSW(12) && c -> getBitPSW(13)) : (c -> getBitPSW(14) && c -> getBitPSW(15)); + fprintf(stderr, "WRITE: is_user %d, offset %d\n", is_user, apf + is_user * 8); + uint32_t m_offset = pages[apf + is_user * 8].par * 64; + + pages[apf].pdr |= 1 << 6; // page has been written to + + if ((a & 1) && word_mode == 0) + fprintf(stderr, "odd addressing\n"); + + fprintf(stderr, "WRITE TO: %o\n", m_offset); + if (word_mode) + m -> writeByte(m_offset + (a & 8191), value); + else + m -> writeWord(m_offset + (a & 8191), value); + + return value; +} + +uint16_t bus::readWord(const uint16_t a) +{ + return read(a, false); +} + +uint16_t bus::writeWord(const uint16_t a, const uint16_t value) +{ + write(a, false, value); + + return value; +} diff --git a/bus.h b/bus.h new file mode 100644 index 0000000..b7b2f69 --- /dev/null +++ b/bus.h @@ -0,0 +1,59 @@ +// (C) 2018 by Folkert van Heusden +// Released under AGPL v3.0 +#pragma once + +#include +#include + +#include "tm-11.h" +#include "rk05.h" +#include "rx02.h" +#include "tty.h" + +class cpu; +class memory; + +typedef struct +{ + uint16_t par, pdr; +} page_t; + +class bus +{ +private: + cpu *c { nullptr }; + tm_11 *tm11 { nullptr }; + rk05 *rk05_ { nullptr }; + rx02 *rx02_ { nullptr }; + tty *tty_ { nullptr }; + + memory *m { nullptr }; + + page_t pages[16]; + + uint16_t MMR2 { 0 }, MMR3 { 0 }, CPUERR { 0 }, PIR { 0 }, CSR { 0 }; + +public: + bus(); + ~bus(); + + void clearmem(); + + void add_cpu(cpu *const c) { this -> c = c; } + void add_tm11(tm_11 *tm11) { this -> tm11 = tm11; } + void add_rk05(rk05 *rk05_) { this -> rk05_ = rk05_; } + void add_rx02(rx02 *rx02_) { this -> rx02_ = rx02_; } + void add_tty(tty *tty_) { this -> tty_ = tty_; } + + cpu *getCpu() { return this -> c; } + + uint16_t read(const uint16_t a, const bool word_mode, const bool use_prev=false); + uint16_t readByte(const uint16_t a) { return read(a, true); } + uint16_t readWord(const uint16_t a); + + uint16_t write(const uint16_t a, const bool word_mode, uint16_t value, const bool use_prev=false); + uint8_t writeByte(const uint16_t a, const uint8_t value) { return write(a, true, value); } + uint16_t writeWord(const uint16_t a, const uint16_t value); + + void setMMR2(const uint16_t value) { MMR2 = value; } +}; diff --git a/cpu.cpp b/cpu.cpp new file mode 100644 index 0000000..3678356 --- /dev/null +++ b/cpu.cpp @@ -0,0 +1,1270 @@ +// (C) 2018 by Folkert van Heusden +// Released under AGPL v3.0 +#include +#include +#include +#include + +#include "cpu.h" +#include "utils.h" +#include "gen.h" + +#ifndef _DEBUG +std::string *src_gam_text = NULL, *dst_gam_text = NULL; +#endif + +#define SIGN(x, wm) ((wm) ? (x) & 0x80 : (x) & 0x8000) + +cpu::cpu(bus *const b) : b(b) +{ + reset(); +} + +cpu::~cpu() +{ +} + +void cpu::reset() +{ + memset(regs0_5, 0x00, sizeof regs0_5); + memset(sp, 0x00, sizeof sp); + pc = 0; + psw = fpsr = 0; + runMode = resetFlag = haltFlag = false; +} + +uint16_t cpu::getRegister(const int nr, const bool prev_mode) const +{ + assert(nr >= 0 && nr <= 7); + + if (nr < 6) + return regs0_5[getBitPSW(11)][nr]; + + if (nr == 6) { + if (prev_mode) + return sp[(getBitPSW(13) << 1) | getBitPSW(12)]; + + return sp[(getBitPSW(15) << 1) | getBitPSW(14)]; + } + + return pc; +} + +void cpu::setRegister(const int nr, const bool prev_mode, const uint16_t value) +{ + assert(nr >= 0 && nr <= 7); + + if (nr < 6) + regs0_5[getBitPSW(11)][nr] = value; + else if (nr == 6) { + if (prev_mode) + sp[(getBitPSW(13) << 1) | getBitPSW(12)] = value; + else + sp[(getBitPSW(15) << 1) | getBitPSW(14)] = value; + } + else { + pc = value; + } +} + +void cpu::addRegister(const int nr, const bool prev_mode, const uint16_t value) +{ + assert(nr >= 0 && nr <= 7); + + if (nr < 6) + regs0_5[getBitPSW(11)][nr] += value; + else if (nr == 6) { + if (prev_mode) + sp[(getBitPSW(13) << 1) | getBitPSW(12)] += value; + else + sp[(getBitPSW(15) << 1) | getBitPSW(14)] += value; + } + else { + assert((pc & 1) == 0); + pc += value; + assert((pc & 1) == 0); + } +} + +bool cpu::getBitPSW(const int bit) const +{ + return !!(psw & (1 << bit)); +} + +bool cpu::getPSW_c() const +{ + return getBitPSW(0); +} + +bool cpu::getPSW_v() const +{ + return getBitPSW(1); +} + +bool cpu::getPSW_z() const +{ + return getBitPSW(2); +} + +bool cpu::getPSW_n() const +{ + return getBitPSW(3); +} + +void cpu::setBitPSW(const int bit, const bool v) +{ + const uint16_t mask = 1 << bit; + + if (v) + psw |= mask; + else + psw &= ~mask; +} + +void cpu::setPSW_c(const bool v) +{ + setBitPSW(0, v); +} + +void cpu::setPSW_v(const bool v) +{ + setBitPSW(1, v); +} + +void cpu::setPSW_z(const bool v) +{ + setBitPSW(2, v); +} + +void cpu::setPSW_n(const bool v) +{ + setBitPSW(3, v); +} + +void cpu::setPSW_spl(const int v) +{ + psw &= 7 << 5; + psw |= (v & 7) << 5; +} + +// GAM = general addressing modes +uint16_t cpu::getGAM(const uint8_t mode, const uint8_t reg, const bool word_mode, const bool prev_mode, std::string *const text) +{ + uint16_t next_word = 0, temp = 0; + + switch(mode) { + case 0: // 000 + D(*text = format("R%d", reg);) + return getRegister(reg, prev_mode) & (word_mode ? 0xff : 0xffff); + case 1: + D(*text = format("(R%d)", reg);) + return b -> read(getRegister(reg, prev_mode), word_mode, prev_mode); + case 2: + temp = b -> read(getRegister(reg, prev_mode), word_mode, prev_mode); + if (reg == 7 || reg == 6) + addRegister(reg, prev_mode, 2); + else + addRegister(reg, prev_mode, word_mode ? 1 : 2); +#if _DEBUG + if (reg == 7) + *text = format("#%o", temp); + else + *text = format("(R%d)+", reg); +#endif + return temp; + case 3: + D(*text = format("@(R%d)+", reg);) + temp = b -> read(b -> read(getRegister(reg, prev_mode), false, prev_mode), word_mode, prev_mode); + addRegister(reg, prev_mode, 2); + return temp; + case 4: + D(*text = format("-(R%d)", reg);) + if (reg == 7 || reg == 6) + addRegister(reg, prev_mode, - 2); + else + addRegister(reg, prev_mode, word_mode ? -1 : -2); + return b -> read(getRegister(reg, prev_mode), word_mode, prev_mode); + case 5: + D(*text = format("@-(R%d)", reg);) + addRegister(reg, prev_mode, -2); + return b -> read(b -> read(getRegister(reg, prev_mode), false, prev_mode), word_mode, prev_mode); + case 6: + next_word = b -> read(getPC(), false, prev_mode); + //fprintf(stderr, "next word %o\n", next_word); + addRegister(7, prev_mode, + 2); + temp = b -> read(getRegister(reg, prev_mode) + next_word, word_mode, prev_mode); + //fprintf(stderr, "-> %d: %o\n", word_mode, temp); +#ifdef _DEBUG + if (reg == 7) + *text = format("0o%o", getPC() + next_word); // FIXME + else + *text = format("0o%o(R%d)", next_word, reg); +#endif + return temp; + case 7: + next_word = b -> read(getPC(), false, prev_mode); + addRegister(7, prev_mode, + 2); + D(*text = format("@0o%o(R%d)", next_word, reg);) + return b -> read(b -> read(getRegister(reg, prev_mode) + next_word, false, prev_mode), word_mode, prev_mode); + } + + return -1; +} + +void cpu::putGAM(const uint8_t mode, const int reg, const bool word_mode, const uint16_t value, bool const prev_mode, std::string *const text) +{ + uint16_t next_word = 0; + + switch(mode) { + case 0: + D(*text = format("R%d", reg);) + if (word_mode) { + uint16_t temp = getRegister(reg, prev_mode); + temp &= 0xff00; + assert(value < 256); + temp |= value; + setRegister(reg, prev_mode, temp); + } + else { + setRegister(reg, prev_mode, value); + } + break; + case 1: + D(*text = format("(R%d)", reg);) + b -> write(getRegister(reg, prev_mode), word_mode, value); + break; + case 2: + D(*text = format("(R%d)+", reg);) + b -> write(getRegister(reg, prev_mode), word_mode, value); + if (reg == 7 || reg == 6) + addRegister(reg, prev_mode, 2); + else + addRegister(reg, prev_mode, word_mode ? 1 : 2); + break; + case 3: + D(*text = format("@(R%d)+", reg);) + b -> write(b -> readWord(getRegister(reg, prev_mode)), word_mode, value); + addRegister(reg, prev_mode, 2); + break; + case 4: + D(*text = format("-(R%d)", reg);) + if (reg == 7 || reg == 6) + addRegister(reg, prev_mode, -2); + else + addRegister(reg, prev_mode, word_mode ? -1 : -2); + b -> write(getRegister(reg, prev_mode), word_mode, value); + break; + case 5: + D(*text = format("@-(R%d)", reg);) + addRegister(reg, prev_mode, -2); + b -> write(b -> readWord(getRegister(reg, prev_mode)), word_mode, value); + break; + case 6: + next_word = b -> readWord(getPC()); + addRegister(7, prev_mode, 2); + D(*text = format("0o%o(R%d)", next_word, reg);) + b -> write(getRegister(reg, prev_mode) + next_word, word_mode, value); + break; + case 7: + next_word = b -> readWord(getPC()); + addRegister(7, prev_mode, 2); + D(*text = format("@0o%o(R%d)", next_word, reg);) + b -> write(b -> readWord(getRegister(reg, prev_mode) + next_word), word_mode, value); + break; + + default: + // error + break; + } +} + +uint16_t cpu::getGAMAddress(const uint8_t mode, const int reg, const bool word_mode, const bool prev_mode) +{ + uint16_t next_word = 0, temp = 0; + + switch(mode) { + case 0: + // registers are also mapped in memory + return 0177700 + reg; + case 1: + return getRegister(reg, prev_mode); + case 2: + temp = getRegister(reg, prev_mode); + if (reg == 6 || reg == 7) + addRegister(reg, prev_mode, 2); + else + addRegister(reg, prev_mode, word_mode ? 1 : 2); + return temp; + case 3: + temp = b -> readWord(getRegister(reg, prev_mode)); + addRegister(reg, prev_mode, 2); + return temp; + case 4: + if (reg == 6 || reg == 7) + addRegister(reg, prev_mode, -2); + else + addRegister(reg, prev_mode, word_mode ? -1 : -2); + return getRegister(reg, prev_mode); + case 5: + addRegister(reg, prev_mode, -2); + return b -> readWord(getRegister(reg, prev_mode)); + case 6: + next_word = b -> readWord(getPC()); + addRegister(7, prev_mode, 2); + return getRegister(reg, prev_mode) + next_word; + case 7: + next_word = b -> readWord(getPC()); + addRegister(7, prev_mode, 2); + return b -> readWord(getRegister(reg, prev_mode) + next_word); + } + + return -1; +} + +bool cpu::double_operand_instructions(const uint16_t instr) +{ + bool word_mode = !!(instr & 0x8000); + + uint8_t operation = (instr >> 12) & 7; + + if (operation == 0b000) + return single_operand_instructions(instr); + + if (operation == 0b111) + return additional_double_operand_instructions(instr); + + const uint8_t src = (instr >> 6) & 63; + const uint8_t src_mode = (src >> 3) & 7; + const uint8_t src_reg = src & 7; + +#ifdef _DEBUG + std::string debug_a, debug_b; + std::string *src_gam_text = &debug_a, *dst_gam_text = &debug_b; +#endif + uint16_t src_value; + + const uint8_t dst = instr & 63; + const uint8_t dst_mode = (dst >> 3) & 7; + const uint8_t dst_reg = dst & 7; + + switch(operation) { + case 0b001: // MOV/MOVB Move Word/Byte + src_value = getGAM(src_mode, src_reg, word_mode, false, src_gam_text); + if (word_mode) { + if (dst_mode == 0) + setRegister(dst_reg, false, int8_t(src_value)); + else + putGAM(dst_mode, dst_reg, word_mode, src_value, false, dst_gam_text); + } + else { + putGAM(dst_mode, dst_reg, word_mode, src_value, false, dst_gam_text); + } + D(fprintf(stderr, "MOV%c %s to %s\n", word_mode ? 'B' : ' ', src_gam_text -> c_str(), dst_gam_text -> c_str());) + + setPSW_n(SIGN(src_value, word_mode)); + setPSW_z(src_value == 0); + setPSW_v(false); + + return true; + + case 0b010: { // CMP/CMPB Compare Word/Byte + //fprintf(stderr, "src mode %d src reg %d, dst mode %d dst reg %d\n", src_mode, src_reg, dst_mode, dst_reg); + src_value = getGAM(src_mode, src_reg, word_mode, false, src_gam_text); + uint16_t dst_value = getGAM(dst_mode, dst_reg, word_mode, false, dst_gam_text); + + uint16_t temp = (src_value - dst_value) & (word_mode ? 0xff : 0xffff); + setPSW_n(SIGN(temp, word_mode)); + setPSW_z(temp == 0); + + setPSW_v(SIGN((src_value ^ dst_value) & (~dst_value ^ temp), word_mode)); + // fprintf(stderr, "SIGNsimh: %d\n", SIGN((src_value ^ dst_value) & (~dst_value ^ temp), word_mode)); + // fprintf(stderr, "SIGNme__: %d\n", SIGN(src_value, word_mode) != SIGN(dst_value, word_mode) && SIGN(dst_value, word_mode) == SIGN(temp, word_mode)); + + setPSW_c(src_value < dst_value); + + //fprintf(stderr, "%o - %o > %o | %d, %d\n", + // src_value, dst_value, src_value - dst_value, + // SIGN((src_value ^ dst_value) & (~dst_value ^ temp), word_mode), + // sign(src_value) != sign(dst_value) && sign(src_value) == sign(temp)); + + D(fprintf(stderr, "CMP%c %s to %s n%dz%dv%dc%d\n", word_mode ? 'B' : ' ', src_gam_text -> c_str(), dst_gam_text -> c_str(), getPSW_n(), getPSW_z(), getPSW_v(), getPSW_c());) + // fprintf(stderr, "%o %o %o\n", b -> readWord(017026), getPC(), b -> readWord(017026 + getPC())); + return true; + } + + case 0b011: { // BIT/BITB Bit Test Word/Byte + src_value = getGAM(src_mode, src_reg, word_mode, false, src_gam_text); + uint16_t dst_value = getGAM(dst_mode, dst_reg, word_mode, false, dst_gam_text); + uint16_t result = dst_value & src_value; + D(fprintf(stderr, "BIT%c %s to %s\n", word_mode ? 'B' : ' ', src_gam_text -> c_str(), dst_gam_text -> c_str());) + setPSW_n(SIGN(result, word_mode)); + setPSW_z(result == 0); + setPSW_v(false); + return true; + } + + case 0b100: { // BIC/BICB Bit Clear Word/Byte + src_value = getGAM(src_mode, src_reg, word_mode, false, src_gam_text); + uint16_t a = getGAMAddress(dst_mode, dst_reg, word_mode, false); + + uint16_t result = b -> readWord(a) & ~src_value; + + if (dst_mode == 0) { + putGAM(dst_mode, dst_reg, word_mode, result, false, dst_gam_text); + D(fprintf(stderr, "BIC%c %s to R%d\n", word_mode ? 'B' : ' ', src_gam_text -> c_str(), dst_reg);) + } + else { + b -> write(a, word_mode, result); + D(fprintf(stderr, "BIC%c %s to @%o\n", word_mode ? 'B' : ' ', src_gam_text -> c_str(), a);) + } + + setPSW_n(SIGN(result, word_mode)); + setPSW_z(result == 0); + setPSW_v(false); + return true; + } + + case 0b101: { // BIS/BISB Bit Set Word/Byte + src_value = getGAM(src_mode, src_reg, word_mode, false, src_gam_text); + uint16_t a = getGAMAddress(dst_mode, dst_reg, word_mode, false); + + uint16_t result = b -> readWord(a) | src_value; + + if (dst_mode == 0) + putGAM(dst_mode, dst_reg, word_mode, result, false, dst_gam_text); + else { +#ifdef _DEBUG + dst_gam_text -> assign(format("(%o)", a)); +#endif + b -> write(a, word_mode, result); + } + + setPSW_n(SIGN(result, word_mode)); + setPSW_z(result == 0); + setPSW_v(false); + D(fprintf(stderr, "BIS%c %s to %s\n", word_mode ? 'B' : ' ', src_gam_text -> c_str(), dst_gam_text -> c_str());) + return true; + } + + case 0b110: { // ADD/SUB Add/Subtract Word + int16_t src_value = getGAM(src_mode, src_reg, false, false, src_gam_text); + uint16_t da = getGAMAddress(dst_mode, dst_reg, false, false); + int16_t dst_value = b -> readWord(da); + int16_t result = 0; + if (instr & 0x8000) { + result = (dst_value + ~src_value + 1) & 0xffff; + setPSW_v(sign(src_value) != sign(dst_value) && sign(src_value) == sign(result)); + setPSW_c(uint16_t(dst_value) < uint16_t(src_value)); + } + else { + result = dst_value + src_value; + setPSW_v(sign(src_value) == sign(dst_value) && sign(dst_value) != sign(result)); + setPSW_c(uint16_t(result) < uint16_t(src_value)); + } + setPSW_n(result < 0); + setPSW_z(result == 0); + if (dst_mode == 0) + setRegister(dst_reg, false, result); + else + b -> writeWord(da, result); + D(fprintf(stderr, "%s %s to %s (%o (%d), %o (%d)) = %d\n", (instr & 0x8000) ? "SUB" : "ADD", src_gam_text -> c_str(), dst_gam_text -> c_str(), src_value, src_value, dst_value, dst_value, result);) + return true; + } + } + + return false; +} + +bool cpu::additional_double_operand_instructions(const uint16_t instr) +{ + const uint8_t reg = (instr >> 6) & 7; + +#ifdef _DEBUG + std::string debug_b; + std::string *dst_gam_text = &debug_b; +#endif + + const uint8_t dst = instr & 63; + const uint8_t dst_mode = (dst >> 3) & 7; + const uint8_t dst_reg = dst & 7; + + int operation = (instr >> 9) & 7; + + switch(operation) { + case 0: { // MUL + D(fprintf(stderr, "MUL\n");) + uint16_t R = getRegister(reg); + int32_t result = R * getGAM(dst_mode, dst_reg, true, false, dst_gam_text); + + if (reg & 1) + setRegister(reg, result >> 16); + else { + setRegister(reg, result & 65535); + setRegister(reg + 1, result >> 16); + } + + setPSW_n(result < 0); + setPSW_z(result == 0); + setPSW_z(result < -32768 || result > 32767); + return true; + } + + case 1: { // DIV + int32_t R0R1 = (getRegister(reg) << 16) | getRegister(reg + 1); + int32_t divider = getGAM(dst_mode, dst_reg, true, false, dst_gam_text); + + if (divider == 0) { + setPSW_n(false); + setPSW_z(true); + setPSW_v(true); + setPSW_c(true); + } + else { + int32_t quot = R0R1 / divider; + uint16_t rem = R0R1 % divider; + + D(fprintf(stderr, "DIV R%d,%s \t%d/%d = %d,%d\n", reg, dst_gam_text -> c_str(), R0R1, divider, quot, rem);) + + setRegister(reg, quot); + setRegister(reg + 1, rem); + + setPSW_n(R0R1 / divider < 0); + setPSW_z(quot == 0); + setPSW_v(quot > 0xffff || quot < -0xffff); + setPSW_c(false); + } + + return true; + } + + case 2: { // ASH + int16_t R = getRegister(reg), oldR = R; + int8_t shift = getGAM(dst_mode, dst_reg, true, false, dst_gam_text); + D(fprintf(stderr, "ASH R%d,%d\n", reg, shift);) + + if (shift > 0) { + R <<= shift - 1; + setPSW_c(R & 0x8000); + R <<= 1; + } + else if (shift < 0) { + R >>= -shift - 1; + setPSW_c(R & 1); + R >>= 1; + } + + setPSW_n(R < 0); + setPSW_z(R == 0); + setPSW_v(sign(R) != sign(oldR)); + + setRegister(reg, R); + return true; + } + + case 3: { // ASHC + D(fprintf(stderr, "ASHC\n");) + uint32_t R0R1 = (getRegister(reg) << 16) | getRegister(reg + 1); + int16_t shift = getGAM(dst_mode, dst_reg, true, false, dst_gam_text); + + if (shift > 0) { + R0R1 <<= (shift & 0b111111) - 1; + setPSW_c(R0R1 >> 31); + R0R1 <<= 1; + } + else if (shift < 0) { + R0R1 >>= -(shift & 0b111111) - 1; + setPSW_c(R0R1 & 1); + R0R1 >>= 1; + } + + setRegister(reg, R0R1 & 65535); + setRegister(reg + 1, R0R1 >> 16); + + setPSW_n(R0R1 >> 31); + setPSW_z(R0R1 == 0); + + return true; + } + + case 4: { // XOR (word only) + D(fprintf(stderr, "XOR\n");) + uint16_t src_value = getGAM(dst_mode, dst_reg, true, false, dst_gam_text) ^ getRegister(reg); + putGAM(dst_mode, dst_reg, false, src_value, false, dst_gam_text); + setPSW_n(src_value & 0x8000); + setPSW_z(src_value == 0); + setPSW_v(false); + return true; + } + + case 7: { // SOB + D(fprintf(stderr, "SOB (%o)\n", reg);) + uint16_t oldPC = getPC(); // FIXME gaat dit wel goed voor R7? + addRegister(reg, false, -1); + if (getRegister(reg, false)) { + assert(dst >= 0); + uint16_t newPC = oldPC - dst * 2; + D(fprintf(stderr, " jump back from %o to %o\n", oldPC, newPC);) + setPC(newPC); + } + return true; + } + } + + return false; +} + +bool cpu::single_operand_instructions(const uint16_t instr) +{ + const uint16_t opcode = (instr >> 6) & 0b111111111; + const uint8_t dst = instr & 63; + const uint8_t dst_mode = (dst >> 3) & 7; + const uint8_t dst_reg = dst & 7; + const bool word_mode = !!(instr & 0x8000); + uint16_t a = -1; + int32_t vl = -1; + uint16_t v = -1; + +#ifdef _DEBUG + std::string debug_b; + std::string *dst_gam_text = &debug_b; +#endif + + switch(opcode) { + case 0b00000011: // SWAB + if (word_mode) // handled elsewhere + return false; + else { + D(fprintf(stderr, "SWAB ");) + a = getGAMAddress(dst_mode, dst_reg, word_mode, false); + uint8_t t1, t2; + uint16_t t; + if (dst_mode == 0) { + D(fprintf(stderr, "R%d\n", dst_reg);) + t = getRegister(dst_reg, false); + t1 = t >> 8; + t2 = t & 255; + setRegister(dst_reg, false, (t2 << 8) | t1); + } + else { + D(fprintf(stderr, "\n");) + t = getRegister(dst_reg, false); + t1 = b -> readByte(a); + t2 = b -> readByte(a + 1); + + b -> writeByte(a, t2); + b -> writeByte(a + 1, t1); + } + + setPSW_n(t2 & 0x80); + setPSW_z(t2 == 0); + setPSW_v(false); + setPSW_c(false); + break; + } + + case 0b000101000: // CLR/CLRB + a = getGAMAddress(dst_mode, dst_reg, word_mode, false); + D(fprintf(stderr, "CLR (wm%d, mode%d,reg%d): addr %o, value %o\n", word_mode, dst_mode, dst_reg, a, b -> read(a, word_mode));) + if (dst_mode == 0) + putGAM(dst_mode, dst_reg, word_mode, 0, false, dst_gam_text); + else + b -> write(a, word_mode, 0); + setPSW_n(false); + setPSW_z(true); + setPSW_v(false); + setPSW_c(false); + break; + + case 0b000101001: // COM/COMB + D(fprintf(stderr, "COM%c\n", word_mode ? 'B' : ' ');) + a = getGAMAddress(dst_mode, dst_reg, word_mode, false); + vl = b -> read(a, word_mode); + if (word_mode) + vl ^= 0xff; + else + vl ^= 0xffff; + + setPSW_n(SIGN(vl, word_mode)); + setPSW_z(vl == 0); + setPSW_v(false); + setPSW_c(true); + + if (dst_mode == 0) + putGAM(dst_mode, dst_reg, word_mode, vl, false, dst_gam_text); + else + b -> write(a, word_mode, vl); + + break; + + case 0b000101010: // INC/INCB + D(fprintf(stderr, "INC%c dst-mode %d\n", word_mode ? 'B' : ' ', dst_mode);) + a = getGAMAddress(dst_mode, dst_reg, word_mode, false); + D(fprintf(stderr, " read from %o\n", a);) + v = b -> read(a, word_mode); + vl = (v + 1) & (word_mode ? 0xff : 0xffff); + setPSW_n(word_mode ? vl > 127 : vl > 32767); + setPSW_z(vl == 0); + setPSW_v(word_mode ? v == 0x7f : v == 0x7fff); + if (dst_mode == 0) + putGAM(dst_mode, dst_reg, word_mode, vl, false, dst_gam_text); + else + b -> write(a, word_mode, vl); + break; + + case 0b000101011: // DEC/DECB + D(fprintf(stderr, "DEC%c\n", word_mode ? 'B' : ' ');) + a = getGAMAddress(dst_mode, dst_reg, word_mode, false); + v = b -> read(a, word_mode); + vl = (v - 1) & (word_mode ? 0xff : 0xffff); + setPSW_n(word_mode ? vl > 127 : vl > 32767); + setPSW_z(vl == 0); + setPSW_v(word_mode ? v == 0x80 : v == 0x8000); + if (dst_mode == 0) + putGAM(dst_mode, dst_reg, word_mode, vl, false, dst_gam_text); + else + b -> write(a, word_mode, vl); + break; + + case 0b000101100: // NEG/NEGB + D(fprintf(stderr, "NEG%c\n", word_mode ? 'B' : ' ');) + a = getGAMAddress(dst_mode, dst_reg, word_mode, false); + v = b -> read(a, word_mode); + vl = word_mode ? uint8_t(-int8_t(v)) : -int16_t(v); + if (dst_mode == 0) + putGAM(dst_mode, dst_reg, word_mode, vl, false, dst_gam_text); + else + b -> write(a, word_mode, vl); + setPSW_n(SIGN(vl, word_mode)); + setPSW_z(vl == 0); + setPSW_v(word_mode ? vl == 0x80 : vl == 0x8000); + setPSW_c(vl); + break; + + case 0b000101101: { // ADC/ADCB + D(fprintf(stderr, "ADC%c\n", word_mode ? 'B' : ' ');) + a = getGAMAddress(dst_mode, dst_reg, word_mode, false); + uint16_t org = b -> read(a, word_mode); + uint16_t new_ = org + getPSW_c(); + if (dst_mode == 0) + putGAM(dst_mode, dst_reg, word_mode, new_, false, dst_gam_text); + else + b -> write(a, word_mode, new_); + setPSW_n(SIGN(new_, word_mode)); + setPSW_z(new_ == 0); + setPSW_v((word_mode ? org == 0x7f : org == 0x7fff) && getPSW_c()); + setPSW_c((word_mode ? org == 0xff : org == 0xffff) && getPSW_c()); + break; + } + + case 0b000101110: // SBC/SBCB + D(fprintf(stderr, "SBC%c\n", word_mode ? 'B' : ' ');) + a = getGAMAddress(dst_mode, dst_reg, word_mode, false); + //fprintf(stderr, "%d,%d\n", dst_mode, dst_reg); + v = b -> read(a, word_mode); + vl = (v - getPSW_c()) & (word_mode ? 0xff : 0xffff); + if (dst_mode == 0) + putGAM(dst_mode, dst_reg, word_mode, vl, false, dst_gam_text); + else + b -> write(a, word_mode, vl); + setPSW_n(SIGN(vl, word_mode)); + setPSW_z(vl == 0); + setPSW_v(vl == 0x8000); + + if (v == 0 && getPSW_c()) + setPSW_c(true); + else + setPSW_c(false); + break; + + case 0b000101111: // TST/TSTB + D(fprintf(stderr, "TST%c\n", word_mode ? 'B' : ' ');) + v = getGAM(dst_mode, dst_reg, word_mode, false, dst_gam_text); + setPSW_n(word_mode ? v & 128 : v & 32768); + setPSW_z(v == 0); + setPSW_v(false); + setPSW_c(false); + break; + + case 0b000110000: { // ROR/RORB + D(fprintf(stderr, "ROR%c\n", word_mode ? 'B' : ' ');) + a = getGAMAddress(dst_mode, dst_reg, word_mode, false); + uint16_t t = b -> read(a, word_mode); + bool new_carry = t & 1; + + uint16_t temp = 0; + if (word_mode) { + temp = (t >> 1) | (getPSW_c() << 7); + if (dst_mode == 0) + putGAM(dst_mode, dst_reg, word_mode, temp, false, dst_gam_text); + else + b -> writeByte(a, temp); + } + else { + temp = (t >> 1) | (getPSW_c() << 15); + if (dst_mode == 0) + putGAM(dst_mode, dst_reg, word_mode, temp, false, dst_gam_text); + else + b -> writeWord(a, temp); + } + + setPSW_c(new_carry); + + //fprintf(stderr, "%04x\n", temp); + setPSW_n(SIGN(temp, word_mode)); + setPSW_z(temp == 0); + setPSW_v(getPSW_c() ^ getPSW_n()); + + break; + } + + case 0b000110001: { // ROL/ROLB + D(fprintf(stderr, "ROL%c, carry is %d\n", word_mode ? 'B' : ' ', getPSW_c());) + a = getGAMAddress(dst_mode, dst_reg, word_mode, false); + uint16_t t = b -> read(a, word_mode); + bool new_carry = false; + + uint16_t temp; + if (word_mode) { + new_carry = t & 0x80; + temp = ((t << 1) | getPSW_c()) & 0xff; + if (dst_mode == 0) + putGAM(dst_mode, dst_reg, word_mode, temp, false, dst_gam_text); + else + b -> writeByte(a, temp); + } + else { + new_carry = t & 0x8000; + temp = (t << 1) | getPSW_c(); + if (dst_mode == 0) + putGAM(dst_mode, dst_reg, word_mode, temp, false, dst_gam_text); + else + b -> writeWord(a, temp); + } + + setPSW_c(new_carry); + + setPSW_n(SIGN(temp, word_mode)); + setPSW_z(temp == 0); + setPSW_v(getPSW_c() ^ getPSW_n()); + + break; + } + + case 0b000110010: { // ASR/ASRB + D(fprintf(stderr, "ASR%c\n", word_mode ? 'B' : ' ');) + a = getGAMAddress(dst_mode, dst_reg, word_mode, false); + vl = b -> read(a, word_mode); + + bool hb = word_mode ? vl & 128 : vl & 32768; + + setPSW_c(vl & 1); + vl >>= 1; + if (word_mode) + vl |= hb << 7; + else + vl |= hb << 15; + + if (dst_mode == 0) + putGAM(dst_mode, dst_reg, word_mode, vl, false, dst_gam_text); + else + b -> write(a, word_mode, vl); + + setPSW_n(SIGN(vl, word_mode)); + setPSW_z(vl == 0); + setPSW_v(getPSW_n() ^ getPSW_c()); + break; + } + + case 0b00110011: // ASL/ASLB + D(fprintf(stderr, "ASL%c\n", word_mode ? 'B' : ' ');) + a = getGAMAddress(dst_mode, dst_reg, word_mode, false); + vl = b -> read(a, word_mode); + v = (vl << 1) & (word_mode ? 0xff : 0xffff); + setPSW_n(word_mode ? v & 0x80 : v & 0x8000); + setPSW_z(v == 0); + setPSW_c(word_mode ? vl & 0x80 : vl & 0x8000); + setPSW_v(getPSW_n() ^ getPSW_c()); + if (dst_mode == 0) + putGAM(dst_mode, dst_reg, word_mode, v, false, dst_gam_text); + else + b -> write(a, word_mode, v); + break; + + case 0b00110101: // MFPD/MFPI + // FIXME + v = getGAM(dst_mode, dst_reg, word_mode, true, dst_gam_text); + D(fprintf(stderr, "MFPD/MFPI %s\n", dst_gam_text -> c_str());) + setPSW_n(word_mode ? v & 0x80 : v & 0x8000); + setPSW_z(v == 0); + setPSW_v(false); + pushStack(v); + break; + + case 0b00110110: // MTPI/MTPD + // FIXME + a = getGAMAddress(dst_mode, dst_reg, word_mode, false); + fprintf(stderr, "MTPI/MTPD\n"); + v = popStack(); + setPSW_n(word_mode ? v & 0x80 : v & 0x8000); + setPSW_z(v == 0); + setPSW_v(false); + if (dst_mode == 0) + putGAM(dst_mode, dst_reg, word_mode, v, true, dst_gam_text); + else + b -> write(a, word_mode, v); // ? + break; + + default: + return false; + } + + return true; +} + +bool cpu::conditional_branch_instructions(const uint16_t instr) +{ + const uint8_t opcode = (instr >> 8) & 255; + const int8_t offset = instr & 255; + bool take = false; +#ifdef _DEBUG + std::string name; +#endif + + switch(opcode) { + case 0b00000001: // BR + take = true; + D(name = "BR";) + break; + + case 0b00000010: // BNE + take = !getPSW_z(); + D(name = "BNE";) + break; + + case 0b00000011: // BEQ + take = getPSW_z(); + D(name = "BEQ";) + break; + + case 0b00000100: // BGE + take = (getPSW_n() ^ getPSW_v()) == false; + D(name = "BGE";) + break; + + case 0b00000101: // BLT + take = getPSW_n() ^ getPSW_v(); + D(name = "BLT";) + break; + + case 0b00000110: // BGT + take = ((getPSW_n() ^ getPSW_v()) | getPSW_z()) == false; + D(name = "BGT";) + break; + + case 0b00000111: // BLE + take = (getPSW_n() ^ getPSW_v()) | getPSW_z(); + D(name = "BLE";) + break; + + case 0b10000000: // BPL + take = getPSW_n() == false; + D(name = "BPL";) + break; + + case 0b10000001: // BMI + take = getPSW_n() == true; + D(name = "BMI";) + break; + + case 0b10000010: // BHI + take = getPSW_c() == false && getPSW_z() == false; + D(name = "BHI";) + break; + + case 0b10000011: // BLOS + take = getPSW_c() | getPSW_z(); + D(name = "BLOS";) + break; + + case 0b10000100: // BVC + take = getPSW_v() == false; + D(name = "BVC";) + break; + + case 0b10000101: // BVS + take = getPSW_v(); + D(name = "BVS";) + break; + + case 0b10000110: // BCC + take = getPSW_c() == false; + D(name = "BCC";) + break; + + case 0b10000111: // BCS / BLO + take = getPSW_c(); + D(name = "BCS/BLO";) + break; + + default: + return false; + } + + D(fprintf(stderr, "%s %o (%d)\n", name.c_str(), offset, take);) + if (take) { + D(fprintf(stderr, "branch %d from 0o%o to 0o%o\n", offset * 2, getPC(), getPC() + offset * 2);) + addRegister(7, false, offset * 2); + } + + return true; +} + +bool cpu::condition_code_operations(const uint16_t instr) +{ + switch(instr) { + case 0b0000000010100000: // NOP + case 0b0000000010110000: // NOP + D(fprintf(stderr, "NOP\n");) + return true; + } + + if ((instr & ~7) == 0000230) { // SPLx + int level = instr & 7; + D(fprintf(stderr, "SPL %d\n", level);) + setPSW_spl(level); + return true; + } + + if ((instr & ~31) == 0b10100000) { // set condition bits + D(fprintf(stderr, "%s n%dz%dv%dc%d\n", instr & 0b10000 ? "SET" : "CLR", !!(instr & 8), !!(instr & 4), !!(instr & 2), instr & 1);) + if (instr & 0b10000) { + setPSW_n(instr & 0b1000); + setPSW_z(instr & 0b0100); + setPSW_v(instr & 0b0010); + setPSW_c(instr & 0b0001); + } + else { + if (instr & 0b1000) + setPSW_n(false); + if (instr & 0b0100) + setPSW_z(false); + if (instr & 0b0010) + setPSW_v(false); + if (instr & 0b0001) + setPSW_c(false); + } + + return true; + } + + return false; +} + +void cpu::pushStack(const uint16_t v) +{ + addRegister(6, false, -2); + b -> writeWord(getRegister(6, false), v); +} + +uint16_t cpu::popStack() +{ + uint16_t temp = b -> readWord(getRegister(6, false)); + addRegister(6, false, 2); + return temp; +} + +void cpu::switchModeToKernel() +{ + int previous_mode = (psw >> 14) & 3; + psw &= 0007777; + psw |= previous_mode << 12; +} + +bool cpu::misc_operations(const uint16_t instr) +{ + switch(instr) { + case 0b0000000000000000: // HALT + D(fprintf(stderr, "HALT\n");) + haltFlag = true; + { + FILE *fh = fopen("halt.dat", "wb"); + for(int i=0; i<256; i++) + fputc(b -> readByte(getPC() - 2 + i), fh); + fclose(fh); + } + return true; + + case 0b0000000000000001: // WAIT + D(fprintf(stderr, "WAIT\n");) + return true; + + case 0b0000000000000010: // RTI + D(fprintf(stderr, "RTI\n");) + setPC(popStack()); + setPSW(popStack()); + return true; + + case 0b0000000000000110: // RTT + D(fprintf(stderr, "RTT\n");) + setPC(popStack()); + setPSW(popStack()); + return true; + + case 0b0000000000000111: // MFPT + D(fprintf(stderr, "MFPT\n");) + if (emulateMFPT) + setRegister(0, true, 1); // PDP-11/44 + else { + pushStack(getPSW()); + pushStack(getPC()); + setPC(b -> readWord(012)); + setPSW(b -> readWord(014)); + } + return true; + + case 0b0000000000000101: // RESET + D(fprintf(stderr, "RESET\n");) + resetFlag = true; + return true; + } + + if ((instr >> 8) == 0b10001000) { // EMT + D(fprintf(stderr, "EMT\n");) + pushStack(getPSW()); + pushStack(getPC()); + setPC(b -> readWord(030)); + setPSW(b -> readWord(032)); + return true; + } + + if ((instr >> 8) == 0b10001001) { // TRAP + pushStack(getPSW()); + pushStack(getPC()); + switchModeToKernel(); + setPC(b -> readWord(034)); + setPSW(b -> readWord(036)); + D(fprintf(stderr, "TRAP (sp: %o, new pc: %o)\n", getRegister(6, false), getPC());) + return true; + } + + if ((instr & ~0b111111) == 0b0000000001000000) { // JMP + int dst_mode = (instr >> 3) & 7, dst_reg = instr & 7; + bool word_mode = false; + setPC(getGAMAddress(dst_mode, dst_reg, word_mode, false)); + D(fprintf(stderr, "JMP %o\n", getPC());) + return true; + } + + if ((instr & 0b1111111000000000) == 0b0000100000000000) { // JSR + const int link_reg = (instr >> 6) & 7; + uint16_t dst_value = getGAMAddress((instr >> 3) & 7, instr & 7, false, false); + D(fprintf(stderr, "JSR R%d,%o\n", link_reg, dst_value);) + + // PUSH link + pushStack(getRegister(link_reg, false)); + + // MOVE PC,link + setRegister(link_reg, false, getPC()); + + // JMP dst + setPC(dst_value); + + return true; + } + + if ((instr & 0b1111111111111000) == 0b0000000010000000) { // RTS + const int link_reg = instr & 7; + + // MOVE link, PC + setPC(getRegister(link_reg, false)); + uint16_t temp = getPC(); + + // POP link + setRegister(link_reg, false, popStack()); + + D(fprintf(stderr, "RTS new PC: %o, link reg %d: %o\n", temp, link_reg, getRegister(link_reg, false));) + + return true; + } + + return false; +} + +void cpu::busError() +{ + fprintf(stderr, " *** BUS ERROR ***\n"); + // PSW = 177776 + // mov @#PSW, -(sp) + pushStack(getPSW()); + + // mov pc, -(sp) + pushStack(getPC()); + + // mov @#VEC+2, @#PSW + setPSW(b -> readWord(6)); + + // mov @#VEC, pc + setPC(b -> readWord(4)); + fprintf(stderr, " GO TO %o, PSW %o\n", getPC(), getPSW()); +} + +bool cpu::step() +{ + if (getPC() == 0xbfba) { + FILE *fh = fopen("debug.dat", "wb"); + for(int i=0; i<256; i++) + fputc(b -> readByte(getPC() + i), fh); + fclose(fh); + } + + if (getPC() & 1) { + D(fprintf(stderr, "odd addressing\n");) + busError(); + } + + D(fprintf(stderr, "0x%04x/%d/0o%06o:", getPC(), getPC(), getPC());) + b -> setMMR2(getPC()); + uint16_t instr = b -> readWord(getPC()); + D(fprintf(stderr, " INSTR 0x%04x 0o%06o\n", instr, instr);) + addRegister(7, false, 2); + D(fprintf(stderr, " PC is now %o\n", getPC());) + + // if (single_operand_instructions(instr)) + // goto ok; + + if (double_operand_instructions(instr)) + goto ok; + + if (conditional_branch_instructions(instr)) + goto ok; + + if (condition_code_operations(instr)) + goto ok; + + if (misc_operations(instr)) + goto ok; + + fprintf(stderr, "UNHANDLED instruction %o\n\n", instr); + + { + FILE *fh = fopen("fail.dat", "wb"); + for(int i=0; i<256; i++) + fputc(b -> readByte(getPC() - 2 + i), fh); + fclose(fh); + } + busError(); + exit(1); + return false; + + return true; + +ok: +#ifdef _DEBUG + for(int r=0; r<8; r++) + fprintf(stderr, "%06o ", getRegister(r, false)); + fprintf(stderr, " | n%dz%dv%dc%d P%dC%d S%d", getPSW_n(), getPSW_z(), getPSW_v(), getPSW_c(), (getPSW() >> 12) & 3, getPSW() >> 14, getBitPSW(11)); + fprintf(stderr, "\n\n"); +#endif + + return haltFlag; // return flags that indicate that special attention is required +} diff --git a/cpu.h b/cpu.h new file mode 100644 index 0000000..b88f986 --- /dev/null +++ b/cpu.h @@ -0,0 +1,92 @@ +// (C) 2018 by Folkert van Heusden +// Released under AGPL v3.0 +#pragma once + +#include +#include + +#include "bus.h" + +class cpu +{ +private: + uint16_t regs0_5[2][6]; // R0...5, selected by bit 11 in PSW, + uint16_t sp[3+1]; // stackpointers, MF../MT.. select via 12/13 from PSW, others via 14/15 + uint16_t pc { 0 }; + uint16_t psw { 0 }, fpsr { 0 }; + uint16_t stackLimitRegister { 0 }; + bool haltFlag { false }, resetFlag { false }; + bool runMode { false }; + + bool emulateMFPT { false }; + + bus *const b { nullptr }; + + uint16_t getRegister(const int nr, const bool MF_MT) const; + void setRegister(const int nr, const bool MF_MT, const uint16_t value); + void addRegister(const int nr, const bool MF_MT, const uint16_t value); + + uint16_t getGAMAddress(const uint8_t mode, const int reg, const bool word_mode, const bool MF_MT); + uint16_t getGAM(const uint8_t mode, const uint8_t reg, const bool word_mode, const bool MF_MT, std::string *const text); + void putGAM(const uint8_t mode, const int reg, const bool word_mode, const uint16_t value, const bool MF_FT, std::string *const text); + + void switchModeToKernel(); + + bool double_operand_instructions(const uint16_t instr); + bool additional_double_operand_instructions(const uint16_t instr); + bool single_operand_instructions(const uint16_t instr); + bool conditional_branch_instructions(const uint16_t instr); + bool condition_code_operations(const uint16_t instr); + bool misc_operations(const uint16_t instr); + +public: + explicit cpu(bus *const b); + ~cpu(); + + bus *getBus() { return b; } + + void reset(); + + bool step(); // FIXME return mask of flags (halt, reset, etc) + void resetHalt() { haltFlag = false; } + void resetReset() { resetFlag = false; } + + void pushStack(const uint16_t v); + uint16_t popStack(); + + void busError(); + + void setEmulateMFPT(const bool v) { emulateMFPT = v; } + + bool getRunMode() { return runMode; } + + bool getPSW_c() const; + bool getPSW_v() const; + bool getPSW_z() const; + bool getPSW_n() const; + bool getBitPSW(const int bit) const; + + void setPSW_c(const bool v); + void setPSW_v(const bool v); + void setPSW_z(const bool v); + void setPSW_n(const bool v); + void setPSW_spl(const int v); + void setBitPSW(const int bit, const bool v); + + uint16_t getPSW() const { return psw; } + void setPSW(const uint16_t v) { psw = v; } + + uint16_t getStackLimitRegister() { return stackLimitRegister; } + void setStackLimitRegister(const uint16_t v) { stackLimitRegister = v; } + + uint16_t getRegister(const bool user, const int nr) const { assert(nr >= 0 && nr < 6); return regs0_5[user][nr]; } + uint16_t getStackPointer(const int which) const { assert(which >= 0 && which < 4); return sp[which]; } + uint16_t getPC() const { return pc; } + + void setRegister(const bool user, const int nr, const uint16_t value) { assert(nr >= 0 && nr < 6); regs0_5[user][nr] = value; } + void setStackPointer(const int which, const uint16_t value) { assert(which >= 0 && which < 4); sp[which] = value; } + void setPC(const uint16_t value) { pc = value; } + + uint16_t getRegister(const int nr) const { return getRegister(nr, false); } // FIXME remove + void setRegister(const int nr, const uint16_t v) { setRegister(nr, false, v); } // FIXME remove +}; diff --git a/error.cpp b/error.cpp new file mode 100644 index 0000000..7ecee20 --- /dev/null +++ b/error.cpp @@ -0,0 +1,29 @@ +// (C) 2018 by folkert@vanheusden.com, released under AGPL 3.0 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "error.h" + +[[ noreturn ]] void error_exit(bool sys_err, const char *format, ...) +{ + int e = errno; + va_list ap; + + (void)endwin(); + + va_start(ap, format); + (void)vfprintf(stderr, format, ap); + va_end(ap); + + if (sys_err == TRUE) + fprintf(stderr, "error: %s (%d)\n", strerror(e), e); + + exit(1); +} diff --git a/error.h b/error.h new file mode 100644 index 0000000..108a3a4 --- /dev/null +++ b/error.h @@ -0,0 +1,2 @@ +// (C) 2018 by folkert@vanheusden.com, released under AGPL 3.0 +[[ noreturn ]] void error_exit(bool sys_err, const char *format, ...); diff --git a/gen.h b/gen.h new file mode 100644 index 0000000..d101bd0 --- /dev/null +++ b/gen.h @@ -0,0 +1,7 @@ +// (C) 2018 by Folkert van Heusden +// Released under AGPL v3.0 +#ifdef _DEBUG +#define D(x) do { x } while(0); +#else +#define D(...) do { } while(0); +#endif diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..f6646eb --- /dev/null +++ b/main.cpp @@ -0,0 +1,329 @@ +// (C) 2018 by Folkert van Heusden +// Released under AGPL v3.0 +#include +#include +#include +#include + +#include "memory.h" +#include "cpu.h" +#include "tty.h" +#include "utils.h" +#include "tests.h" +#include "terminal.h" +#include "error.h" + +void loadbin(bus *const b, uint16_t base, const char *const file) +{ + FILE *fh = fopen(file, "rb"); + + while(!feof(fh)) + b -> writeByte(base++, fgetc(fh)); + + fclose(fh); +} + +void setBootLoader(bus *const b) +{ + cpu *const c = b -> getCpu(); + +#if 0 + const uint16_t offset = 0200; + constexpr uint16_t bootrom[] = { + 0012737, + 0000400, + 0177572, + 0012737, + 0070707, + 0000200, + 0000000 + }; +#else +#if 1 + const uint16_t offset = 01000; + constexpr uint16_t bootrom[] = { + 0012700, + 0177406, + 0012710, + 0177400, + 0012740, + 0000005, + 0105710, + 0100376, + 0005007 + }; +#else + const uint16_t offset = 02000; + constexpr uint16_t bootrom[] = { +// 0042113, + 0012706, + 0002000, + 0012700, + 0000000, /* boot unit */ + 0010003, + 0000303, + 0006303, + 0006303, + 0006303, + 0006303, + 0006303, + 0012701, + 0177412, + 0010311, + 0005041, + 0012741, + 0177000, + 0012741, + 0000005, + 0005002, + 0005003, + 0012704, + 0002020, + 0005005, + 0105711, + 0100376, + 0105011, + 0005007 + }; +#endif +#endif + + FILE *fh = fopen("boot.dat", "wb"); + + for(size_t i=0; i writeWord(offset + i * 2, bootrom[i]); + fputc(bootrom[i] & 255, fh); + fputc(bootrom[i] >> 8, fh); + } + + fclose(fh); + + c -> setRegister(7, offset); +} + +uint16_t loadTape(bus *const b, const char *const file) +{ + FILE *fh = fopen(file, "rb"); + if (!fh) { + fprintf(stderr, "Cannot open %s\n", file); + return -1; + } + + uint16_t start = 0, end = 0; + + for(;!feof(fh);) { + uint8_t buffer[6]; + + if (fread(buffer, 1, 6, fh) != 6) + break; + + int count = (buffer[3] << 8) | buffer[2]; + int p = (buffer[5] << 8) | buffer[4]; + + uint8_t csum = 0; + for(int i=2; i<6; i++) + csum += buffer[i]; + + if (count == 6) { // eg no data + if (p != 1) { + fprintf(stderr, "Setting start address to %o\n", p); + start = p; + } + } + + fprintf(stderr, "%ld] reading %d (dec) bytes to %o (oct)\n", ftell(fh), count - 6, p); + + for(int i=0; i writeByte(p++, c); + + if (p > end) + end = p; + } + + int fcs = fgetc(fh); + csum += fcs; + + if (csum != 255) + fprintf(stderr, "checksum error %d\n", csum); + } + + fclose(fh); + + fh = fopen("test.dat", "wb"); + for(int i=0; i readByte(i), fh); + fclose(fh); + + return start; +} + +NEWWIN *w_main_b = nullptr, *w_main = nullptr; + +void resize_terminal() +{ + determine_terminal_size(); + + if (ERR == resizeterm(max_y, max_x)) + error_exit(true, "problem resizing terminal"); + + wresize(stdscr, max_y, max_x); + + endwin(); + refresh(); + + wclear(stdscr); + + delete_window(w_main_b); + delete_window(w_main); + create_win_border(0, 0, max_x - 2, max_y - 2, "window", &w_main_b, &w_main, false); + scrollok(w_main -> win, TRUE); + + mydoupdate(); +} + +volatile bool sw = false; +void sw_handler(int s) +{ + sw = true; +} + +void help() +{ + printf("-h this help\n"); + printf("-m mode \"test\": for running xxdp (stop on bell)\n"); + printf(" \"tc\": run testcases\n"); + printf("-T t.bin load file as a binary tape file (like simh \"load\" command)\n"); + printf("-R d.rk load file as a RK05 disk device\n"); + printf("-p 123 set CPU start pointer to decimal(!) value\n"); + printf("-L f.bin load file into memory at address given by -p (and run it)\n"); + printf("-n ncurses UI\n"); +} + +int main(int argc, char *argv[]) +{ + //setlocale(LC_ALL, ""); + + bus *b = new bus(); + cpu *c = new cpu(b); + b->add_cpu(c); + + c -> setEmulateMFPT(true); + + bool testMode = false, testCases = false, withUI = false; + int opt = -1; + while((opt = getopt(argc, argv, "hm:T:R:p:nL:")) != -1) + { + switch(opt) { + case 'h': + help(); + return 1; + + case 'n': + withUI = true; + break; + + case 'm': + if (strcasecmp(optarg, "test") == 0) + testMode = true; + else if (strcasecmp(optarg, "tc") == 0) + testCases = true; + else { + fprintf(stderr, "\"-m %s\" is not known\n", optarg); + return 1; + } + break; + + case 'T': + c->setRegister(7, loadTape(b, optarg)); + break; + + case 'R': { + b->add_rk05(new rk05(optarg, b)); + setBootLoader(b); + break; + } + + case 'p': + c->setRegister(7, atoi(optarg)); + break; + + case 'L': + loadbin(b, c->getRegister(7), optarg); + break; + + default: + fprintf(stderr, "-%c is not understood\n", opt); + return 1; + } + } + + tty *tty_ = new tty(withUI); + b->add_tty(tty_); + + if (testMode) + tty_->setTest(); + + if (testCases) + tests(c); + + fprintf(stderr, "Start running at %o\n", c->getRegister(7)); + + if (withUI) { + init_ncurses(true); + + struct sigaction sa; + sa.sa_handler = sw_handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + sigaction(SIGWINCH, &sa, nullptr); + resize_terminal(); + } + + struct pollfd fds[] = { { 0, POLLIN, 0 } }; + + const unsigned long start = get_ms(); + unsigned long icount = 0; + for(;;) { + if (c->step()) { + //c->setRegister(7, 01000); + //c->resetHalt(); + break; + } + + icount++; + + if (icount % 1000 == 0) { + if (poll(fds, 1, 0) == 1) { + int ch = getch(); + + if (ch == 3) + break; + + if (ch > 0 && ch < 127) + tty_->sendChar(ch); + + fds[0].revents = 0; + } + + if (icount % 1000000 == 0 && withUI) { + unsigned long now = get_ms(); + mvwprintw(w_main_b -> win, 0, 24, "%.1f/s ", icount * 1000.0 / (now - start)); + mydoupdate(); + } + } + } + + if (withUI) + endwin(); + + delete b; + + return 0; +} diff --git a/memory.cpp b/memory.cpp new file mode 100644 index 0000000..13c0601 --- /dev/null +++ b/memory.cpp @@ -0,0 +1,20 @@ +// (C) 2018 by Folkert van Heusden +// Released under AGPL v3.0 +#include + +#include "memory.h" + +memory::memory(const uint32_t size) : size(size) +{ + m = new uint8_t[size](); +} + +memory::~memory() +{ + delete [] m; +} + +void memory::reset() +{ + memset(m, 0x00, size); +} diff --git a/memory.h b/memory.h new file mode 100644 index 0000000..bf971e6 --- /dev/null +++ b/memory.h @@ -0,0 +1,24 @@ +// (C) 2018 by Folkert van Heusden +// Released under AGPL v3.0 +#pragma once + +#include + +class memory +{ +private: + const uint32_t size; + uint8_t *m { nullptr }; + +public: + memory(const uint32_t size); + ~memory(); + + void reset(); + + uint16_t readByte(const uint16_t a) const { return m[a]; } + void writeByte(const uint16_t a, const uint16_t v) { m[a] = v; } + + uint16_t readWord(const uint16_t a) const { return m[a] | (m[a + 1] << 8); } + void writeWord(const uint16_t a, const uint16_t v) { m[a] = v; m[a + 1] = v >> 8; } +}; diff --git a/rk05.cpp b/rk05.cpp new file mode 100644 index 0000000..21e85fb --- /dev/null +++ b/rk05.cpp @@ -0,0 +1,209 @@ +// (C) 2018 by Folkert van Heusden +// Released under AGPL v3.0 +#include +#include + +#include "rk05.h" +#include "gen.h" +#include "bus.h" +#include "utils.h" + +const char * const regnames[] = { + "RK05_DS drivestatus", + "RK05_ERROR ", + "RK05_CS ctrlstatus", + "RK05_WC word count", + "RK05_BA busaddress", + "RK05_DA disk addrs", + "RK05_DATABUF " + }; + +rk05::rk05(const std::string & file, bus *const b) : b(b) +{ + memset(registers, 0x00, sizeof registers); + memset(xfer_buffer, 0x00, sizeof xfer_buffer); + + fh = fopen(file.c_str(), "rb"); +} + +rk05::~rk05() +{ + fclose(fh); +} + +uint8_t rk05::readByte(const uint16_t addr) +{ + uint16_t v = readWord(addr & ~1); + + if (addr & 1) + return v >> 8; + + return v; +} + +uint16_t rk05::readWord(const uint16_t addr) +{ + const int reg = (addr - RK05_BASE) / 2; + + fprintf(stderr, "RK05 read %s/%o: ", reg[regnames], addr); + + if (addr == RK05_DS) { // 0177400 + setBit(registers[reg], 11, true); // disk on-line + setBit(registers[reg], 8, true); // sector ok + setBit(registers[reg], 7, true); // drive ready + setBit(registers[reg], 6, true); // seek ready + setBit(registers[reg], 4, true); // heads in position + } + else if (addr == RK05_ERROR) // 0177402 + registers[reg] = 0; + else if (addr == RK05_CS) { // 0177404 + setBit(registers[reg], 15, false); // clear error + setBit(registers[reg], 14, false); // clear hard error +#if 0 + if (registers[reg] & 1) + registers[reg] &= ~128; // controller ready + else + registers[reg] |= 128; // controller ready +#else + setBit(registers[reg], 7, true); // controller ready +#endif + } + + uint16_t vtemp = registers[reg]; + + if (addr == RK05_CS) + setBit(registers[reg], 0, false); // clear go + + fprintf(stderr, "%o\n", vtemp); + + return vtemp; +} + +void rk05::writeByte(const uint16_t addr, const uint8_t v) +{ + uint16_t vtemp = registers[(addr - RK05_BASE) / 2]; + + if (addr & 1) { + vtemp &= ~0xff00; + vtemp |= v << 8; + } + else { + vtemp &= ~0x00ff; + vtemp |= v; + } + + writeWord(addr, vtemp); +} + +void rk05::writeWord(const uint16_t addr, uint16_t v) +{ + const int reg = (addr - RK05_BASE) / 2; + fprintf(stderr, "RK05 write %s/%o: %o\n", regnames[reg], addr, v); + + if (addr == RK05_CS) { + if (v & 1) { // GO + const int func = (v >> 1) & 7; // FUNCTION + int16_t wc = registers[(RK05_WC - RK05_BASE) / 2]; + const size_t reclen = wc < 0 ? (-wc * 2) : wc * 2; + fprintf(stderr, "RK05 rec len %zd\n", reclen); + + uint16_t dummy = registers[(RK05_DA - RK05_BASE) / 2]; + uint8_t sector = dummy & 0b1111; + uint8_t surface = (dummy >> 4) & 1; + int track = (dummy >> 4) & 511; + uint16_t cylinder = (dummy >> 5) & 255; + fprintf(stderr, "RK05 position sec %d surf %d cyl %d\n", sector, surface, cylinder); + + const int diskoff = track * 12 + sector; + + const int diskoffb = diskoff * 512; // RK05 is high density + const uint16_t memoff = registers[(RK05_BA - RK05_BASE) / 2]; + + fprintf(stderr, "invoke %d\n", func); + + if (func == 0) { // controller reset + } + else if (func == 1) { // write + D(fprintf(stderr, "RK05 writing %zo bytes to offset %o (%d dec)\n", reclen, diskoffb, diskoffb);) + + int p = reclen; // FIXME + for(size_t i=0; i readByte(memoff + i); + + if (fseek(fh, diskoffb, SEEK_SET) == -1) + fprintf(stderr, "RK05 seek error %s\n", strerror(errno)); + if (fwrite(xfer_buffer, 1, reclen, fh) != reclen) + fprintf(stderr, "RK05 fwrite error %s\n", strerror(errno)); + + if (v & 2048) + fprintf(stderr, "RK05 inhibit BA increase\n"); + else + registers[(RK05_BA - RK05_BASE) / 2] += p; + + if (++sector >= 12) { + sector = 0; + if (++surface >= 2) { + surface = 0; + cylinder++; + } + } + + registers[(RK05_DA - RK05_BASE) / 2] = sector | (surface << 4) | (cylinder << 5); + } + else if (func == 2) { // read + fprintf(stderr, "RK05 reading %zo bytes from offset %o (%d dec) to %o\n", reclen, diskoffb, diskoffb, memoff); + + if (fseek(fh, diskoffb, SEEK_SET) == -1) + fprintf(stderr, "RK05 seek error %s\n", strerror(errno)); + + int temp = reclen; + int p = memoff; + while(temp > 0) { + int cur = std::min(int(sizeof xfer_buffer), temp); + + if (fread(xfer_buffer, 1, cur, fh) != size_t(cur)) + D(fprintf(stderr, "RK05 fread error: %s\n", strerror(errno));) + + for(int i=0; i writeByte(p, xfer_buffer[i]); + p++; + } + + temp -= cur; + } + + if (v & 2048) + fprintf(stderr, "RK05 inhibit BA increase\n"); + else + registers[(RK05_BA - RK05_BASE) / 2] += p; + + if (++sector >= 12) { + sector = 0; + if (++surface >= 2) { + surface = 0; + cylinder++; + } + } + registers[(RK05_DA - RK05_BASE) / 2] = sector | (surface << 4) | (cylinder << 5); + } + else if (func == 4) { + fprintf(stderr, "RK05 seek to offset %o\n", diskoffb); + } + else if (func == 7) + fprintf(stderr, "RK05 write lock\n"); + else { + fprintf(stderr, "RK05 command %d UNHANDLED\n", func); + } + + if (v & 64) { // bit 6, invoke interrupt when done vector address 220, see http://www.pdp-11.nl/peripherals/disk/rk05-info.html + fprintf(stderr, "RK05 HIER\n"); // FIXME + } + + registers[(RK05_WC - RK05_BASE) / 2] = 0; + } + } + + D(fprintf(stderr, "set register %o to %o\n", addr, v);) + registers[reg] = v; +} diff --git a/rk05.h b/rk05.h new file mode 100644 index 0000000..03b5252 --- /dev/null +++ b/rk05.h @@ -0,0 +1,39 @@ +// (C) 2018 by Folkert van Heusden +// Released under AGPL v3.0 +#pragma once + +#include +#include +#include + +// FIXME namen van defines +#define RK05_DS 0177400 // drive status +#define RK05_ERROR 0177402 // error +#define RK05_CS 0177404 // control status +#define RK05_WC 0177406 // word count +#define RK05_BA 0177410 // bus address +#define RK05_DA 0177412 // disk address +#define RK05_DATABUF 0177414 // data buffer +#define RK05_BASE RK05_DS +#define RK05_END (RK05_DATABUF + 2) + +class bus; + +class rk05 +{ +private: + bus *const b; + uint16_t registers[7]; + uint8_t xfer_buffer[512]; + FILE *fh; + +public: + rk05(const std::string & file, bus *const b); + virtual ~rk05(); + + uint8_t readByte(const uint16_t addr); + uint16_t readWord(const uint16_t addr); + + void writeByte(const uint16_t addr, const uint8_t v); + void writeWord(const uint16_t addr, uint16_t v); +}; diff --git a/rx02.cpp b/rx02.cpp new file mode 100644 index 0000000..28f4463 --- /dev/null +++ b/rx02.cpp @@ -0,0 +1,72 @@ +// (C) 2018 by Folkert van Heusden +// Released under AGPL v3.0 +#include +#include + +#include "rx02.h" +#include "gen.h" +#include "memory.h" +#include "utils.h" + +rx02::rx02(const std::string & file, memory *const m) : m(m) +{ + offset = 0; + memset(registers, 0x00, sizeof registers); + + fh = fopen(file.c_str(), "rb"); +} + +rx02::~rx02() +{ + fclose(fh); +} + +uint8_t rx02::readByte(const uint16_t addr) +{ + uint16_t v = readWord(addr & ~1); + + if (addr & 1) + return v >> 8; + + return v; +} + +uint16_t rx02::readWord(const uint16_t addr) +{ + const int reg = (addr - RX02_BASE) / 2; + uint16_t vtemp = registers[reg]; + + D(printf("RX02 read addr %o: ", addr);) + + // FIXME + + D(printf("%o\n", vtemp);) + + return vtemp; +} + +void rx02::writeByte(const uint16_t addr, const uint8_t v) +{ + uint16_t vtemp = registers[(addr - RX02_BASE) / 2]; + + if (addr & 1) { + vtemp &= ~0xff00; + vtemp |= v << 8; + } + else { + vtemp &= ~0x00ff; + vtemp |= v; + } + + writeWord(addr, vtemp); +} + +void rx02::writeWord(const uint16_t addr, uint16_t v) +{ + D(printf("RX02 write %o: %o\n", addr, v);) + + // FIXME + + D(printf("set register %o to %o\n", addr, v);) + registers[(addr - RX02_BASE) / 2] = v; +} diff --git a/rx02.h b/rx02.h new file mode 100644 index 0000000..2eda04e --- /dev/null +++ b/rx02.h @@ -0,0 +1,33 @@ +// (C) 2018 by Folkert van Heusden +// Released under AGPL v3.0 +#pragma once + +#include +#include +#include + +// FIXME namen van defines +#define RX02_BASE 0 +#define RX02_END (1 + 2) + +class memory; + +class rx02 +{ +private: + memory *const m; + uint16_t registers[7]; + uint8_t xfer_buffer[512]; + int offset; + FILE *fh; + +public: + rx02(const std::string & file, memory *const m); + virtual ~rx02(); + + uint8_t readByte(const uint16_t addr); + uint16_t readWord(const uint16_t addr); + + void writeByte(const uint16_t addr, const uint8_t v); + void writeWord(const uint16_t addr, uint16_t v); +}; diff --git a/terminal.cpp b/terminal.cpp new file mode 100644 index 0000000..cab05a3 --- /dev/null +++ b/terminal.cpp @@ -0,0 +1,322 @@ +// (C) 2018 by folkert@vanheusden.com, released under AGPL 3.0 +#include +#include +#include +#include +#include +#include +#include + +#include "terminal.h" +#include "error.h" +#include "utils.h" + +int max_x = 80, max_y = 25; + +void determine_terminal_size() +{ + struct winsize size; + + if (ioctl(1, TIOCGWINSZ, &size) == 0) + { + max_y = size.ws_row; + max_x = size.ws_col; + } + else + { + char *dummy = getenv("COLUMNS"); + if (dummy) + max_x = atoi(dummy); + + dummy = getenv("LINES"); + if (dummy) + max_x = atoi(dummy); + } +} + +void apply_mouse_setting(void) +{ + if (1) // FIXME (ignore_mouse) + mousemask(0, nullptr); + else + mousemask(BUTTON1_CLICKED | BUTTON1_DOUBLE_CLICKED | BUTTON3_CLICKED, nullptr); +} + +void init_ncurses(bool init_mouse) +{ + initscr(); + start_color(); + use_default_colors(); + keypad(stdscr, TRUE); + cbreak(); + intrflush(stdscr, FALSE); + noecho(); + nonl(); + refresh(); + nodelay(stdscr, FALSE); + meta(stdscr, TRUE); /* enable 8-bit input */ + raw(); /* to be able to catch ctrl+c */ + leaveok(stdscr, FALSE); + + if (init_mouse) + apply_mouse_setting(); + + max_y = LINES; + max_x = COLS; +} + +void wrong_key(void) +{ + flash(); + beep(); + flushinp(); +} + +void color_on(NEWWIN *win, int pair) +{ + wattron(win -> win, COLOR_PAIR(pair)); +} + +void color_off(NEWWIN *win, int pair) +{ + wattroff(win -> win, COLOR_PAIR(pair)); +} + +void delete_window(NEWWIN *mywin) +{ + mydelwin(mywin); + + free(mywin); +} + +void mydelwin(NEWWIN *win) +{ + if (win) + { + if (win -> pwin && ERR == del_panel(win -> pwin)) + error_exit(false, "del_panel() failed"); + + if (win -> win && ERR == delwin(win -> win)) + error_exit(false, "delwin() failed"); + } +} + +void mydoupdate() +{ + update_panels(); + +#if 0 // FIXME + else if (input_window) + { + wmove(input_window -> win, 0, ul_x); + setsyx(input_window -> y + 0, input_window -> x + ul_x); + } +#endif + + doupdate(); +} + +WINDOW * mynewwin(int nlines, int ncols, int y, int x) +{ + assert(x >= 0 && y >= 0); + assert(x < max_x && y < max_y); + assert(x + ncols <= max_x && y + nlines <= max_y); + + WINDOW *dummy = newwin(nlines, ncols, y, x); + if (!dummy) + error_exit(false, "failed to create window (subwin) with dimensions %d-%d at offset %d,%d (terminal size: %d,%d)", ncols, nlines, x, y, max_x, max_y); + + keypad(dummy, TRUE); + leaveok(dummy, TRUE); + + return dummy; +} + +NEWWIN * create_window(int n_lines, int n_colls) +{ + return create_window_xy((max_y/2) - (n_lines/2), (max_x/2) - (n_colls/2), n_lines, n_colls); +} + +NEWWIN * create_window_xy(int y, int x, int n_lines, int n_colls) +{ + assert(x >= 0 && y >= 0); + assert(x < max_x && y < max_y); + assert(x + n_colls <= max_x && y + n_lines <= max_y); + NEWWIN *newwin = (NEWWIN *)malloc(sizeof(NEWWIN)); + + /* create new window */ + newwin -> win = mynewwin(n_lines, n_colls, y, x); + newwin -> pwin = new_panel(newwin -> win); + werase(newwin -> win); + + newwin -> ncols = n_colls; + newwin -> nlines = n_lines; + + newwin -> x = x; + newwin -> y = y; + + return newwin; +} + +void limit_print(NEWWIN *win, int width, int y, int x, char *format, ...) +{ + va_list ap; + int len = 0; + char *buf = nullptr; + + va_start(ap, format); + len = vasprintf(&buf, format, ap); + va_end(ap); + + if (len > width) + buf[width] = 0x00; + + mvwprintw(win -> win, y, x, "%s", buf); + + free(buf); +} + +void escape_print_xy(NEWWIN *win, int y, int x, char *str) +{ + int loop = 0, cursor_x = 0, len = strlen(str); + bool inv = false, underline = false; + + for(loop=0; loop win, A_REVERSE); + else + mywattroff(win -> win, A_REVERSE); + + inv = 1 - inv; + } + else if (str[loop] == '_') + { + if (!underline) + mywattron(win -> win, A_UNDERLINE); + else + mywattroff(win -> win, A_UNDERLINE); + + underline = 1 - underline; + } + else if (str[loop] == '\n') + { + cursor_x = 0; + y++; + } + else + { + mvwprintw(win -> win, y, x + cursor_x++, "%c", str[loop]); + } + } + + if (inv) + mywattroff(win -> win, A_REVERSE); + + if (underline) + mywattroff(win -> win, A_UNDERLINE); +} + +void escape_print(NEWWIN *win, const char *str, const char rev, const char un) +{ + int loop, len = strlen(str); + bool inv = false, underline = false; + + for(loop=0; loop win, A_REVERSE); + else + mywattroff(win -> win, A_REVERSE); + + inv = 1 - inv; + } + else if (str[loop] == un) + { + if (!underline) + mywattron(win -> win, A_UNDERLINE); + else + mywattroff(win -> win, A_UNDERLINE); + + underline = 1 - underline; + } + else + { + waddch(win -> win, str[loop]); + } + } + + if (inv) + mywattroff(win -> win, A_REVERSE); + + if (underline) + mywattroff(win -> win, A_UNDERLINE); +} + +void create_win_border(int x, int y, int width, int height, const char *title, NEWWIN **bwin, NEWWIN **win, bool f1, bool with_blank_border) +{ + assert(x >= 0 && y >= 0); + assert(x < max_x && y < max_y); + assert(x + width <= max_x && y + height <= max_y); + const char f1_for_help [] = " F1 for help "; + + bool wbb = with_blank_border; + + *bwin = create_window_xy(y + 0, x + 0, height + 2 + wbb * 2, width + 2 + wbb * 2); + *win = create_window_xy(y + 1 + wbb, x + 1 + wbb, height + 0, width + 0); + + mywattron((*bwin) -> win, A_REVERSE); + box((*bwin) -> win, 0, 0); + mywattroff((*bwin) -> win, A_REVERSE); + + mywattron((*bwin) -> win, A_STANDOUT); + + mvwprintw((*bwin) -> win, 0, 1, "[ %s ]", title); + + if (f1) + mvwprintw((*bwin) -> win, (*bwin) -> nlines - 1, 2, "[ %s ]", f1_for_help); + + mywattroff((*bwin) -> win, A_STANDOUT); +} + +void create_wb_popup(int width, int height, const char *title, NEWWIN **bwin, NEWWIN **win) +{ + create_win_border(max_x / 2 - width / 2, max_y / 2 - height / 2, width, height, title, bwin, win, false, true); +} + +void mywattron(WINDOW *w, int a) +{ + if (a != A_BLINK && a != A_BOLD && a != A_NORMAL && a != A_REVERSE && a != A_STANDOUT && a != A_UNDERLINE) + error_exit(false, "funny attributes: %d", a); + + wattron(w, a); +} + +void mywattroff(WINDOW *w, int a) +{ + if (a != A_BLINK && a != A_BOLD && a != A_NORMAL && a != A_REVERSE && a != A_STANDOUT && a != A_UNDERLINE) + error_exit(false, "funny attributes: %d", a); + + wattroff(w, a); +} + +void reset_attributes(NEWWIN *win) +{ + wattrset(win -> win, A_NORMAL); +} + +bool is_in_window(NEWWIN *win, int x, int y) +{ + return wenclose(win -> win, y, x); +} + +bool right_mouse_button_clicked(void) +{ + MEVENT event; + + return getmouse(&event) == OK && (event.bstate & BUTTON3_CLICKED); +} diff --git a/terminal.h b/terminal.h new file mode 100644 index 0000000..4d4c2c7 --- /dev/null +++ b/terminal.h @@ -0,0 +1,46 @@ +// (C) 2018 by folkert@vanheusden.com, released under AGPL 3.0 +#pragma once + +#include +#include +#include + +typedef struct +{ + WINDOW *win; + PANEL *pwin; + + unsigned nlines, ncols; + int x, y; +} NEWWIN; + +extern int default_colorpair, highlight_colorpair, meta_colorpair, error_colorpair, notice_colorpair, markerline_colorpair; +extern int max_y, max_x; + +void wrong_key(void); +void color_on(NEWWIN *win, int pair); +void color_off(NEWWIN *win, int pair); +void mywattron(WINDOW *w, int a); +void mywattroff(WINDOW *w, int a); +void mywbkgd(NEWWIN *win, int pair); +void mydelwin(NEWWIN *win); +void mydoupdate(); +void delete_window(NEWWIN *mywin); +NEWWIN * create_window(int n_lines, int n_colls); +NEWWIN * create_window_xy(int y_offset, int x_offset, int n_lines, int n_colls); +void limit_print(NEWWIN *win, int width, int y, int x, const char *format, ...); +void escape_print_xy(NEWWIN *win, int y, int x, const char *str); +void escape_print(NEWWIN *win, const char *str, const char reverse, const char underline); +void determine_terminal_size(void); +void create_win_border(int x, int y, int width, int height, const char *title, NEWWIN **bwin, NEWWIN **win, bool f1, bool with_blank_border = false); +void create_wb_popup(int width, int height, const char *title, NEWWIN **bwin, NEWWIN **win); +void initcol(void); +void apply_mouse_setting(void); +void init_ncurses(bool ignore_mouse); +void reset_attributes(NEWWIN *win); +bool is_in_window(NEWWIN *win, int x, int y); +bool right_mouse_button_clicked(void); +void display_markerline(NEWWIN *win, const char *msg); +void simple_marker(NEWWIN *win); + +void estimate_popup_size(const std::string & in, int *const w, int *const h); diff --git a/tests.cpp b/tests.cpp new file mode 100644 index 0000000..4b2efcf --- /dev/null +++ b/tests.cpp @@ -0,0 +1,2073 @@ +// (C) 2018 by Folkert van Heusden +// Released under AGPL v3.0 +#include +#include +#include + +#include "cpu.h" + +void do_test(cpu *const c, const int nInstr) +{ + FILE *fh = fopen("test.dat", "w"); + for(int i=0; i<256; i++) + fputc(c -> getBus() -> readByte(i), fh); + fclose(fh); + + for(int i=0; i step(); +} + +void test__initial(cpu *const c) +{ + assert(!c -> getPSW_z()); + assert(!c -> getPSW_n()); + assert(!c -> getPSW_c()); + assert(!c -> getPSW_v()); + + for(int i=0; i<8; i++) + assert(c -> getRegister(i) == 0); + + c -> setPSW_n(true); + assert(c -> getPSW_n() == true); +} + +void test__registers(cpu *const c) +{ + bus *const b = c -> getBus(); + + // kernel/user R0...R5 + c -> reset(); + b -> writeWord(0, 0012700); // mov #1,r0 kernel + b -> writeWord(2, 1); + do_test(c, 1); + + c -> setBitPSW(11, true); + c -> setPC(0); + b -> writeWord(0, 0012700); // mov #2,r0 user + b -> writeWord(2, 2); + do_test(c, 1); + c -> setBitPSW(11, false); + + assert(c -> getRegister(false, 0) == 1); + assert(c -> getRegister(true, 0) == 2); + assert(b -> readWord(0177700) == 1); + assert(b -> readWord(0177710) == 2); + + // SP + b -> writeWord(0177706, 3); + b -> writeWord(0177716, 4); + b -> writeWord(0177717, 5); + assert(c -> getStackPointer(0) == 3); + assert(c -> getStackPointer(1) == 4); + assert(c -> getStackPointer(3) == 5); + c -> setPSW(0); + assert(c -> getRegister(6) == 3); + c -> setPSW(0b0100000000000000); + assert(c -> getRegister(6) == 4); + c -> setPSW(0b1100000000000000); + assert(c -> getRegister(6) == 5); + + // PSW + c -> reset(); + assert(c -> getPSW() == 0); + + c -> reset(); + c -> setPSW_c(true); + assert(c -> getPSW() == 1); + + c -> reset(); + c -> setPSW_v(true); + assert(c -> getPSW() == 2); + + c -> reset(); + c -> setPSW_z(true); + assert(c -> getPSW() == 4); + + c -> reset(); + c -> setPSW_n(true); + assert(c -> getPSW() == 8); + + c -> reset(); + c -> setPSW_spl(7); + assert(c -> getPSW() == (7 << 5)); +} + +void test_cmp(cpu *const c) +{ + bus *const b = c -> getBus(); + + /// test CMP + // equal 1000 / 1000 + c -> reset(); + b -> writeWord(0, 0012700); // mov #100,r0 + b -> writeWord(2, 1000); + b -> writeWord(4, 0012701); // mov #100,r1 + b -> writeWord(6, 1000); + b -> writeWord(8, 0020001); // cmp r0,r1 + + do_test(c, 3); + + assert(c -> getPSW_z()); + assert(!c -> getPSW_n()); + assert(!c -> getPSW_c()); + assert(!c -> getPSW_v()); + + // > 1400 / 1000 + c -> reset(); + b -> writeWord(2, 1400); + + do_test(c, 3); + + assert(!c -> getPSW_z()); + assert(!c -> getPSW_n()); + assert(!c -> getPSW_c()); + assert(!c -> getPSW_v()); + + // 800 / 1000 + c -> reset(); + b -> writeWord(2, 800); + + do_test(c, 3); + + assert(!c -> getPSW_z()); + assert(c -> getPSW_n()); + assert(c -> getPSW_c()); + assert(!c -> getPSW_v()); + + // overflow 32768, -1 + c -> reset(); + b -> writeWord(2, 32768); + b -> writeWord(6, -1); + + do_test(c, 3); + + assert(!c -> getPSW_z()); + assert(c -> getPSW_n()); + assert(c -> getPSW_c()); + assert(!c -> getPSW_v()); + + ////////// + + // equal 10 / 10 + c -> reset(); + b -> writeWord(0, 0012700); // mov #10,r0 + b -> writeWord(2, 10); + b -> writeWord(4, 0012701); // mov #10,r1 + b -> writeWord(6, 10); + b -> writeWord(8, 0120001); // cmpb r0,r1 + + do_test(c, 3); + + assert(c -> getPSW_z()); + assert(!c -> getPSW_n()); + assert(!c -> getPSW_c()); + assert(!c -> getPSW_v()); + + // > 40 / 10 + c -> reset(); + b -> writeWord(2, 40); + + do_test(c, 3); + + assert(!c -> getPSW_z()); + assert(!c -> getPSW_n()); + assert(!c -> getPSW_c()); + assert(!c -> getPSW_v()); + + // 8 / 10 + c -> reset(); + b -> writeWord(2, 8); + + do_test(c, 3); + + assert(!c -> getPSW_z()); + assert(c -> getPSW_n()); + assert(c -> getPSW_c()); + assert(!c -> getPSW_v()); + + // overflow -128, -1 + c -> reset(); + b -> writeWord(2, -128); + b -> writeWord(6, -1); + + do_test(c, 3); + + assert(!c -> getPSW_z()); + assert(c -> getPSW_n()); + assert(c -> getPSW_c()); + assert(!c -> getPSW_v()); + +} + +void test_clr(cpu *const c) +{ + bus *const b = c -> getBus(); + + // equal + c -> reset(); + b -> writeWord(0, 0012700); // mov #ffff,r0 + b -> writeWord(2, 0xffff); + b -> writeWord(4, 0b0000101000000000); // clr + do_test(c, 2); + + assert(!c -> getPSW_n()); + assert(c -> getPSW_z()); + assert(!c -> getPSW_c()); + assert(!c -> getPSW_v()); + + assert(c -> getRegister(0) == 0); + + c -> reset(); + b -> writeWord(6, 0b0000101111000000); // tst + do_test(c, 3); + + assert(!c -> getPSW_n()); + assert(c -> getPSW_z()); + assert(!c -> getPSW_c()); + assert(!c -> getPSW_v()); + + c -> reset(); + b -> writeWord(4, 0b1000101000000000); // clrb + + do_test(c, 2); + + assert(!c -> getPSW_n()); + assert(c -> getPSW_z()); + assert(!c -> getPSW_c()); + assert(!c -> getPSW_v()); + assert(c -> getRegister(0) == 0xff00); + + c -> reset(); + b -> writeWord(6, 0b1000101111000000); // tstb + do_test(c, 3); + + assert(!c -> getPSW_n()); + assert(c -> getPSW_z()); + assert(!c -> getPSW_c()); + assert(!c -> getPSW_v()); + + c -> reset(); + b -> writeWord(6, 0b0000101111000000); // tst + do_test(c, 3); + + assert(c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_c()); + assert(!c -> getPSW_v()); +} + +void test_add(cpu *const c) +{ + bus *const b = c -> getBus(); + + // no overflow + c -> reset(); + c -> setRegister(0, 123); + c -> setRegister(1, 456); + b -> writeWord(0, 0060001); // add r0,r1 + do_test(c, 1); + + assert(!c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_c()); + assert(!c -> getPSW_v()); + + assert(c -> getRegister(0) == 123); + assert(c -> getRegister(1) == 123 + 456); + + // overflow + c -> reset(); + c -> setRegister(0, 0x123); + c -> setRegister(1, 0x7fff); + do_test(c, 1); + + assert(c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_c()); + assert(c -> getPSW_v()); + + assert(c -> getRegister(0) == 0x123); + assert(c -> getRegister(1) == 0x8122); + + // no overflow + c -> reset(); + c -> setRegister(0, 0x0001); + c -> setRegister(1, 0xfffe); + b -> writeWord(0, 0060001); // add r0,r1 + do_test(c, 1); + + assert(c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_c()); + assert(!c -> getPSW_v()); + + assert(c -> getRegister(0) == 1); + assert(c -> getRegister(1) == 0xffff); +} + +void test_sub(cpu *const c) +{ + bus *const b = c -> getBus(); + + // no overflow + c -> reset(); + c -> setRegister(0, 123); + c -> setRegister(1, 456); + b -> writeWord(0, 0160001); // SUB r0,r1 + do_test(c, 1); + + assert(!c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_c()); + assert(!c -> getPSW_v()); + + assert(c -> getRegister(0) == 123); + assert(int16_t(c -> getRegister(1)) == 456 - 123); + + // negative + c -> reset(); + c -> setRegister(0, 456); + c -> setRegister(1, 123); + b -> writeWord(0, 0160001); // SUB r0,r1 + do_test(c, 1); + + assert(c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(c -> getPSW_c()); + assert(!c -> getPSW_v()); + + assert(c -> getRegister(0) == 456); + assert(int16_t(c -> getRegister(1)) == 123 - 456); + + // overflow + c -> reset(); + c -> setRegister(0, 1); + c -> setRegister(1, 0x8000); + b -> writeWord(0, 0160001); // SUB r0,r1 + do_test(c, 1); + + assert(!c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_c()); + assert(c -> getPSW_v()); + + assert(c -> getRegister(0) == 1); + assert(c -> getRegister(1) == 0x7fff); + + // from docs + c -> reset(); + c -> setRegister(1, 011111); + c -> setRegister(2, 012345); + b -> writeWord(0, 0160102); // SUB r1,r2 + do_test(c, 1); + + assert(c -> getRegister(1) == 011111); + assert(c -> getRegister(2) == 001234); + + assert(!c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_c()); + assert(!c -> getPSW_v()); +} + +void test_bit(cpu *const c) +{ + bus *const b = c -> getBus(); + + // no overflow + c -> reset(); + c -> setRegister(0, 0xf0); + c -> setRegister(1, 0xff); + b -> writeWord(0, 0130001); // bit r0,r1 + do_test(c, 1); + + assert(c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_c()); + assert(!c -> getPSW_v()); + + assert(c -> getRegister(0) == 0xf0); + assert(c -> getRegister(1) == 0xff); + + c -> reset(); + c -> setRegister(0, 0xf0f0); + c -> setRegister(1, 0xffff); + b -> writeWord(0, 0030001); // bit r0,r1 + do_test(c, 1); + + assert(c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_c()); + assert(!c -> getPSW_v()); + + assert(c -> getRegister(0) == 0xf0f0); + assert(c -> getRegister(1) == 0xffff); +} + +void test_bis(cpu *const c) +{ + bus *const b = c -> getBus(); + + c -> reset(); + c -> setRegister(0, 0xf0); + c -> setRegister(1, 0x0f); + c -> setPSW_c(true); + b -> writeWord(0, 0150001); // bisb r0,r1 + do_test(c, 1); + + assert(c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(c -> getPSW_c()); + assert(!c -> getPSW_v()); + + assert(c -> getRegister(0) == 0xf0); + assert(c -> getRegister(1) == 0xff); + + // + + c -> reset(); + c -> setRegister(0, 0xf0f0); + c -> setRegister(1, 0x0f0f); + b -> writeWord(0, 0050001); // bis r0,r1 + do_test(c, 1); + + assert(c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_c()); + assert(!c -> getPSW_v()); + + assert(c -> getRegister(0) == 0xf0f0); + assert(c -> getRegister(1) == 0xffff); + + // + + c -> reset(); + c -> setRegister(0, 01234); + c -> setRegister(1, 01111); + b -> writeWord(0, 0050001); // bis r0,r1 + do_test(c, 1); + + assert(!c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_c()); + assert(!c -> getPSW_v()); + + assert(c -> getRegister(0) == 01234); + assert(c -> getRegister(1) == 01335); +} + +void test_condcode(cpu *const c) +{ + bus *const b = c -> getBus(); + + c -> reset(); + // sNZVC + b -> writeWord(0, 0b0000000010111001); // condcode + do_test(c, 1); + + assert(c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_v()); + assert(c -> getPSW_c()); + + // cNZVC + c -> setRegister(7, 0); + b -> writeWord(0, 0b0000000010101001); // condcode + do_test(c, 1); + assert(!c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_v()); + assert(!c -> getPSW_c()); + + // sNZVC + c -> setRegister(7, 0); + b -> writeWord(0, 0b0000000010110110); // condcode + do_test(c, 1); + + assert(!c -> getPSW_n()); + assert(c -> getPSW_z()); + assert(c -> getPSW_v()); + assert(!c -> getPSW_c()); + + // cNZVC + c -> setRegister(7, 0); + b -> writeWord(0, 0b0000000010100110); // condcode + do_test(c, 1); + + assert(!c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_v()); + assert(!c -> getPSW_c()); +} + +void test_asl(cpu *const c) +{ + bus *const b = c -> getBus(); + + c -> reset(); + c -> setRegister(0, 0x4000); + b -> writeWord(0, 0b0000110011000000); // asl + do_test(c, 1); + + assert(c -> getRegister(0) == 0x8000); + + assert(c -> getPSW_n()); + assert(!c -> getPSW_z()); +// FIXME assert(!c -> getPSW_v()); + assert(!c -> getPSW_c()); + + c -> reset(); + c -> setRegister(0, 0x80); + b -> writeWord(0, 0b1000110011000000); // asl + do_test(c, 1); + + assert(c -> getRegister(0) == 0); +} + +void test_adc(cpu *const c) +{ + bus *const b = c -> getBus(); + + c -> reset(); + c -> setPSW_c(true); + c -> setRegister(0, 0x7f); + b -> writeWord(0, 0b1000101101000000); // adc r0 + do_test(c, 1); + + assert(c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(c -> getPSW_v()); + assert(!c -> getPSW_c()); + + assert(c -> getRegister(0) == 128); + + // + + c -> reset(); + c -> setPSW_c(true); + c -> setRegister(0, 0x00); + do_test(c, 1); + + assert(!c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_v()); + assert(!c -> getPSW_c()); + + assert(c -> getRegister(0) == 1); + + // + + c -> reset(); + c -> setPSW_c(false); + c -> setRegister(0, 0x00); + do_test(c, 1); + + assert(!c -> getPSW_n()); + assert(c -> getPSW_z()); + assert(!c -> getPSW_v()); + assert(!c -> getPSW_c()); + + assert(c -> getRegister(0) == 0); + + //////////// + + c -> reset(); + c -> setPSW_c(true); + c -> setRegister(0, 0x7fff); + b -> writeWord(0, 0b0000101101000000); // adc r0 + do_test(c, 1); + + assert(c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(c -> getPSW_v()); + assert(!c -> getPSW_c()); + + assert(c -> getRegister(0) == 0x8000); + + // + + c -> reset(); + c -> setPSW_c(true); + c -> setRegister(0, 0x0000); + b -> writeWord(0, 0b0000101101000000); // adc r0 + do_test(c, 1); + + assert(!c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_v()); + assert(!c -> getPSW_c()); + + assert(c -> getRegister(0) == 1); +} + +void test_ror_rol(cpu *const c) +{ + bus *const b = c -> getBus(); + + c -> reset(); + c -> setPSW_c(true); + c -> setRegister(0, 0x81); + b -> writeWord(0, 0b1000110000000000); // rorb r0 + do_test(c, 1); + + assert(c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(c -> getPSW_c()); + assert(c -> getPSW_v() == (c -> getPSW_n() ^ c -> getPSW_c())); + + assert(c -> getRegister(0) == 0xc0); + + // + + c -> reset(); + c -> setPSW_c(true); + c -> setRegister(0, 0x8001); + b -> writeWord(0, 0b0000110000000000); // ror r0 + do_test(c, 1); + + assert(c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(c -> getPSW_c()); + assert(c -> getPSW_v() == (c -> getPSW_n() ^ c -> getPSW_c())); + + assert(c -> getRegister(0) == 0xc000); + + // + + c -> reset(); + c -> setRegister(0, 0x1); + b -> writeWord(0, 0b0000110000000000); // ror r0 + do_test(c, 1); + + assert(!c -> getPSW_n()); + assert(c -> getPSW_z()); + assert(c -> getPSW_c()); + assert(c -> getPSW_v()); + + assert(c -> getRegister(0) == 0); + + //// + + c -> reset(); + c -> setPSW_c(true); + c -> setRegister(0, 0x80); + b -> writeWord(0, 0b1000110001000000); // rolb r0 + do_test(c, 1); + + assert(!c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(c -> getPSW_c()); + assert(c -> getPSW_v() == (c -> getPSW_n() ^ c -> getPSW_c())); + + assert(c -> getRegister(0) == 0x01); + + // + + c -> reset(); + c -> setPSW_c(true); + c -> setRegister(0, 0x8000); + b -> writeWord(0, 0b0000110001000000); // rol r0 + do_test(c, 1); + + assert(!c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(c -> getPSW_c()); + assert(c -> getPSW_v() == (c -> getPSW_n() ^ c -> getPSW_c())); + + assert(c -> getRegister(0) == 0x0001); +} + +void test_neg(cpu *const c) +{ + bus *const b = c -> getBus(); + + c -> reset(); + c -> setRegister(0, 0x1000); + b -> writeWord(0, 0b0000101100000000); // neg r0 + do_test(c, 1); + + assert(c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(c -> getPSW_c()); + assert(!c -> getPSW_v()); + + assert(c -> getRegister(0) == 0xf000); + + // + + c -> reset(); + c -> setRegister(0, 0x8000); + b -> writeWord(0, 0b0000101100000000); // neg r0 + do_test(c, 1); + + assert(c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(c -> getPSW_c()); + assert(c -> getPSW_v()); + + assert(c -> getRegister(0) == 0x8000); + + ////////// + + c -> reset(); + c -> setRegister(0, 0x8010); + b -> writeWord(0, 0b1000101100000000); // neg r0 + do_test(c, 1); + + assert(c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(c -> getPSW_c()); + assert(!c -> getPSW_v()); + + assert(c -> getRegister(0) == 0x80f0); + + ////////// + + c -> reset(); + c -> setRegister(0, 010); + b -> writeWord(0, 0b0000101100000000); // neg r0 + do_test(c, 1); + + assert(c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_v()); + assert(c -> getPSW_c()); + + assert(c -> getRegister(0) == 0177770); + + ////////// + + c -> reset(); + c -> setRegister(0, 0x10); + b -> writeWord(0, 0b1000101100000000); // negb r0 + do_test(c, 1); + + assert(c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(c -> getPSW_c()); + assert(!c -> getPSW_v()); + + assert(c -> getRegister(0) == 0xf0); + + // + + c -> reset(); + c -> setRegister(0, 0x80); + b -> writeWord(0, 0b1000101100000000); // neg r0 + do_test(c, 1); + + assert(c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(c -> getPSW_c()); + assert(c -> getPSW_v()); + + assert(c -> getRegister(0) == 0x80); +} + +void test_inc(cpu *const c) +{ + bus *const b = c -> getBus(); + + c -> reset(); + c -> setRegister(0, 00); + b -> writeWord(0, 0005200); // INC r0 + do_test(c, 1); + + assert(!c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_c()); + assert(!c -> getPSW_v()); + + assert(c -> getRegister(0) == 1); + + c -> reset(); + c -> setRegister(0, 0x7fff); + b -> writeWord(0, 0005200); // INC r0 + do_test(c, 1); + + assert(c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_c()); + assert(c -> getPSW_v()); + + assert(c -> getRegister(0) == 0x8000); + + c -> reset(); + c -> setRegister(0, 0xffff); + b -> writeWord(0, 0005200); // INC r0 + do_test(c, 1); + + assert(!c -> getPSW_n()); + assert(c -> getPSW_z()); + assert(!c -> getPSW_c()); + assert(!c -> getPSW_v()); + + assert(c -> getRegister(0) == 0x0000); + + ///////// + + c -> reset(); + c -> setRegister(0, 00); + b -> writeWord(0, 0105200); // INCB r0 + do_test(c, 1); + + assert(!c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_c()); + assert(!c -> getPSW_v()); + + assert(c -> getRegister(0) == 1); + + c -> reset(); + c -> setRegister(0, 0x7f); + b -> writeWord(0, 0105200); // INCB r0 + do_test(c, 1); + + assert(c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_c()); + assert(c -> getPSW_v()); + + assert(c -> getRegister(0) == 0x80); + + c -> reset(); + c -> setRegister(0, 0xff); + b -> writeWord(0, 0105200); // INC r0 + do_test(c, 1); + + assert(!c -> getPSW_n()); + assert(c -> getPSW_z()); + assert(!c -> getPSW_c()); + assert(!c -> getPSW_v()); + + assert(c -> getRegister(0) == 0x00); + + c -> reset(); + c -> setRegister(0, 01000); + b -> writeWord(01000, 123); + b -> writeWord(0, 0005210); // INC (r0) + do_test(c, 1); + + assert(c -> getRegister(0) == 01000); + assert(b -> readWord(01000) == 124); + + c -> reset(); + c -> setRegister(0, 01000); + b -> writeWord(01000, 123); + b -> writeWord(0, 0005220); // INC (r0)+ + do_test(c, 1); + + assert(c -> getRegister(0) == 01002); + assert(b -> readWord(01000) == 124); + + c -> reset(); + c -> setRegister(0, 01000); + b -> writeWord(01000, 02000); + b -> writeWord(02000, 123); + b -> writeWord(0, 0005230); // INC @(R0)+ + do_test(c, 1); + + assert(c -> getRegister(0) == 01002); + assert(b -> readWord(02000) == 124); + + c -> reset(); + c -> setRegister(0, 01000); + b -> writeWord(0776, 123); + b -> writeWord(0, 0005240); // INC (r0)- + do_test(c, 1); + + assert(c -> getRegister(0) == 0776); + assert(b -> readWord(0776) == 124); + + c -> reset(); + c -> setRegister(0, 01002); + b -> writeWord(01000, 02000); + b -> writeWord(02000, 123); + b -> writeWord(0, 0005250); // INC @-(R0) + do_test(c, 1); + + assert(c -> getRegister(0) == 01000); + assert(b -> readWord(02000) == 124); + + c -> reset(); + c -> setRegister(0, 01000); + b -> writeWord(01124, 100); + b -> writeWord(0, 0005260); // INC X(r0) + b -> writeWord(2, 0124); // X = 0124 + do_test(c, 1); + + assert(c -> getRegister(0) == 01000); + assert(b -> readWord(01124) == 101); + + c -> reset(); + c -> setRegister(0, 01000); + b -> writeWord(01124, 02000); + b -> writeWord(02000, 100); + b -> writeWord(0, 0005270); // INC @X(r0) + b -> writeWord(2, 0124); // X = 0124 + do_test(c, 1); + + assert(c -> getRegister(0) == 01000); + assert(b -> readWord(02000) == 101); + + // mode 4, register 6 + + c -> reset(); + c -> setRegister(7, 01000); + c -> setRegister(6, 01000); + b -> writeWord(01000, 0005246); // INC (r6)- + b -> writeWord(0776, 123); + do_test(c, 1); + + fprintf(stderr, "%o\n", c -> getRegister(6)); + fprintf(stderr, "%o\n", b -> readWord(0776)); + assert(c -> getRegister(6) == 0776); + assert(b -> readWord(0776) == 124); + + // mode 4, register 7 + c -> reset(); + c -> setRegister(7, 01000); + b -> writeWord(0776, 123); + b -> writeWord(01000, 0005247); // INC (r7)- + do_test(c, 1); + + assert(c -> getRegister(7) == 01000); + assert(b -> readWord(01000) == 0005250); + assert(b -> readWord(0776) == 123); +} + +void test_dec(cpu *const c) +{ + bus *const b = c -> getBus(); + + c -> reset(); + c -> setRegister(0, 00); + b -> writeWord(0, 0005300); // DEC r0 + do_test(c, 1); + + assert(c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_c()); + assert(!c -> getPSW_v()); + + assert(c -> getRegister(0) == 0xffff); + + c -> reset(); + c -> setRegister(0, 0x7fff); + b -> writeWord(0, 0005300); // DEC r0 + do_test(c, 1); + + assert(!c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_c()); + assert(!c -> getPSW_v()); + + assert(c -> getRegister(0) == 0x7ffe); + + c -> reset(); + c -> setRegister(0, 0x0001); + b -> writeWord(0, 0005300); // DEC r0 + do_test(c, 1); + + assert(!c -> getPSW_n()); + assert(c -> getPSW_z()); + assert(!c -> getPSW_c()); + assert(!c -> getPSW_v()); + + assert(c -> getRegister(0) == 0x000); + + ///////// + + c -> reset(); + c -> setRegister(0, 00); + b -> writeWord(0, 0105300); // DEC r0 + do_test(c, 1); + + assert(c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_c()); + assert(!c -> getPSW_v()); + + assert(c -> getRegister(0) == 0xff); + + c -> reset(); + c -> setRegister(0, 0x7f); + b -> writeWord(0, 0105300); // DEC r0 + do_test(c, 1); + + assert(!c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_c()); + assert(!c -> getPSW_v()); + + assert(c -> getRegister(0) == 0x7e); + + c -> reset(); + c -> setRegister(0, 0x01); + b -> writeWord(0, 0105300); // DEC r0 + do_test(c, 1); + + assert(!c -> getPSW_n()); + assert(c -> getPSW_z()); + assert(!c -> getPSW_c()); + assert(!c -> getPSW_v()); + + assert(c -> getRegister(0) == 0x00); +} + +void test_bic(cpu *const c) +{ + bus *const b = c -> getBus(); + + c -> reset(); + c -> setRegister(0, 0xf0); + c -> setRegister(1, 0x0f); + b -> writeWord(0, 0140001); // bicb r0,r1 + do_test(c, 1); + + assert(!c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_v()); + + assert(c -> getRegister(0) == 0xf0); + assert(c -> getRegister(1) == 0x0f); + + // + + c -> reset(); + c -> setRegister(0, 0xf0); + c -> setRegister(1, 0x1f); + b -> writeWord(0, 0140001); // bicb r0,r1 + do_test(c, 1); + + assert(!c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_v()); + + assert(c -> getRegister(0) == 0xf0); + assert(c -> getRegister(1) == 0x0f); + + // + + c -> reset(); + c -> setRegister(0, 0x00); + c -> setRegister(1, 0xff); + b -> writeWord(0, 0140001); // bicb r0,r1 + do_test(c, 1); + + assert(c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_v()); + + assert(c -> getRegister(0) == 0x00); + assert(c -> getRegister(1) == 0xff); + + ////////////// + + c -> reset(); + c -> setRegister(0, 0xf000); + c -> setRegister(1, 0x0f00); + b -> writeWord(0, 0040001); // bic r0,r1 + do_test(c, 1); + + assert(!c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_v()); + + assert(c -> getRegister(0) == 0xf000); + assert(c -> getRegister(1) == 0x0f00); + + // + + c -> reset(); + c -> setRegister(0, 0xf000); + c -> setRegister(1, 0x1fff); + b -> writeWord(0, 0040001); // bic r0,r1 + do_test(c, 1); + + assert(!c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_v()); + + assert(c -> getRegister(0) == 0xf000); + assert(c -> getRegister(1) == 0x0fff); + + // + + c -> reset(); + c -> setRegister(0, 0x0000); + c -> setRegister(1, 0xffff); + b -> writeWord(0, 0040001); // bic r0,r1 + do_test(c, 1); + + assert(c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_v()); + + assert(c -> getRegister(0) == 0x0000); + assert(c -> getRegister(1) == 0xffff); + + // + + c -> reset(); + c -> setRegister(0, 01234); + c -> setRegister(1, 01111); + c -> setPSW(15); + b -> writeWord(0, 0040001); // bic r0,r1 + do_test(c, 1); + + assert(!c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_v()); + assert(c -> getPSW_c()); + + assert(c -> getRegister(0) == 01234); + assert(c -> getRegister(1) == 00101); +} + +void test_b__(cpu *const c) +{ + bus *const b = c -> getBus(); + + c -> reset(); + b -> writeWord(0, 0000404); // BR + do_test(c, 1); + assert(c -> getRegister(7) == 10); + + //////// + + c -> reset(); + b -> writeWord(0, 0103404); // BCS/BLO + do_test(c, 1); + assert(c -> getRegister(7) == 2); + // + c -> reset(); + c -> setPSW_c(true); + b -> writeWord(0, 0103404); // BCS/BLO + do_test(c, 1); + assert(c -> getRegister(7) == 10); + + //////// + + c -> reset(); + b -> writeWord(0, 0001404); // BEQ + do_test(c, 1); + assert(c -> getRegister(7) == 2); + // + c -> reset(); + c -> setPSW_z(true); + b -> writeWord(0, 0001404); // BEQ + do_test(c, 1); + assert(c -> getRegister(7) == 10); + + //////// + + c -> reset(); + b -> writeWord(0, 0100404); // BMI + do_test(c, 1); + assert(c -> getRegister(7) == 2); + // + c -> reset(); + c -> setPSW_n(true); + b -> writeWord(0, 0100404); // BMI + do_test(c, 1); + assert(c -> getRegister(7) == 10); + + //////// + + c -> reset(); + b -> writeWord(0, 0103004); // BCC + do_test(c, 1); + assert(c -> getRegister(7) == 10); + // + c -> reset(); + c -> setPSW_c(true); + b -> writeWord(0, 0103004); // BCC + do_test(c, 1); + assert(c -> getRegister(7) == 2); + + //////// + + c -> reset(); + b -> writeWord(0, 0100004); // BPL + do_test(c, 1); + assert(c -> getRegister(7) == 10); + // + c -> reset(); + c -> setPSW_n(true); + b -> writeWord(0, 0100004); // BPL + do_test(c, 1); + assert(c -> getRegister(7) == 2); + + //////// + + c -> reset(); + c -> setPSW_z(true); + b -> writeWord(0, 003404); // BLE + do_test(c, 1); + assert(c -> getRegister(7) == 10); + // + c -> reset(); + c -> setPSW_n(true); + b -> writeWord(0, 003404); // BLE + do_test(c, 1); + assert(c -> getRegister(7) == 10); + // + c -> reset(); + c -> setPSW_v(true); + b -> writeWord(0, 003404); // BLE + do_test(c, 1); + assert(c -> getRegister(7) == 10); + // + c -> reset(); + c -> setPSW_n(true); + c -> setPSW_v(true); + b -> writeWord(0, 003404); // BLE + do_test(c, 1); + assert(c -> getRegister(7) == 2); + + //////// + + c -> reset(); + b -> writeWord(0, 0002404); // BLT + do_test(c, 1); + assert(c -> getRegister(7) == 2); + // + c -> reset(); + c -> setPSW_n(true); + c -> setPSW_v(true); + b -> writeWord(0, 0002404); // BLT + do_test(c, 1); + assert(c -> getRegister(7) == 2); + // + c -> reset(); + c -> setPSW_n(true); + b -> writeWord(0, 0002404); // BLT + do_test(c, 1); + assert(c -> getRegister(7) == 10); + // + c -> reset(); + c -> setPSW_v(true); + b -> writeWord(0, 0002404); // BLT + do_test(c, 1); + assert(c -> getRegister(7) == 10); + + //////// + + c -> reset(); + b -> writeWord(0, 0002004); // BGE + do_test(c, 1); + assert(c -> getRegister(7) == 10); + // + c -> reset(); + c -> setPSW_n(true); + c -> setPSW_v(true); + b -> writeWord(0, 0002004); // BGE + do_test(c, 1); + assert(c -> getRegister(7) == 10); + // + c -> reset(); + c -> setPSW_n(true); + b -> writeWord(0, 0002004); // BGE + do_test(c, 1); + assert(c -> getRegister(7) == 2); + // + c -> reset(); + c -> setPSW_v(true); + b -> writeWord(0, 0002004); // BGE + do_test(c, 1); + assert(c -> getRegister(7) == 2); + + //////// + + c -> reset(); + b -> writeWord(0, 0101004); // BHI + do_test(c, 1); + assert(c -> getRegister(7) == 10); + // + c -> reset(); + c -> setPSW_c(true); + b -> writeWord(0, 0101004); // BHI + do_test(c, 1); + assert(c -> getRegister(7) == 2); + // + c -> reset(); + c -> setPSW_z(true); + b -> writeWord(0, 0101004); // BHI + do_test(c, 1); + assert(c -> getRegister(7) == 2); + + //////// + + c -> reset(); + c -> setPSW_z(true); + b -> writeWord(0, 003004); // BGT + do_test(c, 1); + assert(c -> getRegister(7) == 2); + // + c -> reset(); + c -> setPSW_n(true); + b -> writeWord(0, 003004); // BGT + do_test(c, 1); + assert(c -> getRegister(7) == 2); + // + c -> reset(); + c -> setPSW_v(true); + b -> writeWord(0, 003004); // BGT + do_test(c, 1); + assert(c -> getRegister(7) == 2); + // + c -> reset(); + c -> setPSW_n(true); + c -> setPSW_v(true); + b -> writeWord(0, 003004); // BGT + do_test(c, 1); + assert(c -> getRegister(7) == 10); + + //////// + + c -> reset(); + c -> setPSW_z(true); + b -> writeWord(0, 0101404); // BLOS + do_test(c, 1); + assert(c -> getRegister(7) == 10); + // + c -> reset(); + c -> setPSW_c(true); + b -> writeWord(0, 0101404); // BLOS + do_test(c, 1); + assert(c -> getRegister(7) == 10); + // + c -> reset(); + b -> writeWord(0, 0101404); // BLOS + do_test(c, 1); + assert(c -> getRegister(7) == 2); + + //////// + + c -> reset(); + b -> writeWord(0, 0001004); // BNE + do_test(c, 1); + assert(c -> getRegister(7) == 10); + // + c -> reset(); + c -> setPSW_z(true); + b -> writeWord(0, 0001004); // BNE + do_test(c, 1); + assert(c -> getRegister(7) == 2); +} + +void test_jmp(cpu *const c) +{ + bus *const b = c -> getBus(); + + c -> reset(); + c -> setRegister(1, 10); + b -> writeWord(0, 0000111); // JMP + do_test(c, 1); + assert(c -> getRegister(7) == 10); +} + +void test_jsr(cpu *const c) +{ + bus *const b = c -> getBus(); + + c -> reset(); + c -> setRegister(1, 10); + b -> writeWord(0, 0004011); // JSR + do_test(c, 1); + assert(c -> getRegister(0) == 2); + assert(c -> getRegister(7) == 10); +} + +void test_rts(cpu *const c) +{ + bus *const b = c -> getBus(); + + c -> reset(); + c -> setRegister(0, 10); + b -> writeWord(0, 0004010); + b -> writeWord(10, 0b0000000010000000); + do_test(c, 2); + assert(c -> getRegister(0) == 10); + assert(c -> getRegister(7) == 2); + // + c -> reset(); + c -> setRegister(0, 10); + b -> writeWord(0, 0004110); + b -> writeWord(10, 0b0000000010000001); + do_test(c, 2); + assert(c -> getRegister(0) == 10); + assert(c -> getRegister(1) == 0); + assert(c -> getRegister(7) == 2); +} + +void test_mov(cpu *const c) +{ + bus *const b = c -> getBus(); + + // 0, register, movb r0 to r1, check sign extending + c -> reset(); + c -> setRegister(0, 255); + b -> writeWord(0, 0110001); + + do_test(c, 1); + + assert(c -> getRegister(0) == 255); + assert(c -> getRegister(1) == 65535); + // + c -> reset(); + c -> setRegister(0, 123); + b -> writeWord(0, 0110001); + + do_test(c, 1); + + assert(c -> getRegister(0) == 123); + assert(c -> getRegister(1) == 123); + + // + c -> reset(); + c -> setRegister(0, 128); + c -> setRegister(1, 200); + b -> writeWord(0, 0010001); + + do_test(c, 1); + + assert(c -> getRegister(0) == 128); + assert(c -> getRegister(1) == 128); + + // 1, register deferred + // FIXME byte + c -> reset(); + c -> setRegister(0, 100); + c -> setRegister(1, 200); + b -> writeWord(100, 123); + b -> writeWord(200, 99); + b -> writeWord(0, 011011); + do_test(c, 1); + + assert(c -> getRegister(0) == 100); + assert(c -> getRegister(1) == 200); + assert(b -> readWord(100) == 123); + assert(b -> readWord(200) == 123); + + // 2, auto increment, mov (r0)+,(r1)+ + c -> reset(); + b -> writeWord(100, 123); + b -> writeWord(200, 456); + c -> setRegister(0, 100); + c -> setRegister(1, 200); + b -> writeWord(0, 012021); + + do_test(c, 1); + + assert(c -> getRegister(0) == 102); + assert(c -> getRegister(1) == 202); + assert(b -> readWord(100) == 123); + assert(b -> readWord(200) == 123); + + // movb (r0)+,(r1)+ + c -> reset(); + b -> writeByte(100, 123); + b -> writeByte(200, 200); + c -> setRegister(0, 100); + c -> setRegister(1, 200); + b -> writeWord(0, 0112021); + + do_test(c, 1); + + assert(c -> getRegister(0) == 101); + assert(c -> getRegister(1) == 201); + assert(b -> readByte(100) == 123); + assert(b -> readWord(200) == 123); + + // 3, auto increment deferred, move @(r0)+, @(r1)+ + // FIXME byte + c -> reset(); + b -> writeWord(100, 123); + b -> writeWord(123, 19); + b -> writeWord(200, 456); + b -> writeWord(456, 12); + c -> setRegister(0, 100); + c -> setRegister(1, 200); + b -> writeWord(0, 013031); + + do_test(c, 1); + + assert(c -> getRegister(0) == 102); + assert(c -> getRegister(1) == 202); + assert(b -> readWord(100) == 123); + assert(b -> readWord(123) == 19); + assert(b -> readWord(200) == 456); + assert(b -> readWord(456) == 19); + + // 4a, auto decrement, mov -(r0),-(r1) + c -> reset(); + b -> writeWord(100, 123); + b -> writeWord(200, 456); + c -> setRegister(0, 102); + c -> setRegister(1, 202); + b -> writeWord(0, 014041); + + do_test(c, 1); + + assert(c -> getRegister(0) == 100); + assert(c -> getRegister(1) == 200); + assert(b -> readWord(100) == 123); + assert(b -> readWord(200) == 123); + + // 4b, auto decrement, mov -(r0),-(r6) + c -> reset(); + b -> writeWord(0100, 123); + b -> writeWord(0200, 456); + c -> setRegister(0, 0102); + c -> setRegister(6, 0202); + b -> writeWord(0, 014046); + + do_test(c, 1); + + assert(c -> getRegister(0) == 0100); + assert(c -> getRegister(6) == 0200); + assert(b -> readWord(0100) == 123); + assert(b -> readWord(0200) == 123); + + // 5, auto decrement deferred, move @-(r0), @-(r1) + // FIXME byte + c -> reset(); + b -> writeWord(100, 123); + b -> writeWord(123, 19); + b -> writeWord(200, 456); + b -> writeWord(456, 12); + c -> setRegister(0, 102); + c -> setRegister(1, 202); + b -> writeWord(0, 015051); + + do_test(c, 1); + + assert(c -> getRegister(0) == 100); + assert(c -> getRegister(1) == 200); + assert(b -> readWord(100) == 123); + assert(b -> readWord(123) == 19); + assert(b -> readWord(200) == 456); + assert(b -> readWord(456) == 19); + + // 6, index + // FIXME byte + c -> reset(); + c -> setRegister(0, 100); + c -> setRegister(1, 200); + b -> writeWord(104, 123); + b -> writeWord(208, 99); + b -> writeWord(0, 016061); + b -> writeWord(2, 4); + b -> writeWord(4, 8); + do_test(c, 1); + + assert(c -> getRegister(0) == 100); + assert(c -> getRegister(1) == 200); + assert(b -> readWord(104) == 123); + assert(b -> readWord(208) == 123); + + // 7, index, deferred + // FIXME byte + c -> reset(); + c -> setRegister(0, 100); + c -> setRegister(1, 200); + b -> writeWord(104, 124); + b -> writeWord(124, 98); + b -> writeWord(208, 210); + b -> writeWord(210, 99); + b -> writeWord(0, 017071); + b -> writeWord(2, 4); + b -> writeWord(4, 8); + do_test(c, 1); + + assert(c -> getRegister(0) == 100); + assert(c -> getRegister(1) == 200); + assert(b -> readWord(124) == 98); + assert(b -> readWord(210) == 98); +} + +void test_ash(cpu *const c) +{ + bus *const b = c -> getBus(); + + c -> reset(); + c -> setRegister(0, 16); + c -> setRegister(1, 1); + b -> writeWord(0, 0072001); // R0 <<= R1 + do_test(c, 1); + assert(!c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_v()); + assert(!c -> getPSW_c()); + assert(c -> getRegister(0) == 32); + // + c -> reset(); + c -> setRegister(0, -16); + c -> setRegister(1, 1); + b -> writeWord(0, 0072001); // R0 <<= R1 + do_test(c, 1); + assert(c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_v()); + assert(c -> getPSW_c()); + assert(int16_t(c -> getRegister(0)) == -32); + ////////// + c -> reset(); + c -> setRegister(0, 16); + c -> setRegister(1, -1); + b -> writeWord(0, 0072001); // R0 >>= R1 + do_test(c, 1); + assert(!c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_v()); + assert(!c -> getPSW_c()); + assert(c -> getRegister(0) == 8); + // + c -> reset(); + c -> setRegister(0, -16); + c -> setRegister(1, -1); + b -> writeWord(0, 0072001); // R0 >>= R1 + do_test(c, 1); + assert(c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_v()); + assert(!c -> getPSW_c()); + assert(int16_t(c -> getRegister(0)) == -8); + /////// + c -> reset(); + c -> setRegister(0, 16); + c -> setRegister(1, -5); + b -> writeWord(0, 0072001); // R0 >>= R1 + do_test(c, 1); + assert(!c -> getPSW_n()); + assert(c -> getPSW_z()); + assert(c -> getPSW_v()); + assert(c -> getPSW_c()); + assert(c -> getRegister(0) == 0); + // + c -> reset(); + c -> setRegister(0, 0x7fff); + c -> setRegister(1, 1); + b -> writeWord(0, 0072001); // R0 <<= R1 + do_test(c, 1); + assert(c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(c -> getPSW_v()); + assert(!c -> getPSW_c()); + assert(c -> getRegister(0) == 0xfffe); +} + +void test_sob(cpu *const c) +{ + bus *const b = c -> getBus(); + + // not taken + c -> reset(); + c -> setRegister(0, 2); + b -> writeWord(10, 077001); // SOB + c -> setRegister(7, 10); + do_test(c, 1); + assert(c -> getRegister(7) == 10); + // taken + c -> reset(); + c -> setRegister(0, 1); + b -> writeWord(10, 077007); // SOB + c -> setRegister(7, 10); + do_test(c, 1); + assert(c -> getRegister(7) == 12); +} + +void test_swab(cpu *const c) +{ + bus *const b = c -> getBus(); + + c -> reset(); + c -> setPSW_c(true); + c -> setPSW_n(true); + c -> setPSW_z(true); + c -> setPSW_v(true); + c -> setRegister(1, 077777); + b -> writeWord(0, 000301); // SWAB R1 + do_test(c, 1); + assert(c -> getRegister(1) == 0177577); + assert(c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_v()); + assert(!c -> getPSW_c()); +} + +void test_div(cpu *const c) +{ + bus *const b = c -> getBus(); + + // regular + c -> reset(); + c -> setRegister(0, 0); + c -> setRegister(1, 020001); + c -> setRegister(2, 2); + b -> writeWord(0, 071002); // DIV R2,R0 + do_test(c, 1); + assert(c -> getRegister(0) == 010000); + assert(c -> getRegister(1) == 01); + assert(!c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_v()); + assert(!c -> getPSW_c()); + + // result does not fit + c -> reset(); + c -> setRegister(0, 0x7fff); + c -> setRegister(1, 0xffff); + c -> setRegister(2, 2); + b -> writeWord(0, 071002); // DIV R2,R0 + do_test(c, 1); + assert(c -> getPSW_v()); + + // div by zero + c -> reset(); + c -> setRegister(0, 0); + c -> setRegister(1, 020001); + c -> setRegister(2, 0); + b -> writeWord(0, 071002); // DIV R2,R0 + do_test(c, 1); + assert(c -> getPSW_v()); + assert(c -> getPSW_c()); +} + +void test_sbc(cpu *const c) +{ + bus *const b = c -> getBus(); + + c -> reset(); + c -> setPSW_c(true); + c -> setRegister(0, 0x80); + b -> writeWord(0, 0105600); // sbc r0 + do_test(c, 1); + + assert(!c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_v()); + assert(!c -> getPSW_c()); + + assert(c -> getRegister(0) == 0x7f); + + // + + c -> reset(); + c -> setPSW_c(true); + c -> setRegister(0, 0x00); + do_test(c, 1); + + assert(c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_v()); + assert(c -> getPSW_c()); + + assert(c -> getRegister(0) == 0xff); + + // + + c -> reset(); + c -> setPSW_c(false); + c -> setRegister(0, 0x00); + do_test(c, 1); + + assert(!c -> getPSW_n()); + assert(c -> getPSW_z()); + assert(!c -> getPSW_v()); + assert(!c -> getPSW_c()); + + assert(c -> getRegister(0) == 0); + + //////////// + + c -> reset(); + c -> setPSW_c(true); + c -> setRegister(0, 0x8000); + b -> writeWord(0, 0005600); // sbc r0 + do_test(c, 1); + + assert(!c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_v()); + assert(!c -> getPSW_c()); + + assert(c -> getRegister(0) == 0x7fff); + + // + + c -> reset(); + c -> setPSW_c(true); + c -> setRegister(0, 0x0000); + b -> writeWord(0, 0005600); // sbc r0 + do_test(c, 1); + + assert(c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_v()); + assert(c -> getPSW_c()); + + assert(c -> getRegister(0) == 0xffff); +} + +void test_com(cpu *const c) +{ + bus *const b = c -> getBus(); + + c -> reset(); + c -> setRegister(0, 013333); + b -> writeWord(0, 0005100); // COM R0 + do_test(c, 1); + assert(c -> getRegister(0) == 0164444); + assert(c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_v()); + assert(c -> getPSW_c()); + + c -> reset(); + c -> setRegister(0, 013333); + b -> writeWord(0, 0105100); // COMB R0 + do_test(c, 1); + assert(c -> getRegister(0) == 013044); + assert(!c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_v()); + assert(c -> getPSW_c()); +} + +void test_rti(cpu *const c) +{ + bus *const b = c -> getBus(); + + c -> reset(); + c -> setRegister(6, 02000); + c -> pushStack(15); // SP + c -> pushStack(01234); // pc + + b -> writeWord(0, 02); // RT + do_test(c, 1); + assert(c -> getRegister(6) == 02000); + assert(c -> getRegister(7) == 01234); + assert(c -> getPSW() == 15); +} + +void test_trap(cpu *const c) +{ + bus *const b = c -> getBus(); + + c -> reset(); + c -> setRegister(6, 02000); + b -> writeWord(034, 01234); // new pc + b -> writeWord(036, 15); // new sp + b -> writeWord(0, 0104400); // TRAP + do_test(c, 1); + + assert(c -> getRegister(6) == 01774); + assert(c -> getRegister(7) == 01234); + assert(c -> getPSW() == 15); +} + +void test_asr(cpu *const c) +{ + bus *const b = c -> getBus(); + + c -> reset(); + c -> setRegister(0, 0x4001); + b -> writeWord(0, 0006200); // asr + do_test(c, 1); + + printf("%04x\n", c -> getRegister(0)); + assert(c -> getRegister(0) == 0x2000); + assert(!c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(c -> getPSW_c()); + assert(c -> getPSW_v() == (c -> getPSW_n() ^ c -> getPSW_c())); + + c -> reset(); + c -> setRegister(0, 0x8001); + b -> writeWord(0, 0006200); // asr + do_test(c, 1); + + assert(c -> getRegister(0) == 0xc000); + assert(c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(c -> getPSW_c()); + assert(c -> getPSW_v() == (c -> getPSW_n() ^ c -> getPSW_c())); + + ////// + + c -> reset(); + c -> setRegister(0, 0x41); + b -> writeWord(0, 0106200); // asrb + do_test(c, 1); + + printf("%04x\n", c -> getRegister(0)); + assert(c -> getRegister(0) == 0x20); + assert(!c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(c -> getPSW_c()); + assert(c -> getPSW_v() == (c -> getPSW_n() ^ c -> getPSW_c())); + + c -> reset(); + c -> setRegister(0, 0x81); + b -> writeWord(0, 0106200); // asrb + do_test(c, 1); + + assert(c -> getRegister(0) == 0xc0); + assert(c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(c -> getPSW_c()); + assert(c -> getPSW_v() == (c -> getPSW_n() ^ c -> getPSW_c())); +} + +void test_tst(cpu *const c) +{ + bus *const b = c -> getBus(); + + c -> reset(); + c -> setRegister(0, 0); + c -> setPSW_c(true); + c -> setPSW_v(true); + b -> writeWord(0, 0005700); // TST 0 + do_test(c, 1); + + assert(c -> getRegister(0) == 0); + assert(!c -> getPSW_n()); + assert(c -> getPSW_z()); + assert(!c -> getPSW_c()); + assert(!c -> getPSW_v()); + + c -> reset(); + c -> setRegister(0, 0); + c -> setPSW_c(true); + c -> setPSW_v(true); + b -> writeWord(0, 0105700); // TSTB 0 + do_test(c, 1); + + assert(c -> getRegister(0) == 0); + assert(!c -> getPSW_n()); + assert(c -> getPSW_z()); + assert(!c -> getPSW_c()); + assert(!c -> getPSW_v()); + + c -> reset(); + c -> setRegister(0, 0x8010); + c -> setPSW_c(true); + c -> setPSW_v(true); + b -> writeWord(0, 0005700); // TST 0 + do_test(c, 1); + + assert(c -> getRegister(0) == 0x8010); + assert(c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_c()); + assert(!c -> getPSW_v()); + + c -> reset(); + c -> setRegister(0, 0x8010); + c -> setPSW_c(true); + c -> setPSW_v(true); + b -> writeWord(0, 0105700); // TSTB 0 + do_test(c, 1); + + assert(c -> getRegister(0) == 0x8010); + assert(!c -> getPSW_n()); + assert(!c -> getPSW_z()); + assert(!c -> getPSW_c()); + assert(!c -> getPSW_v()); +} + +void test_mfp(cpu *const c) +{ + bus *const b = c -> getBus(); + + // current operation mode == previous operation mode + b -> clearmem(); + c -> reset(); + b -> writeWord(01000, 07711); + c -> setRegister(0, 01000); + c -> setRegister(6, 02000); + b -> writeWord(0, 006500); // MFPD R0 + do_test(c, 1); + + assert(c -> getRegister(0) == 01000); + // FIXME flags + assert(c -> getRegister(6) == 01776); + assert(b -> readWord(01776) == 01000); + + // current operation mode != previous operation mode + b -> clearmem(); + c -> reset(); + b -> writeWord(0172340, 896); // setup memory user + // write a word 07711 to 0100 in current mode which is kernel + fprintf(stderr, "---\n"); + b -> write(0100, false, 07711, false); + b -> writeWord(0177640, 0); // setup memory kernel + c -> setPSW(3 << 14); + // write a word 0123 to 0100 in current mode which is user + fprintf(stderr, "===\n"); + b -> write(0100, false, 0123, false); + // go back to kernel mode + c -> setPSW(0 << 14); + fprintf(stderr, "+++\n"); + + c -> setRegister(0, 0100); + c -> setRegister(6, 02000); + c -> setPSW((0 << 14) | (3 << 12)); + b -> writeWord(0, 006510); // MFPD (R0) + do_test(c, 1); + + assert(c -> getRegister(0) == 0100); + assert(c -> getRegister(6) == 01776); + // FIXME flags + + c -> setPSW(3 << 14); + //fprintf(stderr, "%o == 07711\n", b -> read(0100, false, false)); fflush(NULL); + assert(b -> read(0100, false, false) == 0123); + + c -> setPSW(0 << 14); + fprintf(stderr, "%o == 0123\n", b -> read(0100, false, false)); fflush(NULL); + assert(b -> read(0100, false, false) == 07711); +} + +void tests(cpu *const c) +{ + test__initial(c); + test_cmp(c); + test_clr(c); + test_add(c); + test_sub(c); + test_bit(c); + test_bis(c); + test_condcode(c); + test_asl(c); + test_adc(c); + test_ror_rol(c); + test_neg(c); + test_inc(c); + test_dec(c); + test_bic(c); + test_b__(c); + test_jmp(c); + test_jsr(c); + test_rts(c); + test_mov(c); + test_ash(c); + test_sob(c); + test_swab(c); + test_div(c); + test_sbc(c); + test_com(c); + test_rti(c); + test_trap(c); + test_asr(c); + test_tst(c); + test__registers(c); + test_mfp(c); + + printf("\nALL FINE\n"); + + exit(0); +} diff --git a/tests.h b/tests.h new file mode 100644 index 0000000..e987c20 --- /dev/null +++ b/tests.h @@ -0,0 +1,3 @@ +// (C) 2018 by Folkert van Heusden +// Released under AGPL v3.0 +void tests(cpu *const c); diff --git a/tm-11.cpp b/tm-11.cpp new file mode 100644 index 0000000..2287532 --- /dev/null +++ b/tm-11.cpp @@ -0,0 +1,140 @@ +// (C) 2018 by Folkert van Heusden +// Released under AGPL v3.0 +#include +#include + +#include "tm-11.h" +#include "gen.h" +#include "memory.h" +#include "utils.h" + +tm_11::tm_11(const std::string & file, memory *const m) : m(m) +{ + offset = 0; + memset(registers, 0x00, sizeof registers); + + fh = fopen(file.c_str(), "rb"); +} + +tm_11::~tm_11() +{ + fclose(fh); +} + +uint8_t tm_11::readByte(const uint16_t addr) +{ + uint16_t v = readWord(addr & ~1); + + if (addr & 1) + return v >> 8; + + return v; +} + +uint16_t tm_11::readWord(const uint16_t addr) +{ + const int reg = (addr - TM_11_BASE) / 2; + uint16_t vtemp = registers[reg]; + + D(printf("TM-11 read addr %o: ", addr);) + + if (addr == TM_11_MTS) { + setBit(vtemp, 15, false); // ILC + setBit(vtemp, 14, false); // EOC + setBit(vtemp, 13, false); // CRE + setBit(vtemp, 12, false); // PAE + setBit(vtemp, 11, false); // BGL + setBit(vtemp, 10, false); // EOT + setBit(vtemp, 9, false); // RLE + setBit(vtemp, 8, false); // BTE + setBit(vtemp, 7, false); // NXM + setBit(vtemp, 6, true); // SELR + setBit(vtemp, 5, false); // BOT - beginning of tape + setBit(vtemp, 4, false); // 7CH + setBit(vtemp, 3, false); // SDWN + setBit(vtemp, 2, false); // WRL - write lock + setBit(vtemp, 1, false); // RWS + setBit(vtemp, 0, true); // TUR - tape unit ready + } + else if (addr == TM_11_MTC) { + registers[reg] ^= 1 << 7; // CU RDY + } + else if (addr == TM_11_MTBRC) { // record length + vtemp = 0; + } + + D(printf("%o\n", vtemp);) + + return vtemp; +} + +void tm_11::writeByte(const uint16_t addr, const uint8_t v) +{ + uint16_t vtemp = registers[(addr - TM_11_BASE) / 2]; + + if (addr & 1) { + vtemp &= ~0xff00; + vtemp |= v << 8; + } + else { + vtemp &= ~0x00ff; + vtemp |= v; + } + + writeWord(addr, vtemp); +} + +void tm_11::writeWord(const uint16_t addr, uint16_t v) +{ + D(printf("TM-11 write %o: %o\n", addr, v);) + + if (addr == TM_11_MTC) { + if (v & 1) { // GO + const int func = (v >> 1) & 7; // FUNCTION + const int reclen = 512; + + D(printf("invoke %d\n", func);) + + if (func == 0) { // off-line + v = 128; // FIXME set error if error + } + else if (func == 1) { // read + D(printf("reading %d bytes from offset %d\n", reclen, offset);) + if (fread(xfer_buffer, 1, reclen, fh) != reclen) + D(printf("failed: %s\n", strerror(errno));) + for(int i=0; i writeByte(registers[(TM_11_MTCMA - TM_11_BASE) / 2] + i, xfer_buffer[i]); + offset += reclen; + + v = 128; // FIXME set error if error + } + else if (func == 2) { // write + for(int i=0; i readByte(registers[(TM_11_MTCMA - TM_11_BASE) / 2] + i); + fwrite(xfer_buffer, 1, reclen, fh); + offset += reclen; + v = 128; // FIXME + } + else if (func == 4) { // space forward + offset += reclen; + v = 128; // FIXME + } + else if (func == 5) { // space backward + if (offset >= reclen) + offset -= reclen; + v = 128; // FIXME + } + else if (func == 7) { // rewind + offset = 0; + v = 128; // FIXME set error if error + } + } + } + else if (addr == TM_11_MTCMA) { + v &= ~1; + D(printf("Set DMA address to %o\n", v);) + } + + D(printf("set register %o to %o\n", addr, v);) + registers[(addr - TM_11_BASE) / 2] = v; +} diff --git a/tm-11.h b/tm-11.h new file mode 100644 index 0000000..9452fc3 --- /dev/null +++ b/tm-11.h @@ -0,0 +1,38 @@ +// (C) 2018 by Folkert van Heusden +// Released under AGPL v3.0 +#pragma once + +#include +#include +#include + +#define TM_11_MTS 0172520 // status register +#define TM_11_MTC 0172522 // command register +#define TM_11_MTBRC 0172524 // byte record counter +#define TM_11_MTCMA 0172526 // current memory address register +#define TM_11_MTD 0172530 // data buffer register +#define TM_11_MTRD 0172532 // TU10 read lines +#define TM_11_BASE TM_11_MTS +#define TM_11_END (TM_11_MTRD + 2) + +class memory; + +class tm_11 +{ +private: + memory *const m; + uint16_t registers[6]; + uint8_t xfer_buffer[65536]; + int offset; + FILE *fh; + +public: + tm_11(const std::string & file, memory *const m); + virtual ~tm_11(); + + uint8_t readByte(const uint16_t addr); + uint16_t readWord(const uint16_t addr); + + void writeByte(const uint16_t addr, const uint8_t v); + void writeWord(const uint16_t addr, uint16_t v); +}; diff --git a/tty.cpp b/tty.cpp new file mode 100644 index 0000000..7acdc2f --- /dev/null +++ b/tty.cpp @@ -0,0 +1,119 @@ +// (C) 2018 by Folkert van Heusden +// // Released under AGPL v3.0 +#include +#include +#include +#include + +#include "tty.h" +#include "gen.h" +#include "memory.h" +#include "utils.h" +#include "terminal.h" + +extern NEWWIN *w_main; + +const char * const regnames[] = { + "reader status ", + "reader buffer ", + "puncher status", + "puncher buffer" + }; + +tty::tty(const bool withUI) : withUI(withUI) +{ + memset(registers, 0x00, sizeof registers); +} + +tty::~tty() +{ +} + +uint8_t tty::readByte(const uint16_t addr) +{ + uint16_t v = readWord(addr & ~1); + + if (addr & 1) + return v >> 8; + + return v; +} + +uint16_t tty::readWord(const uint16_t addr) +{ + const int reg = (addr - PDP11TTY_BASE) / 2; + uint16_t vtemp = registers[reg]; + + fprintf(stderr, "PDP11TTY read addr %o (%s): ", addr, regnames[reg]); + + if (addr == PDP11TTY_TKS) { + vtemp = c ? 128 : 0; + } + else if (addr == PDP11TTY_TKB) { + vtemp = c | (parity(c) << 7); + c = 0; + } + else if (addr == PDP11TTY_TPS) { + vtemp = 128; + } + + fprintf(stderr, "%o\n", vtemp); + + registers[reg] = vtemp; + + return vtemp; +} + +void tty::writeByte(const uint16_t addr, const uint8_t v) +{ + uint16_t vtemp = registers[(addr - PDP11TTY_BASE) / 2]; + + if (addr & 1) { + vtemp &= ~0xff00; + vtemp |= v << 8; + } + else { + vtemp &= ~0x00ff; + vtemp |= v; + } + + writeWord(addr, vtemp); +} + +void tty::writeWord(const uint16_t addr, uint16_t v) +{ + const int reg = (addr - PDP11TTY_BASE) / 2; + fprintf(stderr, "PDP11TTY write %o (%s): %o\n", addr, regnames[reg], v); + + if (v == 0207 && testMode) { + fprintf(stderr, "TestMode: TTY 0207 char\n"); + exit(0); + } + + // FIXME + if (addr == PDP11TTY_TPB) { + v &= 127; + + FILE *tf = fopen("tty.dat", "a+"); + if (tf) { + fprintf(tf, "%c", v); + fclose(tf); + } + + if (withUI) { + if (v >= 32 || (v != 12 && v != 27 && v != 13)) { + wprintw(w_main -> win, "%c", v); + mydoupdate(); + } + } + else { + printf("%c", v); + fflush(NULL); + } + + fprintf(stderr, "punch char: '%c'\n", v); + } + + D(fprintf(stderr, "set register %o to %o\n", addr, v);) + registers[(addr - PDP11TTY_BASE) / 2] = v; +} diff --git a/tty.h b/tty.h new file mode 100644 index 0000000..2f0638e --- /dev/null +++ b/tty.h @@ -0,0 +1,38 @@ +// (C) 2018 by Folkert van Heusden +// Released under AGPL v3.0 +#pragma once + +#include +#include +#include + +#define PDP11TTY_TKS 0177560 // reader status +#define PDP11TTY_TKB 0177562 // reader buffer +#define PDP11TTY_TPS 0177564 // puncher status +#define PDP11TTY_TPB 0177566 // puncher buffer +#define PDP11TTY_BASE PDP11TTY_TKS +#define PDP11TTY_END (PDP11TTY_TPB + 2) + +class memory; + +class tty +{ +private: + uint16_t registers[4]; + bool testMode { false }, withUI { false }; + char c { 0 }; + +public: + tty(const bool withUI); + virtual ~tty(); + + void setTest() { testMode = true; } + + void sendChar(const char v) { c = v; }; + + uint8_t readByte(const uint16_t addr); + uint16_t readWord(const uint16_t addr); + + void writeByte(const uint16_t addr, const uint8_t v); + void writeWord(const uint16_t addr, uint16_t v); +}; diff --git a/utils.cpp b/utils.cpp new file mode 100644 index 0000000..7b56eb6 --- /dev/null +++ b/utils.cpp @@ -0,0 +1,45 @@ +// (C) 2018 by Folkert van Heusden +// Released under AGPL v3.0 +#include +#include +#include +#include + +void setBit(uint16_t & v, const int bit, const bool vb) +{ + const uint16_t mask = 1 << bit; + + v &= ~mask; + + if (vb) + v |= mask; +} + +std::string format(const char *const fmt, ...) +{ + char *buffer = nullptr; + va_list ap; + + va_start(ap, fmt); + (void)vasprintf(&buffer, fmt, ap); + va_end(ap); + + std::string result = buffer; + free(buffer); + + return result; +} + +unsigned long get_ms() +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + + return tv.tv_sec * 1000 + tv.tv_usec / 1000; +} + +int parity(int v) +{ + return __builtin_parity(v); // FIXME +} diff --git a/utils.h b/utils.h new file mode 100644 index 0000000..10337a3 --- /dev/null +++ b/utils.h @@ -0,0 +1,12 @@ +// (C) 2018 by Folkert van Heusden +// Released under AGPL v3.0 +#include +#include + +void setBit(uint16_t & v, const int bit, const bool vb); +int parity(int v); + +#define sign(a) ( ( (a) < 0 ) ? -1 : ( (a) > 0 ) ) + +std::string format(const char *const fmt, ...); +unsigned long get_ms();