From 39d7ef3d878f243ffa2347ed6a21cb8332f0b37f Mon Sep 17 00:00:00 2001 From: Sascha Nitsch Date: Thu, 14 Jul 2022 23:21:43 +0200 Subject: [PATCH] initial import of rewrite in typescript --- .eslintignore | 2 + .eslintrc.json | 7 + .gitignore | 9 + .prettierrc.json | 4 + COPYING | 674 +++++++++++++++++++++++++++++ Gruntfile.js | 201 +++++++++ components/breadboard.less | 42 ++ components/breadboard.svg | 62 +++ components/breadboard.ts | 239 ++++++++++ components/conn1x16.svg | 27 ++ components/conn1x16.ts | 36 ++ components/conn1x20.svg | 33 ++ components/conn1x20.ts | 36 ++ components/conn1x24.svg | 37 ++ components/conn1x24.ts | 36 ++ components/dflipflop.svg | 15 + components/dflipflop.ts | 52 +++ components/dilbase.ts | 64 +++ components/dilswitchx4.svg | 51 +++ components/dilswitchx4.ts | 80 ++++ components/dilswitchx8.svg | 95 ++++ components/dilswitchx8.ts | 80 ++++ components/ground.svg | 6 + components/ground.ts | 27 ++ components/html.ts | 41 ++ components/ic14.svg | 57 +++ components/ic16.svg | 63 +++ components/ic18.svg | 69 +++ components/ic20.svg | 75 ++++ components/ic74xx00.ts | 78 ++++ components/ic74xx02.ts | 78 ++++ components/ic74xx08.ts | 71 +++ components/ic74xx138.ts | 65 +++ components/ic74xx21.ts | 63 +++ components/ic74xx238.ts | 65 +++ components/ic74xx244.ts | 63 +++ components/ic74xx266.ts | 68 +++ components/ic74xx273.ts | 83 ++++ components/ic74xx283.ts | 70 +++ components/ic74xx374.ts | 76 ++++ components/ic74xx86.ts | 55 +++ components/jkflipflop.svg | 17 + components/jkflipflop.ts | 69 +++ components/jumper3.svg | 18 + components/jumper3.ts | 90 ++++ components/label.svg | 9 + components/label.ts | 82 ++++ components/led.svg | 6 + components/led.ts | 118 +++++ components/ledarray.svg | 6 + components/ledarray.ts | 141 ++++++ components/ledbar8.svg | 78 ++++ components/ledbar8.ts | 63 +++ components/logic74xx161.svg | 38 ++ components/logic74xx161.ts | 83 ++++ components/logic74xx21.svg | 12 + components/logic74xx21.ts | 56 +++ components/logic74xx244.svg | 26 ++ components/logic74xx244.ts | 75 ++++ components/logic74xx273.svg | 45 ++ components/logic74xx273.ts | 117 +++++ components/logicand.svg | 10 + components/logicand.ts | 45 ++ components/logicbase.ts | 33 ++ components/logicinvtristate.svg | 10 + components/logicinvtristate.ts | 44 ++ components/logicnand.svg | 11 + components/logicnand.ts | 43 ++ components/logicnor.svg | 11 + components/logicnor.ts | 43 ++ components/logicnot.svg | 10 + components/logicnot.ts | 40 ++ components/logicor.svg | 10 + components/logicor.ts | 43 ++ components/logicsource.svg | 9 + components/logicsource.ts | 84 ++++ components/logictristate.svg | 9 + components/logicxnor.svg | 11 + components/logicxnor.ts | 43 ++ components/logicxor.svg | 10 + components/logicxor.ts | 43 ++ components/momentarypushbutton.svg | 7 + components/momentarypushbutton.ts | 60 +++ components/power.svg | 6 + components/power.ts | 27 ++ components/pull.svg | 16 + components/pull.ts | 75 ++++ components/rsflipflop.svg | 17 + components/rsflipflop.ts | 64 +++ components/togglebutton.svg | 26 ++ components/togglebutton.ts | 152 +++++++ less/colors-light.less | 9 + less/colors.less | 19 + less/default.less | 24 + less/light.less | 24 + less/logic.less | 191 ++++++++ less/physical.less | 112 +++++ less/wire.less | 10 + package.json | 33 ++ plugins/README | 1 + ts/basecomponent.ts | 290 +++++++++++++ ts/helper.ts | 25 ++ ts/simulator.ts | 635 +++++++++++++++++++++++++++ ts/tristate.ts | 123 ++++++ ts/wire.ts | 317 ++++++++++++++ tsconfig.json | 15 + 106 files changed, 7044 insertions(+) create mode 100644 .eslintignore create mode 100644 .eslintrc.json create mode 100644 .gitignore create mode 100644 .prettierrc.json create mode 100644 COPYING create mode 100644 Gruntfile.js create mode 100644 components/breadboard.less create mode 100644 components/breadboard.svg create mode 100644 components/breadboard.ts create mode 100644 components/conn1x16.svg create mode 100644 components/conn1x16.ts create mode 100644 components/conn1x20.svg create mode 100644 components/conn1x20.ts create mode 100644 components/conn1x24.svg create mode 100644 components/conn1x24.ts create mode 100644 components/dflipflop.svg create mode 100644 components/dflipflop.ts create mode 100644 components/dilbase.ts create mode 100644 components/dilswitchx4.svg create mode 100644 components/dilswitchx4.ts create mode 100644 components/dilswitchx8.svg create mode 100644 components/dilswitchx8.ts create mode 100644 components/ground.svg create mode 100644 components/ground.ts create mode 100644 components/html.ts create mode 100644 components/ic14.svg create mode 100644 components/ic16.svg create mode 100644 components/ic18.svg create mode 100644 components/ic20.svg create mode 100644 components/ic74xx00.ts create mode 100644 components/ic74xx02.ts create mode 100644 components/ic74xx08.ts create mode 100644 components/ic74xx138.ts create mode 100644 components/ic74xx21.ts create mode 100644 components/ic74xx238.ts create mode 100644 components/ic74xx244.ts create mode 100644 components/ic74xx266.ts create mode 100644 components/ic74xx273.ts create mode 100644 components/ic74xx283.ts create mode 100644 components/ic74xx374.ts create mode 100644 components/ic74xx86.ts create mode 100644 components/jkflipflop.svg create mode 100644 components/jkflipflop.ts create mode 100644 components/jumper3.svg create mode 100644 components/jumper3.ts create mode 100644 components/label.svg create mode 100644 components/label.ts create mode 100644 components/led.svg create mode 100644 components/led.ts create mode 100644 components/ledarray.svg create mode 100644 components/ledarray.ts create mode 100644 components/ledbar8.svg create mode 100644 components/ledbar8.ts create mode 100644 components/logic74xx161.svg create mode 100644 components/logic74xx161.ts create mode 100644 components/logic74xx21.svg create mode 100644 components/logic74xx21.ts create mode 100644 components/logic74xx244.svg create mode 100644 components/logic74xx244.ts create mode 100644 components/logic74xx273.svg create mode 100644 components/logic74xx273.ts create mode 100644 components/logicand.svg create mode 100644 components/logicand.ts create mode 100644 components/logicbase.ts create mode 100644 components/logicinvtristate.svg create mode 100644 components/logicinvtristate.ts create mode 100644 components/logicnand.svg create mode 100644 components/logicnand.ts create mode 100644 components/logicnor.svg create mode 100644 components/logicnor.ts create mode 100644 components/logicnot.svg create mode 100644 components/logicnot.ts create mode 100644 components/logicor.svg create mode 100644 components/logicor.ts create mode 100644 components/logicsource.svg create mode 100644 components/logicsource.ts create mode 100644 components/logictristate.svg create mode 100644 components/logicxnor.svg create mode 100644 components/logicxnor.ts create mode 100644 components/logicxor.svg create mode 100644 components/logicxor.ts create mode 100644 components/momentarypushbutton.svg create mode 100644 components/momentarypushbutton.ts create mode 100644 components/power.svg create mode 100644 components/power.ts create mode 100644 components/pull.svg create mode 100644 components/pull.ts create mode 100644 components/rsflipflop.svg create mode 100644 components/rsflipflop.ts create mode 100644 components/togglebutton.svg create mode 100644 components/togglebutton.ts create mode 100644 less/colors-light.less create mode 100644 less/colors.less create mode 100644 less/default.less create mode 100644 less/light.less create mode 100644 less/logic.less create mode 100644 less/physical.less create mode 100644 less/wire.less create mode 100644 package.json create mode 100644 plugins/README create mode 100644 ts/basecomponent.ts create mode 100644 ts/helper.ts create mode 100644 ts/simulator.ts create mode 100644 ts/tristate.ts create mode 100644 ts/wire.ts create mode 100644 tsconfig.json diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..8c126b0 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +**/*.js +decl/**/*.ts diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..67272b0 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,7 @@ +{ + "extends": [ + "./node_modules/gts/.eslintrc.json" + ], + "rules": { + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..263fde2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +.project +.settings +package-lock.json +node_modules +htdocs +.tscache +js-temp/ +decl/ +plugins/*/* diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..d3c9635 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,4 @@ +{ + "printWidth": 120, + "singleQuote": true +} \ No newline at end of file diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 0000000..27a5e8b --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,201 @@ +module.exports = function(grunt) { + // Project configuration. + grunt + .initConfig({ + pkg : grunt.file.readJSON('package.json'), + uglify : { + options : { + beautify : false, + keepFArgs: false, + keepFName: false + }, + base : { + files : { + 'htdocs/simulator.min.js' : [ + 'js-temp/ts/basecomponent.js', + 'js-temp/ts/simulator.js', + 'js-temp/ts/tristate.js', + 'js-temp/ts/wire.js', + 'js-temp/ts/polyfill.js', + 'js-temp/ts/helper.js' + ] + } + }, + components : { + files: [{ + cwd: 'js-temp/components/', + src: '*.js', + expand: true, + dest: 'htdocs/components/', + rename: function (dst, src) { + return dst + '/' + src.replace('.js', '.min.js'); + } + }], + }, + plugins : { + files: [{ + cwd: 'js-temp/plugins/', + src: './**/*.js', + expand: true, + dest: 'htdocs/components/', + rename: function (dst, src) { + return dst + src.replace('.js', '.min.js').replace(/\.\/.*\//, '/'); + } + }], + } + }, + ts: { + base: { + tsconfig: './tsconfig.json', + src: ["ts/*.ts", "!components/*.ts", "!node_modules/**", "!plugins/**/*.ts", "!decl/**/*.ts"], + options: { + declaration: true, + declarationDir: "decl" + } + }, + components: { + tsconfig: './tsconfig.json', + src: ["!ts/*.ts", "components/*.ts", "!node_modules/**", "!plugins/**/*.ts", "decl/ts/*.ts", "!decl/components/*.ts"], + options: { + declaration: true, + declarationDir: "decl" + } + }, + plugins: { + tsconfig: './tsconfig.json', + src: ["plugins/**/*.ts", "!node_modules/**", "!components/*.ts", "!ts/*.ts", "decl/**/*.ts"], + } + }, + svgmin: { + options: { + plugins: [ + { removeViewBox: true }, + { removeUselessStrokeAndFill: true }, + { removeDoctype: false }, + { cleanupIDs: false }, + { collapseGroups: false }, + { removeAttrs: [] }, + { mergePaths: false }, + { removeEmptyText: false }, + { convertShapeToPath: false }, + ] + }, + components: { + files: [{ + cwd: 'components/', + src: '*.svg', + expand: true, + dest: 'htdocs/components/', + rename: function (dst, src) { + return dst + '/' + src.replace('.js', '.min.js'); + } + }] + }, + plugins : { + files: [{ + cwd: 'plugins/', + src: '**/*.svg', + expand: true, + dest: 'htdocs/components/', + rename: function (dst, src) { + return dst + src.replace(/^.*\//, '\/'); + } + }], + } + }, + watch : { + tsbase : { + files : [ 'ts/*.ts' ], + tasks : [ 'ts:base' ] + }, + tscomponents : { + files : [ 'ts/*.ts', 'components/*.ts' ], + tasks : [ 'ts:components' ] + }, + tsplugins : { + files : [ 'plugins/**/*.ts' ], + tasks : [ 'ts:plugins' ] + }, + jsbase : { + files : [ 'js-temp/*.js' ], + tasks : [ 'uglify:base' ] + }, + jscomponents : { + files : [ 'js-temp/components/*.js' ], + tasks : [ 'newer:uglify:components' ] + }, + jsplugins : { + files : [ 'js-temp/plugins/**/*.js' ], + tasks : [ 'newer:uglify:plugins' ] + }, + componentssvg : { + files : [ 'components/*.svg' ], + tasks : [ 'newer:svgmin:components' ] + }, + pluginsvg : { + files : [ 'plugins/**/*.svg' ], + tasks : [ 'newer:svgmin:plugins' ] + }, + lessbase : { + files : [ 'less/*.less' ], + tasks : [ 'less:base' ] + }, + lesscomponents : { + files : [ 'components/*.less'], + tasks : [ 'less:components' ] + }, + lessplugins : { + files : [ 'plugins/**/*.less'], + tasks : [ 'less:plugins' ] + }, + }, + less : { + base : { + options : { + "strictImports" : true, + "compress" : true + }, + files : { + "htdocs/css/default.css" : "less/default.less", + "htdocs/css/light.css" : "less/light.less", + }, + }, + components : { + options : { + "strictImports" : true, + "compress" : true + }, + files : { + "htdocs/components/breadboard.css" : "components/breadboard.less" + }, + }, + plugins : { + options : { + "strictImports" : true, + "compress" : true + }, + files: [{ + cwd: 'plugins/', + src: '**/*.less', + expand: true, + dest: 'htdocs/components/', +/* rename: function (dst, src) { + return dst + src.replace(/^.*\//, '\/'); + }*/ + }], + } + } + }); + + grunt.loadNpmTasks('grunt-contrib-uglify'); + grunt.loadNpmTasks('grunt-contrib-watch'); + grunt.loadNpmTasks('grunt-contrib-less'); + grunt.loadNpmTasks('grunt-svgmin'); + grunt.loadNpmTasks('grunt-newer'); + grunt.loadNpmTasks('grunt-ts'); + + // Default task(s). + grunt.registerTask('default', [ 'ts', 'less', 'uglify', 'svgmin', 'watch' ]); + grunt.registerTask('release', [ 'ts', 'less', 'uglify', 'svgmin' ]); + +}; diff --git a/components/breadboard.less b/components/breadboard.less new file mode 100644 index 0000000..d0057a2 --- /dev/null +++ b/components/breadboard.less @@ -0,0 +1,42 @@ +.breadboard { + .board { + fill:#d9d9d9; + } + .shadow-right, .shadow-bottom { + stroke: #bfbfbf; + stroke-width:1px; + } + .shadow-left, .shadow-top { + stroke: #e6e6e6; + stroke-width:1px; + } + .pin { + path { + &:nth-child(2n) { + fill: #e6e6e6; + } + &:nth-child(2n+1) { + fill: #bfbfbf; + } + } + circle { + fill: #383838; + } + } + .pintemplate { + display:none; + } + .plus { + stroke: #F00; + stroke-width:1; + + } + .minus { + stroke: #00F; + stroke-width:1; + } + + font-size:6px; + fill:#999; + font-family:sans-serif; +} \ No newline at end of file diff --git a/components/breadboard.svg b/components/breadboard.svg new file mode 100644 index 0000000..dd1adfd --- /dev/null +++ b/components/breadboard.svg @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + - + + 0 + 5 + 10 + 15 + 20 + 25 + 30 + 35 + 40 + 45 + 50 + 55 + + + A + B + C + D + E + F + G + H + I + J + + + + + + + + diff --git a/components/breadboard.ts b/components/breadboard.ts new file mode 100644 index 0000000..5e18474 --- /dev/null +++ b/components/breadboard.ts @@ -0,0 +1,239 @@ +// Breadboard component for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +interface BreadboardParam extends BaseComponentParam { + nopower?: boolean; + onlypower?: boolean; +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +class Breadboard extends BaseComponent { + private l: string; + private unitX: number; + private unitY: number; + private active: Map; + private nopower: boolean; + private onlypower: boolean; + private descrlabel: string; + + constructor(simulator: Simulator, id: string, param: BreadboardParam) { + simulator.loadCSS('breadboard'); + super(simulator, id, param); + this.nopower = param.nopower || false; + this.onlypower = param.onlypower || false; + this.descrlabel = typeof param.label === 'string' ? param.label : '' || ''; + this.active = new Map(); + this.l = 'ABCDEFGHIJ'; + let x: number; + let y: number; + this.unitX = 2 * 2.54; + this.unitY = 2 * 2.54; + if (!param || !param.onlypower) { + for (x = 0; x < 60; ++x) { + for (y = 0; y < 10; ++y) { + this.pins.set( + this.l[y] + x, + new TriState(this, (x + 1.5) * this.unitX, this.unitY * (16 - y - (y > 4 ? 2 : 0)), undefined, { + id: this.l[y] + x, + }) + ); + } + } + } + y = 12; + if (!param || !param.nopower) { + for (x = 0; x < 50; ++x) { + this.pins.set( + 'pa' + x, + new TriState(this, this.unitX * (x + 2.5 + Math.floor(x / 5)), 2.5 * this.unitY, undefined, { id: 'pa' + x }) + ); + this.pins.set( + 'ma' + x, + new TriState(this, this.unitX * (x + 2.5 + Math.floor(x / 5)), 1.5 * this.unitY, undefined, { id: 'ma' + x }) + ); + if (!param || !param.onlypower) { + this.pins.set( + 'pb' + x, + new TriState(this, this.unitX * (x + 2.5 + Math.floor(x / 5)), 19.5 * this.unitY, undefined, { + id: 'pb' + x, + }) + ); + this.pins.set( + 'mb' + x, + new TriState(this, this.unitX * (x + 2.5 + Math.floor(x / 5)), 18.5 * this.unitY, undefined, { + id: 'mb' + x, + }) + ); + } + } + } + //this.wires = {}; + this.zIndex = 0; + } + + setup(parent: SVGElement) { + super.doSetup('breadboard', parent); + if (this.element === null) return; + const g = this.element.childNodes[0]; + const tpl = this.element.querySelectorAll('.pintemplate')[0]; + this.pins.forEach((k) => { + // const k = this.pins.get(key); + if (!(k instanceof TriState)) { + return; + } + //console.log(this.state[key].x(),this.state[key].y()); + const node = tpl.cloneNode(true); + node.setAttribute('class', 'pin'); + node.setAttribute('transform', 'translate(' + k.getOffsetX() + ',' + k.getOffsetY() + ')'); + g.appendChild(node); + }); + if (this.nopower) { + const power = this.element.querySelectorAll('.power'); + for (let i = 0; i < power.length; ++i) { + const p = power[i].parentNode; + if (p !== null) { + p.removeChild(power[i]); + } + } + } + if (this.onlypower) { + let rem = this.element.querySelectorAll('.bottom'); + for (let i = 0; i < rem.length; ++i) { + const r = rem[i].parentNode; + if (r !== null) { + r.removeChild(rem[i]); + } + } + rem = this.element.querySelectorAll('.center'); + for (let i = 0; i < rem.length; ++i) { + const r = rem[i].parentNode; + if (r !== null) { + r.removeChild(rem[i]); + } + } + if (!this.descrlabel) { + rem = this.element.querySelectorAll('.label'); + for (let i = 0; i < rem.length; ++i) { + const r = rem[i].parentNode; + if (r !== null) { + r.removeChild(rem[i]); + } + } + } + } + } + + connected(pin: TriState) { + this.active.set(pin.getParam().id || '', pin); + } + + finalize() { + let row; + let pins; + let col; + let i; + const bus = ['pa', 'ma', 'pb', 'mb']; + for (row = 0; row < 4; ++row) { + pins = []; + for (col = 0; col < 50; ++col) { + const a = this.active.get(bus[row] + col); + if (a !== undefined) { + pins.push(a); + } + } + if (pins.length > 1) { + let sourceId = 0; + // find names wire + for (i = 0; i < pins.length; ++i) { + const pin = pins[i].connectedWire; + if (pin && pin.autoid === false) { + sourceId = i; + break; + } + } + const source = pins[sourceId].connectedWire; + if (source !== null) { + for (i = 0; i < pins.length; ++i) { + if (i === sourceId) continue; + source.join(pins[i].connectedWire); + pins[i].connectedWire = null; + const id = pins[i].getParam().id; + if (id) { + this.active.delete(id); + } + } + const id = pins[sourceId].getParam().id; + if (id) { + this.active.delete(id); + } + // remove ourself + source.remove(this); + } + } + } + for (col = 0; col < 60; ++col) { + for (let start = 0; start < 10; start += 5) { + pins = []; + for (row = start; row < start + 5; ++row) { + const a = this.active.get(this.l[row] + col); + if (a !== undefined) { + pins.push(a); + } + } + if (pins.length > 1) { + for (i = 1; i < pins.length; ++i) { + if (pins[0].connectedWire) { + pins[0].connectedWire.join(pins[i].connectedWire); + pins[i].connectedWire = null; + const id = pins[i].getParam().id; + if (id) { + this.active.delete(id); + } + } + } + const id = pins[0].getParam().id; + if (id) { + this.active.delete(id); + } + // remove ourself + if (pins[0].connectedWire) { + pins[0].connectedWire.remove(this); + } + } + } + } + } + + getPos(column: string): number[] { + let t; + if (column[0] >= 'A' && column[0] <= 'J') { + // exact pin + t = this.pins.get(column); + } else { + t = this.pins.get('E' + column); + } + if (t === undefined) return [0, 0]; + const x = this.x + t.getOffsetX() * this.scaleX; + const y = this.y + t.getOffsetY() * this.scaleY; + return [x, y]; + } + + io(): void { + this.active.forEach((value) => { + value.getAndReset(); + }); + } +} diff --git a/components/conn1x16.svg b/components/conn1x16.svg new file mode 100644 index 0000000..36c24c5 --- /dev/null +++ b/components/conn1x16.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/components/conn1x16.ts b/components/conn1x16.ts new file mode 100644 index 0000000..96877f8 --- /dev/null +++ b/components/conn1x16.ts @@ -0,0 +1,36 @@ +// 1x16 pin connection component for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +class Conn1x16 extends BaseComponent { + constructor(simulator: Simulator, id: string, param: BaseComponentParam) { + super(simulator, id, param); + for (let i = 0; i < 16; ++i) { + this.pins.set('a' + (i + 1), new TriState(this, -20, (i - 7) * 15 - 1.5)); + } + } + + setup(canvas: SVGElement) { + super.doSetup('conn1x16', canvas); + } + + io() { + for (let i = 1; i <= 16; ++i) { + this.getPin('a' + i).getAndReset(); + } + return false; + } +} diff --git a/components/conn1x20.svg b/components/conn1x20.svg new file mode 100644 index 0000000..87b9862 --- /dev/null +++ b/components/conn1x20.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/components/conn1x20.ts b/components/conn1x20.ts new file mode 100644 index 0000000..2fb4103 --- /dev/null +++ b/components/conn1x20.ts @@ -0,0 +1,36 @@ +// 1x20 pin connection component for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +class Conn1x20 extends BaseComponent { + constructor(simulator: Simulator, id: string, param: BaseComponentParam) { + super(simulator, id, param); + for (let i = 0; i < 20; ++i) { + this.pins.set('a' + (i + 1), new TriState(this, -20, (i - 9) * 15 - 1.5)); + } + } + + setup(canvas: SVGElement) { + super.doSetup('conn1x20', canvas); + } + + io() { + for (let i = 1; i <= 20; ++i) { + this.getPin('a' + i).getAndReset(); + } + return false; + } +} diff --git a/components/conn1x24.svg b/components/conn1x24.svg new file mode 100644 index 0000000..f928cc9 --- /dev/null +++ b/components/conn1x24.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/components/conn1x24.ts b/components/conn1x24.ts new file mode 100644 index 0000000..dd45620 --- /dev/null +++ b/components/conn1x24.ts @@ -0,0 +1,36 @@ +// 1x24 bin connection component for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +class Conn1x24 extends BaseComponent { + constructor(simulator: Simulator, id: string, param: BaseComponentParam) { + super(simulator, id, param); + for (let i = 0; i < 24; ++i) { + this.pins.set('a' + (i + 1), new TriState(this, -20, (i - 11) * 15 - 1.5)); + } + } + + setup(canvas: SVGElement) { + super.doSetup('conn1x24', canvas); + } + + io() { + for (let i = 1; i <= 24; ++i) { + this.getPin('a' + i).getAndReset(); + } + return false; + } +} diff --git a/components/dflipflop.svg b/components/dflipflop.svg new file mode 100644 index 0000000..0faafcb --- /dev/null +++ b/components/dflipflop.svg @@ -0,0 +1,15 @@ + + + + + + + + + + D + Q + + Q + + diff --git a/components/dflipflop.ts b/components/dflipflop.ts new file mode 100644 index 0000000..cf69c90 --- /dev/null +++ b/components/dflipflop.ts @@ -0,0 +1,52 @@ +// D-Flip-Flop component for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +class DFlipFlop extends BaseComponent { + private d = false; + private CLK = false; + private oldCLK = false; + private q = false; + constructor(simulator: Simulator, id: string, param: BaseComponentParam) { + super(simulator, id, param); + this.pins.set('d', new TriState(this, -30, -10)); + this.pins.set('clk', new TriState(this, -30, 10)); + this.pins.set('q', new TriState(this, 30, -10)); + this.getPin('q').setBool(false); + this.pins.set('notq', new TriState(this, 30, 10)); + this.getPin('notq').setBool(true); + } + + setup(canvas: SVGElement) { + super.doSetup('dflipflop', canvas); + } + + io() { + this.d = this.binary(this.getPin('d').getAndReset(), true); + this.CLK = this.binary(this.getPin('clk').getAndReset(), true); + } + + update(): boolean { + const oldstate = this.q; + if (this.oldCLK === false && this.CLK === true) { + this.q = this.d; + } + this.getPin('q').setBool(this.q); + this.getPin('notq').setBool(this.q === false); + this.oldCLK = this.CLK; + return oldstate !== this.q; + } +} diff --git a/components/dilbase.ts b/components/dilbase.ts new file mode 100644 index 0000000..7f012a6 --- /dev/null +++ b/components/dilbase.ts @@ -0,0 +1,64 @@ +// DIL Base class for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +class DILBase extends BaseComponent { + protected pincount: number; + constructor(simulator: Simulator, id: string, pincount: number, param: BaseComponentParam) { + super(simulator, id, param); + for (let i = 0; i < pincount / 2; ++i) { + this.pins.set('p' + (i + 1), new TriState(this, i * 5.08, 0, undefined, { id: 'p' + (i + 1) })); + this.pins.set( + 'p' + (pincount - i), + new TriState(this, i * 5.08, -5.08 * 3, undefined, { id: 'p' + (pincount - i) }) + ); + this.stateMapWire.set('p' + (i + 1), WireState.float); + this.stateMapWire.set('p' + (pincount - i), WireState.float); + } + this.pincount = pincount; + } + + DILBASEsetup(id: string, canvas: SVGElement, title: string) { + super.doSetup(id, canvas); + if (this.breadboard) { + for (let i = 0; i < this.pincount / 2; ++i) { + this.simulator.wire( + null, + [this.getPin('p' + (i + 1)), this.breadboard.getPin('E' + (i + this.col))], + this.id + 'p' + (i + 1) + ); + this.simulator.wire( + null, + [this.getPin('p' + (this.pincount - i)), this.breadboard.getPin('F' + (i + this.col))], + this.id + 'p' + (this.pincount - i) + ); + } + } + if (title && this.element) { + const text = this.element.getElementsByTagName('text'); + if (text) { + const node = document.createTextNode(title); + text[0].appendChild(node); + } + } + } + + io() { + for (let p = 1; p <= this.pincount; ++p) { + this.stateMapWire.set('p' + p, this.getPin('p' + p).getAndReset()); + } + } +} diff --git a/components/dilswitchx4.svg b/components/dilswitchx4.svg new file mode 100644 index 0000000..63c72bb --- /dev/null +++ b/components/dilswitchx4.svg @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/components/dilswitchx4.ts b/components/dilswitchx4.ts new file mode 100644 index 0000000..30a5ee2 --- /dev/null +++ b/components/dilswitchx4.ts @@ -0,0 +1,80 @@ +// 4x DIL switch component for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function DILSwitchX4Init() { + class DILSwitchX4 extends DILBase { + pressed: boolean[]; + constructor(simulator: Simulator, id: string, param: BaseComponentParam) { + super(simulator, id, 8, param); + this.pressed = []; + for (let i = 0; i < 4; ++i) { + this.pressed[i] = false; + } + } + + setup(canvas: SVGElement) { + super.DILBASEsetup('dilswitchx4', canvas, ''); + if (this.element) { + this.element.addEventListener('mousedown', this.activate.bind(this)); + const on = this.element.querySelectorAll('.on'); + for (let o = 0; o < on.length; ++o) { + on[o].setAttribute('style', 'display:none'); + } + } + } + + update(): boolean { + let mod = false; + for (let i = 1; i <= 4; ++i) { + const oldstate = this.getStateWire('p' + i); + let newstate = WireState.float; + if (this.pressed[i - 1]) { + newstate = this.getStateWire('p' + (9 - i)); + } + this.stateMapWire.set('p' + i, newstate); + this.getPin('p' + i).setState(newstate); + mod ||= newstate !== oldstate; + } + return mod; + } + + activate(e: MouseEvent) { + const parent = (e.target).parentElement; + if (parent === null || /*parent === this.element ||*/ parent.id === '') { + return; + } + const id = parseInt(parent.id.substr(1)) - 1; + this.pressed[id] = !this.pressed[id]; + const off = parent.querySelectorAll('.off')[0]; + const on = parent.querySelectorAll('.on')[0]; + if (this.pressed[id]) { + off.setAttribute('style', 'display:none'); + on.removeAttribute('style'); + } else { + on.setAttribute('style', 'display:none'); + off.removeAttribute('style'); + } + this.simulator.manualtick(); + } + } + window.DILSwitchX4 = DILSwitchX4; +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function DILSwitchX4Depends() { + return 'dilbase'; +} diff --git a/components/dilswitchx8.svg b/components/dilswitchx8.svg new file mode 100644 index 0000000..4c07357 --- /dev/null +++ b/components/dilswitchx8.svg @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/components/dilswitchx8.ts b/components/dilswitchx8.ts new file mode 100644 index 0000000..6ebc0dc --- /dev/null +++ b/components/dilswitchx8.ts @@ -0,0 +1,80 @@ +// 8x DIL switch Breadboard component for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function DILSwitchX8Init() { + class DILSwitchX8 extends DILBase { + pressed: boolean[]; + constructor(simulator: Simulator, id: string, param: BaseComponentParam) { + super(simulator, id, 16, param); + this.pressed = []; + for (let i = 0; i < 8; ++i) { + this.pressed[i] = false; + } + } + + setup(canvas: SVGElement) { + super.DILBASEsetup('dilswitchx8', canvas, ''); + if (this.element) { + this.element.addEventListener('mousedown', this.activate.bind(this)); + const on = this.element.querySelectorAll('.on'); + for (let o = 0; o < on.length; ++o) { + on[o].setAttribute('style', 'display:none'); + } + } + } + + update(): boolean { + let mod = false; + for (let i = 1; i <= 8; ++i) { + const oldstate = this.getStateWire('p' + i); + let newstate = WireState.float; + if (this.pressed[i - 1]) { + newstate = this.getStateWire('p' + (17 - i)); + } + this.stateMapWire.set('p' + i, newstate); + this.getPin('p' + i).setState(newstate); + mod ||= newstate !== oldstate; + } + return mod; + } + + activate(e: MouseEvent) { + const parent = (e.target).parentElement; + if (parent === null || /*parent === this.element ||*/ parent.id === '') { + return; + } + const id = parseInt(parent.id.substr(1)) - 1; + this.pressed[id] = !this.pressed[id]; + const off = parent.querySelectorAll('.off')[0]; + const on = parent.querySelectorAll('.on')[0]; + if (this.pressed[id]) { + off.setAttribute('style', 'display:none'); + on.removeAttribute('style'); + } else { + on.setAttribute('style', 'display:none'); + off.removeAttribute('style'); + } + this.simulator.manualtick(); + } + } + window.DILSwitchX8 = DILSwitchX8; +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function DILSwitchX8Depends() { + return 'dilbase'; +} diff --git a/components/ground.svg b/components/ground.svg new file mode 100644 index 0000000..e5ee3a6 --- /dev/null +++ b/components/ground.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/components/ground.ts b/components/ground.ts new file mode 100644 index 0000000..544b89c --- /dev/null +++ b/components/ground.ts @@ -0,0 +1,27 @@ +// Ground component for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +class Ground extends BaseComponent { + constructor(simulator: Simulator, id: string) { + super(simulator, id); + this.pins.set('q', new TriState(this, 0, 0, WireState.low, { id: 'ground' })); + } + + setup(canvas: SVGElement): void { + super.doSetup('ground', canvas); + } +} diff --git a/components/html.ts b/components/html.ts new file mode 100644 index 0000000..e50eebf --- /dev/null +++ b/components/html.ts @@ -0,0 +1,41 @@ +// HTML generation component for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +interface HtmlParam extends BaseComponentParam { + html: string; +} +// eslint-disable-next-line @typescript-eslint/no-unused-vars +class Html extends BaseComponent { + private div: HTMLDivElement | undefined; + private html: string; + constructor(simulator: Simulator, id: string, param: HtmlParam) { + super(simulator, id, param); + this.html = param.html; + } + + setup(canvas: SVGElement) { + super.doSetup('html', canvas); + this.div = document.createElement('div'); + this.div.innerHTML = this.html; + this.simulator.getParent().appendChild(this.div); + } + + close() { + if (this.div) { + this.simulator.getParent().removeChild(this.div); + } + } +} diff --git a/components/ic14.svg b/components/ic14.svg new file mode 100644 index 0000000..412a078 --- /dev/null +++ b/components/ic14.svg @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/components/ic16.svg b/components/ic16.svg new file mode 100644 index 0000000..a15a641 --- /dev/null +++ b/components/ic16.svg @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/components/ic18.svg b/components/ic18.svg new file mode 100644 index 0000000..698ca5f --- /dev/null +++ b/components/ic18.svg @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/components/ic20.svg b/components/ic20.svg new file mode 100644 index 0000000..d720a96 --- /dev/null +++ b/components/ic20.svg @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/components/ic74xx00.ts b/components/ic74xx00.ts new file mode 100644 index 0000000..0522702 --- /dev/null +++ b/components/ic74xx00.ts @@ -0,0 +1,78 @@ +// 74xx00 emulation component for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function IC74xx00Init() { + class IC74xx00 extends DILBase { + constructor(simulator: Simulator, id: string, param: BaseComponentParam) { + super(simulator, id, 14, param); + } + + setup(canvas: SVGElement) { + super.DILBASEsetup('ic14', canvas, '74xx00'); + } + + update(): boolean { + if (this.startdelay) { + --this.startdelay; + return false; + } + if (this.getStateWire('p14') !== WireState.high || this.getStateWire('p7') !== WireState.low) return false; // no power + let oldstate; + let mod = false; + let s; + oldstate = this.getStateWire('p3'); + s = !( + this.binary(this.getStateWire('p1'), true) !== false && this.binary(this.getStateWire('p2'), true) !== false + ); + this.stateMapWire.set('p3', s ? WireState.high : WireState.low); + mod ||= this.getStateWire('p3') !== oldstate; + + oldstate = this.getStateWire('p6'); + s = !( + this.binary(this.getStateWire('p4'), true) !== false && this.binary(this.getStateWire('p5'), true) !== false + ); + this.stateMapWire.set('p6', s ? WireState.high : WireState.low); + mod ||= this.getStateWire('p6') !== oldstate; + + oldstate = this.getStateWire('p8'); + s = !( + this.binary(this.getStateWire('p9'), true) !== false && this.binary(this.getStateWire('p10'), true) !== false + ); + this.stateMapWire.set('p8', s ? WireState.high : WireState.low); + mod ||= this.getStateWire('p8') !== oldstate; + + oldstate = this.getStateWire('p11'); + s = !( + this.binary(this.getStateWire('p12'), true) !== false && this.binary(this.getStateWire('p13'), true) !== false + ); + this.stateMapWire.set('p11', s ? WireState.high : WireState.low); + mod ||= this.getStateWire('p11') !== oldstate; + + this.getPin('p3').setState(this.getStateWire('p3')); + this.getPin('p6').setState(this.getStateWire('p6')); + this.getPin('p8').setState(this.getStateWire('p8')); + this.getPin('p11').setState(this.getStateWire('p11')); + return mod; + } + } + window.IC74xx00 = IC74xx00; +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function IC74xx00Depends() { + return 'dilbase'; +} diff --git a/components/ic74xx02.ts b/components/ic74xx02.ts new file mode 100644 index 0000000..66a9931 --- /dev/null +++ b/components/ic74xx02.ts @@ -0,0 +1,78 @@ +// 74xx02 emulation component for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function IC74xx02Init() { + class IC74xx02 extends DILBase { + constructor(simulator: Simulator, id: string, param: BaseComponentParam) { + super(simulator, id, 14, param); + } + + setup(canvas: SVGElement) { + super.DILBASEsetup('ic14', canvas, '74xx02'); + } + + update(): boolean { + if (this.startdelay) { + --this.startdelay; + return false; + } + if (this.getStateWire('p14') !== WireState.high || this.getStateWire('p7') !== WireState.low) return false; // no power + let oldstate; + let mod = false; + let s; + oldstate = this.getStateWire('p1'); + s = !( + this.binary(this.getStateWire('p2'), true) !== false || this.binary(this.getStateWire('p3'), true) !== false + ); + this.stateMapWire.set('p1', s ? WireState.high : WireState.low); + mod ||= this.getStateWire('p1') !== oldstate; + + oldstate = this.getStateWire('p4'); + s = !( + this.binary(this.getStateWire('p5'), true) !== false || this.binary(this.getStateWire('p6'), true) !== false + ); + this.stateMapWire.set('p4', s ? WireState.high : WireState.low); + mod ||= this.getStateWire('p4') !== oldstate; + + oldstate = this.getStateWire('p10'); + s = !( + this.binary(this.getStateWire('p8'), true) !== false || this.binary(this.getStateWire('p9'), true) !== false + ); + this.stateMapWire.set('p10', s ? WireState.high : WireState.low); + mod ||= this.getStateWire('p10') !== oldstate; + + oldstate = this.getStateWire('p13'); + s = !( + this.binary(this.getStateWire('p11'), true) !== false || this.binary(this.getStateWire('p12'), true) !== false + ); + this.stateMapWire.set('p13', s ? WireState.high : WireState.low); + mod ||= this.getStateWire('p13') !== oldstate; + + this.getPin('p1').setState(this.getStateWire('p1')); + this.getPin('p4').setState(this.getStateWire('p4')); + this.getPin('p10').setState(this.getStateWire('p10')); + this.getPin('p13').setState(this.getStateWire('p13')); + return mod; + } + } + window.IC74xx02 = IC74xx02; +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function IC74xx02Depends() { + return 'dilbase'; +} diff --git a/components/ic74xx08.ts b/components/ic74xx08.ts new file mode 100644 index 0000000..5ce27eb --- /dev/null +++ b/components/ic74xx08.ts @@ -0,0 +1,71 @@ +// 74xx08 emulation component for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function IC74xx08Init() { + class IC74xx08 extends DILBase { + constructor(simulator: Simulator, id: string, param: BaseComponentParam) { + super(simulator, id, 14, param); + } + + setup(canvas: SVGElement) { + super.DILBASEsetup('ic14', canvas, '74xx08'); + } + + update(): boolean { + if (this.startdelay) { + --this.startdelay; + return false; + } + if (this.getStateWire('p14') !== WireState.high || this.getStateWire('p7') !== WireState.low) return false; // no power + let mod = false; + let oldstate; + let s; + oldstate = this.getStateWire('p3'); + s = this.binary(this.getStateWire('p1'), true) !== false && this.binary(this.getStateWire('p2'), true) !== false; + this.stateMapWire.set('p3', s ? WireState.high : WireState.low); + mod ||= this.getStateWire('p3') !== oldstate; + + oldstate = this.getStateWire('p6'); + s = this.binary(this.getStateWire('p4'), true) !== false && this.binary(this.getStateWire('p5'), true) !== false; + this.stateMapWire.set('p6', s ? WireState.high : WireState.low); + mod ||= this.getStateWire('p6') !== oldstate; + + oldstate = this.getStateWire('p8'); + s = this.binary(this.getStateWire('p9'), true) !== false && this.binary(this.getStateWire('p10'), true) !== false; + this.stateMapWire.set('p8', s ? WireState.high : WireState.low); + mod ||= this.getStateWire('p8') !== oldstate; + + oldstate = this.getStateWire('p11'); + s = + this.binary(this.getStateWire('p12'), true) !== false && this.binary(this.getStateWire('p13'), true) !== false; + this.stateMapWire.set('p11', s ? WireState.high : WireState.low); + mod ||= this.getStateWire('p11') !== oldstate; + + this.getPin('p3').setState(this.getStateWire('p3')); + this.getPin('p6').setState(this.getStateWire('p6')); + this.getPin('p8').setState(this.getStateWire('p8')); + this.getPin('p11').setState(this.getStateWire('p11')); + return mod; + } + } + window.IC74xx08 = IC74xx08; +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function IC74xx08Depends() { + return 'dilbase'; +} diff --git a/components/ic74xx138.ts b/components/ic74xx138.ts new file mode 100644 index 0000000..30f4ad2 --- /dev/null +++ b/components/ic74xx138.ts @@ -0,0 +1,65 @@ +// 74xx138 emulation component for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function IC74xx138Init() { + class IC74xx138 extends DILBase { + private val = 0; + private enable = false; + constructor(simulator: Simulator, id: string, param: BaseComponentParam) { + super(simulator, id, 16, param); + } + + setup(canvas: SVGElement) { + super.DILBASEsetup('ic16', canvas, '74xx138'); + } + + update(): boolean { + if (this.startdelay) { + --this.startdelay; + return false; + } + if (this.getStateWire('p16') !== WireState.high || this.getStateWire('p8') !== WireState.low) return false; // no power + // enable = !E1 AND !E2 AND E3 + const enable = + this.binary(this.getStateWire('p4'), true) === false && + this.binary(this.getStateWire('p5'), true) === false && + this.binary(this.getStateWire('p6'), true) === true; + const val = + this.asint(this.binary(this.getStateWire('p3'), true), 4) + + this.asint(this.binary(this.getStateWire('p2'), true), 2) + + this.asint(this.binary(this.getStateWire('p1'), true), 1); + const mod = this.val !== val || this.enable !== enable; + this.val = val; + this.enable = enable; + this.getPin('p15').setBool(!enable && val === 0); + this.getPin('p14').setBool(!enable && val === 1); + this.getPin('p13').setBool(!enable && val === 2); + this.getPin('p12').setBool(!enable && val === 3); + this.getPin('p11').setBool(!enable && val === 4); + this.getPin('p10').setBool(!enable && val === 5); + this.getPin('p9').setBool(!enable && val === 6); + this.getPin('p7').setBool(!enable && val === 7); + return mod; + } + } + window.IC74xx138 = IC74xx138; +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function IC74xx138Depends() { + return 'dilbase'; +} diff --git a/components/ic74xx21.ts b/components/ic74xx21.ts new file mode 100644 index 0000000..3871d32 --- /dev/null +++ b/components/ic74xx21.ts @@ -0,0 +1,63 @@ +// 74xx21 emulation component for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function IC74xx21Init() { + class IC74xx21 extends DILBase { + constructor(simulator: Simulator, id: string, param: BaseComponentParam) { + super(simulator, id, 14, param); + } + + setup(canvas: SVGElement) { + super.DILBASEsetup('ic14', canvas, '74xx21'); + } + + update(): boolean { + if (this.startdelay) { + --this.startdelay; + return false; + } + if (this.getStateWire('p14') !== WireState.up || this.getStateWire('p7') !== WireState.down) return false; // no power + let oldstate; + let mod = false; + let state; + oldstate = this.binary(this.getStateWire('p6'), true); + state = + this.binary(this.getStateWire('p1'), true) !== false && + this.binary(this.getStateWire('p2'), true) !== false && + this.binary(this.getStateWire('p4'), true) !== false && + this.binary(this.getStateWire('p5'), true) !== false; + this.getPin('p6').setBool(state); + mod ||= state !== oldstate; + + oldstate = this.binary(this.getStateWire('p8'), true); + state = + this.binary(this.getStateWire('p9'), true) !== false && + this.binary(this.getStateWire('p10'), true) !== false && + this.binary(this.getStateWire('p12'), true) !== false && + this.binary(this.getStateWire('p13'), true) !== false; + mod ||= state !== oldstate; + this.getPin('p8').setBool(state); + return mod; + } + } + window.IC74xx21 = IC74xx21; +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function IC74xx21Depends() { + return 'dilbase'; +} diff --git a/components/ic74xx238.ts b/components/ic74xx238.ts new file mode 100644 index 0000000..8acf763 --- /dev/null +++ b/components/ic74xx238.ts @@ -0,0 +1,65 @@ +// 74xx238 emulation component for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function IC74xx238Init() { + class IC74xx238 extends DILBase { + private val = 0; + private enable = false; + constructor(simulator: Simulator, id: string, param: BaseComponentParam) { + super(simulator, id, 16, param); + } + + setup(canvas: SVGElement) { + super.DILBASEsetup('ic16', canvas, '74xx238'); + } + + update(): boolean { + if (this.startdelay) { + --this.startdelay; + return false; + } + if (this.getStateWire('p16') !== WireState.high || this.getStateWire('p8') !== WireState.low) return false; // no power + // enable = !E1 AND !E2 AND E3 + const enable = + this.binary(this.getStateWire('p4'), true) === false && + this.binary(this.getStateWire('p5'), true) === false && + this.binary(this.getStateWire('p6'), true) === true; + const val = + this.asint(this.binary(this.getStateWire('p3'), true), 4) + + this.asint(this.binary(this.getStateWire('p2'), true), 2) + + this.asint(this.binary(this.getStateWire('p1'), true), 1); + const mod = this.val !== val || this.enable !== enable; + this.val = val; + this.enable = enable; + this.getPin('p15').setBool(enable && val === 0); + this.getPin('p14').setBool(enable && val === 1); + this.getPin('p13').setBool(enable && val === 2); + this.getPin('p12').setBool(enable && val === 3); + this.getPin('p11').setBool(enable && val === 4); + this.getPin('p10').setBool(enable && val === 5); + this.getPin('p9').setBool(enable && val === 6); + this.getPin('p7').setBool(enable && val === 7); + return mod; + } + } + window.IC74xx238 = IC74xx238; +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function IC74xx238Depends() { + return 'dilbase'; +} diff --git a/components/ic74xx244.ts b/components/ic74xx244.ts new file mode 100644 index 0000000..94f16be --- /dev/null +++ b/components/ic74xx244.ts @@ -0,0 +1,63 @@ +// 74xx244 emulation component for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function IC74xx244Init() { + class IC74xx244 extends DILBase { + constructor(simulator: Simulator, id: string, param: BaseComponentParam) { + super(simulator, id, 20, param); + } + setup(canvas: SVGElement) { + super.DILBASEsetup('ic20', canvas, '74xx244'); + } + + updatefunction() { + if (this.startdelay) { + --this.startdelay; + return false; + } + if (this.getStateWire('p20') !== WireState.high || this.getStateWire('p10') !== WireState.low) return false; // no power + let mod = false; + if (this.binary(this.getStateWire('p1'), true) === false) { + mod ||= this.binary(this.getStateWire('p18'), true) !== this.binary(this.getStateWire('p2'), true); + this.getPin('p18').setBool(this.binary(this.getStateWire('p2'), true)); + mod ||= this.binary(this.getStateWire('p16'), true) !== this.binary(this.getStateWire('p4'), true); + this.getPin('p16').setBool(this.binary(this.getStateWire('p4'), true)); + mod ||= this.binary(this.getStateWire('p14'), true) !== this.binary(this.getStateWire('p6'), true); + this.getPin('p14').setBool(this.binary(this.getStateWire('p6'), true)); + mod ||= this.binary(this.getStateWire('p12'), true) !== this.binary(this.getStateWire('p8'), true); + this.getPin('p12').setBool(this.binary(this.getStateWire('p8'), true)); + } + if (this.binary(this.getStateWire('p19'), true) === false) { + mod ||= this.binary(this.getStateWire('p9'), true) !== this.binary(this.getStateWire('p11'), true); + this.getPin('p9').setBool(this.binary(this.getStateWire('p11'), true)); + mod ||= this.binary(this.getStateWire('p7'), true) !== this.binary(this.getStateWire('p13'), true); + this.getPin('p7').setBool(this.binary(this.getStateWire('p13'), true)); + mod ||= this.binary(this.getStateWire('p5'), true) !== this.binary(this.getStateWire('p15'), true); + this.getPin('p5').setBool(this.binary(this.getStateWire('p15'), true)); + mod ||= this.binary(this.getStateWire('p3'), true) !== this.binary(this.getStateWire('p17'), true); + this.getPin('p3').setBool(this.binary(this.getStateWire('p17'), true)); + } + return mod; + } + } + window.IC74xx244 = IC74xx244; +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function IC74xx244Depends() { + return 'dilbase'; +} diff --git a/components/ic74xx266.ts b/components/ic74xx266.ts new file mode 100644 index 0000000..df91c4d --- /dev/null +++ b/components/ic74xx266.ts @@ -0,0 +1,68 @@ +// 74xx266 emulation component for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function IC74xx266Init() { + class IC74xx266 extends DILBase { + constructor(simulator: Simulator, id: string, param: BaseComponentParam) { + super(simulator, id, 14, param); + } + + setup(canvas: SVGElement) { + if (this.simulator.getType() === 'physical') { + super.DILBASEsetup('ic14', canvas, '74xx266'); + } else { + super.DILBASEsetup('ic74xx08', canvas, ''); + } + } + update(): boolean { + if (this.startdelay) { + --this.startdelay; + return false; + } + if (this.getStateWire('p14') !== WireState.high || this.getStateWire('p7') !== WireState.low) return false; // no power + let mod = false; + let oldstate; + let state; + oldstate = this.binary(this.getStateWire('p3'), true); + state = this.binary(this.getStateWire('p1'), true) !== this.binary(this.getStateWire('p2'), true); + this.getPin('p3').setBool(state); + mod ||= state !== oldstate; + + oldstate = this.binary(this.getStateWire('p4'), true); + state = this.binary(this.getStateWire('p5'), true) !== this.binary(this.getStateWire('p6'), true); + this.getPin('p4').setBool(state); + mod ||= state !== oldstate; + + oldstate = this.binary(this.getStateWire('p10'), true); + state = this.binary(this.getStateWire('p8'), true) !== this.binary(this.getStateWire('p9'), true); + this.getPin('p10').setBool(state); + mod ||= state !== oldstate; + + oldstate = this.binary(this.getStateWire('p11'), true); + this.binary(this.getStateWire('p12'), true) !== this.binary(this.getStateWire('p13'), true); + this.getPin('p11').setBool(state); + mod ||= state !== oldstate; + return mod; + } + } + window.IC74xx266 = IC74xx266; +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function IC74xx266Depends() { + return 'dilbase'; +} diff --git a/components/ic74xx273.ts b/components/ic74xx273.ts new file mode 100644 index 0000000..dbaa00e --- /dev/null +++ b/components/ic74xx273.ts @@ -0,0 +1,83 @@ +// 74xx273 emulation component for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function IC74xx273Init() { + class IC74xx273 extends DILBase { + private clock = false; + private internalstate: boolean[]; + constructor(simulator: Simulator, id: string, param: BaseComponentParam) { + super(simulator, id, 20, param); + this.internalstate = []; + for (let i = 0; i < 8; ++i) { + this.internalstate[i] = false; + } + } + + setup(canvas: SVGElement) { + super.DILBASEsetup('ic20', canvas, '74xx273'); + } + + update(): boolean { + if (this.startdelay) { + --this.startdelay; + return false; + } + if (this.getStateWire('p20') !== WireState.high || this.getStateWire('p10') !== WireState.low) return false; // no power + if (this.binary(this.getStateWire('p1'), true) === false) { + // reset + for (let i = 0; i < 8; ++i) { + this.internalstate[i] = false; + } + } + let mod = false; + if (this.binary(this.getStateWire('p11'), true) && this.clock === false) { + mod ||= this.internalstate[0] !== this.binary(this.getStateWire('p3'), true); + this.internalstate[0] = this.binary(this.getStateWire('p3'), true); + mod ||= this.internalstate[1] !== this.binary(this.getStateWire('p4'), true); + this.internalstate[1] = this.binary(this.getStateWire('p4'), true); + mod ||= this.internalstate[2] !== this.binary(this.getStateWire('p7'), true); + this.internalstate[2] = this.binary(this.getStateWire('p7'), true); + mod ||= this.internalstate[3] !== this.binary(this.getStateWire('p8'), true); + this.internalstate[3] = this.binary(this.getStateWire('p8'), true); + mod ||= this.internalstate[4] !== this.binary(this.getStateWire('p13'), true); + this.internalstate[4] = this.binary(this.getStateWire('p13'), true); + mod ||= this.internalstate[5] !== this.binary(this.getStateWire('p14'), true); + this.internalstate[5] = this.binary(this.getStateWire('p14'), true); + mod ||= this.internalstate[6] !== this.binary(this.getStateWire('p17'), true); + this.internalstate[6] = this.binary(this.getStateWire('p17'), true); + mod ||= this.internalstate[7] !== this.binary(this.getStateWire('p18'), true); + this.internalstate[7] = this.binary(this.getStateWire('p18'), true); + } + this.getPin('p2').setBool(this.internalstate[0]); + this.getPin('p5').setBool(this.internalstate[1]); + this.getPin('p6').setBool(this.internalstate[2]); + this.getPin('p9').setBool(this.internalstate[3]); + this.getPin('p12').setBool(this.internalstate[4]); + this.getPin('p15').setBool(this.internalstate[5]); + this.getPin('p16').setBool(this.internalstate[6]); + this.getPin('p19').setBool(this.internalstate[7]); + this.clock = this.binary(this.getStateWire('p11'), true); + return mod; + } + } + window.IC74xx273 = IC74xx273; +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function IC74xx273Depends() { + return 'dilbase'; +} diff --git a/components/ic74xx283.ts b/components/ic74xx283.ts new file mode 100644 index 0000000..e307aa5 --- /dev/null +++ b/components/ic74xx283.ts @@ -0,0 +1,70 @@ +// 74xx283 emulation component for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function IC74xx283Init() { + class IC74xx283 extends DILBase { + constructor(simulator: Simulator, id: string, param: BaseComponentParam) { + super(simulator, id, 16, param); + } + setup(canvas: SVGElement) { + super.DILBASEsetup('ic16', canvas, '74xx283'); + } + update(): boolean { + if (this.startdelay) { + --this.startdelay; + return false; + } + if (this.binary(this.getStateWire('p16'), true) !== true || this.binary(this.getStateWire('p8'), true) !== false) + return false; // no power + let mod = false; + let state; + state = + this.asint(this.binary(this.getStateWire('p5'), true), 1) + + this.asint(this.binary(this.getStateWire('p6'), true), 1) + + this.asint(this.binary(this.getStateWire('p7'), true), 1); + mod ||= this.asint(this.binary(this.getStateWire('p4'), true), 1) !== (state & 1); + this.getPin('p4').setBool((state & 1) === 1); + state = + this.asint(this.binary(this.getStateWire('p3'), true), 1) + + this.asint(this.binary(this.getStateWire('p2'), true), 1) + + (state > 1 ? 1 : 0); + mod ||= this.asint(this.binary(this.getStateWire('p1'), true), 1) !== (state & 1); + this.getPin('p1').setBool((state & 1) === 1); + state = + this.asint(this.binary(this.getStateWire('p14'), true), 1) + + this.asint(this.binary(this.getStateWire('p15'), true), 1) + + (state > 1 ? 1 : 0); + mod ||= this.asint(this.binary(this.getStateWire('p13'), true), 1) !== (state & 1); + this.getPin('p13').setBool((state & 1) === 1); + state = + this.asint(this.binary(this.getStateWire('p12'), true), 1) + + this.asint(this.binary(this.getStateWire('p11'), true), 1) + + (state > 1 ? 1 : 0); + mod ||= this.asint(this.binary(this.getStateWire('p10'), true), 1) !== (state & 1); + this.getPin('p10').setBool((state & 1) === 1); + mod ||= this.asint(this.binary(this.getStateWire('p9'), true), 2) !== (state & 2); + this.getPin('p9').setBool((state & 2) === 2); + return mod; + } + } + window.IC74xx283 = IC74xx283; +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function IC74xx283Depends() { + return 'dilbase'; +} diff --git a/components/ic74xx374.ts b/components/ic74xx374.ts new file mode 100644 index 0000000..c2400c4 --- /dev/null +++ b/components/ic74xx374.ts @@ -0,0 +1,76 @@ +// 74xx374 emulation component for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function IC74xx374Init() { + class IC74xx374 extends DILBase { + private clock = false; + private internalstate: boolean[]; + private oldinternalstate: boolean[]; + constructor(simulator: Simulator, id: string, param: BaseComponentParam) { + super(simulator, id, 20, param); + this.internalstate = []; + this.oldinternalstate = []; + for (let i = 0; i <= 8; ++i) { + this.internalstate[i] = false; + this.oldinternalstate[i] = false; + } + } + + setup(canvas: SVGElement) { + super.DILBASEsetup('ic20', canvas, '74xx374'); + } + + update(): boolean { + if (this.startdelay) { + --this.startdelay; + return false; + } + if (this.getStateWire('p20') !== WireState.high || this.getStateWire('p10') !== WireState.low) return false; // no power + if (this.binary(this.getStateWire('p11'), true) && this.clock === false) { + this.oldinternalstate = this.internalstate; + this.internalstate[0] = this.binary(this.getStateWire('p3'), true); + this.internalstate[1] = this.binary(this.getStateWire('p4'), true); + this.internalstate[2] = this.binary(this.getStateWire('p7'), true); + this.internalstate[3] = this.binary(this.getStateWire('p8'), true); + this.internalstate[4] = this.binary(this.getStateWire('p13'), true); + this.internalstate[5] = this.binary(this.getStateWire('p14'), true); + this.internalstate[6] = this.binary(this.getStateWire('p16'), true); + this.internalstate[7] = this.binary(this.getStateWire('p18'), true); + } + let mod = false; + if (this.binary(this.getStateWire('p1'), true) === false) { + this.getPin('p2').setBool(this.internalstate[0]); + this.getPin('p5').setBool(this.internalstate[1]); + this.getPin('p6').setBool(this.internalstate[2]); + this.getPin('p9').setBool(this.internalstate[3]); + this.getPin('p12').setBool(this.internalstate[4]); + this.getPin('p15').setBool(this.internalstate[5]); + this.getPin('p16').setBool(this.internalstate[6]); + this.getPin('p19').setBool(this.internalstate[7]); + mod = this.internalstate !== this.oldinternalstate; + } + this.clock = this.binary(this.getStateWire('p11'), true); + return mod; + } + } + window.IC74xx374 = IC74xx374; +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function IC74xx374Depends() { + return 'dilbase'; +} diff --git a/components/ic74xx86.ts b/components/ic74xx86.ts new file mode 100644 index 0000000..77e699d --- /dev/null +++ b/components/ic74xx86.ts @@ -0,0 +1,55 @@ +// 74xx86 emulation component for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function IC74xx86Init() { + class IC74xx86 extends DILBase { + constructor(simulator: Simulator, id: string, param: BaseComponentParam) { + super(simulator, id, 14, param); + } + setup(canvas: SVGElement) { + super.DILBASEsetup('ic14', canvas, '74xx86'); + } + update(): boolean { + if (this.startdelay) { + --this.startdelay; + return false; + } + if (this.getStateWire('p14') !== WireState.high || this.getStateWire('p7') !== WireState.low) return false; // no power + let mod = false; + let state; + state = this.binary(this.getStateWire('p1'), true) !== this.binary(this.getStateWire('p2'), true); + mod ||= this.binary(this.getStateWire('p3'), true) !== state; + this.getPin('p3').setBool(state); + state = this.binary(this.getStateWire('p4'), true) !== this.binary(this.getStateWire('p5'), true); + mod ||= this.binary(this.getStateWire('p6'), true) !== state; + this.getPin('p6').setBool(state); + state = this.binary(this.getStateWire('p9'), true) !== this.binary(this.getStateWire('p10'), true); + mod ||= this.binary(this.getStateWire('p8'), true) !== state; + this.getPin('p8').setBool(state); + state = this.binary(this.getStateWire('p12'), true) !== this.binary(this.getStateWire('p13'), true); + mod ||= this.binary(this.getStateWire('p11'), true) !== state; + this.getPin('p11').setBool(state); + return mod; + } + } + window.IC74xx86 = IC74xx86; +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function IC74xx86Depends() { + return 'dilbase'; +} diff --git a/components/jkflipflop.svg b/components/jkflipflop.svg new file mode 100644 index 0000000..5f6824c --- /dev/null +++ b/components/jkflipflop.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + J + K + Q + + Q + + diff --git a/components/jkflipflop.ts b/components/jkflipflop.ts new file mode 100644 index 0000000..280ccd7 --- /dev/null +++ b/components/jkflipflop.ts @@ -0,0 +1,69 @@ +// J-K-Flip-Flop component for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +interface JKFlipFlopParam extends BaseComponentParam { + default?: boolean; +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +class JKFlipFlop extends BaseComponent { + private j = false; + private k = false; + private CLK = true; + private oldCLK = true; + private q = false; + constructor(simulator: Simulator, id: string, param: JKFlipFlopParam = {}) { + super(simulator, id, param); + this.pins.set('j', new TriState(this, -30, -15)); + this.pins.set('k', new TriState(this, -30, 15)); + this.pins.set('clk', new TriState(this, -30, 0)); + this.pins.set('q', new TriState(this, 30, -15)); + this.pins.set('notq', new TriState(this, 30, 15)); + if (param.default) this.q = param.default; + this.getPin('q').setBool(this.q); + this.getPin('notq').setBool(!this.q); + } + + setup(canvas: SVGElement) { + super.doSetup('jkflipflop', canvas); + } + + io() { + this.j = this.binary(this.getPin('j').getAndReset(), true); + this.k = this.binary(this.getPin('k').getAndReset(), true); + this.CLK = this.binary(this.getPin('clk').getAndReset(), true); + } + + update(): boolean { + const oldstate = this.q; + if (this.oldCLK === false && this.CLK === true) { + if (this.j === true && this.k === true) { + this.q = !this.q; + } else { + if (this.j === true) { + this.q = true; + } + if (this.k === true) { + this.q = false; + } + } + } + this.getPin('q').setBool(this.q); + this.getPin('notq').setBool(this.q === false); + this.oldCLK = this.CLK; + return oldstate !== this.q; + } +} diff --git a/components/jumper3.svg b/components/jumper3.svg new file mode 100644 index 0000000..69aaffb --- /dev/null +++ b/components/jumper3.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + diff --git a/components/jumper3.ts b/components/jumper3.ts new file mode 100644 index 0000000..e430f45 --- /dev/null +++ b/components/jumper3.ts @@ -0,0 +1,90 @@ +// 3-pin jumper component for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +class Jumper3 extends BaseComponent { + private pressed = false; + private text: NodeList | null = null; + private l: HTMLElement | null = null; + private r: HTMLElement | null = null; + private p1 = WireState.float; + private p2 = WireState.float; + private p3 = WireState.float; + constructor(simulator: Simulator, id: string, param: BaseComponentParam) { + super(simulator, id, param); + + if (this.breadboard) { + this.pins.set('p1', new TriState(this, 0, -5.08 * 3)); + this.pins.set('p2', new TriState(this, 10.16, 5.08 * 3)); + this.pins.set('p3', new TriState(this, 0, -5.08 * 3)); + } else { + this.pins.set('p1', new TriState(this, -30, 0)); + this.pins.set('p2', new TriState(this, 0, 20)); + this.pins.set('p3', new TriState(this, 30, 0)); + } + } + + setup(canvas: SVGElement) { + super.doSetup('jumper3', canvas); + if (!this.element) return; + this.text = this.element.querySelectorAll('.inner'); + this.element.addEventListener('mousedown', this.activate.bind(this)); + this.l = this.element.querySelectorAll('.l')[0]; + this.r = this.element.querySelectorAll('.r')[0]; + this.updateSwitch(); + } + + updateSwitch() { + if (!this.r || !this.l) return; + if (this.pressed) { + this.r.classList.add('hidden'); + this.l.classList.remove('hidden'); + } else { + this.l.classList.add('hidden'); + this.r.classList.remove('hidden'); + } + } + + activate(e: MouseEvent) { + e.preventDefault(); + this.pressed = !this.pressed; + if (this.text && this.text.length > 0) { + // document.text = this.text; + if (this.breadboard) { + this.text[1].textContent = this.pressed ? '1' : '0'; + } else { + this.text[0].textContent = this.pressed ? '---' : '] ['; + } + } + this.updateSwitch(); + this.simulator.manualtick(); + } + + io() { + /*this.state.a = this.a.getAndReset(); + this.state.b = this.b.getAndReset();*/ + this.p1 = this.getPin('p1').getAndReset(); + this.p2 = this.getPin('p2').getAndReset(); + this.p3 = this.getPin('p3').getAndReset(); + } + + update(): boolean { + const oldstate = this.p2; + const state = this.pressed ? this.p1 : this.p3; + this.getPin('p2').setState(state); + return state !== oldstate; + } +} diff --git a/components/label.svg b/components/label.svg new file mode 100644 index 0000000..d7c4826 --- /dev/null +++ b/components/label.svg @@ -0,0 +1,9 @@ + + + + + +   + + + \ No newline at end of file diff --git a/components/label.ts b/components/label.ts new file mode 100644 index 0000000..278a820 --- /dev/null +++ b/components/label.ts @@ -0,0 +1,82 @@ +// Label component for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +interface LabelParam extends BaseComponentParam { + label?: string; + width: number; + attach: string; +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +class Label extends BaseComponent { + private mod: boolean; + private labelText: string; + private width: number; + private attach: string; + private state: boolean | null; + constructor(simulator: Simulator, id: string, param: LabelParam) { + super(simulator, id, param); + this.pins.set('q', new TriState(this, 0, 0)); + this.mod = false; + this.labelText = param.label || ''; + this.width = param.width; + this.attach = param.attach || ''; + this.state = null; + } + + setup(canvas: SVGElement) { + super.doSetup('label', canvas); + if (this.element) { + this.element.querySelectorAll('.txt')[0].innerHTML = this.labelText; + } + const width = this.width ? this.width : 50; + if (this.attach !== '' && this.element) { + const g = this.element.querySelectorAll('g'); + switch (this.attach) { + case 'e': + g[1].setAttribute('transform', 'translate(-' + width + ',0)'); + break; + case 's': + g[1].setAttribute('transform', 'rotate(-90)'); + break; + } + } + if (this.width && this.element) { + this.element.querySelectorAll('.outer')[0].setAttribute('width', width.toString()); + } + } + + io() { + const s = this.state; + this.state = this.tri(this.getPin('q').getAndReset()); + this.mod = this.state !== s; + return false; + } + + update(): boolean { + if (this.mod && this.element) { + this.element.classList.remove('low'); + this.element.classList.remove('high'); + if (this.state === true) { + this.element.classList.add('high'); + } + if (this.state === false) { + this.element.classList.add('low'); + } + } + return false; + } +} diff --git a/components/led.svg b/components/led.svg new file mode 100644 index 0000000..73b39d9 --- /dev/null +++ b/components/led.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/components/led.ts b/components/led.ts new file mode 100644 index 0000000..2f43d01 --- /dev/null +++ b/components/led.ts @@ -0,0 +1,118 @@ +// Single LED component for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +interface LEDParam extends BaseComponentParam { + color: string; +} +// eslint-disable-next-line @typescript-eslint/no-unused-vars +class LED extends BaseComponent { + private color: string; + private onColor: string; + private offColor: string; + private a: boolean | null = false; + private b: boolean | null = false; + constructor(simulator: Simulator, id: string, param: LEDParam) { + super(simulator, id, param); + this.color = param.color; + switch (this.color) { + case 'red': + this.onColor = 'ffb9b9'; + this.offColor = '990000'; + break; + case 'blue': + this.onColor = 'b9b9ff'; + this.offColor = '2222ff'; + break; + case 'green': + this.onColor = 'b9ffb9'; + this.offColor = '22b922'; + break; + case 'orange': + this.onColor = 'ff9900'; + this.offColor = 'cc5500'; + break; + case 'yellow': + this.onColor = 'ffffb9'; + this.offColor = 'cccc00'; + break; + default: + this.onColor = 'ffffff'; + this.offColor = '444444'; + } + this.pins.set( + 'a', + new TriState(this, 0, -10, undefined, { + type: 'LED', + id: id + '-a', + }) + ); + this.pins.set( + 'b', + new TriState(this, 0, 10, undefined, { + type: 'LED', + id: id + '-b', + }) + ); + } + + addGradient(canvas: SVGElement, type: string, color1: string, color2: string) { + const grad = document.createElementNS('http://www.w3.org/2000/svg', 'radialGradient'); + grad.id = 'radialGradient' + type + this.color; + let stop = document.createElementNS('http://www.w3.org/2000/svg', 'stop'); + stop.setAttribute('offset', '0.4'); + stop.setAttribute('style', 'stop-color:#' + color1 + ';stop-opacity:1'); + grad.appendChild(stop); + + stop = document.createElementNS('http://www.w3.org/2000/svg', 'stop'); + stop.setAttribute('offset', '1'); + stop.setAttribute('style', 'stop-color:#' + color2 + ';stop-opacity:1'); + grad.appendChild(stop); + canvas.appendChild(grad); + } + + setup(parent: SVGElement) { + // color already registered? + const canvas = parent.ownerSVGElement; + if (canvas === null) return; + const c = canvas.getElementById('radialGradientOff' + this.color); + if (c === null) { + this.addGradient(canvas, 'Off', this.offColor, this.offColor); + this.addGradient(canvas, 'On', this.onColor, this.offColor); + } + super.doSetup('led', parent); + if (this.element) { + this.element.setAttribute('style', 'fill:url(#radialGradientOff' + this.color + ')'); + } + } + + io() { + this.a = this.tri(this.getPin('a').getAndReset()); + this.b = this.tri(this.getPin('b').getAndReset()); + } + + update() { + const stateA = this.a; + const stateB = this.b; + if (this.element !== null) { + if (stateA !== stateB && stateA !== null && stateB !== null) { + this.element.setAttribute('style', 'fill:url(#radialGradientOn' + this.color + ')'); + } else { + this.element.setAttribute('style', 'fill:url(#radialGradientOff' + this.color + ')'); + } + } + return false; + } +} diff --git a/components/ledarray.svg b/components/ledarray.svg new file mode 100644 index 0000000..73b39d9 --- /dev/null +++ b/components/ledarray.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/components/ledarray.ts b/components/ledarray.ts new file mode 100644 index 0000000..18df412 --- /dev/null +++ b/components/ledarray.ts @@ -0,0 +1,141 @@ +// LED array component for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +interface LEDArrayParam extends BaseComponentParam { + color: string; + count: number; + direction: number; + padding?: number; +} +// eslint-disable-next-line @typescript-eslint/no-unused-vars +class LEDArray extends BaseComponent { + private color: string; + private count: number; + private direction: number; + private padding: number; + private onColor: string; + private offColor: string; + private a: Array = []; + private b: Array = []; + private elements: SVGElement[] = []; + constructor(simulator: Simulator, id: string, param: LEDArrayParam) { + super(simulator, id, param); + this.color = param.color; + this.count = param.count; + this.direction = param.direction; + this.padding = param.padding ? param.padding : 20; + switch (this.color) { + case 'red': + this.onColor = 'ffb9b9'; + this.offColor = '990000'; + break; + default: + this.onColor = 'ffffff'; + this.offColor = '000000'; + } + let x = this.x; + let y = this.y; + for (let i = 0; i < this.count; ++i) { + this.pins.set('a' + i, new TriState(this, x, y)); + this.pins.set('b' + i, new TriState(this, x, y)); + // move next element in given direction + switch (this.direction) { + case 8: // up + y += this.padding; + break; + case 6: // right + x += this.padding; + break; + case 2: // down + y -= this.padding; + break; + case 4: // left + x -= this.padding; + break; + } + this.a[i] = null; + this.b[i] = null; + } + } + + addGradient(canvas: SVGElement, type: string, color1: string, color2: string) { + const grad = document.createElementNS('http://www.w3.org/2000/svg', 'radialGradient'); + grad.id = 'radialGradient' + type + this.color; + let stop = document.createElementNS('http://www.w3.org/2000/svg', 'stop'); + stop.setAttribute('offset', '0.4'); + stop.setAttribute('style', 'stop-color:#' + color1 + ';stop-opacity:1'); + grad.appendChild(stop); + + stop = document.createElementNS('http://www.w3.org/2000/svg', 'stop'); + stop.setAttribute('offset', '1'); + stop.setAttribute('style', 'stop-color:#' + color2 + ';stop-opacity:1'); + grad.appendChild(stop); + canvas.appendChild(grad); + } + + setup(parent: SVGElement) { + super.doSetup('ledarray', parent); + // color already registered? + const canvas = parent.ownerSVGElement; + if (canvas === null || this.element === null) return; + const c = canvas.getElementById('radialGradientOff' + this.color); + if (c === null) { + this.addGradient(canvas, 'Off', this.offColor, this.offColor); + this.addGradient(canvas, 'On', this.onColor, this.offColor); + } + + const e = []; + for (let i = 0; i < this.count; ++i) { + super.doSetup('ledarray', canvas); + e[i] = this.element; + e[i].setAttribute('style', 'fill:url(#radialGradientOff' + this.color + ')'); + // move next element in given direction + switch (this.direction) { + case 8: // up + this.y += this.padding; + break; + case 6: // right + this.x += this.padding; + break; + case 2: // down + this.y -= this.padding; + break; + case 4: // left + this.x -= this.padding; + break; + } + } + this.elements = e; + } + + io() { + for (let i = 0; i < this.count; ++i) { + this.a[i] = this.tri(this.getPin('a' + i).getAndReset()); + this.b[i] = this.tri(this.getPin('b' + i).getAndReset()); + } + } + + update(): boolean { + for (let i = 0; i < this.count; ++i) { + if (this.a[i] !== this.b[i] && this.a[i] !== null && this.b[i] !== null) { + this.elements[i].setAttribute('style', 'fill:url(#radialGradientOn' + this.color + ')'); + } else { + this.elements[i].setAttribute('style', 'fill:url(#radialGradientOff' + this.color + ')'); + } + } + return false; + } +} diff --git a/components/ledbar8.svg b/components/ledbar8.svg new file mode 100644 index 0000000..aa9ae8a --- /dev/null +++ b/components/ledbar8.svg @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/components/ledbar8.ts b/components/ledbar8.ts new file mode 100644 index 0000000..43eef68 --- /dev/null +++ b/components/ledbar8.ts @@ -0,0 +1,63 @@ +// 8x LED Bar display component for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +interface LEDBar8Param extends BaseComponentParam { + color: string; +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function LEDBar8Init() { + class LEDBar8 extends DILBase { + private color: string; + constructor(simulator: Simulator, id: string, param: LEDBar8Param) { + super(simulator, id, 16, param); + this.color = param.color; + for (let i = 0; i < 8; ++i) { + this.getPin('p' + (i + 1)).move(i * 5.08, 12.4); + this.stateMapBool.set('l' + i, false); + } + } + + setup(canvas: SVGElement) { + super.DILBASEsetup('ledbar8', canvas, ''); + if (this.element) { + this.element.classList.add(this.color); + } + } + update(): boolean { + for (let i = 0; i < 8; ++i) { + const a = this.getStateWire('p' + (i + 1)); + const b = this.getStateWire('p' + (16 - i)); + const state = this.tri(a) !== this.tri(b) && a !== WireState.float && b !== WireState.float; + if (state !== this.getStateBool('l' + i) && this.element) { + if (state) { + this.element.querySelectorAll('#l' + i)[0].classList.add('on'); + } else { + this.element.querySelectorAll('#l' + i)[0].classList.remove('on'); + } + } + this.stateMapBool.set('l' + i, state); + } + return false; + } + } + window.LEDBar8 = LEDBar8; +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function LEDBar8Depends() { + return 'dilbase'; +} diff --git a/components/logic74xx161.svg b/components/logic74xx161.svg new file mode 100644 index 0000000..0db467b --- /dev/null +++ b/components/logic74xx161.svg @@ -0,0 +1,38 @@ + + + + + + + MR + + + PE + + D0 + + D1 + + D2 + + D3 + + + + + CEP + + CET + + + O0 + + O1 + + O2 + + O3 + + TC + + diff --git a/components/logic74xx161.ts b/components/logic74xx161.ts new file mode 100644 index 0000000..9dd6939 --- /dev/null +++ b/components/logic74xx161.ts @@ -0,0 +1,83 @@ +// Logic simulation for the 74xx161 IC for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +class Logic74xx161 extends BaseComponent { + private o = 0; + private i = 0; + private mr = false; + private cet = false; + private cep = false; + private clk = false; + private oldclk = false; + private pe = false; + constructor(simulator: Simulator, id: string, param: BaseComponentParam) { + super(simulator, id, param); + this.pins.set('clk', new TriState(this, -45, 30)); + this.pins.set('i0', new TriState(this, -45, -30)); + this.pins.set('i1', new TriState(this, -45, -15)); + this.pins.set('i2', new TriState(this, -45, 0)); + this.pins.set('i3', new TriState(this, -45, 15)); + this.pins.set('o0', new TriState(this, 45, -30)); + this.pins.set('o1', new TriState(this, 45, -15)); + this.pins.set('o2', new TriState(this, 45, 0)); + this.pins.set('o3', new TriState(this, 45, 15)); + this.pins.set('cet', new TriState(this, -45, 60)); + this.pins.set('cep', new TriState(this, -45, 45)); + this.pins.set('mr', new TriState(this, -45, -60)); + this.pins.set('pe', new TriState(this, -45, -45)); + this.pins.set('tc', new TriState(this, 45, 60)); + } + + setup(canvas: SVGElement) { + super.doSetup('logic74xx161', canvas); + } + + io() { + this.clk = this.binary(this.getPin('clk').getAndReset(), true); + this.mr = this.binary(this.getPin('mr').getAndReset(), true); + this.cet = this.binary(this.getPin('cet').getAndReset(), true); + this.cep = this.binary(this.getPin('cep').getAndReset(), true); + this.i = + this.asint(this.binary(this.getPin('i0').getAndReset(), true), 1) + + this.asint(this.binary(this.getPin('i1').getAndReset(), true), 2) + + this.asint(this.binary(this.getPin('i2').getAndReset(), true), 4) + + this.asint(this.binary(this.getPin('i3').getAndReset(), true), 8); + this.pe = this.binary(this.getPin('pe').getAndReset(), true); + } + + update(): boolean { + let mod = false; + if (this.mr === false) { + mod = this.o !== 0; + this.o = 0; + } + const rising = this.clk && this.oldclk === false; + this.oldclk = this.clk; + if (this.pe === false && rising) { + // load + this.o = this.i; + } else if (rising && this.cet && this.cep) { + this.o = (this.o + 1) % 16; + } + this.getPin('o0').setBool((this.o & 1) !== 0); + this.getPin('o1').setBool((this.o & 2) !== 0); + this.getPin('o2').setBool((this.o & 4) !== 0); + this.getPin('o3').setBool((this.o & 8) !== 0); + this.getPin('tc').setBool(this.o === 15); + return mod; + } +} diff --git a/components/logic74xx21.svg b/components/logic74xx21.svg new file mode 100644 index 0000000..ecbc356 --- /dev/null +++ b/components/logic74xx21.svg @@ -0,0 +1,12 @@ + + + + + + + + + + & + + diff --git a/components/logic74xx21.ts b/components/logic74xx21.ts new file mode 100644 index 0000000..7a4b747 --- /dev/null +++ b/components/logic74xx21.ts @@ -0,0 +1,56 @@ +// Logic simulation for the 74xx21 IC for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +class Logic74xx21 extends BaseComponent { + private a = false; + private b = false; + private c = false; + private d = false; + private q = false; + constructor(simulator: Simulator, id: string, param: BaseComponentParam) { + super(simulator, id, param); + this.pins.set('a', new TriState(this, -30, -15)); + this.pins.set('b', new TriState(this, -30, -5)); + this.pins.set('c', new TriState(this, -30, 5)); + this.pins.set('d', new TriState(this, -30, 15)); + this.pins.set('q', new TriState(this, 30, 0)); + } + + setup(canvas: SVGElement) { + super.doSetup('logic74xx21', canvas); + } + + io() { + this.a = this.binary(this.getPin('a').getAndReset(), true); + this.b = this.binary(this.getPin('b').getAndReset(), true); + this.c = this.binary(this.getPin('c').getAndReset(), true); + this.d = this.binary(this.getPin('d').getAndReset(), true); + } + + update(): boolean { + if (this.startdelay) { + --this.startdelay; + return false; + } + const oldstate = this.q; + let mod = false; + this.q = this.a && this && this.c && this.d; + mod ||= this.q !== oldstate; + this.getPin('q').setBool(this.q); + return mod; + } +} diff --git a/components/logic74xx244.svg b/components/logic74xx244.svg new file mode 100644 index 0000000..6d6ce06 --- /dev/null +++ b/components/logic74xx244.svg @@ -0,0 +1,26 @@ + + + + + + + OE + + I0 + + I1 + + I2 + + I3 + + + O0 + + O1 + + O2 + + O3 + + diff --git a/components/logic74xx244.ts b/components/logic74xx244.ts new file mode 100644 index 0000000..f8afe19 --- /dev/null +++ b/components/logic74xx244.ts @@ -0,0 +1,75 @@ +// Logic simulation for the 74xx244 IC for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +class Logic74xx244 extends BaseComponent { + private oe = false; + private i0 = false; + private i1 = false; + private i2 = false; + private i3 = false; + private o0 = false; + private o1 = false; + private o2 = false; + private o3 = false; + constructor(simulator: Simulator, id: string, param: BaseComponentParam) { + super(simulator, id, param); + this.pins.set('oe', new TriState(this, -40, -45)); + this.pins.set('i0', new TriState(this, -40, -15)); + this.pins.set('i1', new TriState(this, -40, 0)); + this.pins.set('i2', new TriState(this, -40, 15)); + this.pins.set('i3', new TriState(this, -40, 30)); + this.pins.set('o0', new TriState(this, 40, -15)); + this.pins.set('o1', new TriState(this, 40, 0)); + this.pins.set('o2', new TriState(this, 40, 15)); + this.pins.set('o3', new TriState(this, 40, 30)); + } + + setup(canvas: SVGElement) { + super.doSetup('logic74xx244', canvas); + } + + io() { + this.oe = this.binary(this.getPin('oe').getAndReset(), true); + this.i0 = this.binary(this.getPin('i0').getAndReset(), true); + this.i1 = this.binary(this.getPin('i1').getAndReset(), true); + this.i2 = this.binary(this.getPin('i2').getAndReset(), true); + this.i3 = this.binary(this.getPin('i3').getAndReset(), true); + this.o0 = this.binary(this.getPin('o0').getAndReset(), true); + this.o1 = this.binary(this.getPin('o1').getAndReset(), true); + this.o2 = this.binary(this.getPin('o2').getAndReset(), true); + this.o3 = this.binary(this.getPin('o3').getAndReset(), true); + } + + update(): boolean { + if (this.startdelay) { + --this.startdelay; + return false; + } + let mod = false; + if (this.oe === false) { + mod ||= this.i0 !== this.o0; + this.getPin('o0').setBool(this.i0); + mod ||= this.i1 !== this.o1; + this.getPin('o1').setBool(this.i1); + mod ||= this.i2 !== this.o2; + this.getPin('o2').setBool(this.i2); + mod ||= this.i3 !== this.o3; + this.getPin('o3').setBool(this.i3); + } + return mod; + } +} diff --git a/components/logic74xx273.svg b/components/logic74xx273.svg new file mode 100644 index 0000000..c798b66 --- /dev/null +++ b/components/logic74xx273.svg @@ -0,0 +1,45 @@ + + + + + + D0 + + D1 + + D2 + + D3 + + D4 + + D5 + + D6 + + D7 + + + Cp + + Mr + + + + Q0 + + Q1 + + Q2 + + Q3 + + Q4 + + Q5 + + Q6 + + Q7 + + diff --git a/components/logic74xx273.ts b/components/logic74xx273.ts new file mode 100644 index 0000000..b08713f --- /dev/null +++ b/components/logic74xx273.ts @@ -0,0 +1,117 @@ +// Logic simulation for the 74xx273 IC for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +class Logic74xx273 extends BaseComponent { + private clock = false; + private internalstate: boolean[]; + private d0 = true; + private d1 = true; + private d2 = true; + private d3 = true; + private d4 = true; + private d5 = true; + private d6 = true; + private d7 = true; + private cp = true; + private mr = true; + constructor(simulator: Simulator, id: string, param: BaseComponentParam) { + super(simulator, id, param); + this.pins.set('d0', new TriState(this, -40, -75)); + this.pins.set('d1', new TriState(this, -40, -60)); + this.pins.set('d2', new TriState(this, -40, -45)); + this.pins.set('d3', new TriState(this, -40, -30)); + this.pins.set('d4', new TriState(this, -40, -15)); + this.pins.set('d5', new TriState(this, -40, 0)); + this.pins.set('d6', new TriState(this, -40, 15)); + this.pins.set('d7', new TriState(this, -40, 30)); + this.pins.set('cp', new TriState(this, -40, 60)); + this.pins.set('mr', new TriState(this, -40, 75)); + + this.pins.set('q0', new TriState(this, 40, -75)); + this.pins.set('q1', new TriState(this, 40, -60)); + this.pins.set('q2', new TriState(this, 40, -45)); + this.pins.set('q3', new TriState(this, 40, -30)); + this.pins.set('q4', new TriState(this, 40, -15)); + this.pins.set('q5', new TriState(this, 40, 0)); + this.pins.set('q6', new TriState(this, 40, 15)); + this.pins.set('q7', new TriState(this, 40, 30)); + + this.internalstate = []; + for (let i = 0; i < 8; ++i) { + this.internalstate[i] = false; + } + } + + setup(canvas: SVGElement) { + super.doSetup('logic74xx273', canvas); + } + + io() { + this.d0 = this.binary(this.getPin('d0').getAndReset(), true); + this.d1 = this.binary(this.getPin('d1').getAndReset(), true); + this.d2 = this.binary(this.getPin('d2').getAndReset(), true); + this.d3 = this.binary(this.getPin('d3').getAndReset(), true); + this.d4 = this.binary(this.getPin('d4').getAndReset(), true); + this.d5 = this.binary(this.getPin('d5').getAndReset(), true); + this.d6 = this.binary(this.getPin('d6').getAndReset(), true); + this.d7 = this.binary(this.getPin('d7').getAndReset(), true); + this.cp = this.binary(this.getPin('cp').getAndReset(), true); + this.mr = this.binary(this.getPin('mr').getAndReset(), true); + } + + update(): boolean { + if (this.startdelay) { + --this.startdelay; + return false; + } + if (this.mr === false) { + // reset + for (let i = 0; i < 8; ++i) { + this.internalstate[i] = false; + } + } + let mod = false; + if (this.cp && this.clock === false) { + mod ||= this.internalstate[0] !== this.d0; + this.internalstate[0] = this.d0; + mod ||= this.internalstate[1] !== this.d1; + this.internalstate[1] = this.d1; + mod ||= this.internalstate[2] !== this.d2; + this.internalstate[2] = this.d2; + mod ||= this.internalstate[3] !== this.d3; + this.internalstate[3] = this.d3; + mod ||= this.internalstate[4] !== this.d4; + this.internalstate[4] = this.d4; + mod ||= this.internalstate[5] !== this.d5; + this.internalstate[5] = this.d5; + mod ||= this.internalstate[6] !== this.d6; + this.internalstate[6] = this.d6; + mod ||= this.internalstate[7] !== this.d7; + this.internalstate[7] = this.d7; + } + this.getPin('q0').setBool(this.internalstate[0]); + this.getPin('q1').setBool(this.internalstate[1]); + this.getPin('q2').setBool(this.internalstate[2]); + this.getPin('q3').setBool(this.internalstate[3]); + this.getPin('q4').setBool(this.internalstate[4]); + this.getPin('q5').setBool(this.internalstate[5]); + this.getPin('q6').setBool(this.internalstate[6]); + this.getPin('q7').setBool(this.internalstate[7]); + this.clock = this.cp; + return mod; + } +} diff --git a/components/logicand.svg b/components/logicand.svg new file mode 100644 index 0000000..b654562 --- /dev/null +++ b/components/logicand.svg @@ -0,0 +1,10 @@ + + + + + + + + & + + diff --git a/components/logicand.ts b/components/logicand.ts new file mode 100644 index 0000000..3c4a4b8 --- /dev/null +++ b/components/logicand.ts @@ -0,0 +1,45 @@ +// Logic AND component for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function LogicAndInit() { + class LogicAnd extends LogicBase { + constructor(simulator: Simulator, id: string, param: BaseComponentParam) { + super(simulator, id, param); + } + + setup(canvas: SVGElement) { + super.doSetup('logicand', canvas); + } + + update() { + if (this.startdelay) { + --this.startdelay; + return false; + } + const oldstate = this.q; + this.q = this.a && this.b; + this.getPin('q').setBool(this.q); + return oldstate !== this.q; + } + } + window.LogicAnd = LogicAnd; +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function LogicAndDepends(): string { + return 'LogicBase'; +} diff --git a/components/logicbase.ts b/components/logicbase.ts new file mode 100644 index 0000000..32b73c9 --- /dev/null +++ b/components/logicbase.ts @@ -0,0 +1,33 @@ +// Logic base class for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +class LogicBase extends BaseComponent { + protected a = true; + protected b = true; + protected q = false; + constructor(simulator: Simulator, id: string, param: BaseComponentParam) { + super(simulator, id, param); + this.pins.set('a', new TriState(this, -30, -10, undefined, { id: 'a' })); + this.pins.set('b', new TriState(this, -30, 10, undefined, { id: 'b' })); + this.pins.set('q', new TriState(this, 30, 0, undefined, { id: 'q' })); + } + + io() { + this.a = this.binary(this.getPin('a').getAndReset(), true); + this.b = this.binary(this.getPin('b').getAndReset(), true); + } +} diff --git a/components/logicinvtristate.svg b/components/logicinvtristate.svg new file mode 100644 index 0000000..682ae01 --- /dev/null +++ b/components/logicinvtristate.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/components/logicinvtristate.ts b/components/logicinvtristate.ts new file mode 100644 index 0000000..a2ab0f5 --- /dev/null +++ b/components/logicinvtristate.ts @@ -0,0 +1,44 @@ +// Logic inverted Tri-State component for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +class LogicInvTristate extends BaseComponent { + private a = false; + private b = true; + private q = WireState.float; + constructor(simulator: Simulator, id: string, param: BaseComponentParam) { + super(simulator, id, param); + this.pins.set('a', new TriState(this, -25, 0)); + this.pins.set('b', new TriState(this, 0, -20)); + this.pins.set('q', new TriState(this, 25, 0)); + } + + setup(canvas: SVGElement) { + super.doSetup('logicinvtristate', canvas); + } + + io() { + this.a = this.binary(this.getPin('a').getAndReset(), true); + this.b = this.binary(this.getPin('b').getAndReset(), true); + } + + update(): boolean { + const oldstate = this.q; + this.q = this.b ? WireState.float : this.a ? WireState.high : WireState.low; + this.getPin('q').setState(this.q); + return oldstate !== this.q; + } +} diff --git a/components/logicnand.svg b/components/logicnand.svg new file mode 100644 index 0000000..03c5e5a --- /dev/null +++ b/components/logicnand.svg @@ -0,0 +1,11 @@ + + + + + + + + + & + + diff --git a/components/logicnand.ts b/components/logicnand.ts new file mode 100644 index 0000000..a51ad36 --- /dev/null +++ b/components/logicnand.ts @@ -0,0 +1,43 @@ +// Logic NAND component for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function LogicNAndInit() { + class LogicNAnd extends LogicBase { + constructor(simulator: Simulator, id: string, param: BaseComponentParam) { + super(simulator, id, param); + } + setup(canvas: SVGElement) { + super.doSetup('logicnand', canvas); + } + update(): boolean { + if (this.startdelay) { + --this.startdelay; + return false; + } + const oldstate = this.q; + this.q = !(this.a && this.b); + this.getPin('q').setBool(this.q); + return oldstate !== this.q; + } + } + window.LogicNAnd = LogicNAnd; +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function LogicNAndDepends() { + return 'LogicBase'; +} diff --git a/components/logicnor.svg b/components/logicnor.svg new file mode 100644 index 0000000..07d5086 --- /dev/null +++ b/components/logicnor.svg @@ -0,0 +1,11 @@ + + + + + + + + + ≥1 + + diff --git a/components/logicnor.ts b/components/logicnor.ts new file mode 100644 index 0000000..fdfe915 --- /dev/null +++ b/components/logicnor.ts @@ -0,0 +1,43 @@ +// Logic NOR component for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function LogicNorInit() { + class LogicNor extends LogicBase { + constructor(simulator: Simulator, id: string, param: BaseComponentParam) { + super(simulator, id, param); + } + setup(canvas: SVGElement) { + super.doSetup('logicnor', canvas); + } + update(): boolean { + if (this.startdelay) { + --this.startdelay; + return false; + } + const oldstate = this.q; + this.q = !(this.a || this.b); + this.getPin('q').setBool(this.q); + return oldstate !== this.q; + } + } + window.LogicNor = LogicNor; +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function LogicNorDepends() { + return 'LogicBase'; +} diff --git a/components/logicnot.svg b/components/logicnot.svg new file mode 100644 index 0000000..23e5a83 --- /dev/null +++ b/components/logicnot.svg @@ -0,0 +1,10 @@ + + + + + + + + 1 + + diff --git a/components/logicnot.ts b/components/logicnot.ts new file mode 100644 index 0000000..12a65d3 --- /dev/null +++ b/components/logicnot.ts @@ -0,0 +1,40 @@ +// Logic NOT component for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +class LogicNot extends BaseComponent { + private a = false; + private q = false; + constructor(simulator: Simulator, id: string, param: BaseComponentParam) { + super(simulator, id, param); + this.pins.set('a', new TriState(this, -30, 0)); + this.pins.set('q', new TriState(this, 30, 0)); + } + + setup(canvas: SVGElement) { + super.doSetup('logicnot', canvas); + } + + io() { + this.a = this.binary(this.getPin('a').getAndReset(), true); + } + update(): boolean { + const oldstate = this.q; + this.q = this.a !== true; + this.getPin('q').setBool(this.q); + return oldstate !== this.q; + } +} diff --git a/components/logicor.svg b/components/logicor.svg new file mode 100644 index 0000000..02bdd79 --- /dev/null +++ b/components/logicor.svg @@ -0,0 +1,10 @@ + + + + + + + + ≥1 + + diff --git a/components/logicor.ts b/components/logicor.ts new file mode 100644 index 0000000..53f374c --- /dev/null +++ b/components/logicor.ts @@ -0,0 +1,43 @@ +// Logic OR component for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function LogicOrInit() { + class LogicOr extends LogicBase { + constructor(simulator: Simulator, id: string, param: BaseComponentParam) { + super(simulator, id, param); + } + setup(canvas: SVGElement) { + super.doSetup('logicor', canvas); + } + update(): boolean { + if (this.startdelay) { + --this.startdelay; + return false; + } + const oldstate = this.q; + this.q = this.a || this.b; + this.getPin('q').setBool(this.q); + return oldstate !== this.q; + } + } + window.LogicOr = LogicOr; +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function LogicOrDepends() { + return 'LogicBase'; +} diff --git a/components/logicsource.svg b/components/logicsource.svg new file mode 100644 index 0000000..52b87c5 --- /dev/null +++ b/components/logicsource.svg @@ -0,0 +1,9 @@ + + + + + + - + + + diff --git a/components/logicsource.ts b/components/logicsource.ts new file mode 100644 index 0000000..525e32b --- /dev/null +++ b/components/logicsource.ts @@ -0,0 +1,84 @@ +// Logic source (toggle area) component for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +interface LogicSourceParam extends BaseComponentParam { + tristate?: boolean; + state?: boolean | null; + floating?: string; + attach?: string; +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +class LogicSource extends BaseComponent { + private tristate: boolean; + private pressed: number; + private floating: WireState; + private text: NodeList | null = null; + private q = WireState.float; + constructor(simulator: Simulator, id: string, param: LogicSourceParam) { + super(simulator, id, param); + this.tristate = param.tristate === undefined || param.tristate; + if (this.tristate) { + this.pressed = param.state !== undefined ? (param.state === null ? 0 : param.state === false ? 1 : 2) : 0; + } else { + this.pressed = param.state === true ? 2 : 1; + } + let x = 10; + let y = 0; + switch (param.attach) { + case 'w': + x = -10; + y = 0; + } + + this.floating = Wire.WireStateFromString(param.floating || 'down'); + this.pins.set('q', new TriState(this, x, y)); + this.update(); + } + + setup(canvas: SVGElement) { + super.doSetup('logicsource', canvas); + if (!this.element) return; + this.text = this.element.querySelectorAll('.inner'); + this.text[0].textContent = this.pressed === 0 ? '-' : this.pressed === 1 ? '0' : '1'; + this.element.addEventListener('mousedown', this.activate.bind(this)); + } + + activate(e: MouseEvent) { + e.preventDefault(); + if (this.tristate) { + this.pressed = (this.pressed + 1) % 3; + } else { + this.pressed = this.pressed === 1 ? 2 : 1; + } + //document.text = this.text; + if (this.text) { + this.text[0].textContent = this.pressed === 0 ? '-' : this.pressed === 1 ? '0' : '1'; + } + this.simulator.manualtick(); + } + + io() { + this.getPin('q').getAndReset(); + } + + update(): boolean { + const oldstate = this.q; + this.q = this.pressed === 0 ? this.floating : this.pressed === 1 ? WireState.down : WireState.up; + this.getPin('q').setState(this.q); + return oldstate !== this.q; + } +} diff --git a/components/logictristate.svg b/components/logictristate.svg new file mode 100644 index 0000000..0fffa10 --- /dev/null +++ b/components/logictristate.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/components/logicxnor.svg b/components/logicxnor.svg new file mode 100644 index 0000000..3b0c537 --- /dev/null +++ b/components/logicxnor.svg @@ -0,0 +1,11 @@ + + + + + + + + + =1 + + diff --git a/components/logicxnor.ts b/components/logicxnor.ts new file mode 100644 index 0000000..ffe22f4 --- /dev/null +++ b/components/logicxnor.ts @@ -0,0 +1,43 @@ +// Logic exclusive NOR component for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function LogicXNorInit() { + class LogicXNor extends LogicBase { + constructor(simulator: Simulator, id: string, param: BaseComponentParam) { + super(simulator, id, param); + } + setup(canvas: SVGElement) { + super.doSetup('logicxnor', canvas); + } + update(): boolean { + if (this.startdelay) { + --this.startdelay; + return false; + } + const oldstate = this.q; + this.q = !(this.a !== this.b); + this.getPin('q').setBool(this.q); + return oldstate !== this.q; + } + } + window.LogicXNor = LogicXNor; +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function LogicXNorDepends() { + return 'LogicBase'; +} diff --git a/components/logicxor.svg b/components/logicxor.svg new file mode 100644 index 0000000..b14726e --- /dev/null +++ b/components/logicxor.svg @@ -0,0 +1,10 @@ + + + + + + + + =1 + + diff --git a/components/logicxor.ts b/components/logicxor.ts new file mode 100644 index 0000000..0db3389 --- /dev/null +++ b/components/logicxor.ts @@ -0,0 +1,43 @@ +// Logic exclusive OR component for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function LogicXorInit() { + class LogicXor extends LogicBase { + constructor(simulator: Simulator, id: string, param: BaseComponentParam) { + super(simulator, id, param); + } + setup(canvas: SVGElement) { + super.doSetup('logicxor', canvas); + } + update(): boolean { + if (this.startdelay) { + --this.startdelay; + return false; + } + const oldstate = this.q; + this.q = this.a !== this.b; + this.getPin('q').setBool(this.q); + return oldstate !== this.q; + } + } + window.LogicXor = LogicXor; +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function LogicXorDepends() { + return 'LogicBase'; +} diff --git a/components/momentarypushbutton.svg b/components/momentarypushbutton.svg new file mode 100644 index 0000000..2c0c79a --- /dev/null +++ b/components/momentarypushbutton.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/components/momentarypushbutton.ts b/components/momentarypushbutton.ts new file mode 100644 index 0000000..a8f5676 --- /dev/null +++ b/components/momentarypushbutton.ts @@ -0,0 +1,60 @@ +// Momentary push button component for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +class MomentaryPushButton extends BaseComponent { + private pressed = false; + private a: WireState = WireState.float; + private q: WireState = WireState.float; + constructor(simulator: Simulator, id: string, param: BaseComponentParam) { + super(simulator, id, param); + this.pins.set('a', new TriState(this, 0, 0, undefined, { type: 'MomentaryPushButton', id: id + '-a' })); + this.pins.set('q', new TriState(this, 0, 0, undefined, { type: 'MomentaryPushButton', id: id + '-q' })); + } + + setup(canvas: SVGElement) { + super.doSetup('momentarypushbutton', canvas); + if (this.element) { + this.element.addEventListener('mousedown', this.activate.bind(this)); + } + document.addEventListener('mouseup', this.deactivate.bind(this)); + } + + activate(e: MouseEvent) { + e.stopPropagation(); + this.pressed = true; + this.simulator.manualtick(); + } + + deactivate(e: MouseEvent) { + e.stopPropagation(); + if (this.pressed) { + this.pressed = false; + this.simulator.manualtick(); + } + } + + io() { + this.a = this.getPin('a').getAndReset(); + } + + update(): boolean { + const oldstate = this.q; + this.q = this.pressed ? this.a : WireState.float; + this.getPin('q').setState(this.q); + return oldstate !== this.q; + } +} diff --git a/components/power.svg b/components/power.svg new file mode 100644 index 0000000..08bed5d --- /dev/null +++ b/components/power.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/components/power.ts b/components/power.ts new file mode 100644 index 0000000..a188239 --- /dev/null +++ b/components/power.ts @@ -0,0 +1,27 @@ +// Power component for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +class Power extends BaseComponent { + constructor(simulator: Simulator, id: string) { + super(simulator, id); + this.pins.set('q', new TriState(this, 0, 0, WireState.high, { id: 'power' })); + } + + setup(canvas: SVGElement) { + super.doSetup('power', canvas); + } +} diff --git a/components/pull.svg b/components/pull.svg new file mode 100644 index 0000000..aade48c --- /dev/null +++ b/components/pull.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/components/pull.ts b/components/pull.ts new file mode 100644 index 0000000..048fa85 --- /dev/null +++ b/components/pull.ts @@ -0,0 +1,75 @@ +// Pull-Up or Pull-Down component for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +class Pull extends BaseComponent { + private a = WireState.float; + private b = WireState.float; + constructor(simulator: Simulator, id: string, param: BaseComponentParam) { + super(simulator, id, param); + const r = (this.rotate / 180) * Math.PI; + this.pins.set('a', new TriState(this, 0, 0, undefined, { id: id + '-a' })); + this.pins.set( + 'b', + new TriState(this, Math.sin(r) * -40 * this.scaleX, Math.cos(r) * 40 * this.scaleY, undefined, { + type: 'Pull', + id: id + '-b', + }) + ); + } + + setup(canvas: SVGElement) { + super.doSetup('pull', canvas); + if (this.breadboard) { + this.simulator.wire(null, [this.getPin('a'), this.breadboard.getPin(this.pin)]); + } + } + + io() { + this.a = this.getPin('a').getAndReset(); + this.b = this.getPin('b').getAndReset(); + } + + update() { + let mod = false; + let s: WireState; + if (this.b === WireState.high || this.b === WireState.low) { + // up, down driven by b + s = this.b === WireState.high ? WireState.up : WireState.down; + mod ||= s !== this.a; + this.getPin('a').setState(s); + this.getPin('b').setState(WireState.float); + } else if (this.a === WireState.high || this.a === WireState.low) { + // up, down driven by a + s = this.a === WireState.high ? WireState.up : this.a === WireState.low ? WireState.down : this.a; + this.getPin('b').setState(s); + mod ||= s !== this.b; + this.getPin('a').setState(WireState.float); + } else if (this.b !== WireState.float) { + // b is pushed up or pulled down + s = this.b === WireState.up ? WireState.up : WireState.down; + mod ||= s !== this.a; + this.getPin('a').setState(s); + } else if (this.a !== WireState.float) { + // s is pushed up or pulled down + s = this.a === WireState.up ? WireState.up : WireState.down; + mod ||= s !== this.b; + this.getPin('b').setState(s); + } + //console.log(this.id,"a:",this.state.a, "b:",this.state.b, "new a:", this.a.get(), "new b:", this.b.get()); + return mod; + } +} diff --git a/components/rsflipflop.svg b/components/rsflipflop.svg new file mode 100644 index 0000000..e23ce08 --- /dev/null +++ b/components/rsflipflop.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + R + S + Q + + Q + + diff --git a/components/rsflipflop.ts b/components/rsflipflop.ts new file mode 100644 index 0000000..883cf7a --- /dev/null +++ b/components/rsflipflop.ts @@ -0,0 +1,64 @@ +// R-S-Flip-Flop component for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +class RSFlipFlop extends BaseComponent { + private a = true; + private b = true; + private r = true; + private s = true; + private clk = true; + private oldclk = true; + private q = false; + constructor(simulator: Simulator, id: string, param: BaseComponentParam) { + super(simulator, id, param); + this.pins.set('r', new TriState(this, -30, -15)); + this.pins.set('s', new TriState(this, -30, 15)); + this.pins.set('clk', new TriState(this, -30, 0)); + this.pins.set('q', new TriState(this, 30, -15)); + this.getPin('q').setState(WireState.low); + this.pins.set('notq', new TriState(this, 30, 15)); + this.getPin('notq').setState(WireState.high); + } + + setup(canvas: SVGElement) { + super.doSetup('rsflipflop', canvas); + } + + io() { + this.r = this.binary(this.getPin('r').getAndReset(), true); + this.s = this.binary(this.getPin('s').getAndReset(), true); + this.clk = this.binary(this.getPin('clk').getAndReset(), true); + } + + update() { + const oldstate = this.q; + const rising = this.oldclk === false && this.clk === true; + if (rising) { + if (this.s === true) { + this.q = true; + } + if (this.r === true) { + this.q = false; + } + } + const newstate = this.q; + this.getPin('q').setBool(newstate); + this.getPin('notq').setBool(newstate === false); + this.oldclk = this.clk; + return oldstate !== newstate; + } +} diff --git a/components/togglebutton.svg b/components/togglebutton.svg new file mode 100644 index 0000000..5d0769b --- /dev/null +++ b/components/togglebutton.svg @@ -0,0 +1,26 @@ + + + + + + ] [ + + + + + + + + + + + + + + + + + 0 + + + diff --git a/components/togglebutton.ts b/components/togglebutton.ts new file mode 100644 index 0000000..cb13582 --- /dev/null +++ b/components/togglebutton.ts @@ -0,0 +1,152 @@ +// Toggle-button component for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +class ToggleButton extends BaseComponent { + private pressed = false; + private forcephysical = false; + private text: NodeListOf | undefined; + private a: WireState; + private q: WireState; + private p1: WireState; + private p2: WireState; + private p3: WireState; + private p4: WireState; + constructor(simulator: Simulator, id: string, param: BaseComponentParam) { + super(simulator, id, param); + this.pins.set('a', new TriState(this, 0, 0, undefined, { id: id + '-a' })); + this.pins.set('q', new TriState(this, 0, 0, undefined, { id: id + '-q' })); + this.pins.set('p1', new TriState(this, 0, 0, undefined, { id: id + '-p1' })); + this.pins.set('p2', new TriState(this, 10.16, 0, undefined, { id: id + '-p2' })); + this.pins.set( + 'p3', + new TriState(this, 10.16, -5.08 * 3, undefined, { + id: id + '-p3', + }) + ); + this.pins.set('p4', new TriState(this, 0, -5.08 * 3)); + if (this.extraclass && this.extraclass.indexOf('forcephysical') !== -1) { + this.forcephysical = true; + } + this.a = WireState.float; + this.q = WireState.float; + this.p1 = WireState.float; + this.p2 = WireState.float; + this.p3 = WireState.float; + this.p4 = WireState.float; + } + + setup(canvas: SVGElement) { + super.doSetup('togglebutton', canvas); + if (this.element !== null) { + this.text = this.element.querySelectorAll('.inner'); + this.element.addEventListener('mousedown', this.activate.bind(this)); + } + if (this.breadboard) { + this.simulator.wire(null, [this.getPin('p1'), this.breadboard.getPin('E' + this.col)]); + this.simulator.wire(null, [this.getPin('p2'), this.breadboard.getPin('E' + (2 + this.col))]); + this.simulator.wire(null, [this.getPin('p3'), this.breadboard.getPin('F' + (2 + this.col))]); + this.simulator.wire(null, [this.getPin('p4'), this.breadboard.getPin('F' + this.col)]); + } + } + + activate(e: Event) { + // console.log(e); + e.preventDefault(); + this.pressed = !this.pressed; + // document.text = this.text; + if (this.breadboard || this.forcephysical) { + if (this.text !== undefined) { + this.text[1].textContent = this.pressed ? '1' : '0'; + } + } else { + if (this.text !== undefined) { + this.text[0].textContent = this.pressed ? '---' : '] ['; + } + } + this.simulator.manualtick(); + } + + io() { + this.a = this.getPin('a').getAndReset(); + this.p1 = this.getPin('p1').getAndReset(); + this.p2 = this.getPin('p2').getAndReset(); + this.p3 = this.getPin('p3').getAndReset(); + this.p4 = this.getPin('p4').getAndReset(); + // console.log(this.id, "input:", this.state.p1, this.state.p4); + } + + update() { + if (this.breadboard || this.forcephysical) { + let top = this.p3; + if (top !== WireState.float) { + if (this.p4 !== WireState.float && this.p4 !== top) { + console.log('boom ToggleButton top', this.id); + } + } else { + top = this.p4; + } + const bottom = this.p1; + if (bottom !== WireState.float) { + if (this.p2 !== WireState.float && this.p2 !== bottom) { + console.log('boom ToggleButton buttom', this.id); + } + } else { + top = this.p2; + } + if (this.pressed) { + let mod = false; + if (top === WireState.float || top === WireState.up || top === WireState.down) { + const old = top; + this.p3 = bottom; + this.p4 = bottom; + this.getPin('p3').setState(bottom); + this.getPin('p4').setState(bottom); + mod = bottom !== old; + } else if (bottom === WireState.float || bottom === WireState.up || bottom === WireState.down) { + const old = bottom; + this.p1 = top; + this.p2 = top; + this.getPin('p1').setState(top); + this.getPin('p2').setState(top); + mod = top !== old; + } else { + if (top === bottom) { + this.p1 = top; + this.p2 = top; + this.p3 = top; + this.p4 = top; + this.getPin('p1').setState(top); + this.getPin('p2').setState(top); + this.getPin('p3').setState(top); + this.getPin('p4').setState(top); + } else { + console.log('boom togglebutton', this.id, top, bottom); + } + } + return mod; + } else { + return false; + } + } else { + const oldstate = this.q; + const q = this.pressed ? this.a : WireState.float; + this.q = q; + this.getPin('q').setState(q); + return oldstate !== q; + } + } +} diff --git a/less/colors-light.less b/less/colors-light.less new file mode 100644 index 0000000..b66d54a --- /dev/null +++ b/less/colors-light.less @@ -0,0 +1,9 @@ + +@label-color: #000000; +@wire-floating: #888888; +@wire-high: #00FF00; +@wire-low: #FF0000; +@elem-color: #000000; +@elem-background: #EEEEEE; +@grid-color: #333; +@canvas-color: #FFFFFF; diff --git a/less/colors.less b/less/colors.less new file mode 100644 index 0000000..39e5159 --- /dev/null +++ b/less/colors.less @@ -0,0 +1,19 @@ +/* +@label-color: #000000; +@wire-floating: #888888; +@wire-high: #00FF00; +@wire-low: #FF0000; +@elem-color: #000000; +@canvas-color: #FFFFFF; +*/ + +@label-color: #BBBBBB; +@wire-floating: #888888; +@wire-high: #00FF00; +@wire-low: #FF0000; +@wire-short: #FF9900; +@elem-color: #0d8c0d; +@elem-background: #222222; +@grid-color: #99FF99; +@canvas-color: #333333; +@wire-bus: #0000f4; diff --git a/less/default.less b/less/default.less new file mode 100644 index 0000000..13b539b --- /dev/null +++ b/less/default.less @@ -0,0 +1,24 @@ +@import "colors.less"; +@import "wire.less"; +@import "logic.less"; +@import "physical.less"; + +.logicsimulator { + svg { + font-family: sans-serif; + font-size: 16px; + &>g.labelbg { + font-size:20px; + font-weight:bold; + rect { + fill: rgba(130,130,130,.75); + stroke: #333; + cursor:pointer; + } + text { + fill: #FFFFFF; + cursor:pointer; + } + } + } +} diff --git a/less/light.less b/less/light.less new file mode 100644 index 0000000..2a261a1 --- /dev/null +++ b/less/light.less @@ -0,0 +1,24 @@ +@import "colors-light.less"; +@import "wire.less"; +@import "logic.less"; +@import "physical.less"; + +.logicsimulator { + svg { + font-family: sans-serif; + font-size: 16px; + &>g.labelbg { + font-size:20px; + font-weight:bold; + rect { + fill: rgba(130,130,130,.75); + stroke: #333; + cursor:pointer; + } + text { + fill: #FFFFFF; + cursor:pointer; + } + } + } +} diff --git a/less/logic.less b/less/logic.less new file mode 100644 index 0000000..a3b13df --- /dev/null +++ b/less/logic.less @@ -0,0 +1,191 @@ +.logic { + background: @canvas-color; + .physical { + display:none; + } + .forcephysical { + .physical { + display:block; + } + .logic { + display:none; + } + } + .togglebutton, .logicsource { + .outer { + fill: @canvas-color; + stroke: @elem-color; + stroke-width:1; + } + .inner { + fill: @elem-color; + } + } + + .hidden { + display:none; + } + + .wire { + polyline { + fill:none; + stroke: @wire-floating; + stroke-width:1; + } + circle { + fill: @wire-floating; + stroke: none; + } + + &.high, &.up { + polyline { + stroke:@wire-high; + stroke-width:2; + } + circle { + fill:@wire-high; + } + } + &.low, &.down { + polyline { + stroke: @wire-low; + stroke-width:2; + } + circle { + fill: @wire-low; + } + } + &.short { + polyline { + stroke:@wire-short; + stroke-width:3; + } + } + &:hover { + polyline { + stroke-width:3; + animation-name: hilight; + animation-duration: 1s; + animation-direction: alternate; + animation-iteration-count: infinite; + animation-timing-function: ease-in-out; + } + } + } + text.label { + fill: @label-color; + } + rect.labelbg { + fill:none !important; + stroke: none !important; + } + .buswire { + polyline { + stroke: @wire-bus; + stroke-width:2; + } + } + +.power, .ground, .pull { + circle, path, rect { + fill: @elem-background; + stroke:@elem-color; + stroke-width:1; + } +} +} + +.label { + &.low { + text { + fill: @wire-low; + } + } + &.high { + text { + fill: @wire-high; + } + } +} + +.momentarypushbutton { + .outer { + fill: @canvas-color; + stroke: @elem-color; + stroke-width:1; + cursor:pointer; + } + .inner { + fill: @elem-color; + cursor:pointer; + } +} + + +.togglebutton, .logicsource { + .outer { + cursor:pointer; + } + text.inner { + cursor:pointer; + } + &.forcephysical { + circle { + fill: #ff0000; + } + text.inner { + font-size:8px; + } + } +} + +.logicblock { + .outer, path { + fill: @canvas-color; + stroke: @elem-color; + stroke-width:1; + } + text { + fill: @elem-color; + } +} + +#background { + circle { + fill: @grid-color; + } +} + +.invisible { + display:none; +} + +.ledbar { + .pull { + rect, path { + stroke-width:0.5; + } + } + rect { + fill:#CCCCCC; + } + .led rect { + fill: #bbbbbb; + } +} +.ledbar.blue { + .led rect.on { + fill: #0000FF; + } +} +.ledbar.red { + .led rect.on { + fill: #ff0000; + } +} + +.smalllabel { + text.label { + font-size:75%; + } +} diff --git a/less/physical.less b/less/physical.less new file mode 100644 index 0000000..e5c66fc --- /dev/null +++ b/less/physical.less @@ -0,0 +1,112 @@ +.physical { + background:#FFF; + @keyframes smoke { + from { stroke: #FF0000 } + to { stroke: #000000 } + } + font-size:16px; + .logic { + display:none; + } + .togglebutton { + .outer { + fill: #f5f5f5; + stroke: #000000; + stroke-width: 0.5; + } + circle { + fill:#FF0000; + cursor: pointer; + } + .inner { + font-size:8px; + } + text.label { + font-weight: bold; + } + .labelbg { + fill: rgba(255,255,255,.75); + } + } + .switch { + cursor: pointer; + } + + #dot { + fill: #000000; + opacity: 0.4; + } + .wire { + fill: none; + stroke-width: 2; + stroke: #333; + transition: stroke 0.4s; + &.brightred { + stroke: #FF0000; + } + &.red { + stroke: #990000; + } + &.yellow { + stroke: #FFFF00; + } + &.orange { + stroke: #cc5500; + } + &.orange2 { + stroke: #ee7722; + } + &.blue { + stroke: #2222ff; + } + &.brightblue { + stroke: #a2a2ff; + } + &.green { + stroke: #229922; + } + &.short { + stroke-width:5; + animation-name: smoke; + animation-duration: 2s; + animation-direction: alternate; + animation-iteration-count: infinite; + animation-timing-function: ease-in-out; + } + &.white { + stroke: #FFFFFF; + } + &:hover { + stroke-width:3; + animation-name: hilight; + animation-duration: 1s; + animation-direction: alternate; + animation-iteration-count: infinite; + animation-timing-function: ease-in-out; + } + &.high:hover, &.up:hover { + stroke:#FF0000; + transition: 0.2s; + } + &.low:hover, &.down:hover { + stroke:#000000; + transition: 0.2s; + } + circle { + fill:#333; + } + } +} +.pull { + .outer { + stroke: #000000; + } +} +.pull .physical { + path { + &outline { + stroke: #000000; + } + stroke-width: 1; + } +} diff --git a/less/wire.less b/less/wire.less new file mode 100644 index 0000000..e5ef5ce --- /dev/null +++ b/less/wire.less @@ -0,0 +1,10 @@ +@wire-floating: #888888; +@wire-high: #00FF00; +@wire-low: #FF0000; +@wire-short: #FF9900; +@wire-bus: #0000ff; + +@keyframes hilight { + from { opacity: 1 } + to { opacity: 0.5 } +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..c879b66 --- /dev/null +++ b/package.json @@ -0,0 +1,33 @@ +{ + "name": "logicsimulator", + "version": "1.0.0", + "description": "CMOS/TTL Logic Simulator", + "author": "Sascha Nitsch (grumpydeveloper)", + "license": "GPL-3.0-or-later", + "private": true, + "devDependencies": { + "@types/node": "latest", + "grunt": "latest", + "grunt-cli": "latest", + "grunt-contrib-less": "latest", + "grunt-contrib-uglify": "latest", + "grunt-contrib-watch": "latest", + "grunt-newer": "latest", + "grunt-svgmin": "latest", + "grunt-ts": "latest", + "gts": "latest", + "ts-loader": "latest", + "typescript": "latest", + "uglify-js": "latest" + }, + "scripts": { + "lint": "gts lint", + "clean": "gts clean", + "compile": "tsc", + "fix": "gts fix", + "prepare": "npm run compile", + "pretest": "npm run compile", + "posttest": "npm run lint", + "build": "webpack" + } +} diff --git a/plugins/README b/plugins/README new file mode 100644 index 0000000..54afcb0 --- /dev/null +++ b/plugins/README @@ -0,0 +1 @@ +check out plugins into this directory diff --git a/ts/basecomponent.ts b/ts/basecomponent.ts new file mode 100644 index 0000000..860a15b --- /dev/null +++ b/ts/basecomponent.ts @@ -0,0 +1,290 @@ +// Base class for all component for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +interface BaseComponentParam { + scale?: number; + scaleX?: number; + scaleY?: number; + rotate?: number; + mirror?: string; + extraclass?: string; + label?: ComponentLabel | string; + ref?: ComponentLabel; +} + +interface ComponentLabel { + x: number; + y: number; + text: string; +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +class BaseComponent { + protected simulator: Simulator; + protected x: number; + protected y: number; + protected id: string; + protected element: SVGGElement | null; + protected startdelay: number; + public zIndex: number; + protected scaleX: number; + protected scaleY: number; + protected rotate: number; + private mirror: string; + protected extraclass: string; + protected breadboard: BaseComponent | null = null; + protected pin = ''; + protected col = 0; + private ref: ComponentLabel | undefined; + private label: ComponentLabel | undefined; + protected pins: Map; + protected stateMapBool: Map = new Map(); + protected stateMapWire: Map = new Map(); + private fallback = new TriState(this, 0, 0, WireState.float); + + constructor(simulator: Simulator, id: string, param: BaseComponentParam = {}) { + this.simulator = simulator; + this.x = 0; + this.y = 0; + this.id = id; + this.element = null; + this.startdelay = 0; + this.scaleX = param.scale || 1; + this.scaleY = param.scale || 1; + this.rotate = param.rotate || 0; + this.mirror = param.mirror || ''; + this.extraclass = param.extraclass || ''; + this.label = typeof param.label !== 'string' ? param.label : undefined; + this.ref = param.ref; + this.pins = new Map(); + if (this.mirror !== '') { + switch (this.mirror) { + case 'x': + this.scaleX *= -1; + break; + case 'y': + this.scaleY *= -1; + } + } + this.zIndex = 1; + } + + getPin(name: string): TriState { + const pin = this.pins.get(name); + if (pin === undefined) { + return this.fallback; + } + return pin; + } + + getStateBool(name: string): boolean { + const state = this.stateMapBool.get(name); + if (state === undefined) { + console.log('undefined state bool entry', name); + return false; + } + return state; + } + + getStateWire(name: string): WireState { + const state = this.stateMapWire.get(name); + if (state === undefined) { + console.log('undefined state wire entry', name); + return WireState.float; + } + return state; + } + + setPosition(x: number | BaseComponent, y: number | string) { + if (typeof x === 'number' && typeof y === 'number') { + this.x = x; + this.y = y; + } else if (x instanceof BaseComponent && typeof y === 'string') { + const p = x.getPos(y); + this.x = p[0]; + this.y = p[1]; + if (this.scaleY === 1 && this.scaleX === 1) { + this.scaleX = x.getScaleX(); + this.scaleY = x.getScaleY(); + } + this.breadboard = x; + this.pin = y; + this.col = parseInt(y.substr(1)); + } + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + setup(_canvas: SVGElement) { + console.log('Basecomponent::setup(canvas) called'); + } + + doSetup(id: string, canvas: SVGElement) { + this.element = document.createElementNS('http://www.w3.org/2000/svg', 'g'); + const tid = document.getElementById(id); + if (tid === null || tid.contentDocument === null) return; + const root = tid.contentDocument.getElementById('root'); + if (root === null) return; + const inner = root.cloneNode(true); + inner.removeAttribute('id'); + let c = inner.getAttribute('class') || ''; + if (this.extraclass !== '') { + c += ' ' + this.extraclass; + } + this.element.setAttribute('class', c); + inner.removeAttribute('class'); + this.element.appendChild(inner); + let transform = 'translate(' + this.x + ',' + this.y + ')'; + if (this.scaleX !== 1 || this.scaleY !== 1) { + transform += ' scale(' + this.scaleX + ',' + this.scaleY + ')'; + } + if (this.rotate) { + transform += ' rotate(' + this.rotate + ')'; + } + + inner.setAttribute('transform', transform); + this.element.id = this.id; + + let label; + let ref; + let txt; + if (this.label) { + label = document.createElementNS('http://www.w3.org/2000/svg', 'text'); + label.setAttribute('x', (this.label.x * this.scaleX + this.x).toString()); + label.setAttribute('y', (this.label.y * this.scaleY + this.y).toString()); + label.setAttribute('class', 'label'); + txt = document.createTextNode(this.label.text ? this.label.text : this.id); + label.appendChild(txt); + this.element.appendChild(label); + } + if (this.ref) { + ref = document.createElementNS('http://www.w3.org/2000/svg', 'text'); + ref.setAttribute('x', (this.ref.x * this.scaleX + this.x).toString()); + ref.setAttribute('y', (this.ref.y * this.scaleY + this.y).toString()); + ref.setAttribute('class', 'ref'); + txt = document.createTextNode(this.ref.text); + ref.appendChild(txt); + this.element.appendChild(ref); + } + canvas.appendChild(this.element); + let SVGRect; + let rect; + if (this.label && label) { + const SVGRect = label.getBBox(); + rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect'); + rect.setAttribute('x', SVGRect.x.toString()); + rect.setAttribute('y', SVGRect.y.toString()); + rect.setAttribute('width', SVGRect.width.toString()); + rect.setAttribute('height', SVGRect.height.toString()); + rect.setAttribute('class', 'labelbg'); + this.element.insertBefore(rect, label); + } + if (this.ref && ref) { + SVGRect = ref.getBBox(); + rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect'); + rect.setAttribute('x', SVGRect.x.toString()); + rect.setAttribute('y', SVGRect.y.toString()); + rect.setAttribute('width', SVGRect.width.toString()); + rect.setAttribute('height', SVGRect.height.toString()); + rect.setAttribute('class', 'labelbg'); + this.element.insertBefore(rect, ref); + } + } + + //do nothing + finalize() {} + + // do nothing + io() {} + + //do nothing + update(): boolean { + return false; + } + + showLabel(x: number, y: number, text: string | null) { + if (text) { + this.label = { x: x, y: y, text: text }; + } else { + this.label = { x: x, y: y, text: '' }; + } + } + + showRef(x: number, y: number, text: string) { + this.ref = { x: x, y: y, text: text }; + } + + setStartDelay(delay: number) { + this.startdelay = delay; + } + + close() { + // do nothing + } + + binary(input: WireState, pull: boolean): boolean { + let ret = this.tri(input); + if (typeof ret !== 'boolean') { + ret = pull; + } + return ret; + } + + tri(input: WireState): boolean | null { + if (input === WireState.high || input === WireState.up) { + return true; + } + if (input === WireState.low || input === WireState.down) { + return false; + } + return null; + } + + asint(input: boolean, int: number): number { + return input ? int : 0; + } + + getX(): number { + return this.x; + } + + getY(): number { + return this.y; + } + + getScaleX(): number { + return this.scaleX; + } + + getScaleY(): number { + return this.scaleY; + } + + getRotate(): number { + return this.rotate; + } + + getId(): string { + return this.id; + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + getPos(_column: string): number[] { + return [0, 0]; + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + connected(_triState: TriState): void {} +} diff --git a/ts/helper.ts b/ts/helper.ts new file mode 100644 index 0000000..195bafc --- /dev/null +++ b/ts/helper.ts @@ -0,0 +1,25 @@ +// helper functions for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function makeId(length: number): string { + let text = ''; + const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + for (let i = 0; i < length; i++) { + text += possible.charAt(Math.floor(Math.random() * possible.length)); + } + return text; +} diff --git a/ts/simulator.ts b/ts/simulator.ts new file mode 100644 index 0000000..e5ec699 --- /dev/null +++ b/ts/simulator.ts @@ -0,0 +1,635 @@ +// Simulator main class of the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +/* eslint-disable @typescript-eslint/no-explicit-any */ +// eslint-disable-next-line @typescript-eslint/no-unused-vars +interface Window { + [propName: string]: any; +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +class Simulator { + private svg: SVGElement; + private parent: HTMLElement; + private root: SVGGElement; + private componentsToLoad: string[]; + private onComponentsLoadedCallback: null; + private components: Map; + private wires: Wire[]; + private mode: string; + private type: string; + private basepath: string; + private runInit: Array>; + private cors: boolean; + private ajax: boolean; + private zoom: number; + private x: number; + private y: number; + private zoomlevel: number[] = [0.5, 0.67, 0.75, 0.8, 0.9, 1, 1.1, 1.25, 1.5, 1.75, 2, 2.5]; + private json: Object; + private boundkeypress: any; + private loading: Map> = new Map>(); + private isFullScreen = false; + private moveStart: Array = [0, 0]; + private moving: any = undefined; + + constructor(parent: string, width: string, height: string, className: string, zoom: number) { + this.svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + if (width.indexOf === undefined || width.indexOf('%') === -1) width += 'px'; + this.svg.setAttribute('width', width); + this.svg.setAttribute('height', height + 'px'); + if (!className) { + className = 'logic'; + } + this.svg.setAttribute('class', className); + const p = document.getElementById(parent); + if (p === null) { + throw new TypeError('parent element not found'); + } + this.parent = p; + this.parent.appendChild(this.svg); + this.root = document.createElementNS('http://www.w3.org/2000/svg', 'g'); + this.svg.appendChild(this.root); + this.componentsToLoad = []; + this.onComponentsLoadedCallback = null; + this.components = new Map(); + this.wires = []; + this.mode = 'manual'; + this.type = className; + this.basepath = ''; + this.runInit = []; + const xhrSupported = this.xhr(); + this.cors = xhrSupported !== null && 'withCredentials' in xhrSupported; + this.ajax = xhrSupported !== null; + if (zoom) { + this.zoom = 0; + while (this.zoom < this.zoomlevel.length + 1 && this.zoomlevel[this.zoom + 1] <= zoom) { + ++this.zoom; + } + } else { + this.zoom = 5; + } + this.x = this.y = 0; + this.json = {}; + } + + close(): void { + for (const i in this.components) { + const c = this.components.get(i); + if (c !== undefined) c.close(); + } + if (this.svg.parentNode !== null) { + this.svg.parentNode.removeChild(this.svg); + } + window.removeEventListener('keydown', this.boundkeypress); + } + + setBasePath(path: string): void { + this.basepath = path; + } + + callback(scope: any, funct: any, parameter: any) { + if (!scope) { + funct.call(this, parameter); + } else { + funct.call(scope, parameter); + } + } + + loadJS(file: string, scope: any, callback: any, parameter: any) { + const f = this.loading.get(file); + if (f !== undefined) { + f.push([scope, callback, parameter]); + return; + } + const script = document.createElement('script'); + script.setAttribute('src', this.basepath + 'components/' + file + '.min.js'); + this.loading.set(file, [[scope, callback, parameter]]); + if (callback) { + script.onload = () => { + this.callbackList(file); + }; + } + document.getElementsByTagName('head')[0].appendChild(script); + } + + callbackList(file: string) { + const f = this.loading.get(file); + if (f === undefined) return; + for (let i = 0; i < f.length; ++i) { + const c = f[i]; + if (c[0]) { + this.callback(c[0], c[1], c[2]); + } + } + } + + loadSVG(file: string, scope: any, callback: any, parameter: any) { + const obj = document.createElement('object'); + obj.setAttribute('data', this.basepath + 'components/' + file + '.svg'); + obj.setAttribute('id', file); + obj.setAttribute('type', 'image/svg+xml'); + obj.setAttribute('class', 'invisible'); + if (callback) { + obj.onload = (e: any) => { + if (!e.target.loaded) { + e.target.loaded = true; + this.callback(scope, callback, parameter); + } + }; + } + document.getElementsByTagName('body')[0].appendChild(obj); + } + + loadCSS(file: string): void { + const link = document.createElement('link'); + link.setAttribute('href', this.basepath + 'components/' + file + '.css'); + link.setAttribute('rel', 'stylesheet'); + link.setAttribute('type', 'text/css'); + document.getElementsByTagName('head')[0].appendChild(link); + } + + loadComponent(classname: string, loadsvg: boolean): void { + if (!(classname in window) && this.componentsToLoad.indexOf(classname + 'js') === -1) { + this.componentsToLoad.push(classname + 'js'); + if (loadsvg) { + this.componentsToLoad.push(classname + 'svg'); + } + this.loadJS(classname.toLowerCase(), this, this.componentLoaded, classname + 'js'); + if (loadsvg) { + this.loadSVG(classname.toLowerCase(), this, this.componentLoaded, classname + 'svg'); + } + } + } + + componentLoaded(classname: string): void { + for (let i = 0; i < this.componentsToLoad.length; ++i) { + if (this.componentsToLoad[i] === classname) { + this.componentsToLoad.splice(i, 1); + break; + } + } + const cn = classname.substr(0, classname.length - 2); + for (let j = 0; j < this.runInit.length; ++j) { + const init = this.runInit[j][1] + 'Init'; + if (this.runInit[j][0] === cn && typeof window[init] === 'function') { + window[init](); + this.runInit.splice(j, 1); + --j; + } + } + if (typeof window[cn + 'Depends'] === 'function') { + const depends = window[cn + 'Depends'](); + if (!window[depends]) { + if (this.componentsToLoad.indexOf(depends + 'js') === -1) { + this.componentsToLoad.push(depends + 'js'); + this.loadJS(depends.toLowerCase(), this, this.componentLoaded, depends + 'js'); + } + this.runInit.push([depends, cn]); + } else { + if (window[classname + 'Init']) { + window[classname + 'Init'](); + } + } + } + if (this.componentsToLoad.length === 0) { + this.callback(this, this.onComponentsLoadedCallback, null); + } + } + + onComponentsLoaded(callback: any) { + if (this.componentsToLoad.length === 0) { + this.onComponentsLoadedCallback = null; + this.callback(this, callback, null); + } else { + this.onComponentsLoadedCallback = callback; + } + } + + add(id: string | null, name: string, param: any): BaseComponent | null { + if (!(name in window)) { + console.log('no such component:', name); + return null; + } + if (id === null) { + do { + id = makeId(10); + } while (id in this.components); + } + const component = new (window[name])(this, id, param); + this.components.set(id, component); + return component; + } + + wire(id: string | null, points: any, param?: any) { + const wire = new Wire(id, param, this.type === 'physical'); + wire.connect(points); + this.wires.push(wire); + } + + svgButton(parent: SVGElement, x: string, y: string, label: string, onclick: Function) { + const g = document.createElementNS('http://www.w3.org/2000/svg', 'g'); + g.setAttribute('transform', 'translate(' + x + ',' + y + ')'); + g.setAttribute('data-y', y); + g.setAttribute('class', 'zoombutton'); + const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect'); + rect.setAttribute('x', '0'); + rect.setAttribute('y', '0'); + rect.setAttribute('width', '20'); + rect.setAttribute('height', '20'); + const text = document.createElementNS('http://www.w3.org/2000/svg', 'text'); + text.setAttribute('y', '16'); + const txt = document.createTextNode(label); + text.appendChild(txt); + g.appendChild(rect); + g.appendChild(text); + parent.appendChild(g); + if (onclick) { + g.addEventListener('click', onclick.bind(this)); + } + const bbox = text.getBBox(); + text.setAttribute('x', ((20 - bbox.width) / 2).toString()); + } + + svgZoomIn() { + if (this.zoom < this.zoomlevel.length - 1) { + ++this.zoom; + this.svgTransform(); + } + } + + svgZoomOut() { + if (this.zoom > 0) { + --this.zoom; + this.svgTransform(); + } + } + + svgFullScreen(): void { + this.isFullScreen = !this.isFullScreen; + let oldWidth; + let width; + if (this.isFullScreen) { + this.svg.setAttribute('style', 'position:fixed; left:0; top:0; width:100%; height:100%;z-index:1000'); + oldWidth = this.svg.getBoundingClientRect().width; + width = document.body.clientWidth; + } else { + this.svg.removeAttribute('style'); + width = this.svg.getBoundingClientRect().width; + oldWidth = document.body.clientWidth; + } + const buttons = this.svg.querySelectorAll('.zoombutton'); + for (let i = 0; i < buttons.length; ++i) { + buttons[i].setAttribute('transform', 'translate(' + (width - 21) + ',' + buttons[i].getAttribute('data-y') + ')'); + } + // calc new zoom + const zoom = (this.zoomlevel[this.zoom] * width) / oldWidth; + this.zoom = 0; + while (this.zoom < this.zoomlevel.length + 1 && this.zoomlevel[this.zoom + 1] <= zoom) { + ++this.zoom; + } + this.svgTransform(); + } + + svgTransform() { + this.root.setAttribute( + 'transform', + 'translate(' + this.x + ',' + this.y + '), scale(' + this.zoomlevel[this.zoom] + ')' + ); + } + + startMove(event: MouseEvent) { + if (event.button !== 0) { + return; + } + this.moveStart = [event.clientX, event.clientY]; + if (this.moving) { + this.svg.removeEventListener('mousemove', this.moving); + } + this.moving = this.doMove.bind(this); + this.svg.addEventListener('mousemove', this.moving); + } + + endMove() { + this.svg.removeEventListener('mousemove', this.moving); + delete this.moving; + } + + doMove(event: MouseEvent) { + this.x += event.clientX - this.moveStart[0]; + this.y += event.clientY - this.moveStart[1]; + this.moveStart = [event.clientX, event.clientY]; + this.svgTransform(); + } + + mouseScroll(event: any) { + if (!event.ctrlKey) { + return; + } + event.preventDefault(); + if (event.deltaY > 0) { + this.svgZoomOut(); + } else { + this.svgZoomIn(); + } + } + + keypress(event: KeyboardEvent) { + if (event.altKey) { + if (event.key === '-') { + event.preventDefault(); + this.svgZoomOut(); + } else if (event.key === '+') { + event.preventDefault(); + this.svgZoomIn(); + } + } + } + + run() { + //create zoom and fullscreeen buttons + const g = document.createElementNS('http://www.w3.org/2000/svg', 'g'); + g.setAttribute('class', 'labelbg'); + this.svg.appendChild(g); + const width = this.svg.getBoundingClientRect().width; + this.svgButton(g, (width - 21).toString(), '1', '+', this.svgZoomIn); + this.svgButton(g, (width - 21).toString(), '23', '-', this.svgZoomOut); + this.svgButton(g, (width - 21).toString(), '46', '⇔', this.svgFullScreen); + this.svg.addEventListener('mousedown', this.startMove.bind(this)); + this.svg.addEventListener('mouseup', this.endMove.bind(this)); + this.svg.addEventListener('wheel', this.mouseScroll.bind(this)); + this.boundkeypress = this.keypress.bind(this); + window.addEventListener('keydown', this.boundkeypress); + this.svgTransform(); + for (let i = 0; i < this.wires.length; ++i) { + this.wires[i].update(); + } + this.tick(); + } + + manualtick(): void { + if (this.mode === 'manual') { + this.tick(); + } + } + + tick(): void { + let i; + let instantUpdate = false; + let reruns = 10; + do { + instantUpdate = false; + this.components.forEach((c: BaseComponent) => { + c.io(); + }); + this.components.forEach((c: BaseComponent) => { + instantUpdate = c.update() || instantUpdate; + }); + for (i = 0; i < this.wires.length; ++i) { + instantUpdate = this.wires[i].update() || instantUpdate; + } + } while (instantUpdate && --reruns > 0); + } + + fromJson(json: Object) { + this.json = json; + // round 1, extract components + let id: keyof typeof json; + for (id in json) { + const j = json[id]; + const component = j.component; + if (component !== 'Wire') { + if ('svg' in j) { + this.componentsToLoad.push(component + 'svg'); + this.loadSVG(j.svg.toLowerCase(), this, this.componentLoaded, component + 'svg'); + this.loadComponent(component, false); + } else { + this.loadComponent(component, j.nosvg !== true); + } + } + } + this.onComponentsLoaded(this.jsonSetup); + } + + xhr(): XMLHttpRequest | null { + try { + return new window.XMLHttpRequest(); + } catch (e) { + return null; + } + } + + fromJsonFile(url: string) { + // load json + let xhr = null; + if (this.ajax && this.cors) { + xhr = this.xhr(); + if (xhr !== null) { + xhr.open('get', url); + } + } + if (xhr === null) { + return; + } + xhr.onreadystatechange = function (this: Simulator, a: Event) { + if (a === null || a.target === null) return; + const target = a.target; + if (target.readyState === 4 && target.status === 200) { + const json = JSON.parse(target.responseText); + this.fromJson(json); + } + }.bind(this); + // Send the request + xhr.send(null); + } + + jsonSetup() { + const components = new Map(); + //round 2, create and place components + let json: any; + let id: keyof Object; + for (id in this.json) { + json = this.json[id]; + if (json.component !== 'Wire') { + const component = this.add(id, json.component, json.param); + if (component === null) { + continue; + } + if (json.position && !component.setPosition) { + console.log('error on component', component, json); + } + if (json.position && component.setPosition) { + if (typeof json.position === 'string') { + const pos = json.position.split('.'); + const c = components.get(pos[0]); + if (c !== undefined) { + component.setPosition(c, pos[1]); + } + } else { + component.setPosition(json.position[0], json.position[1]); + } + } + if (json.label) { + component.showLabel(json.label[0], json.label[1], json.label.length === 3 ? json.label[2] : undefined); + } + if (json.ref) { + component.showRef(json.ref[0], json.ref[1], json.ref[2]); + } + if (json.startdelay) { + component.setStartDelay(json.startdelay); + } + components.set(id, component); + } + } + this.components.forEach(function (this: Simulator, c: BaseComponent) { + if (c.zIndex === 0) { + c.setup(this.root); + } + }, this); + // round 3, add wiring + let i; + let tokens; + let obj; + for (id in this.json) { + json = this.json[id]; + if (json.component === 'Wire') { + if (json.path) { + json.paths = [json.path]; + } + const base = json.base; + if (json.paths) { + for (let p = 0; p < json.paths.length; ++p) { + const path = json.paths[p]; + for (i = 0; i < path.length; ++i) { + if (typeof path[i] === 'string') { + tokens = path[i].split('.'); + if (tokens[0] === '') { + tokens[0] = base; + } + obj = components.get(tokens[0]); + if (obj !== undefined) { + path[i] = obj.getPin(tokens[1]); + } else { + console.log('could not find ', tokens[0], '.'); + } + } else if (typeof path[i] === 'number') { + if (path[path[i]] instanceof TriState) { + const t = path[path[i]]; + path[i] = [t.x(), t.y()]; + } + } + } + let rerun = true; + let reruns = 0; + while (rerun && reruns < 100) { + let other; + ++reruns; + rerun = false; + for (i = 0; i < path.length; ++i) { + if (path[i] && typeof path[i] === 'object') { + let pa; + if (typeof path[i][0] === 'string') { + // relative + const offsetX = path[i][0].length > 1 ? parseInt(path[i][0].substr(1)) : 0; + pa = path[i][0][0]; + if (pa === 'b') { + const c = components.get(base); + if (c !== undefined) { + path[i][0] = c.getX() + offsetX; + } + } else { + other = pa === 'p' ? i - 1 : i + 1; + if (path[other] instanceof TriState) { + path[i][0] = path[other].x() + offsetX; + } else { + if (typeof path[other] === 'number' || typeof path[other][0] === 'string') { + rerun = true; + } else { + path[i][0] = path[other][0] + offsetX; + } + } + } + } + if (typeof path[i][1] === 'string') { + // relative + const offsetY = path[i][1].length > 1 ? parseInt(path[i][1].substr(1)) : 0; + pa = path[i][1][0]; + if (pa === 'b') { + const c = components.get(base); + if (c !== undefined) { + path[i][1] = c.getY() + offsetY; + } + } else { + other = pa === 'p' ? i - 1 : i + 1; + if (path[other] instanceof TriState) { + path[i][1] = path[other].y() + offsetY; + } else { + if (typeof path[other] === 'number' || typeof path[other][1] === 'string') { + rerun = true; + } else { + path[i][1] = path[other][1] + offsetY; + } + } + } + } + } else if (path[i] && typeof path[i] === 'number') { + other = path[i]; + if (path[other] instanceof TriState) { + path[i] = path[other]; + } else { + if (typeof path[other][0] === 'string' || typeof path[other][1] === 'string') { + rerun = true; + } else { + path[i] = path[other]; + } + } + } + } + } + this.wire(p ? null : id, path, json.param); // first wire gets id + } + } + } + } + + this.components.forEach(function (this: Simulator, c: BaseComponent) { + if (c.zIndex > 0) { + c.setup(this.root); + } + }, this); + + //finalize component setup + this.components.forEach((c: BaseComponent) => { + c.finalize(); + }); + + for (i = 0; i < this.wires.length; ++i) { + const s = this.wires[i].setup(this.root); + if (!s) { + this.wires.splice(i, 1); + --i; + } + } + this.run(); + } + getParent(): HTMLElement { + return this.parent; + } + + getType() { + return this.type; + } +} diff --git a/ts/tristate.ts b/ts/tristate.ts new file mode 100644 index 0000000..d668554 --- /dev/null +++ b/ts/tristate.ts @@ -0,0 +1,123 @@ +// Tri-State class for IO pis of the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +interface TriStateParam { + id?: string; + type?: string; +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +class TriState { + private parent: BaseComponent; + private offsetX: number; + private offsetY: number; + private state: WireState; + private readonly: boolean; + private param: TriStateParam; + public connectedWire: Wire | null; + private rotate: number; + constructor( + parent: BaseComponent, + offsetX: number, + offsetY: number, + fixed?: undefined | WireState, + param: TriStateParam = {} + ) { + this.parent = parent; + this.offsetX = offsetX; // * parent.scaleX; + this.offsetY = offsetY; // * parent.scaleY; + this.connectedWire = null; + if (fixed !== undefined) { + this.state = fixed; + this.readonly = true; + } else { + this.state = WireState.float; + this.readonly = false; + } + this.param = param; + this.rotate = parent ? (parent.getRotate() / 180) * Math.PI : 0; + } + + move(x: number, y: number): void { + this.offsetX = x; + this.offsetY = y; + } + + x(): number { + return ( + this.parent.getX() + + this.offsetX * Math.cos(this.rotate) * this.parent.getScaleX() - + this.offsetY * Math.sin(this.rotate) * this.parent.getScaleX() + ); + } + + y(): number { + return ( + this.parent.getY() + + this.offsetX * Math.sin(this.rotate) * this.parent.getScaleX() + + this.offsetY * Math.cos(this.rotate) * this.parent.getScaleY() + ); + } + + get(): WireState { + return this.state; + } + + getAndReset(): WireState { + const s = this.state; + if (!this.readonly) { + this.state = WireState.float; + } + return s; + } + + setState(state: WireState) { + if (!this.readonly) { + this.state = state; + } + } + + setBool(state: boolean) { + if (!this.readonly) { + this.state = state ? WireState.high : WireState.low; + } + } + + connected(wire: Wire) { + if (this.connectedWire) { + console.log('multiple wires on', this.parent.getId(), this.param.id, this.connectedWire.getId(), wire.getId()); + } + this.connectedWire = wire; + this.parent.connected(this); + } + + getParent(): BaseComponent { + return this.parent; + } + + getParam(): TriStateParam { + return this.param; + } + + getOffsetX(): number { + return this.offsetX; + } + + getOffsetY(): number { + return this.offsetY; + } +} diff --git a/ts/wire.ts b/ts/wire.ts new file mode 100644 index 0000000..9df52aa --- /dev/null +++ b/ts/wire.ts @@ -0,0 +1,317 @@ +// Wire class for the LogicSimulator +// Copyright (C) 2022 Sascha Nitsch + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +interface WireParam { + color?: string; + scale?: number; + extraclass?: string; +} + +enum WireState { + float = 0, + up = 1, + down = 2, + high = 3, + low = 4, +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +class Wire { + private paths: string[]; + private activeD: string; + public autoid: boolean; + private id: string; + private connections: Array>; + private activeNodes: TriState[]; + private draw: boolean; + private color: string | undefined; + private scale: number; + private extraclass: string; + private dot: boolean; + public joined: Wire | null; + private root: SVGElement | undefined; + private state: WireState; + + constructor(id: string | null, param: WireParam, physical: boolean) { + this.paths = []; + this.activeD = ''; + if (id === null) { + this.autoid = true; + this.id = makeId(10); + } else { + this.autoid = false; + this.id = id; + } + this.connections = []; + this.activeNodes = []; + this.draw = false; + param = param || {}; + this.color = param.color; + this.scale = param.scale || 1; + this.extraclass = param.extraclass || ''; + this.dot = physical; + this.joined = null; + this.root = undefined; + this.state = WireState.float; + } + + getId(): string { + return this.id; + } + + connect(points: Array | null>) { + this.activeD = ''; + let line = false; + let oldx: number | string | undefined = 0; + let oldy: number | string | undefined = 0; + for (let i = 0; i < points.length; ++i) { + let x; + let y; + if (points[i] instanceof TriState) { + const p = points[i]; + p.connected(this); + x = p.x(); + y = p.y(); + this.activeNodes.push(p); + } else if (points[i] instanceof Array) { + const p = >points[i]; + x = p[0]; + y = p[1]; + if (p[2] === true) { + this.connections.push([x, y]); + } + } else if (points[i] === null) { + this.paths.push(this.activeD); + this.activeD = ''; + line = false; + continue; + } + if ( + (typeof oldx === 'number' && typeof x === 'number' && Math.abs(oldx - x) > 0.1) || + (typeof oldy === 'number' && typeof y === 'number' && Math.abs(oldy - y) > 0.1) + ) { + if (line) { + this.draw = true; + } + line = true; + this.activeD += x + ',' + y + ' '; + } + oldx = x; + oldy = y; + } + if (this.draw === false) { + this.activeD = ''; + } else { + this.paths.push(this.activeD); + } + } + + setup(parent: SVGElement) { + if (this.draw) { + if (!document.getElementById('dot')) { + const defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs'); + const marker = document.createElementNS('http://www.w3.org/2000/svg', 'marker'); + marker.setAttribute('id', 'dot'); + marker.setAttribute('viewBox', '0 0 10 10'); + marker.setAttribute('refX', '5'); + marker.setAttribute('refY', '5'); + marker.setAttribute('markerWidth', '8'); + marker.setAttribute('markerHeight', '8'); + const mcircle = document.createElementNS('http://www.w3.org/2000/svg', 'circle'); + mcircle.setAttribute('cx', '5'); + mcircle.setAttribute('cy', '5'); + mcircle.setAttribute('r', '2'); + marker.appendChild(mcircle); + defs.appendChild(marker); + if (parent.parentNode !== null) { + parent.parentNode.prepend(defs); + } + } + this.root = document.createElementNS('http://www.w3.org/2000/svg', 'g'); + this.root.setAttribute( + 'class', + 'wire' + (this.color ? ' ' + this.color : '') + (this.extraclass ? ' ' + this.extraclass : '') + ); + this.root.setAttribute('id', this.id); + // console.log(this.id ,this.paths); + for (let pcount = 0; pcount < this.paths.length; ++pcount) { + const path = document.createElementNS('http://www.w3.org/2000/svg', 'polyline'); + path.setAttribute('points', this.paths[pcount]); + // console.log(this.d[pcount]); + if (this.dot) { + path.setAttribute('marker', 'url(#dot)'); + path.setAttribute('marker-start', 'url(#dot)'); + path.setAttribute('marker-end', 'url(#dot)'); + } + this.root.appendChild(path); + } + parent.appendChild(this.root); + for (let i = 0; i < this.connections.length; ++i) { + const c = this.connections[i]; + if (c !== null && c[0] && c[1]) { + const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle'); + circle.setAttribute('cx', c[0].toString()); + circle.setAttribute('cy', c[1].toString()); + circle.setAttribute('r', (3 * this.scale).toString()); + this.root.appendChild(circle); + } + } + } + return this.draw; + } + + update(): boolean { + if (!this.root) { + return false; + } + this.root.classList.remove('high', 'low', 'up', 'down', 'float', 'short'); + const oldState = this.state; + this.state = WireState.float; + let source = null; + // collect outputs of active components + for (let i = 0; i < this.activeNodes.length; ++i) { + const state = this.activeNodes[i].get(); + if (state === WireState.high || state === WireState.low) { + if ((this.state === WireState.high || this.state === WireState.low) && this.state !== state) { + console.log( + 'error, boom on', + this.id, + 'try to set:', + state, + 'previous', + source && source.getParent().getId(), + source && source.getParam().id, + this.state, + 'new:', + this.activeNodes[i].getParent().getId(), + this.activeNodes[i].getParam().id, + this + ); + this.root.classList.add('short'); + return false; + } + this.state = state; + source = this.activeNodes[i]; + } else if (state === WireState.up || state === WireState.down) { + if (this.state !== WireState.high && this.state !== WireState.low) { + this.state = state; + source = this.activeNodes[i]; + } + } + } + /*if (this.state === "up") { + this.state = true; + } else if (this.state === "down") { + this.state = false; + }*/ + // update inputs of active components with new state + for (let i = 0; i < this.activeNodes.length; ++i) { + this.activeNodes[i].setState(this.state); + } + //this.root.classList.add((this.state === true || this.state === "up")? "high" : ((this.state === false || this.state === "down") ? "low" : "float")); + let classname = ''; + switch (this.state) { + case WireState.high: + classname = 'high'; + break; + case WireState.low: + classname = 'low'; + break; + case WireState.up: + classname = 'up'; + break; + case WireState.down: + classname = 'down'; + break; + default: + classname = 'float'; + } + this.root.classList.add(classname); + return oldState !== this.state; + } + + join(other: Wire | null) { + if (!other) return; + while (other.joined) { + other = other.joined; + } + if (this.joined) { + this.joined.join(other); + } else { + for (let i = 0; i < other.activeNodes.length; ++i) { + this.activeNodes.push(other.activeNodes[i]); + } + for (let i = 0; i < other.connections.length; ++i) { + this.connections.push(other.connections[i]); + } + other.activeNodes = []; + this.paths = this.paths.concat(other.paths); + other.activeD = ''; + other.joined = this; + this.draw ||= other.draw; + other.draw = false; + if (other.color && !this.color) { + this.color = other.color; + } + } + // console.log('join', this.id, this.autoid, 'with', oOther.id, oOther.autoid); + if (this.autoid && !other.autoid) { + const tmp = this.id; + this.id = other.id; + other.id = tmp; + this.autoid = false; + other.autoid = true; + this.color = other.color; + this.scale = other.scale; + } + } + + remove(parent: TriState | BaseComponent) { + if (this.joined) { + this.joined.remove(parent); + return; + } + for (let i = 0; i < this.activeNodes.length; ++i) { + if (this.activeNodes[i].getParent() === parent) { + this.activeNodes.splice(i, 1); + i = 0; + } + } + //console.log("rem", this.id, this.activeNodes); + } + getState(): WireState { + return this.state; + } + + static WireStateFromString(input: string) { + let s = WireState.float; + switch (input) { + case 'up': + s = WireState.up; + break; + case 'down': + s = WireState.down; + break; + case 'high': + s = WireState.high; + break; + case 'low': + s = WireState.low; + break; + } + return s; + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..0392373 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "./node_modules/gts/tsconfig-google.json", + "compilerOptions": { + "rootDir": "./", + "outDir": "js-temp", + "strict": true, + "target":"es5", + "lib": ["es5", "DOM"], + "module": "none", + "moduleResolution": "node", + "sourceMap": true, + "declaration": false, + "exclude": "decl" + } +}