commit c92a6343f6a4c8ab748df09b09dce2abd373322f
parent 816f64d0c97e9cb05a01be6bd5cfa63dd23064b7
Author: Gerd Beuster <gerd@frombelow.net>
Date: Sun, 3 Jan 2021 13:52:28 +0100
Added copyright notice, and various updates
Diffstat:
70 files changed, 3254 insertions(+), 3016 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -1,5 +1,3 @@
-doc/datasheets/
-misc/6502_monitor/__pycache__/
*~
hw/bus_logic/BUS_LOGIC.abs
hw/bus_logic/BUS_LOGIC.doc
@@ -12,10 +10,6 @@ hw/bus_logic/BUS_LOGIC.mx
hw/bus_logic/BUS_LOGIC.pla
hw/bus_logic/BUS_LOGIC.so
hw/bus_logic/BUS_LOGIC.wo
-misc/clock_reset_attiny45/clock_and_reset.bin
-misc/clock_reset_attiny45/clock_and_reset.elf
-misc/clock_reset_attiny45/clock_and_reset.hex
-misc/clock_reset_attiny45/clock_and_reset.map
sw/**/*.bin
sw/**/*.l
sw/**/*.lst
diff --git a/COPYING b/COPYING
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ 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.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 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 <https://www.gnu.org/licenses/>.
+
+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:
+
+ <program> Copyright (C) <year> <name of author>
+ 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
+<https://www.gnu.org/licenses/>.
+
+ 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
+<https://www.gnu.org/licenses/why-not-lgpl.html>.
diff --git a/README.md b/README.md
@@ -0,0 +1,405 @@
+Eris 2010 is homebrew 8-Bit computer in the style of the
+microcomputers of the 1970/80s. Here you find a little description of
+the project including some screenshots and a video, as well as the
+complete source code for the soft- and hardware.
+
+<!--more-->
+
+- [Overview](#overview) ⟸ General information. Continue reading.
+- [User Manual](#user-manual) ⟸ Got an Eris 2010? Get it running!
+- [Developer Documentation](#developer-documentation) ⟸ The gory details.
+- [Main Repository](https://git.frombelow.net/eris2010/files.html)
+ ([Clone](https://git.frombelow.net/eris2010.git))
+ **⟸ Eris 2010 source code. It's free!**
+- [Contrib Repository](https://git.frombelow.net/eris2010_contrib/files.html)
+ ([Clone](https://git.frombelow.net/eris2010_contrib.git))
+ **⟸ Ports of third party software.**
+
+----------------------------------------------------------------
+
+# Overview
+
+## Hardware
+
+Eris 2010 is build around the WDC 65C02 MPU. This is a slightly
+updated version of the legendary MOS 6502 used for example in the
+Commodore VIC20 and the Apple I and ][. The other main components are
+32 KB (parallel) RAM and 8 KB EEPROM. While the old home computers
+from the 1970/80s came with a keyboard and connected to a TV or
+monitor, this design provides a serial communication
+interface. Therefore it is more similar to modern microcontrollers or
+very early home computers like the Altair 8800 than to later computers
+of the home computer era like the Commodore 64 or the Apple ][. The
+serial interface is provided by a 6551 ACAI. This chip has been used
+in computers from the era like the Commodore PET and the Commodore
+Plus/4 to connect a modem. In the Eris 2010, the 6551 ACAI connects to
+a (modern) USB2Serial adapter. This allows to use a standard laptop or
+desktop computer as a terminal for the 8-bit machine.
+
+The second auxiliary chip is a 6522 VIA. Among other things, this chip
+provides a bunch of GPIO lines. A few of them are used to connect an
+SD card reader, providing a mass storage device to the Eris 2010. The
+remaining lines are freely programmable I/O ports. SD cards are
+written in a very simple, proprietary data format. When the computer
+is powered, the "operating system" in EEPROM presents a list of the
+programs on the SD card and acts as a bootloader. Alternatively,
+programs can be uploaded via the serial interface.
+
+{{< image-thumbnail "images/start_screen.png" "300x" "right">}}
+
+The bus is orchestrated by an ATF16V8 EEPLD. While the first chips
+with programmable logic were developed in the 1970/80s, most computers
+of the era did not use programmable logic, but discrete logic
+chips. Using an EEPLD allows to keep the chip count low, and I wanted
+to play with programmable logic a bit.
+
+Reset logic is based on the good old 555 chip. We find similar
+circuits in home computers as well.
+
+The first version of the computer was build on a breadboard. Once the
+breadboard design was functionally complete and stable at 4 Mhz, the
+design was fixed on a PCB in through-hole technology. In order to
+allow connection of additional peripheral devices and extensions, two
+interfaces were added: A user port to provide access to the free I/O
+ports, and an expansion port exposing the buses and other internal
+lines. The KiCad design files of the PCB are included in the main
+repository.
+
+## Software
+
+{{< image-thumbnail "images/tic_tac_toe.png" "300x" "right">}}
+
+The main repository contains the game of Tic-Tac-Toe (no fan of
+WarGames should go without one), the famous 10 PRINT program (check
+out [this book](https://10print.org/ "10 PRINT") on the cultural
+significance of 10 PRINT if you do not know it already), and some test
+programs. The contrib repository contains additional third party
+software ported to Eris 2010. Besides Wozmon, Steve Wozniak's
+"operating system" for the Apple I, this repository contains a port of
+Microchess, the first commercial game for microcomputers. The contrib
+repository also contains a port of Tiny BASIC, so you can even program
+the system in BASIC, just like in the old days.
+
+All components of the system (except for the third party software in
+contrib) are free soft- and hardware. You can find all source code in
+the repositories linked at the top of this page.
+
+----------------------------------------------------------------
+
+# User Manual
+
+So you are one of the lucky few who got a pre-assembled Eris 2010
+computer including an SD card with some software!
+
+## Getting started
+
+Insert the SD card that comes with your Eris 2010 into the SD card
+reader. The standard way to communication with this 8 bit computer is
+via the USB2Serial adapter. For this connect your Eris 2010 via a Mini
+USB cable to your computer. On your desktop or laptop computer, you
+need a terminal program.
+
+### Linux
+
+Run a terminal program like tio. tio should be available in the
+package store of your distribution. If not try minicom or picocom. The
+interface is probably available as `/dev/ttyUSB0`. Connect to Eris 2010
+by
+
+`tio -b 19200 /dev/ttyUSB0`
+
+Press a key or the reset button on Eris 2010. If this does not work,
+try the command above with `/dev/ttyUSB1`, `/dev/ttyUSB2`, ...
+
+### Windows
+
+{{< image-thumbnail "images/microchess.png" "300x" "right">}}
+
+When you connect Eris 2010 to your desktop or laptop computer, you
+first have to install the device driver for the USB2Serial
+connector. You can download it from
+https://www.ftdichip.com/Drivers/CDM/CDM21228_Setup.zip. Now connect
+Eris2010 to a USB port. Next, you have install a terminal program. For
+Windows users, a common terminal program is putty. You can download it
+from https://www.putty.org/. After installation, there is a folder
+PuTTY in your start menu. From this folder, choose PuTTY.
+
+When putty starts, click the radio button "Serial". Set "Serial line"
+to "COM3", and "Speed" to "19200". Click "Open". In the terminal
+windows, type any character or push the reset button on Eris 2010. You
+should now be connected to the computer. If this does not work, try
+"COM1", "COM2", ... for "Serial Line". Note that some programs
+(e.g. 10print will not work, because putty does not support Unicode).
+
+## Running programs
+
+{{< image-thumbnail "images/Tiny_BASIC.png" "300x" "right">}}
+
+You should now see a choice of programs. Typing a number starts the
+corresponding program. The two most interesting programs are:
+
+### Tic-Tac-Toe
+
+This should be self explanatory.
+
+### Microchess
+
+Press 'c' to set up a new board. Enter moves by giving the field
+numbers followed by enter, e.g. '6444'<Enter>. Push 'p' for the next
+move of the computer. You can switch the board with 'e'. 'q' quits the
+game.
+
+Microchess is part of the contrib repository.
+
+### Tiny BASIC
+
+All input must be in caps!
+
+Tiny BASIC is part of the contrib repository.
+
+----------------------------------------------------------------
+
+# Developer Documentation
+
+## Repository Directory Structure
+
+- `doc/` - Documentation
+- `hw/` - Hardware description (pcb not complete yet)
+- `tools/` - PC programs to upload programs and write SD card
+- `roms/` - ROM images. See roms/ROMS.txt for a description.
+- `sw/` - "Userland" software to be loaded into RAM by a suitable ROM. See
+ sw/SW.txt for a description.
+
+## Main Components
+
+{{< image-thumbnail "images/eris2010_schema.png" "300x" "right">}}
+
+The computer is based on a 65C02 running at 4 Mhz with 32K RAM
+(AS6C62256-55PCN) and 8 K EEPROM (AT28C64B-15PU). A W65C51N ACAI
+provides a serial communication interface. A VIA 65C22 provides a GPIO
+interface. 4 of the lines connected an SD card reader via SPI. Bus
+logic is provided by a ATF16V8B EEPLD. The reset logic is based on a
+NE555 in a monostable configuration with a little help (an inverter on
+the AFT16V8B).
+
+The hardware design is rather straightforward; see
+`hw/pcb/eris2010/eris210.sch` for schematics (this is a KiCad file). The
+bus logic is documented in `hw/bus_logic/bus_logic.pld`. The only tricky
+part was getting RAM access synced with the CPU clock.
+
+## Memory Map
+
+{{< image-thumbnail "images/bus_logic.png" "300x" "right">}}
+
+Lowest 32K are RAM, highest 8K are ROM. Up to 4 I/O devices (ACAI or
+VIA) can be accessed at addresses below the ROM. One ACAI and one VIA
+are required to operate the computer, because they provide a serial
+interface (ACIA) and an interface to the SD card reader (VIA). Two
+more devices can be connected via the Extension Port. The third I/O
+device is active when `io_select5` is high and `io_select4` low. The
+fourth I/O device is active when `io_select5` is low and `io_select4` is
+high. See `hw/bus_logic/BUS_LOGIC.PLD` for details.
+
+## Reset logic
+
+{{< image-thumbnail "images/reset_circuit.png" "300x" "left">}}
+
+Resets can be triggered in two ways: A reset button is connected to an
+NE555 in a monostable configuration. Alternatively, a reset can be
+triggered by setting DTR of the serial interface to high. The latter
+is used to automatically trigger a reset before program upload. This
+has the disadvantage that the computer operates only when a terminal
+is connected. Therefore a hardware switch allows to disconnect DTR
+from the reset line, allowing the computer to run even if no terminal
+is connected. In this configuration resets for program upload have to
+be triggered manually. See also section Boot Sequence.
+
+{{< image-thumbnail "images/power_circuit.png" "300x" "right">}}
+
+This implies that the computer will remain in reset state until a
+serial terminal is opened if DTR is connected. In order to avoid this,
+e.g. because you want to run a program while no serial interface is
+attached, you can either configure your serial interface to keep DTR
+low at all times (Linux: `stty -F <serial interface> -hup`) or you
+disconnect the DTR line of the USB2Serial converter. The PCB provides
+a switch "DTR Reset" for this.
+
+Note that the reset line of the 65C02 is active on low. We use the
+AFT16V8B - which mainly provides the bus logic - as an inverter.
+
+## Booting
+
+The "standard" ROM is `roms/os/os.bin`. This ROM includes the standard
+library (for serial communication, accessing SD card, RNG, ...). It
+provides two methods to load a program:
+
+- Via serial line
+
+After a reset, the ROM listens for a serial data transmission at 19200
+BPS 8N1. The first byte is the number of half-blocks of 256 bit to be
+loaded. (Block size is 512 bit, because this is the block size of SD
+cards.) The number of half-blocks is followed by the data. Data is
+stored at $0200. Once the upload is completed. The upload program
+returns a two-byte checksum. The first byte is the sum of all bytes
+transmitted mod 256. The second byte is the xor of all bytes
+transmitted. The upload program than starts executing the loaded
+program at $0200.
+
+On the PC, use `tools/boot.py` for upload.
+
+{{< image-thumbnail "images/wozmon.png" "300x" "right">}}
+
+- From SD Card
+
+The boot ROM includes `sw/load_from_card/load_from_card.asm`, the
+program to load programs from SD card. This program is executed when
+no program is transmitted via serial line.
+
+SD cards can store up to 10 programs. The filesystem format is as
+follows:
+
+Block $00000000 is loaded on initialization. The first byte is the
+version of the filesystem. It should by $00.
+
+The storage space for the first program starts at $00000001. The
+storage space for the second program starts at $00010001, for the
+third program at $00020001, ... Thus, each program has a total of
+$ffff blocks of 512 bytes each available. The first block of each
+program is the program header. It contains the number of blocks to be
+loaded, and the app name, a null-terminated string. The actual program
+code starts with the next block. The program is loaded to address
+$0200 and executed.
+
+On the PC, use `tools/gfs.py` to write the SD card.
+
+## Interfaces
+
+{{< image-thumbnail "images/user_port_expansion_port.png" "300x" "right">}}
+Two interface modules are part of the computer system: A serial
+interface connected to a USB2Serial adapter provides character I/O and
+program upload facilities. An SD card reader provides access to SD
+cards with a rudimentary file system. The default operating system
+allows to read and execute programs stored on an SD card.
+
+In addition a user port on the PCB provides access to all GPIO lines
+not occupied by the SD card communication interface, and an expansion
+port provides access to the buses and other internal lines. The
+expansion port carries two select lines for additional peripheral
+devices. See section Memory Map above.
+
+## Interrupts
+
+The I/O interfaces are wired to IRQ. NMI can be triggered by pushing a
+physical button. While user programs can write their own ISRs for
+IRQs, the standard ROM fixes NMI servicing to an ISR that dumps the
+RAM.
+
+## Toolchain
+
+### Hardware
+
+{{< image-thumbnail "images/pcb.png" "300x" "right">}}
+You can find all files related to the hardware design in
+`hw/pcb/eris2010`. The design uses some additional library symbols from
+https://github.com/Alarm-Siren/6502-kicad-library. These additional
+symbols are copyright (c) 2018, Nicholas Parks Young and licensed
+under the GNU LGPL v2.1. The hardware schematics and PCB layout were
+designed in KiCad. FreeRouting (part of LayoutEditor) has been used
+for routing.
+
+An ATF16V8B-15PU EEPLD is used for the bus logic. It is programmed in
+GALasm (https://github.com/daveho/GALasm).
+
+Both the EEPROM and the EEPLD can be burned with minipro
+(https://gitlab.com/DavidGriffith/minipro/).
+
+### Software
+
+All software for Eris 2010 has been assembled with 64tass. The
+Makefiles generate binary code both for Eris 2010 and Symon. Symon
+(https://github.com/sethm/symon), a 6502 emulator written in Java, has
+been used for debugging.
+
+## Standard Library
+
+A standard library is part of the boot ROM located at roms/os/. In
+order to use it, import `roms/os/os.inc` into your program.
+
+The standard library includes a data stack with subroutines and macros
+for local variables and parameter passing. See `roms/os/os.asm` for
+documentation and `sw/stack_test/stack_test.asm` for examples.
+
+The following functionality is provided:
+
+- Serial communication interface
+
+All serial communication is conducted at 19200BPS with 8N1. Various
+input and output functions are provided, including a function that
+initializes the RNG (see LFSR) by counting ticks when waiting for user
+input.
+
+- ANSI Terminal
+
+On top of the serial communication interface, the core ANSI escape
+sequences are supported.
+
+- LFSR
+
+A 16 bit LFSR with maximum period length is provided as PRNG.
+
+- SPI
+
+SPI messages can be send via the VIA.
+
+- SD Card
+
+SD cards can accessed in SPI mode. All addresses are 32 bit, referring
+to 512 bit blocks on the card.
+
+- Data Stack
+
+A data stack in software for local variables and parameter passing. In
+difference to most stacks, the stack grows from bottom to top. The
+recommended memory layout is program code, followed by heap (if
+applicable), followed by stack. You have to manually create and delete
+stack frames with calls to create_stack_frame and delete_stack_frame.
+Pull and push operations do not push actual data on the stack, but
+just move the stack pointer. Local variables on the stack are
+represented by integers and accessed by `lda_LOCAL \<number\>`,
+`sta_LOCAL \<number\>`, and similar macros. Parameters passed by
+`sta_PARAM \<number\>` can be accessed by the callee by `lda_LOCAL
+\<number\>`. Macro `CALL` may be used to call a subroutine with
+parameters. Refer to `sw/stack_test/stack_test.bin` and
+`sw/load_from_card/load_from_card.asm` for examples.
+
+## Tools
+
+### gfs.py
+
+Program for formatting, listing, and writing SD card in a format
+readable by Eris 2010.
+
+### boot.py
+
+Script to upload programs via serial line.
+
+### reset.py
+
+Triggers a reset by toggling DTR.
+
+## Copyright and License
+
+Copyright © 2021 Gerd Beuster <gerd@frombelow.net>
+
+This project is free soft- and hardware: you can 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 project is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this project. If not, see <http://www.gnu.org/licenses/>.
diff --git a/README.txt b/README.txt
@@ -1 +0,0 @@
-See doc/Documentation.txt
diff --git a/doc/Documentation.txt b/doc/Documentation.txt
@@ -1,197 +0,0 @@
-* Eris 2010 - An 8-Bit Computer
-
-** Main Components
-
-The computer is based on a 65C02 running at 1 Mhz with 32K RAM
-(AS6C62256-55PCN) and 8 K EEPROM (AT28C64B-15PU). A W65C51N ACAI
-provides a serial communication interface. Bus logic is provided by a
-ATF16V8B EEPLD. The reset logic is based on a NE555 in a monostable
-configuration with a little help (an inverter on the AFT16V8B).
-
-See hw/bom.ods for details of the components. (This list also contains
-components not used in the current design.)
-
-** Reset logic
-
-Resets can be triggered in two ways: A reset button is connected to an
-NE555 in a monostable configuration. Alternatively, a reset can be
-triggered by setting DTR of the serial interface to high. The latter
-is used for program upload into RAM. See section Boot Sequence.
-
-This implies that the computer will remain in reset state until a
-serial terminal is opened if DTR is connected. In order to avoid this,
-e.g. because you want to run a program while no serial interface is
-attached, you can either configure your serial interface to keep DTR
-low at all times (stty -F <serial interface> -hup) or you do not
-connect the DTR line of the USB2Serial converter.
-
-Note that the reset line of the 65C02 is active on low. We use the
-AFT16V8B - which mainly provides the bus logic - as an inverter.
-
-** Memory Map
-
-Lowest 32K are RAM, highest 8K are ROM. Up to 4 I/O devices (ACAI or
-VIA) can be accessed at addresses below the ROM. One ACAI and one VIA
-are required to operate the computer, because they provide a serial
-interface (ACIA) and an interface to the SD card reader (VIA). If a
-third I/O device is connected, it can be selected by setting i3 to
-high. If a fourth I/O device is connected, i4 distinguishes between
-device 3 and 4. See hw/bus_logic/BUS_LOGIC.PLD for details.
-
-** Booting
-
-The "standard" ROM is roms/boot/boot.bin. This ROM includes the
-standard library (for serial communication, accessing SD card, RNG,
-...). It provides two methods to load a program:
-
-- Via serial line
-
-After a reset, the ROM listens for a serial data transmission at 19200
-BPS 8N1. The first byte is the number of half-blocks of 256 bit to be
-loaded. (Block size is 512 bit, because this is the block size of SD
-cards.) The number of half-blocks is followed by the data. Data is
-stored at $0200. Once the upload is completed. The upload program
-returns a two-byte checksum. The first byte is the sum of all bytes
-transmitted mod 256. The second byte is the xor of all bytes
-transmitted. The upload program than starts executing the loaded
-program at $0200.
-
-On the PC, use tools/boot.py for upload.
-
-- From SD Card
-
-The boot ROM includes sw/load_from_card/load_from_card.bin, the
-program to load programs from SD card. This program is executed when
-no program is transmitted via serial line.
-
-SD cards can store up to 10 programs. The filesystem format is as
-follows:
-
-Block $00000000 is loaded. The first byte is the version of the
-filesystem. It should by $00.
-
-The storage space for the first program starts at $00000001. The
-storage space for the second program starts at $00010001, for the
-third program at $00020001, ... Thus, each program has a total of
-$ffff blocks of 512 bytes each available. The first block of each
-program is the program header. It contains the number of blocks to be
-loaded, and the app name, a null-terminated string. The actual program
-code starts with the next block. The program is loaded to address
-$0200 and executed.
-
-On the PC, use tools/gfs.py to write the SD card.
-
-** Directory Structure
-
-- doc/ - Documentation
-- hw/ - Hardware description (pcb not complete yet)
-- tools/ - PC programs to upload programs and write SD card
-- misc/ - Miscellaneous files - See section Misc for a description
-- roms/ - ROM images. See roms/ROMS.txt for a description.
-- sw/ - "Userland" software to be loaded into RAM by a suitable ROM. See
- sw/SW.txt for a description.
-
-** Standard Library
-
-A standard library is part of the boot ROM located at roms/boot/. In
-order to use it, import roms/boot/boot.l and roms/boot/boot_macros.inc
-into your program.
-
-The standard library includes a data stack with subroutines and macros
-for local variables and parameter passing. See roms/boot/boot.asm for
-documentation and sw/stack_test/stack_test.asm for examples.
-
-The following functionality is provided:
-
-*** Serial communication interface
-
-All serial communication is conduced at 19200BPS with 8N1. Various
-input and output functions are provided, including a function that
-initializes the RNG (see LFSR) by counting ticks until user input.
-
-*** LFSR
-
-A 16 bit LFSR with maximum period length is provided as PRNG.
-
-*** SPI
-
-SPI message can send via the VIA.
-
-*** SD Card
-
-SD cards can accessed in SPI mode. All addresses are 32 bit, referring
-to 512 bit blocks on the card.
-
-*** Data Stack
-
-A data stack in software for local variables and parameter passing. In
-difference to most stacks, the stack grows from bottom to top. The
-recommended memory layout is program code, followed by heap (if
-applicable), followed by stack. You have to manually create and delete
-stack frames with calls to create_stack_frame and delete_stack_frame.
-Pull and push operations do not push actual data on the stack, but
-just move the stack pointer. Local variables on the stack are
-represented by integers and accessed by lda_LOCAL <number>, sta_LOCAL
-<number>, and similar macros. Parameters passed by sta_PARAM <number>
-can be accessed by the callee by lda_LOCAL <number>. Macro CALL may be
-used to call a subroutine with parameters. Refer to
-sw/stack_test/stack_test.bin and sw/load_from_card/load_from_card.bin
-for examples.
-
-** Tools
-
-*** gfs.py
-
-Program for formatting, listing, and writing SD card in a format
-readable by Eris 2010.
-
-*** boot.py
-
-Script to upload programs via serial line.
-
-** Misc
-
-*** 6502_monitor/
-
-A ESP32 monitor for the 65C02. This project has never really been
-completed, because it would require some level shifter between the
-3.3v of the ESP32 and the 5v of the 65C02.
-
-This is a MicroPython script for the ESP32. The pin are connected as
-follows:
-
-- Address Bus (16 pins - only lower 14 connected)
-- Data bus (8 pins)
-- Clock (1 pin)
-- R/W (1 pin)
-
-Note that ESP32 pins 34 to 39 are read only, pins 6 to 11 (SPI) and 34
-& 36 (UART) cannot be used, thus we have 24 pins available. Therefore
-we only connect the lower 14 lines of the address bus. Actually, it
-looks like PIN 10 can be used both as input and output. At least the
-MCU did not crash ...
-
-See http://wilsonminesco.com/6502primer/MysteryPins.html and 65c02
-data sheet on how to connect the pins.
-
-*** clock_reset_attiny45/
-
-Generates clocks at different speeds and provides a reset logic. The
-clocks have been used in the early implementation phase, before
-replacing it with a 1 Mhz packaged crystal. The reset logic was
-replaced by a NE555 configured as a monostable multivibrator.
-
-** TODOs
-
-*** Reset Logic
-
-- Add switch or jumper to enable/disable connection between DTR and reset
-- Change indicator LED to off-on-reset
-
-*** Write library functions
-
-- Multiplication and division
-- CRC generator
-
-*** Design PCB
-
diff --git a/hw/bus_logic/BUS_LOGIC.PLD b/hw/bus_logic/BUS_LOGIC.PLD
@@ -9,7 +9,11 @@ Assembly None;
Device g16v8a;
/* Bus logic for 8 bit computer based on 65C02
- */
+
+Copyright 2021 Gerd Beuster (gerd@frombelow.net). This is free
+soft-/hardware under the GNU GPL v3 license or any later version. See
+COPYING in the root directory for details.
+*/
pin 1 = rwb; /* cpu_RWB */
pin 2 = i0; /* cpu_A15 */
@@ -20,13 +24,14 @@ pin 6 = i4; /* cpu_A11 */
pin 7 = i5; /* cpu_A10 */
pin 8 = reset_in; /* Reset logic output */
pin 9 = phi; /* cpu_PHI2 */
+/* pin 11 = connected to cpu_A9, but not used at the moment */
pin 12 = o0; /* eeprom_#ce */
pin 13 = o1; /* ram_#oe */
pin 14 = o2; /* I/O Devices */
pin 15 = o3; /* ram_#ce */
pin 16 = o4; /* I/O Devices */
pin 17 = o5; /* I/O Devices */
-pin 18 = o6; /* #we */
+pin 18 = o6; /* ram_#we */
pin 19 = o7; /* cpu_RESB */
/* ROM (8k)
@@ -44,9 +49,9 @@ o0 = (!i0 # !i1 # !i2);
oe#, ce#, and we# are qualified by !phi.
*/
-o1 = !phi # !rwb; /* ram_oe# */
-o3 = !phi # i0; /* ram_ce# */
-o6 = !phi # rwb; /* ram_we# */
+o1 = !phi # !rwb; /* ram_#oe */
+o3 = !phi # i0; /* ram_#ce */
+o6 = !phi # rwb; /* ram_#we */
/* In order to drive up to 4 I/O devices from 3 output lines, we
exploit that both the 6522 and the 6551 have two ce lines, of which
@@ -57,19 +62,19 @@ o6 = !phi # rwb; /* ram_we# */
the I/O devices to input addresses.
ce #ce i3 i4
- acia0 : o2 & !o4 & !o5 0 0
- via0 : o4 & !o2 & o5 0 1
- acia1 : o5 & !o4 & !o2 1 0
- via1 : o4 & !o5 & o2 1 1
+ acia1 : o2 & !o4 & !o5 0 0
+ via1 : o4 & !o2 & o5 0 1
+ acia2 : o5 & !o4 & !o2 1 0
+ via2 : o4 & !o5 & o2 1 1
We place the I/O devices right below the ROM,
i.e. i0 i1 i2 == 0b110, resulting in the
following address mapping:
- acia0 : 0b11000........... = 0xc000 .. 0xc7ff
- via0 : 0b11001........... = 0xc800 .. 0xcfff
- acia1 : 0b11010........... = 0xd000 .. 0xd7ff
- via1 : 0b11011........... = 0xd800 .. 0xdfff
+ acia1 : 0b11000........... = 0xc000 .. 0xc7ff
+ via1 : 0b11001........... = 0xc800 .. 0xcfff
+ acia2 : 0b11010........... = 0xd000 .. 0xd7ff
+ via2 : 0b11011........... = 0xd800 .. 0xdfff
*/
diff --git a/misc/6502_monitor/Makefile b/misc/6502_monitor/Makefile
@@ -1,7 +0,0 @@
-all: install run
-
-install:
- wb_ampy -p /dev/ttyUSB0 put monitor.py
- wb_ampy -p /dev/ttyUSB0 reset
-run:
- konsole -e tio /dev/ttyUSB0
diff --git a/misc/6502_monitor/machine.py b/misc/6502_monitor/machine.py
@@ -1,22 +0,0 @@
-# Dummy class to run ESP32 programs on normal PC
-
-import random
-
-class Pin():
-
- OUT = 0
- IN = 1
- PULL_UP = 2
-
- def __init__(self, pin, _direction, _pullup=False):
- pass
-
- def on(self):
- pass
-
- def off(self):
- pass
-
- def value(self):
- return random.choice([0, 1])
-
diff --git a/misc/6502_monitor/monitor.py b/misc/6502_monitor/monitor.py
@@ -1,266 +0,0 @@
-#!/usr/bin/env python3
-"""Monitor and control 6502 busses
-
-This program controls the busses (and other required signals like PHI2)
-of a 6502 from an ESP32.
-
-IMPORTANT:
-
----------------------------------------------------------------
-Remember that ESP32 operates on 3.3v while 6502 operates on 5V!
----------------------------------------------------------------
-
-"""
-
-import sys
-import machine
-import time
-#import pdb
-
-# Connect 6502 address bus (mostly) to left side of ESP32
-# Can I really use GPIO10?
-# A14 & A15 not connect due to lack of GPIO pins
-A_Pins = [36, 39, 34, 35, 32, 33, 25, 26, 27, 14, 12, 13, 10, 15]
-D_Pins = [2, 0, 4, 16, 17, 5, 18, 19]
-RW_Pin = 21
-PHI2_Pin = 22
-RST_Pin = 23
-
-# Max memory size is 2**14, because due to a lack of GPIO pins, only
-# A0..A13 are connected
-MEM_SIZE = 2**14
-mem = bytearray(b'\xea' * MEM_SIZE)
-# By default, we emulate memory. This allows to run a 6502 with an ESP32
-# as the only "peripheral" device. If emulate_memory is set to False,
-# this monitor can be used to monitor the bus lines between a 6502
-# and an actual memory chip.
-emulate_memory = True
-
-# Thin wrapper taking care of switching I/O mode of Pins.
-class Pin:
- def __init__(self, pin):
- self.pin_number = pin
- self.mode = None
- self.set_mode(machine.Pin.IN)
-
- def set_mode(self, mode):
- if self.mode != mode:
- self.mode = mode
- if self.mode == machine.Pin.IN:
- self.pin = machine.Pin(self.pin_number, self.mode,
- machine.Pin.PULL_UP)
- else:
- self.pin = machine.Pin(self.pin_number, self.mode)
- # time.sleep(.01)
-
- def low(self):
- self.set_mode(machine.Pin.OUT)
- self.pin.off()
-
- def high(self):
- self.set_mode(machine.Pin.OUT)
- self.pin.on()
-
- def set(self, value):
- self.set_mode(machine.Pin.OUT)
- if value == 0:
- self.pin.off()
- else:
- self.pin.on()
-
- def read(self):
- self.set_mode(machine.Pin.IN)
- return self.pin.value()
-
-def read_address_bus():
- rw = RW.read()
- data = 0
- for p in reversed(A):
- data = ((data << 1) + p.read())
- return (rw, data)
-
-def read_data_bus():
- data = 0
- for p in reversed(D):
- data = ((data << 1) + p.read())
- return data
-
-def write_data_bus(data):
- for p in D:
- p.set(data & 1)
- data = data >> 1
-
-def pp_mem(start, end):
- """Pretty print memory content"""
- print_flush(' 0 1 2 3 4 5 6 7 8 9 A B C D E F')
- for chunk in range(start//0x100*0x100, end+1, 16):
- l = "{:04X}: ".format(chunk)
- l += " ".join(["{:02X}".format(x)
- for x in mem[chunk:min(chunk+16, end+1)]])
- print_flush(l)
-
-def update_buses_and_print_state(end='\n'):
- (rw, address) = read_address_bus()
- # Get/set data bus and print command prompt
- if rw:
- format_string = "{address:04X} > {data:02X}"
- else:
- format_string = "{address:04X} < {data:02X}"
- if rw and emulate_memory:
- # CPU wants to read memory, and we
- # emulate the memory. Therefore we
- # answer this request
- data = mem[address]
- write_data_bus(data)
- else:
- # This is either a memory write operation or
- # a memory read operation answered by the
- # actual memory, not our emulation.
- # Therefore we read the data from the bus.
- data = read_data_bus()
- mem[address] = data
- print_flush(format_string.format(address=address, data=data), end=end)
-
-def tick():
- # Execute on CPU cycle with a clock frequency of 500 Hz or
- # less.
- PHI2.low()
- #time.sleep(.001)
- PHI2.high()
- #time.sleep(.001)
-
-def run(cycles):
- # Run for a number of cycles. This function is calle from rep.
- # Since the buses' states are printed at the begin of each
- # loop, it is not printed here for the final cycle.
- assert(cycles > 0)
- for _ in range(cycles-1):
- tick()
- update_buses_and_print_state()
- tick()
-
-def warn_if_memory_not_emulated():
- if not emulate_memory:
- print_flush('WARNING: Memory not emulated; monitoring buses only.')
-
-
-def print_help_msg():
- print_flush("""\
-Commands:
-r: Reset
-<n>: Run for n clock cycles (empty = 1 cycle)
-m <addr> [<data>]: Write to emulated RAM
-d <from> <to>: Dump emulated RAM
-w <filename>: Write emulated RAM to file
-l <filename>: Load emulated RAM from file
-b <filename>: Execute batch file
-n: RAM emulation off: No bus write operations, listen only
-e: RAM emulation on: Write from emulated memory to data bus
-h: This information
-x: Exit
-""")
-
-def print_flush(*args, **kwargs):
- # MicroPython does not support flush=True, therefore
- # we use this little wrapper
- if esp32:
- print(*args, **kwargs)
- else:
- print(*args, **kwargs, flush=True)
-
-def repl(script=[]):
- global mem
- global emulate_memory
- while True:
- update_buses_and_print_state(end='')
- if len(script) == 0:
- print_flush(": ", end='')
- l = ""
- while True:
- c = sys.stdin.read(1)
- if esp32:
- print_flush(c, end='')
- if c == '\n':
- break
- l += c
- else:
- print_flush(": {}".format(script[0]))
- l = script[0]
- script = script[1:]
- # Filter out comments
- comment_start = l.find('#')
- if comment_start != -1:
- l = l[:comment_start]
- # Tokenize input
- l = l.split(" ")
- cmd = l[0]
- args = l[1:]
- # Empty command = run for one clock cycle
- if(len(cmd) == 0):
- cmd = '1'
- if cmd.isdigit():
- # Run for a number of cycles
- run(int(cmd, 16))
- elif cmd == 'r':
- # Reset
- RST.low()
- print_flush('RST low')
- run(2)
- update_buses_and_print_state()
- RST.high()
- print_flush('RST high')
- run(7)
- elif cmd == 'm':
- # Write to emulated RAM
- warn_if_memory_not_emulated()
- addr = int(args[0], 16)
- data = list(map(lambda x: int(x, 16), args[1:]))
- mem[addr:addr+len(data)] = bytearray(data)
- elif cmd == 'd':
- # Dump emulated RAM
- warn_if_memory_not_emulated()
- pp_mem(int(args[0], 16), int(args[1], 16))
- elif cmd == 'w':
- # Write emulated RAM to file
- warn_if_memory_not_emulated()
- with open(args[0], 'wb') as f:
- f.write(mem)
- elif cmd == 'l':
- # Read emulated RAM from file
- warn_if_memory_not_emulated()
- with open(args[0], 'rb') as f:
- mem = bytearray(f.read())
- elif cmd == 'b':
- # Execute batch file
- with open(args[0], 'rt') as f:
- script = f.read().split('\n')
- elif cmd == 'n':
- # Turn RAM emulation off
- emulate_memory = False
- print_flush('Memory emulation off; listening to buses only.')
- elif cmd == 'e':
- # Turn RAM emulation on
- emulate_memory = True
- print_flush('Memory emulation on.')
- elif cmd == 'h':
- print_help_msg()
- elif cmd == 'x':
- # Exit
- sys.exit(0)
- else:
- print_flush('Unknown command')
-
-def m():
- global esp32
- esp32 = (sys.platform == 'esp32')
- global A, D, RW, PHI2, RST
- # Create objects for Pins
- A = list(map(Pin, A_Pins))
- D = list(map(Pin, D_Pins))
- (RW, PHI2, RST) = map(Pin, [RW_Pin, PHI2_Pin, RST_Pin])
- print_flush("** 6502 Monitor **\n")
- print_help_msg()
- repl()
-
-if __name__ == '__main__':
- m()
diff --git a/misc/clock_reset_attiny45/Makefile b/misc/clock_reset_attiny45/Makefile
@@ -1,35 +0,0 @@
-PRG = clock_and_reset
-OBJ = clock_and_reset.c
-AALPATH = /media/gb/m/projects/avr/
-LIBS = -L$(AALPATH)/libaal/ -L$(AALPATH)/libaal/32u4_usb_serial/ -laal_$(MCU_TARGET)_$(CLOCK) -lusb_serial_$(MCU_TARGET)_$(CLOCK)
-DEFS = -std=gnu99 -g -Wall -O2
-
-######################
-# Target Platform #
-######################
-
-#MCU_TARGET = atmega328p
-#MCU_TARGET = arduino_uno
-MCU_TARGET = attiny45
-#MCU_TARGET = arduino_pro_micro
-
-######################
-# Target Clock Speed #
-######################
-
-CLOCK = 1000000L
-#CLOCK = 8000000L
-#CLOCK = 16000000L
-
-######################
-# ISP #
-######################
-
-#PROGRAMMER = arduino # Direct upload to Arduino Uno
-#PROGRAMMER = avrisp # Arduino as ISP
-PROGRAMMER = usbasp # USB ASP Stick
-#PROGRAMMER = avr109 # Direct upload to Arudino ProMicro
-
-###############################################################
-
-include $(AALPATH)/libaal/Makefile.common
diff --git a/misc/clock_reset_attiny45/clock_and_reset.c b/misc/clock_reset_attiny45/clock_and_reset.c
@@ -1,71 +0,0 @@
-#include <libaal.h>
-#include <util/delay.h>
-
-
-// Set rest pin low for this number of iterations if reset is triggered.
-#define RESET_DELAY 0x4F
-
-
-void fast_clock (void){
- SET_MODE_OUTPUT_DIGITAL(CONTACT_5_DDR_REG,
- CONTACT_5_DDR_BIT);
- while(1) {
- CONTACT_5_PORT_REG ^= 0xFF;
- // No delay: 842.1 kHz
- // _delay_us(3); // 516,1 kHz
- _delay_us(37); // 99,38 kHz
- }
-}
-
-void clock_and_reset (void){
- /* usb_init(); */
- /* Configure Pins for reset logic. */
- SET_MODE_INPUT_PULLUP(CONTACT_6_DDR_REG,
- CONTACT_6_DDR_BIT,
- CONTACT_6_PORT_REG,
- CONTACT_6_PORT_BIT);
- SET_MODE_OUTPUT_DIGITAL(CONTACT_7_DDR_REG,
- CONTACT_7_DDR_BIT);
- /* Configure pins for clock signal. */
- SET_MODE_OUTPUT_DIGITAL(CONTACT_2_DDR_REG,
- CONTACT_2_DDR_BIT);
- SET_MODE_OUTPUT_DIGITAL(CONTACT_3_DDR_REG,
- CONTACT_3_DDR_BIT);
- SET_MODE_OUTPUT_DIGITAL(CONTACT_5_DDR_REG,
- CONTACT_5_DDR_BIT);
- uint8_t clock_counter = 0;
- uint16_t reset_counter = RESET_DELAY; // power on reset
- while (1){
- clock_counter++;
- // Generate clock
- DIGITAL_WRITE(CONTACT_2_PORT_REG, CONTACT_2_PORT_BIT,
- (clock_counter & 0x01) >> 0);
- DIGITAL_WRITE(CONTACT_3_PORT_REG, CONTACT_3_PORT_BIT,
- (clock_counter & 0x02) >> 1);
- DIGITAL_WRITE(CONTACT_5_PORT_REG, CONTACT_5_PORT_BIT,
- (clock_counter & 0x04) >> 2);
- // Has a reset been triggered?
- if (!DIGITAL_READ(CONTACT_6_PIN_REG,
- CONTACT_6_PIN_BIT)) {
- reset_counter = 0x4F;
- }
- // If a reset has been triggered, keep the reset pin
- // low for some time
- if (reset_counter > 0) {
- DIGITAL_WRITE(CONTACT_7_PORT_REG, CONTACT_7_PORT_BIT,
- 0);
- reset_counter--;
- }
- else {
- DIGITAL_WRITE(CONTACT_7_PORT_REG, CONTACT_7_PORT_BIT,
- 1);
- }
- _delay_ms(20);
- }
-}
-
-int main (void){
- fast_clock(); // Can't get fast PWM working. Using this instead...
- // clock_and_reset();
- return 1; // dummy
-}
diff --git a/misc/write_default_files.sh b/misc/write_default_files.sh
@@ -1,21 +0,0 @@
-#!/bin/bash
-GFS_CMD="sudo ../tools/gfs.py"
-SW=../sw
-
-for i in 10print ansi_test serial_line_echo stack_test ttt via_test
-do
- pushd ../sw/$i
- make clean all
- popd
-done
-pushd ../sw/ttt
-make test
-popd
-
-${GFS_CMD} format
-${GFS_CMD} store 0 "10print" ${SW}/10print/10print.bin
-${GFS_CMD} store 1 "Tic Tac Toe" ${SW}/ttt/ttt.bin
-${GFS_CMD} store 2 "Tic Tac Toe (Computer vs. Computer)" ${SW}/ttt/ttt_test.bin
-${GFS_CMD} store 3 "Stack Test" ${SW}/stack_test/stack_test.bin
-${GFS_CMD} store 4 "Ansi Test" ${SW}/ansi_test/ansi_test.bin
-${GFS_CMD} ls
diff --git a/roms/Makefile.common b/roms/Makefile.common
@@ -5,7 +5,7 @@ LABELS=--labels-root=os -l $(TARGET).l \
--labels-root=ds -l ds.l \
--labels-root=sd -l sd.l \
--labels-root=lfsr -l lfsr.l \
- --labels-root=spi -l spi.l \
+ --labels-root=via -l via.l \
--labels-root=term -l term.l
LABELS_SYMON=--labels-root=os -l $(TARGET)_symon.l \
@@ -13,20 +13,21 @@ LABELS_SYMON=--labels-root=os -l $(TARGET)_symon.l \
--labels-root=ds -l ds_symon.l \
--labels-root=sd -l sd_symon.l \
--labels-root=lfsr -l lfsr_symon.l \
- --labels-root=spi -l spi_symon.l \
+ --labels-root=via -l via_symon.l \
--labels-root=term -l term_symon.l
+VERSION="$(shell git describe --tags)"
all: $(TARGET).bin $(TARGET)_symon.bin
%.bin: %.asm
- 64tass $(CFLAGS) -DSYMON=false $(LABELS) -L $(TARGET).lst "$<" -o "$@"
+ 64tass $(CFLAGS) $(DEFS) -DVERSION="'$(VERSION)'" -DSYMON=false $(LABELS) -L $(TARGET).lst "$<" -o "$@"
%_symon.bin: %.asm
- 64tass $(CFLAGS) -DSYMON=true $(LABELS_SYMON) -L $(TARGET)_symon.lst "$<" -o "$@"
+ 64tass $(CFLAGS) $(DEFS) -DVERSION="'$(VERSION)'" -DSYMON=true $(LABELS_SYMON) -L $(TARGET)_symon.lst "$<" -o "$@"
flash: $(TARGET).bin
- sudo ~/opt/minipro-0.3/minipro -p AT28C64B -w $<
+ sudo ~/opt/minipro-0.5/minipro -p AT28C64B -w $<
clean:
rm -f *.bin *.l *.lst
diff --git a/roms/ROMS.txt b/roms/ROMS.txt
@@ -1,20 +1,5 @@
-boot/
+os/
This is the main "operating system" ROM. It provides some library
functions and most notably a mechanism to load software from the
serial interface into the RAM and execute it.
-
-nop/
-
-Just a sequence of NOP instructions. Used to test if the CPU
-interacts with the EEPROM.
-
-serial_char_out/
-
-Outputs "A" to the serial interface in an endless loop. Used to test
-if the CPU interacts with the ACAI W65C22.
-
-simple_loop/
-
-Just endlessly jumping between memory locations. Used to test if the
-CPU interacts with the EEPROM.
diff --git a/roms/boot/Makefile b/roms/boot/Makefile
@@ -1,3 +0,0 @@
-TARGET=boot
-
-include ../Makefile.common
diff --git a/roms/boot/boot.asm b/roms/boot/boot.asm
@@ -1,193 +0,0 @@
- DEBUG = false
- ;; DEBUG = true
-
- CLOCK_SPEED = 4 ;4 Mhz Clock
-
-.if SYMON
- rom_start = $c000
-.else
- rom_start = $e000
-.endif
-
-.include "boot.inc"
-
- * = $00
-.dsection zero_page
-.cerror * > $ff, "Zero page exhausted"
- * = rom_start
-.dsection rom
-.cerror * > $ffff, "ROM exhausted"
-
-os .namespace
-ram_end = $7fff
-.endn
-
-;;; The actual (hardware) interrupt vectors are located in the ROM
-;;; at $ fffa/fffb (nmi) and $fffe/$ffff (irq). Since we want to be
-;;; able to handle interrupts in RAM, the ROM isr routines jump to the
-;;; addresses given here. After a reset, this RAM address is set to
-;;; default_irq_handler.
-.namespace os
-nmi_vector = os.ram_end - $3
-irq_vector = os.ram_end - $1
-.endn
-
- .section rom
-;;; ----------------------------------------------------------
-;;; RESET
-;;;
-;;; Initialize serial interface, read code from serial
-;;; interface into RAM, execute it.
-;;; ----------------------------------------------------------
-
-boot: .block
- sei
- #mem.STORE_WORD os.default_irq_handler, os.irq_vector
- #mem.STORE_WORD os.default_nmi_handler, os.nmi_vector
- cld
- cli
- jsr io.init_acia
- #ds.INIT_STACK os.ram_end-$206+$1 ;Minimal stack size to access SD card
- jsr spi.init_via
- jsr check_for_program_download
- bcs no_program_download
- jmp download_program
-no_program_download:
- ;; No program download?
- ;; Start embedded program.
- jmp default_program
- .bend
-
-;;; If we receive a byte right after a reset, this
-;;; triggers a program download via serial line.
-;;; The value of the byte is the number of $100 byte
-;;; blocks to receive.
-check_for_program_download: .block
- #io.PRINTSNL "READY!"
- ldy #$20 ; Outer loop counter
- ldx #$00 ; Inner counter
-loop:
- ;; Try to get number of 0x100 byte blocks to be read
- jsr io.getc_nonblocking
- bcc got_byte
- dex
- bne loop
- dey
- bne loop
- #io.PRINTSNL "Nothing to download."
- sec
-got_byte:
- rts
- .bend
-
-download_program: .block
- ;; Program is loaded to this memory address
- start_addr = $0200
- ;; Location of temporary variables
- addr_pointer = $12 ; addr_pointer+1 stores MSB
- msb_last_addr = $14
- add_checksum = $15
- xor_checksum = $16
- ;; We expect the number of $100 blocks to be
- ;; downloaded in A.
- ;; The msb of the last block to be written is the msb
- ;; of the start address plus the number of blocks.
- clc
- adc #>start_addr
- sta msb_last_addr
- ;; Read n * 0x100 bytes from acia
- lda #<start_addr
- sta addr_pointer
- lda #>start_addr
- sta addr_pointer+1
- ;; We calculate a two checksums over the data:
- ;; The first byte is the addition of all bytes
- ;; received. The second byte is the xor of all
- ;; bytes received.
- lda #$00
- sta add_checksum
- sta xor_checksum
-transmit_block:
- jsr io.getc
- ;; Store data
- ldy #$00
- sta (addr_pointer), y
- ;; Update checksums
- pha
- clc
- adc add_checksum
- sta add_checksum
- pla
- eor xor_checksum
- sta xor_checksum
- ;; Update LSB of target address and check if
- ;; transmission of block is completed.
- inc addr_pointer
- bne transmit_block
- ;; Transmission of one block completed.
- ;; If we are not done, increase
- ;; MSB of target address and continue
- inc addr_pointer+1
- lda addr_pointer+1
- cmp msb_last_addr
- bne transmit_block
- ;; Transmission completed.
- ;; Transmit result of checksum calculation
- lda add_checksum
- jsr io.putc
- lda xor_checksum
- jsr io.putc
- ;; Start program
- jmp $0200
- .bend
-
-
-.include "io.asm"
-.include "ds.asm"
-.include "lfsr.asm"
-.include "spi.asm"
-.include "sd.asm"
-.include "term.asm"
-
-.namespace os
-.section zero_page
-rom_zero_page_end:
-.send zero_page
-.endn
-
-;;; Default IRQ handler. Unless the user program changes
-;;; the irq_vector, IRQs are handled here.
-.namespace os
-default_irq_handler:
-.endn
- rti
-
-derefer_ram_irq:
- ;; Jump to the address given in the IRQ vector
- ;; in RAM
- jmp (os.irq_vector)
-
-;;; Default NMI handler. Unless the user program changes
-;;; the nmi_vector, NMIs are handled here.
-.namespace os
-default_nmi_handler:
-.endn
- rti
-
-derefer_ram_nmi:
- ;; Jump to the address given in the NMI vector
- ;; in RAM
- jmp (os.nmi_vector)
-
-default_program:
-;; .include "../../sw/10print/10print.asm"
-;; .include "../../sw/ttt/ttt.asm"
-.include "../../sw/load_from_card/load_from_card.asm"
-
-;;; Vectors
- .fill $fffa-*, $ff
- .word derefer_ram_nmi ; nmi
- .word boot ; reset
- .word derefer_ram_irq ; irq
-
-.send rom
diff --git a/roms/boot/boot.inc b/roms/boot/boot.inc
@@ -1,133 +0,0 @@
-;;; -*- asm -*-
-
-;;; This file may be included from boot.asm or from
-;;; some userland program.
-;;; We use the definition of CLOCK_SPPED in boot.asm to
-;;; check if we are compiling in the context of a userland
-;;; program.
-.weak
- CLOCK_SPEED = 0
-.endweak
-.if CLOCK_SPEED == 0
- .if SYMON
- filename_addition = "_symon"
- start_address = $0300
- .else
- filename_addition = ""
- start_address = $0200
- .endif
- os .namespace
- .include "boot" .. filename_addition .. ".l"
- .endn
- io .namespace
- .include "io" .. filename_addition .. ".l"
- .endn
- ds .namespace
- .include "ds" .. filename_addition .. ".l"
- .endn
- sd .namespace
- .include "sd" .. filename_addition .. ".l"
- .endn
- lfsr .namespace
- .include "lfsr" .. filename_addition .. ".l"
- .endn
- spi .namespace
- .include "spi" .. filename_addition .. ".l"
- .endn
- term .namespace
- .include "term" .. filename_addition .. ".l"
- .endn
- BOOT_EMBEDDED = false
- ;; Set start addresses for program and
- ;; zero page variables
- * = os.rom_zero_page_end
- .dsection zero_page
- * = start_address
-.else
- BOOT_EMBEDDED = true
-.endif
-
-.include "io.inc"
-.include "ds.inc"
-.include "sd.inc"
-.include "term.inc"
-
-;;; -----------------------------------
-;;;
-;;; Miscellaneous utility functions
-;;;
-;;; -----------------------------------
-
-mem .namespace
-
-SET_BITS .macro
- lda \1
- ora \2
- sta \1
- .endm
-
-CLEAR_BITS .macro
- lda \1
- and ~\2
- sta \1
- .endm
-
-TOGGLE_BITS .macro
- lda \1
- eor \2
- sta \1
- .endm
-
-;;; STORE_WORD(word,dst)
-;;; Store word at dst
-;;; Input:
-;;; word
-;;; Output:
-;;; -
-;;; Changes:
-;;; a, dst
-STORE_WORD .macro
- lda #<\1
- sta \2
- lda #>\1
- sta \2+1
- .endm
-
-
-COPY_WORD_IMMEDIATE: .macro
- lda #<\1
- sta \2
- lda #>\1
- sta \2+1
- .endm
-
- ;; Copy macro for absolute and indirect addressing modes
-COPY_WORD_ABSOLUTE_INDIRECT: .macro
- lda \1
- sta \2
- ldy #$01
- lda \1,y
- sta \2,y
- .endm
-
-SUB_WORD: .macro
- lda \1
- sec
- sbc #<\2
- sta \1
- lda \1+1
- sbc #>\2
- sta \1+1
- .endm
-
-ADD_WORD: .macro
- lda \1
- clc
- adc #<\2
- sta \1
- lda \1+1
- adc #>\2
- sta \1+1
- .endm
-
-.endn
diff --git a/roms/boot/ds.asm b/roms/boot/ds.asm
@@ -1,76 +0,0 @@
-;;; ----------------------------------------------------------
-;;;
-;;; STACK
-;;;
-;;; ----------------------------------------------------------
-
-;;; Software implementation of data stack. In difference to typical
-;;; stack implementations, this stack grows from bottom to top, because
-;;; we want to access eleements using indirected indexed access with
-;;; the stack frame pointer as base. Therefore the stack should
-;;; be initialized with the first unoccupied RAM address after the
-;;; program code.
-;;;
-;;; PULL and PUSH do not move actual stack data, they just shift
-;;; the stack pointer.
-;;;
-;;; Local variables on the data stack are supported by macro
-;;; VAR. VAR i refers to the ith byte on the stack. PARAM allows
-;;; to pass variables to a callee. PARAM i of the caller becomes
-;;; VAR i of the callee. The callee should reserve stack space
-;;; for both its parameters and local variables.
-;;;
-;;; Stack frames used for these calls are created/destroyed by
-;;; subroutines create_stack_frame and delete_stack_frame.
-;;;
-;;; CALL is like jsr with parameter passing. There addressing
-;;; modes are supported: immediate, zero-page, and local variables.
-;;; For immediate and zero page, the third parameter must be lda.
-;;; For local variables, the third parameter must be lda_LOCAL.
-;;; See sw/stack_test/stack_test.asm for examles.
-
-ds .namespace
-
-.section zero_page
- ;; Stack
-ptr: .word ?
-frame_ptr: .word ?
-.send zero_page
-
-.section rom
-
-create_stack_frame:
- .block
- ;; Store old frame pointer on stack.
- ;; Since our stack grows bottom to top, we write
- ;; the data before increasing the stack pointer
- #mem.COPY_WORD_ABSOLUTE_INDIRECT ds.frame_ptr, (ds.ptr)
- #ds.PUSH $02 ; Store old frame pointer
- #mem.COPY_WORD_ABSOLUTE_INDIRECT ds.ptr, ds.frame_ptr ; Set new frame pointer
- rts
- .bend
-
-delete_stack_frame:
- .block
- #mem.COPY_WORD_ABSOLUTE_INDIRECT ds.frame_ptr, ds.ptr ; Reset stack pointer to begin of frame.
- #ds.PULL $02 ; Move stack pointer to frame reference
- #mem.COPY_WORD_ABSOLUTE_INDIRECT (ds.ptr), ds.frame_ptr ; Restore old frame pointer.
- rts
- .bend
-
-push_a:
- .block
- sta (ds.ptr)
- #ds.PUSH $01
- rts
- .bend
-
-pull_a:
- .block
- #ds.PULL $01
- lda (ds.ptr)
- rts
- .bend
-
-.send rom
-.endn
diff --git a/roms/boot/ds.inc b/roms/boot/ds.inc
@@ -1,79 +0,0 @@
-;;; -*- asm -*-
-
-;;; Macros for accessing the data stack
-;;; and managing local variables and
-;;; call parameters. Check ds.asm for
-;;; documentation.
-
-.namespace ds
-
-INIT_STACK: .macro
- #mem.STORE_WORD \1, ds.ptr
- .endm
-
-PUSH: .macro ; Reserve bytes on stack (no actual push operation)
- #mem.ADD_WORD ds.ptr, \1
- .endm
-
-PULL: .macro ; Remove bytes from stack (no return value)
- #mem.SUB_WORD ds.ptr, \1
- .endm
-
-PUSH_WORD: .macro ; Push a word on the stack
- lda #<\1
- sta (ds.ptr)
- lda #>\1
- ldy #$01
- sta (ds.ptr),y
- #ds.PUSH $02
- .endm
-
-LOCAL: .macro
- ldy #\1
- \2 (ds.frame_ptr),y
- .endm
-lda_LOCAL: .macro
- #ds.LOCAL \1, lda
- .endm
-sta_LOCAL: .macro
- #ds.LOCAL \1, sta
- .endm
-adc_LOCAL: .macro
- #ds.LOCAL \1, adc
- .endm
-PARAM: .macro
- ldy #\1
- \2 (ds.ptr),y
- .endm
-sta_PARAM: .macro
- #ds.PARAM \1+2, sta
- .endm
-lda_PARAM: .macro
- #ds.PARAM \1+2, lda
- .endm
-
-CALL: .macro subroutine, param=[], param_lda=""
- .block
- .if len(\param) != 0
- .for CALL_param := 0, CALL_param < len(\param), CALL_param += $1
- \param_lda \param[CALL_param]
- #ds.sta_PARAM CALL_param
- .next
- .endif
- jsr \subroutine
- .bend
- .endm
-
-;; Push sequence of bytes to stack. Parameters: From, Length
-PUSH_RANGE: .macro
- ldy #$00
-loop_PUSH_RANGE:
- lda \1,y
- sta (ds_pointer),y
- iny
- cpy \2
- bne loop_PUSH_RANGE
- #PUSH \2
- .endm
-
-.endn
diff --git a/roms/boot/io.asm b/roms/boot/io.asm
@@ -1,406 +0,0 @@
-;;; Serial IO and string manipulation
-
-io .namespace
-
-.if SYMON
- acia_base = $8800
- via_start = $8000
-.else
- acia_base = $c000
- via_start = $c800
-.endif
- acia_data_reg = acia_base
- acia_status_reg = acia_base + $1
- acia_cmd_reg = acia_base + $2
- acia_ctrl_reg = acia_base + $3
- via_irb = via_start
- via_orb = via_start
- via_ira = via_start+$1
- via_ora = via_start+$1
- via_ddrb = via_start+$2
- via_ddra = via_start+$3
- via_t1cl = via_start+$4
- via_t1ch = via_start+$5
- via_t1ll = via_start+$6
- via_t1lh = via_start+$7
- via_t2cl = via_start+$8
- via_t2ch = via_start+$9
- via_sr = via_start+$10
- via_acr = via_start+$11
- via_pcr = via_start+$12
- via_ifr = via_start+$13
- via_ier = via_start+$14
- via_ira2 = via_start+$15
- via_ora2 = via_start+$15
-
-
-.section zero_page
- ;; Temporary zero page variables;
- ;; only used in subroutine
-puts_str: .word ?
-gets_len: .byte ?
-gets_str: .word ?
-.send zero_page
-
-.section rom
-
-;;; init_acia
-;;; Initialize acai for communication
-;;; Input:
-;;; -
-;;; Output:
-;;; -
-;;; Changes:
-;;; a, acai registers
-init_acia:
- .block
- ;; Reset acai
- sta io.acia_status_reg
- lda #%00011111 ; 19200 bps, 8 data bits, 1 stop bit
- sta io.acia_ctrl_reg
- ;; No parity, no echo, no interrupts, DTR ready
- lda #%11001011
- sta io.acia_cmd_reg
- rts
- .bend
-
-;;; ----------------------------------------------------------
-;;; STANDARD LIBRARY FUNCTIONS
-;;;
-;;; These functions may be used by user programs. Labels
-;;; marked with 'EXPORT' are exported to liba.h. When
-;;; the user program imports this header file, the functions
-;;; are available.
-;;; ----------------------------------------------------------
-
-;;; puts
-;;; Send up to 256 characters terminated by null via acia
-;;; Input:
-;;; puts_str, put_str+1:
-;;; 2 bytes on zero page containing string address
-;;; Output:
-;;; -
-;;; Changes:
-;;; a, x, y, puts_str, put_str+1
-puts:
- .block
- ;; Send string terminated by '\0'
- ldy #$00
-_puts_loop:
- lda (io.puts_str),y
- beq _puts_end
- phy
- jsr io.putc
- ply
- iny
- jmp _puts_loop
-_puts_end:
- rts
- .bend
-
-;;; putc
-;;; Send character via acia
-;;; Input:
-;;; a:
-;;; Character to be printed
-;;; Output:
-;;; -
-;;; Changes:
-;;; x, acai registers
-putc:
- .block
- ;; Send character.
- ;; Due to bugs in the register and interrupt
- ;; handling of the WDC 65C02, we have to use
- ;; a manual delay.
- sta io.acia_data_reg
-.switch CLOCK_SPEED
-.case 4 ; 4 Mhz Clock
- SERIAL_SEND_DELAY_X = $c3
- SERIAL_SEND_DELAY_Y = $02
-.case 2 ; 4 Mhz Clock
- SERIAL_SEND_DELAY_X = $c2
- SERIAL_SEND_DELAY_Y = $01
-.default ; 1 Mhz Clock
- SERIAL_SEND_DELAY_X = $88
- SERIAL_SEND_DELAY_Y = $01
-.endswitch
- ldy #SERIAL_SEND_DELAY_Y
-outer_loop:
- ldx #SERIAL_SEND_DELAY_X
-inner_loop:
- dex
- bne inner_loop
- dey
- bne outer_loop
- rts
- .bend
-
-;;; puth
-;;; Convert byte to hex and send via acia
-;;; Input:
-;;; a:
-;;; Byte to be printed
-;;; Output:
-;;; -
-;;; Changes:
-;;; a, x, acai registers, c-flag
-puth:
- .block
- ;; Send byte a as hex number
- pha
- lsr a
- lsr a
- lsr a
- lsr a
- jsr io.puth_nibble
- pla
- jsr io.puth_nibble
- rts
- .bend
-
-;;; puth_nibble
-;;; Convert lower half of byte to hex and send via acia
-;;; Input:
-;;; a:
-;;; Nibble to be printed
-;;; Output:
-;;; -
-;;; Changes:
-;;; a, x, acai registers, c-flag
-puth_nibble:
- .block
- ;; Print hex digit
- clc
- and #$0F
- adc #$30 ; Decimal number
- cmp #$3A ; >10 ?
- bmi _puth_putc
- adc #$26
-_puth_putc:
- jsr io.putc
- rts
- .bend
-
-;;; putd
-;;; Output decimal number
-;;; Input:
-;;; a:
-;;; The number
-;;; Output:
-;;; -
-;;; Changes:
-;;; a, x, y, acai registers, c-flag
-putd:
- .block
- ldy #$00
-first_digit:
- cmp #100
- bcc first_digit_set
- sec
- sbc #100
- iny
- jmp first_digit
-first_digit_set:
- ldx #$00
- pha
- tya
- beq skip_first_leading_zero
- jsr io.puth_nibble
- ldx #$01 ; Force printing of zeros in rest of term
-skip_first_leading_zero:
- pla
-second_digit:
- cmp #10
- bcc second_digit_set
- sec
- sbc #10
- iny
- jmp second_digit
-second_digit_set:
- pha
- tya
- cpx #$01
- beq do_not_skip_potential_second_leading_zero
- cmp #$00
- beq skip_second_leading_zero
-do_not_skip_potential_second_leading_zero:
- jsr io.puth_nibble
-skip_second_leading_zero:
- pla
- jmp io.puth_nibble
- .bend
-
-
-;;; putnl
-;;; Send newline via acia
-;;; Input:
-;;; -
-;;; Output:
-;;; -
-;;; Changes:
-;;; a
-putnl:
- .block
- lda #$0d
- jsr io.putc
- lda #$0a
- jsr io.putc
- rts
- .bend
-
-;;; getc
-;;; Read character from acia. Blocks until character is read
-;;; Input:
-;;; -
-;;; Output:
-;;; a:
-;;; Character read
-;;; Changes:
-;;; a, acai registers
-getc:
- .block
- ;; Read character from acia
- lda io.acia_status_reg
- and #%00001000
- beq io.getc
- lda io.acia_data_reg
- rts
- .bend
-
-;;; getc_seed_rng
-;;; Like getc, but also updates lsfr
-;;; This is our only source of randomness right now:
-;;; While we are busy waiting for a character, lsfr is
-;;; updated.
-;;; Input:
-;;; -
-;;; Output:
-;;; a:
-;;; Character read
-;;; Changes:
-;;; a, acai registers, lsfr_state
-getc_seed_rng:
- .block
- ;; Read character from acia
- ;; We also use the time between keystrokes
- ;; as entropy source for the RNG
- jsr lfsr.step
- lda io.acia_status_reg
- and #%00001000
- beq io.getc_seed_rng
- lda io.acia_data_reg
- rts
- .bend
-
-getc_nonblocking:
- .block
- ;; Non-blocking read: If
- ;; character has been read,
- ;; it is returned in A and
- ;; C is cleared. If not,
- ;; C is set
- sec
- lda io.acia_status_reg
- and #%00001000
- beq nothing_read
- clc
- lda io.acia_data_reg
-nothing_read:
- rts
- .bend
-
-;;; gets
-;;; Read up to 255 characters terminated by CR via acia
-;;; Input:
-;;; gets_len:
-;;; Max. length of input.
-;;; gets_str, gets_str+1:
-;;; 2 bytes on zero page containing destination address
-;;; +-------------------------------------------------------+
-;;; |IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! |
-;;; +-------------------------------------------------------+
-;;; Note that a terminating zero is added to the string.
-;;; Therefore gets_len+1 bytes may be written!
-;;; Output:
-;;; Zero terminated string at gets_str, gets_str+1
-;;; Changes:
-;;; a, y, ACAI registers
-gets:
- .block
- ;; Read string terminated by CR
- ldy #$00
-loop:
- phy
- jsr io.getc
- jsr io.putc
- ply
- cmp #$0d ; GOT CR?
- beq terminate_string ; if not
- sta (io.gets_str), y ; store character
- iny ; if max length
- cpy io.gets_len ; not reached,
- bne loop ; continue loop.
-terminate_string:
- lda #$00
- sta (io.gets_str), y
- rts
- .bend
-
-;;; str2byte
-;;; Convert number (<= 255) given as string to byte.
-;;; Expects zero-terminated string on stack
-;;; Input:
-;;; Zero-terminated string on stack
-;;; Output:
-;;; a:
-;;; Byte value
-;;; Changes:
-;;; a, x, y
-str2byte:
- .block
- jsr ds.create_stack_frame
- #ds.PUSH $04 ; Local variable (string of length four)
- lda #$00
- pha
- ldy #$00
-add_digit:
- lda (ds.frame_ptr),y
- beq done
- sec ; Convert next digit
- sbc #'0' ; from ascii to
- sta (ds.frame_ptr),y ; number.
- pla ; Multiply current
- jsr io.multiply_by_ten ; accumulator by 10.
- clc ; Add new
- adc (ds.frame_ptr),y ; digit.
- pha
- iny ; Advance to next digit
- jmp add_digit
-done:
- pla
- #ds.sta_LOCAL 0
- jsr ds.delete_stack_frame
- .bend
-
-;;; multiply_by_ten
-;;; Multiply A by ten.
-;;; Input:
-;;; a: Number
-;;; Output:
-;;; a: Number * 10
-;;; Changes:
-;;; a, c
-multiply_by_ten:
- .block
- sta (ds.ptr)
- lda #$00
- clc
- .for i = 0, i < 10, i += 1
- adc (ds.ptr)
- .next
- rts
- .bend
-
-.send rom
-.endn
diff --git a/roms/boot/io.inc b/roms/boot/io.inc
@@ -1,103 +0,0 @@
-;;; -*- asm -*-
-
-.namespace io
-
-;;; PRINT(addr)
-;;; Send zero terminated string at addr via acia.
-;;; Input:
-;;; addr:
-;;; Address of zero terminated string (<= 256 characters)
-;;; Output:
-;;; -
-;;; Changes:
-;;; a, x, y, puts_str, put_str+1
-
-PRINT .macro
- lda #<\1
- sta io.puts_str
- lda #>\1
- sta io.puts_str+1
- jsr io.puts
- .endm
-
-;;; PRINTS(string) (<= 255 characters)
-;;; Send string via acia
-;;; Input:
-;;; string:
-;;; String to be send.
-;;; Output:
-;;; -
-;;; Changes:
-;;; a, x, y, puts_str, put_str+1
-
-PRINTS .macro
- #io.PRINT saddr
- jmp cont_PRINTS
-saddr:
- .null \1
-cont_PRINTS:
- .endm
-
-;;; PRINTSNL(string) (<= 253 characters)
-;;; Send string and newline via acia
-;;; Input:
-;;; string:
-;;; String to be send.
-;;; Output:
-;;; -
-;;; Changes:
-;;; a, x, y, puts_str, put_str+1
-
-PRINTSNL .macro
- #io.PRINT saddr
- jmp cont_PRINTSNL
-saddr:
- .null \1, $0d, $0a
-cont_PRINTSNL:
- .endm
-
-;;; INPUTS(addr, len)
-;;; Read up to len characters from acai and store them at addr
-;;; This is a wrapper for gets
-;;; Input:
-;;; addr:
-;;; Target address for zero-terminated string
-;;; len:
-;;; Maximal length of string to be read (not terminating zero!)
-;;; +-------------------------------------------------------+
-;;; |IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! |
-;;; +-------------------------------------------------------+
-;;; Note that a terminating zero is added to the string.
-;;; Therefore len+1 bytes may be written!
-;;; Output:
-;;; -
-;;; Changes:
-;;; a, y, ACAI registers
-
-INPUTS .macro
- lda #<\1
- sta io.gets_str
- lda #>\1
- sta io.gets_str+1
- lda \2
- sta io.gets_len
- jsr io.gets
- .endm
-
-;;; PRINTNL
-;;; Send newline via acia
-;;; Input:
-;;; -
-;;; Output:
-;;; -
-;;; Changes:
-;;; a, x, acai registers
-
-PRINTNL .macro
- lda #$0d
- jsr io.putc
- lda #$0a
- jsr io.putc
- .endm
-
-.endn
diff --git a/roms/boot/lfsr.asm b/roms/boot/lfsr.asm
@@ -1,68 +0,0 @@
-;;; 16 Bit Galouis LFSR
-;;; If you use the whole state as RNG, the quality of the
-;;; random numbers is rather poor. Just extracting one
-;;; bit from the state yields random numbers with reasonable
-;;; statistical properties.
-
-;;; init
-;;; Initialize lfsr
-;;; Input:
-;;; -
-;;; Output:
-;;; -
-;;; Changes:
-;;; lfsr
-lfsr .namespace
-
-.section zero_page
- ;; LFSR
-state: .word ?
-.send zero_page
-
-.section rom
-
-init:
- .block
- #mem.STORE_WORD $beef, lfsr.state
- .bend
-
-;;; step
-;;; update lfsr
-;;; Input:
-;;; -
-;;; Output:
-;;; -
-;;; Changes:
-;;; lfsr
-step:
- .block
- asl lfsr.state
- lda lfsr.state+1
- rol a
- bcc cont
- eor #$0b
-cont:
- sta lfsr.state+1
- lda lfsr.state
- adc #$00
- sta lfsr.state
- rts
- .bend
-
-.if false
-;;; Test function for LFSR
-test_lfsr: .block
-loop:
- jsr lfsr.step
- lda lfsr.state+1
- jsr puth
- lda lfsr.state
- jsr io.puth
- PRINTSNL("")
- jsr io.getc
- jmp loop
- .bend
-.endif ; false
-
-.send rom
-.endn
diff --git a/roms/boot/sd.asm b/roms/boot/sd.asm
@@ -1,269 +0,0 @@
-;; -----------------------------------
-;;;
-;;; SD card communication interface
-;;;
-;;; -----------------------------------
-
-
-sd .namespace
-
-.section zero_page
-data: .word ?
- ;; type does not need to be on the zero page.
-type: .byte ? ; $05 = SD; $c1 = SDHC
- ;; Everything beyond these reserved variables available to
- ;; programs in RAM.
-.send zero_page
-
-.section rom
-
-send_cmd:
- .block
- ldy #$00
-cmd_bytes_loop:
- lda (sd.data),y
- jsr spi.set
- iny
- cpy #$06
- bne cmd_bytes_loop
- lda #$ff ; Finalize transmission
- jsr spi.set ; with dummy byte
- rts
- .bend
-
-
-print_block:
- .block
- ldx #$02
- ldy #$00
-loop:
- phx
- phy
- lda (sd.data),y
- jsr io.puth
- #io.PRINTS " "
- ply
- plx
- iny
- bne loop
- #mem.ADD_WORD sd.data, $0100
- dex
- bne loop
- #io.PRINTNL
- #mem.SUB_WORD sd.data, $0200
- rts
- .bend
-
-read_block:
- ;; Read a block of data
- ;; Block number is passed via stack.
- .block
- jsr ds.create_stack_frame
- #ds.PUSH $04 ; Local variable (32 bit block number)
-.if DEBUG
- io.PRINTSNL "Sending CMD17"
-.endif
- lda #$51 ; CMD17 (READ_SINGLE_BLOCK)
- jsr spi.set
- jsr send_block_number
- lda #$01 ; Dummy CRC
- jsr spi.set
- jsr spi.get
- cmp #$00
- beq wait_for_start_token
- jmp read_failed
-wait_for_start_token:
- jsr spi.get_nonblocking
- cmp #$ff
- beq wait_for_start_token
- cmp #$fe
- bne read_failed
- ldx #$02
- ldy #$00
-loop:
- phx
- phy
- jsr spi.get_nonblocking
- ply
- plx
- sta (sd.data),y
- iny
- bne loop
- #mem.ADD_WORD sd.data, $0100 ; Second half of block
- dex
- bne loop
- #mem.SUB_WORD sd.data, $0200 ; Restore pointer
- clc
- #sd.RECEIVE $2 ; Get CRC
- jsr ds.delete_stack_frame
- rts
-read_failed:
- #io.PRINTSNL "Error reading SD card!"
- sec
- jsr ds.delete_stack_frame
- rts
- .bend
-
-send_block_number:
- .block
- lda sd.type ; Old cards expect the position
- cmp #$05 ; as bytes, not blocks
- beq old_card
- #ds.lda_LOCAL 0 ; Block number
- jsr spi.set
- #ds.lda_LOCAL 1
- jsr spi.set
- #ds.lda_LOCAL 2
- jsr spi.set
- #ds.lda_LOCAL 3
- jsr spi.set
- rts
-old_card:
- #ds.lda_LOCAL 3
- asl a
- #ds.sta_LOCAL 3
- #ds.lda_LOCAL 2
- rol a
- #ds.sta_LOCAL 2
- #ds.lda_LOCAL 1
- rol a
- jsr spi.set
- #ds.lda_LOCAL 2
- jsr spi.set
- #ds.lda_LOCAL 3
- jsr spi.set
- lda #$00
- jsr spi.set
- rts
- .bend
-
-write_block:
- ;; Write block at data to card.
- ;; Block number is passed via stack.
- .block
- jsr ds.create_stack_frame
- #ds.PUSH $04 ; Local variable (32 bit block number)
-.if DEBUG
- io.PRINTSNL "Sending CMD24"
-.endif
- lda #$58 ; CMD24 (WRITE_BLOCK)
- jsr spi.set
- jsr send_block_number
- lda #$01 ; Dummy CRC
- jsr spi.set
- lda #$ff
- jsr spi.set ; Dummy byte
- #sd.RECEIVE $1
- ;; Send start token
- lda #$fe
- jsr spi.set
- ;; Send data
- ldx #$02
- ldy #$00
-write_loop:
- phx
- phy
- lda (sd.data),y
- jsr spi.set
- ply
- plx
- iny
- cpy #$00
- bne write_loop
- #mem.ADD_WORD sd.data, $0100 ; Second half of block
- dex
- cpx #$00
- bne write_loop
- #mem.SUB_WORD sd.data, $0200 ; Restore pointer
- ;; Wait for write operation to complete
- #sd.RECEIVE $1
- cmp #$e5
- bne error_writing
-wait_loop:
- #sd.RECEIVE $1
- beq wait_loop
- jsr ds.delete_stack_frame
- clc
- rts
-error_writing:
- #io.PRINTSNL "Error writing to SD card!"
- jsr ds.delete_stack_frame
- sec
- rts
- .bend
-
-close:
- #mem.SET_BITS io.via_ora, #spi.CS
- rts
-
-open:
- .block
-.if DEBUG
- #io.PRINTSNL "Initializing SD Card"
-.endif
- ;; Configure ports for in- and output
- #mem.SET_BITS io.via_ddra, #(spi.CS | spi.SCK | spi.MOSI)
- #mem.CLEAR_BITS io.via_ddra, #spi.MISO
- ldy #$10 ; Error counter
- phy
-start_init:
- ;; Card power on sequence:
- ;; Keep CS high, toggle clock for at least 74 cycles.
- #mem.SET_BITS io.via_ora, #(spi.CS | spi.MOSI)
- ;; Wait for 80 clock pulses
- ldx #160
-boot_wait_loop:
- #mem.TOGGLE_BITS io.via_ora, #spi.SCK
- dex
- bne boot_wait_loop
- #mem.CLEAR_BITS io.via_ora, #(spi.CS|spi.MOSI|spi.SCK)
- #sd.SEND_CMD [$40, $00, $00, $00, $00, $95] ; CMD0
- #sd.RECEIVE $1
- cmp #$01 ; Should be $01 (In idle state).
- beq cont
- ply
- dey
- phy
- beq failed_init
- jmp start_init ; If not, start over.
-failed_init:
- ply
- #io.PRINTSNL "Error initializing SD card!"
- sec
- rts
-cont:
- ;; Card is in SPI mode
- ply
- #sd.SEND_CMD [$48, $00, $00, $01, $aa, $87] ; CMD8
- #sd.RECEIVE $1
- #sd.SEND_CMD [$45, $00, $00, $00, $00, $5b] ; CMD5
- #sd.RECEIVE $1 ; $05 = SD; $c1 = SDHC
- sta sd.type
- #sd.SEND_CMD [$7b, $00, $00, $00, $00, $83] ; CMD59
- #sd.RECEIVE $1 ; should be $01
- ;; Send ACMD41 until card is initialized
- ldy #$10 ; Fail counter
- phy
-wait_for_card_initialized:
- #sd.SEND_CMD [$77, $00, $00, $00, $00, $65] ; CMD55
- #sd.RECEIVE $1 ; should be $01
- #sd.SEND_CMD [$69, $40, $00, $00, $00, $95] ; ACMD41 (v1 & v2 card)
- #sd.RECEIVE $1 ; $01 if busy, $00 if ready (initialized)
- cmp #$00
- beq initialized
- ply
- dey
- phy
- bne cont2 ; More tries?
-cont2:
- jmp wait_for_card_initialized
-initialized:
- ply
- #sd.SEND_CMD [$50, $00, $00, $02, $00, $15] ; CMD16
- #sd.RECEIVE $1
- clc
- rts
- .bend
-
-.send rom
-.endn
diff --git a/roms/boot/sd.inc b/roms/boot/sd.inc
@@ -1,51 +0,0 @@
-;;; -*- asm -*-
-
-;;; Macros for accessing SD cards
-
-.namespace sd
-
-SEND_CMD .macro
-.if DEBUG
- PRINTS "Sending CMD$"
- lda cmd
- sec
- sbc #$40
- jsr io.puth
- PRINTNL
-.endif
- lda #<cmd
- sta sd.data
- lda #>cmd
- sta sd.data+1
- jsr sd.send_cmd
- jmp cont_SEND_CMD
-cmd: .byte \1
-cont_SEND_CMD:
- .endm
-
-
-
-RECEIVE .macro
- ldx #\1
-loop_RECEIVE:
- phx
- jsr spi.get
-.if DEBUG
- pha
- jsr io.puth
- PRINTS " "
- pla
-.endif
- plx
- dex
- bne loop_RECEIVE
- pha
-.if DEBUG
- PRINTNL
-.endif
- ;; Read non-existing dummy byte for snychronization
- jsr spi.get_nonblocking
- pla
- .endm
-
-.endn
diff --git a/roms/boot/spi.asm b/roms/boot/spi.asm
@@ -1,107 +0,0 @@
-;;; -----------------------------------
-;;;
-;;; SPI read and write operations
-;;;
-;;; -----------------------------------
-
-spi .namespace
-
-.section zero_page
- ;; Used by VIA for (de-)serialization
-via_buffer: .byte ?
-.send zero_page
-
- ;; Wiring:
- ;; PA0: CS
- CS = %00000001
- ;; PA1: SCK
- SCK = %00000010
- ;; PA2: MOSI
- MOSI = %00000100
- ;; PA3: MISO
- MISO = %00001000
-
-
-
-.section rom
-
-;;; Initialize VIA: Switch all ports to output
-;;; and all pins to low
-init_via:
- lda #$ff
- sta io.via_ddra
- sta io.via_ddrb
- lda #$00
- sta io.via_ora
- sta io.via_orb
- rts
-
-;;; Blocking read - read until
-;;; byte != $ff is read or retry counter
-;;; expires.
-get:
- .block
- ldy #$00 ; Try up to $ff times
-retry_loop:
- jsr spi.get_nonblocking
- ;; If we read $ff, we did not receive
- ;; a byte.
- cmp #$ff
- bne done
- dey ; Try again unless retry
- bne retry_loop ; counter expired.
-done:
- #mem.CLEAR_BITS io.via_ora, #(MOSI|SCK) ; Set MOSI and SCK low on completion.
- lda spi.via_buffer
- rts
- .bend
-
-;;; Return next byte; may be $ff in case of no transmission
-get_nonblocking:
- #mem.SET_BITS io.via_ora, #MOSI ; Keep MOSI high during read
- lda #$00 ; Clear
- sta spi.via_buffer ; receive buffer.
- ldx #$08 ; 8 bit to read.
-read_bits_loop:
- .block
- asl spi.via_buffer ; Make room for next bit
- #mem.CLEAR_BITS io.via_ora, #SCK ; Generate clock impulse
- #mem.SET_BITS io.via_ora, #SCK
- lda io.via_ira ; Get next bit.
- and #MISO
- cmp #MISO
- bne cont ; Nothing to do if we got a zero bit.
- inc spi.via_buffer ; Set lowest bit in via_buffer
-cont:
- dex
- bne read_bits_loop
- #mem.CLEAR_BITS io.via_ora, #(MOSI|SCK) ; Set MOSI and SCK low on completion.
- lda spi.via_buffer
- rts
- .bend
-
-;;; Write byte via SPI
-set:
- .block
- sta spi.via_buffer ; We send from via_buffer
- ldx #$08 ; 8 bit to write
-write_bits_loop:
- #mem.CLEAR_BITS io.via_ora, #SCK ; Set bits on low clock
- #mem.CLEAR_BITS io.via_ora,#MOSI
- lda spi.via_buffer ; Get next bit to send
- and #%10000000 ; Isolate bit to send.
- beq cont ; Set MOSI accordingly.
- #mem.SET_BITS io.via_ora,#MOSI
- jmp cont
-cont:
- #mem.SET_BITS io.via_ora, #SCK ; Send bits on high clock
- asl spi.via_buffer ; Advance to next bit
- dex
- bne write_bits_loop
- #mem.CLEAR_BITS io.via_ora,#SCK
- rts
- .bend
-
-.send rom
-
-.endn
diff --git a/roms/boot/term.asm b/roms/boot/term.asm
@@ -1,260 +0,0 @@
-;;;
-;;; Special commands for ANSI terminals
-;;;
-
-term .namespace
-.section rom
-
-;;; ANSI text attributes
-plain = 0
-bold = 1
-blink = 5
-reverse = 7
-
-;;; ANSI colors
-black = 0
-red = 1
-green = 2
-yellow = 3
-blue = 4
-magenta = 5
-cyan = 6
-white = 7
-bright_black = 8
-bright_red = 9
-bright_green = 10
-bright_yellow = 11
-bright_blue = 12
-bright_magenta = 13
-bright_cyan = 14
-bright_white = 15
-
-;;; get_screen_size
-;;; Returns screen size in X, A.
-;;; If not supported by terminal, C is set
-;;; Input:
-;;; -
-;;; Output:
-;;; X: Screen width
-;;; A: Screen height
-;;; Changes:
-;;; a, x, y, c
-get_screen_size:
- .block
- jsr ds.create_stack_frame
- #ds.PUSH $02
- cursor_save_x = 0 ; Local variable
- cursor_save_y = 1 ; Local variable
- ;; Local variables 0 & 1: Saved cursor position
- jsr term.get_cursor ; Save cursor position
- bcs terminal_does_not_answer
- #ds.sta_LOCAL cursor_save_y
- txa
- #ds.sta_LOCAL cursor_save_x
- #SET_CURSOR #$FF, #$FF ; Try to move cursor to max. position
- jsr term.get_cursor ; Get cursor position (= screen size)
- pha ; Save it temporary
- txa ; on the
- pha ; hardware stack.
- #ds.lda_LOCAL cursor_save_x ; Restore
- tax ; old
- #ds.lda_LOCAL cursor_save_y ; cursor
- jsr term.set_cursor ; position
- jsr ds.delete_stack_frame
- pla
- tax
- pla
- rts
-terminal_does_not_answer:
- jsr ds.delete_stack_frame
- ldx #80
- lda #25
- rts
- .bend
-
-clear_screen:
- #io.PRINTS x"1b" .. "[2J"
- rts
-
-set_color:
- pha
- #io.PRINTS x"1b" .. "[38;5;"
- pla
- jsr io.putd
- lda #"m"
- jmp io.putc
-
-set_background_color:
- pha
- #io.PRINTS x"1b" .. "[48;5;"
- pla
- jsr io.putd
- lda #"m"
- jmp io.putc
-
-set_text:
- tax
- lda #"m"
- jmp send_CSI
-
-cursor_up:
- tax
- lda #"A"
- jmp send_CSI
-
-cursor_down:
- tax
- lda #"B"
- jmp send_CSI
-
-cursor_forward:
- tax
- lda #"C"
- jmp send_CSI
-
-cursor_back:
- tax
- lda #"D"
- jmp send_CSI
-
-scroll_up:
- tax
- lda #"S"
- jmp send_CSI
-
-scroll_down:
- tax
- lda #"T"
- jmp send_CSI
-
-;;; Send ANSI escape sequence to terminal.
-;;; A contains command, X value
-send_CSI:
- pha
- phx
- lda #$1b
- jsr io.putc
- lda #"["
- jsr io.putc
- pla
- jsr io.putd
- pla
- jmp io.putc
-
-;;; set_cursor
-;;; Sets cursor to position x, a.
-;;; Position starts at 1, not 0!
-;;; Input:
-;;; x: x position
-;;; a: y position
-;;; Output:
-;;; -
-;;; Changes:
-;;; a, x, y, c
-;;; Set Cursor; x position in X, y position in A
-set_cursor:
- phx
- pha
- lda #$1b
- jsr io.putc
- lda #"["
- jsr io.putc
- pla
- jsr io.putd
- lda #";"
- jsr io.putc
- pla
- jsr io.putd
- lda #"H"
- jmp io.putc
-
-;;; get_cursor
-;;; Store cursor position in x, a.
-;;; Position starts at 1, not 0!
-;;; Input:
-;;; -
-;;; Output:
-;;; x: x position
-;;; a: y position
-;;; Changes:
-;;; a, x, y, c
-get_cursor:
- .block
- jsr ds.create_stack_frame
- #ds.PUSH $08 ; Position as string
- ;; We store the position string as local variables.
- ;; By initializing the local variables with #$00,
- ;; the strings terminate automatically when no
- ;; more digits are written.
- lda #$00
- .for i = 0, i < 8, i += 1
- #ds.sta_LOCAL i
- .next
- ;; Request cursor position
- #io.PRINTS x"1b" .. "[6n"
- ;; Read answer
- ldx #$08 ; Number of tries
- ldy #$ff ; to get an answer.
-wait_for_answer:
- phx
- phy
- jsr io.getc_nonblocking ; $1b
- ply
- plx
- bcc terminal_answers
- dey
- cpy #$00
- bne wait_for_answer
- dex
- cpx #$00
- bne wait_for_answer
- ;; Terminal does not answer.
- jsr ds.delete_stack_frame
- lda #$00
- ldx #$00
- sec
- rts
-terminal_answers:
- jsr io.getc ; "["
- jsr io.getc ; First digit y
- #ds.sta_LOCAL 0
- jsr io.getc ; Second digit y
- cmp #";"
- beq got_y_position
- #ds.sta_LOCAL 1
- jsr io.getc ; Third digit y
- cmp #";"
- beq got_y_position
- #ds.sta_LOCAL 2
- jsr io.getc ; Read and ignore terminating ';'
-got_y_position:
- jsr io.getc ; First digit x
- #ds.sta_LOCAL 4
- jsr io.getc ; Second digit x
- cmp #"R"
- beq got_x_position
- #ds.sta_LOCAL 5
- jsr io.getc ; Third digit x
- cmp #"R"
- beq got_x_position
- #ds.sta_LOCAL 6
- jsr io.getc ; Read and ignore terminating 'R'
-got_x_position:
- ;; x position is in local variable(s) 4...
- ;; y position is in local variable(s) 0...
- ;; Convert them to ints
- #ds.CALL io.str2byte, [0, 1, 2, 3], #ds.lda_LOCAL
- #ds.lda_PARAM 0
- pha
- #ds.CALL io.str2byte, [4, 5, 6, 7], #ds.lda_LOCAL
- #ds.lda_PARAM 0
- pha
- jsr ds.delete_stack_frame
- plx
- pla
- clc
- rts
- .bend
-
-.send rom
-.endn
diff --git a/roms/boot/term.inc b/roms/boot/term.inc
@@ -1,62 +0,0 @@
-;;; -*- asm -*-
-
-;;; -----------------------------------
-;;;
-;;; ANSI Terminal
-;;;
-;;; -----------------------------------
-
-.namespace term
-
-SET_COLOR .macro color
- lda #color
- jsr term.set_color
- .endm
-
-SET_BACKGROUND_COLOR .macro color
- lda #color
- jsr term.set_background_color
- .endm
-
-SET_TEXT .macro attribute
- lda #\attribute
- jsr term.set_text
- .endm
-
-CURSOR_UP .macro steps
- lda #\steps
- jsr term.cursor_up
- .endm
-
-CURSOR_DOWN .macro steps
- lda #\steps
- jsr term.cursor_down
- .endm
-
-CURSOR_FORWARD .macro steps
- lda #\steps
- jsr term.cursor_forward
- .endm
-
-CURSOR_BACK .macro steps
- lda #\steps
- jsr term.cursor_back
- .endm
-
-SCROLL_UP .macro steps
- lda #\steps
- jsr term.scroll_up
- .endm
-
-SCROLL_DOWN .macro steps
- lda #\steps
- jsr term.scroll_DOWN
- .endm
-
-SET_CURSOR .macro x, y
- lda \y
- ldx \x
- jsr term.set_cursor
- .endm
-
-.endn
diff --git a/roms/nop/nop.bin b/roms/nop/nop.bin
@@ -1 +0,0 @@
-êêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêê-
\ No newline at end of file
diff --git a/roms/os/Makefile b/roms/os/Makefile
@@ -0,0 +1,3 @@
+TARGET=os
+
+include ../Makefile.common
diff --git a/roms/os/ds.asm b/roms/os/ds.asm
@@ -0,0 +1,80 @@
+;;; Copyright 2021 Gerd Beuster (gerd@frombelow.net). This is free
+;;; software under the GNU GPL v3 license or any later version. See
+;;; COPYING in the root directory for details.
+
+;;; ----------------------------------------------------------
+;;;
+;;; STACK
+;;;
+;;; ----------------------------------------------------------
+
+;;; Software implementation of data stack. In difference to typical
+;;; stack implementations, this stack grows from bottom to top, because
+;;; we want to access eleements using indirected indexed access with
+;;; the stack frame pointer as base. Therefore the stack should
+;;; be initialized with the first unoccupied RAM address after the
+;;; program code.
+;;;
+;;; PULL and PUSH do not move actual stack data, they just shift
+;;; the stack pointer.
+;;;
+;;; Local variables on the data stack are supported by macro
+;;; VAR. VAR i refers to the ith byte on the stack. PARAM allows
+;;; to pass variables to a callee. PARAM i of the caller becomes
+;;; VAR i of the callee. The callee should reserve stack space
+;;; for both its parameters and local variables.
+;;;
+;;; Stack frames used for these calls are created/destroyed by
+;;; subroutines create_stack_frame and delete_stack_frame.
+;;;
+;;; CALL is like jsr with parameter passing. There addressing
+;;; modes are supported: immediate, zero-page, and local variables.
+;;; For immediate and zero page, the third parameter must be lda.
+;;; For local variables, the third parameter must be lda_LOCAL.
+;;; See sw/stack_test/stack_test.asm for examles.
+
+ds .namespace
+
+.section zero_page
+ ;; Stack
+ptr: .word ?
+frame_ptr: .word ?
+.send zero_page
+
+.section rom
+
+create_stack_frame:
+ .block
+ ;; Store old frame pointer on stack.
+ ;; Since our stack grows bottom to top, we write
+ ;; the data before increasing the stack pointer
+ #mem.COPY_WORD_ABSOLUTE_INDIRECT ds.frame_ptr, (ds.ptr)
+ #ds.PUSH $02 ; Store old frame pointer
+ #mem.COPY_WORD_ABSOLUTE_INDIRECT ds.ptr, ds.frame_ptr ; Set new frame pointer
+ rts
+ .bend
+
+delete_stack_frame:
+ .block
+ #mem.COPY_WORD_ABSOLUTE_INDIRECT ds.frame_ptr, ds.ptr ; Reset stack pointer to begin of frame.
+ #ds.PULL $02 ; Move stack pointer to frame reference
+ #mem.COPY_WORD_ABSOLUTE_INDIRECT (ds.ptr), ds.frame_ptr ; Restore old frame pointer.
+ rts
+ .bend
+
+push_a:
+ .block
+ sta (ds.ptr)
+ #ds.PUSH $01
+ rts
+ .bend
+
+pull_a:
+ .block
+ #ds.PULL $01
+ lda (ds.ptr)
+ rts
+ .bend
+
+.send rom
+.endn
diff --git a/roms/os/ds.inc b/roms/os/ds.inc
@@ -0,0 +1,83 @@
+;;; -*- asm -*-
+
+;;; Copyright 2021 Gerd Beuster (gerd@frombelow.net). This is free
+;;; software under the GNU GPL v3 license or any later version. See
+;;; COPYING in the root directory for details.
+
+;;; Macros for accessing the data stack
+;;; and managing local variables and
+;;; call parameters. Check ds.asm for
+;;; documentation.
+
+.namespace ds
+
+INIT_STACK: .macro
+ #mem.STORE_WORD \1, ds.ptr
+ .endm
+
+PUSH: .macro ; Reserve bytes on stack (no actual push operation)
+ #mem.ADD_WORD ds.ptr, \1
+ .endm
+
+PULL: .macro ; Remove bytes from stack (no return value)
+ #mem.SUB_WORD ds.ptr, \1
+ .endm
+
+PUSH_WORD: .macro ; Push a word on the stack
+ lda #<\1
+ sta (ds.ptr)
+ lda #>\1
+ ldy #$01
+ sta (ds.ptr),y
+ #ds.PUSH $02
+ .endm
+
+LOCAL: .macro
+ ldy #\1
+ \2 (ds.frame_ptr),y
+ .endm
+lda_LOCAL: .macro
+ #ds.LOCAL \1, lda
+ .endm
+sta_LOCAL: .macro
+ #ds.LOCAL \1, sta
+ .endm
+adc_LOCAL: .macro
+ #ds.LOCAL \1, adc
+ .endm
+PARAM: .macro
+ ldy #\1
+ \2 (ds.ptr),y
+ .endm
+sta_PARAM: .macro
+ #ds.PARAM \1+2, sta
+ .endm
+lda_PARAM: .macro
+ #ds.PARAM \1+2, lda
+ .endm
+
+CALL: .macro subroutine, param=[], param_lda=""
+ .block
+ .if len(\param) != 0
+ .for CALL_param := 0, CALL_param < len(\param), CALL_param += $1
+ \param_lda \param[CALL_param]
+ #ds.sta_PARAM CALL_param
+ .next
+ .endif
+ jsr \subroutine
+ .bend
+ .endm
+
+;; Push sequence of bytes to stack. Parameters: From, Length
+PUSH_RANGE: .macro
+ ldy #$00
+loop_PUSH_RANGE:
+ lda \1,y
+ sta (ds_pointer),y
+ iny
+ cpy \2
+ bne loop_PUSH_RANGE
+ #PUSH \2
+ .endm
+
+.endn
diff --git a/roms/os/io.asm b/roms/os/io.asm
@@ -0,0 +1,388 @@
+;;; Copyright 2021 Gerd Beuster (gerd@frombelow.net). This is free
+;;; software under the GNU GPL v3 license or any later version. See
+;;; COPYING in the root directory for details.
+
+;;; Serial IO and string manipulation
+
+io .namespace
+
+.if SYMON
+ acia_base = $8800
+.else
+ acia_base = $c000
+.endif
+ acia_data_reg = acia_base
+ acia_status_reg = acia_base + $1
+ acia_cmd_reg = acia_base + $2
+ acia_ctrl_reg = acia_base + $3
+
+.section zero_page
+ ;; Temporary zero page variables;
+ ;; only used in subroutine
+puts_str: .word ?
+gets_len: .byte ?
+gets_str: .word ?
+.send zero_page
+
+.section rom
+
+;;; init_acia
+;;; Initialize acai for communication
+;;; Input:
+;;; -
+;;; Output:
+;;; -
+;;; Changes:
+;;; a, acai registers
+init_acia:
+ .block
+ ;; Reset acai
+ sta io.acia_status_reg
+ lda #%00011111 ; 19200 bps, 8 data bits, 1 stop bit
+ sta io.acia_ctrl_reg
+ ;; No parity, no echo, no interrupts, DTR ready
+ lda #%11001011
+ sta io.acia_cmd_reg
+ rts
+ .bend
+
+;;; ----------------------------------------------------------
+;;; STANDARD LIBRARY FUNCTIONS
+;;;
+;;; These functions may be used by user programs. Labels
+;;; marked with 'EXPORT' are exported to liba.h. When
+;;; the user program imports this header file, the functions
+;;; are available.
+;;; ----------------------------------------------------------
+
+;;; puts
+;;; Send up to 256 characters terminated by null via acia
+;;; Input:
+;;; puts_str, put_str+1:
+;;; 2 bytes on zero page containing string address
+;;; Output:
+;;; -
+;;; Changes:
+;;; a, x, y, puts_str, put_str+1
+puts:
+ .block
+ ;; Send string terminated by '\0'
+ ldy #$00
+_puts_loop:
+ lda (io.puts_str),y
+ beq _puts_end
+ phy
+ jsr io.putc
+ ply
+ iny
+ jmp _puts_loop
+_puts_end:
+ rts
+ .bend
+
+;;; putc
+;;; Send character via acia
+;;; Input:
+;;; a:
+;;; Character to be printed
+;;; Output:
+;;; -
+;;; Changes:
+;;; x, acai registers
+putc:
+ .block
+ ;; Send character.
+ ;; Due to bugs in the register and interrupt
+ ;; handling of the WDC 65C02, we have to use
+ ;; a manual delay.
+ sta io.acia_data_reg
+.switch CLOCK_SPEED
+.case 4 ; 4 Mhz Clock
+ SERIAL_SEND_DELAY_X = $c3
+ SERIAL_SEND_DELAY_Y = $02
+.case 2 ; 2 Mhz Clock
+ SERIAL_SEND_DELAY_X = $c2
+ SERIAL_SEND_DELAY_Y = $01
+.default ; 1 Mhz Clock
+ SERIAL_SEND_DELAY_X = $88
+ SERIAL_SEND_DELAY_Y = $01
+.endswitch
+ ldy #SERIAL_SEND_DELAY_Y
+outer_loop:
+ ldx #SERIAL_SEND_DELAY_X
+inner_loop:
+ dex
+ bne inner_loop
+ dey
+ bne outer_loop
+ rts
+ .bend
+
+;;; puth
+;;; Convert byte to hex and send via acia
+;;; Input:
+;;; a:
+;;; Byte to be printed
+;;; Output:
+;;; -
+;;; Changes:
+;;; a, x, acai registers, c-flag
+puth:
+ .block
+ ;; Send byte a as hex number
+ pha
+ lsr a
+ lsr a
+ lsr a
+ lsr a
+ jsr io.puth_nibble
+ pla
+ jsr io.puth_nibble
+ rts
+ .bend
+
+;;; puth_nibble
+;;; Convert lower half of byte to hex and send via acia
+;;; Input:
+;;; a:
+;;; Nibble to be printed
+;;; Output:
+;;; -
+;;; Changes:
+;;; a, x, acai registers, c-flag
+puth_nibble:
+ .block
+ ;; Print hex digit
+ clc
+ and #$0F
+ adc #$30 ; Decimal number
+ cmp #$3A ; >10 ?
+ bmi _puth_putc
+ adc #$26
+_puth_putc:
+ jsr io.putc
+ rts
+ .bend
+
+;;; putd
+;;; Output decimal number
+;;; Input:
+;;; a:
+;;; The number
+;;; Output:
+;;; -
+;;; Changes:
+;;; a, x, y, acai registers, c-flag
+putd:
+ .block
+ ldy #$00
+first_digit:
+ cmp #100
+ bcc first_digit_set
+ sec
+ sbc #100
+ iny
+ jmp first_digit
+first_digit_set:
+ ldx #$00
+ pha
+ tya
+ beq skip_first_leading_zero
+ jsr io.puth_nibble
+ ldx #$01 ; Force printing of zeros in rest of term
+skip_first_leading_zero:
+ pla
+second_digit:
+ cmp #10
+ bcc second_digit_set
+ sec
+ sbc #10
+ iny
+ jmp second_digit
+second_digit_set:
+ pha
+ tya
+ cpx #$01
+ beq do_not_skip_potential_second_leading_zero
+ cmp #$00
+ beq skip_second_leading_zero
+do_not_skip_potential_second_leading_zero:
+ jsr io.puth_nibble
+skip_second_leading_zero:
+ pla
+ jmp io.puth_nibble
+ .bend
+
+
+;;; putnl
+;;; Send newline via acia
+;;; Input:
+;;; -
+;;; Output:
+;;; -
+;;; Changes:
+;;; a
+putnl:
+ .block
+ lda #$0d
+ jsr io.putc
+ lda #$0a
+ jsr io.putc
+ rts
+ .bend
+
+;;; getc
+;;; Read character from acia. Blocks until character is read
+;;; Input:
+;;; -
+;;; Output:
+;;; a:
+;;; Character read
+;;; Changes:
+;;; a, acai registers
+getc:
+ .block
+ ;; Read character from acia
+ lda io.acia_status_reg
+ and #%00001000
+ beq io.getc
+ lda io.acia_data_reg
+ rts
+ .bend
+
+;;; getc_seed_rng
+;;; Like getc, but also updates lsfr
+;;; This is our only source of randomness right now:
+;;; While we are busy waiting for a character, lsfr is
+;;; updated.
+;;; Input:
+;;; -
+;;; Output:
+;;; a:
+;;; Character read
+;;; Changes:
+;;; a, acai registers, lsfr_state
+getc_seed_rng:
+ .block
+ ;; Read character from acia
+ ;; We also use the time between keystrokes
+ ;; as entropy source for the RNG
+ jsr lfsr.step
+ lda io.acia_status_reg
+ and #%00001000
+ beq io.getc_seed_rng
+ lda io.acia_data_reg
+ rts
+ .bend
+
+getc_nonblocking:
+ .block
+ ;; Non-blocking read: If
+ ;; character has been read,
+ ;; it is returned in A and
+ ;; C is cleared. If not,
+ ;; C is set
+ sec
+ lda io.acia_status_reg
+ and #%00001000
+ beq nothing_read
+ clc
+ lda io.acia_data_reg
+nothing_read:
+ rts
+ .bend
+
+;;; gets
+;;; Read up to 255 characters terminated by CR via acia
+;;; Input:
+;;; gets_len:
+;;; Max. length of input.
+;;; gets_str, gets_str+1:
+;;; 2 bytes on zero page containing destination address
+;;; +-------------------------------------------------------+
+;;; |IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! |
+;;; +-------------------------------------------------------+
+;;; Note that a terminating zero is added to the string.
+;;; Therefore gets_len+1 bytes may be written!
+;;; Output:
+;;; Zero terminated string at gets_str, gets_str+1
+;;; Changes:
+;;; a, y, ACAI registers
+gets:
+ .block
+ ;; Read string terminated by CR
+ ldy #$00
+loop:
+ phy
+ jsr io.getc
+ jsr io.putc
+ ply
+ cmp #$0d ; GOT CR?
+ beq terminate_string ; if not
+ sta (io.gets_str), y ; store character
+ iny ; if max length
+ cpy io.gets_len ; not reached,
+ bne loop ; continue loop.
+terminate_string:
+ lda #$00
+ sta (io.gets_str), y
+ rts
+ .bend
+
+;;; str2byte
+;;; Convert number (<= 255) given as string to byte.
+;;; Expects zero-terminated string on stack
+;;; Input:
+;;; Zero-terminated string on stack
+;;; Output:
+;;; a:
+;;; Byte value
+;;; Changes:
+;;; a, x, y
+str2byte:
+ .block
+ jsr ds.create_stack_frame
+ #ds.PUSH $04 ; Local variable (string of length four)
+ lda #$00
+ pha
+ ldy #$00
+add_digit:
+ lda (ds.frame_ptr),y
+ beq done
+ sec ; Convert next digit
+ sbc #'0' ; from ascii to
+ sta (ds.frame_ptr),y ; number.
+ pla ; Multiply current
+ jsr io.multiply_by_ten ; accumulator by 10.
+ clc ; Add new
+ adc (ds.frame_ptr),y ; digit.
+ pha
+ iny ; Advance to next digit
+ jmp add_digit
+done:
+ pla
+ #ds.sta_LOCAL 0
+ jsr ds.delete_stack_frame
+ .bend
+
+;;; multiply_by_ten
+;;; Multiply A by ten.
+;;; Input:
+;;; a: Number
+;;; Output:
+;;; a: Number * 10
+;;; Changes:
+;;; a, c
+multiply_by_ten:
+ .block
+ sta (ds.ptr)
+ lda #$00
+ clc
+ .for i = 0, i < 10, i += 1
+ adc (ds.ptr)
+ .next
+ rts
+ .bend
+
+.send rom
+.endn
diff --git a/roms/os/io.inc b/roms/os/io.inc
@@ -0,0 +1,130 @@
+;;; -*- asm -*-
+
+;;; Copyright 2021 Gerd Beuster (gerd@frombelow.net). This is free
+;;; software under the GNU GPL v3 license or any later version. See
+;;; COPYING in the root directory for details.
+
+.namespace io
+
+;;; SETUP
+;;; Setup serial connection: Initialize ACIA and clear screen.
+;;; Input:
+;;; -
+;;; Output:
+;;; -
+;;; Changes:
+;;; a, x, y, acia registers, io, pus_str
+
+SETUP .macro
+ jsr io.init_acia
+ ;; Terminal program on PC needs some time to start up
+ ldy #$ff
+ ldx #$ff
+io_setup_delay_delay:
+ dex
+ bne io_setup_delay_delay
+ dey
+ bne io_setup_delay_delay
+ jsr term.clear_screen
+ #term.SET_CURSOR #$01, #$01
+ .endm
+
+;;; PRINT(addr)
+;;; Send zero terminated string at addr via acia.
+;;; Input:
+;;; addr:
+;;; Address of zero terminated string (<= 256 characters)
+;;; Output:
+;;; -
+;;; Changes:
+;;; a, x, y, puts_str, put_str+1
+
+PRINT .macro
+ lda #<\1
+ sta io.puts_str
+ lda #>\1
+ sta io.puts_str+1
+ jsr io.puts
+ .endm
+
+;;; PRINTS(string) (<= 255 characters)
+;;; Send string via acia
+;;; Input:
+;;; string:
+;;; String to be send.
+;;; Output:
+;;; -
+;;; Changes:
+;;; a, x, y, puts_str, put_str+1
+
+PRINTS .macro
+ #io.PRINT saddr
+ jmp cont_PRINTS
+saddr:
+ .null \1
+cont_PRINTS:
+ .endm
+
+;;; PRINTSNL(string) (<= 253 characters)
+;;; Send string and newline via acia
+;;; Input:
+;;; string:
+;;; String to be send.
+;;; Output:
+;;; -
+;;; Changes:
+;;; a, x, y, puts_str, put_str+1
+
+PRINTSNL .macro
+ #io.PRINT saddr
+ jmp cont_PRINTSNL
+saddr:
+ .null \1, $0d, $0a
+cont_PRINTSNL:
+ .endm
+
+;;; INPUTS(addr, len)
+;;; Read up to len characters from acai and store them at addr
+;;; This is a wrapper for gets
+;;; Input:
+;;; addr:
+;;; Target address for zero-terminated string
+;;; len:
+;;; Maximal length of string to be read (not terminating zero!)
+;;; +-------------------------------------------------------+
+;;; |IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! |
+;;; +-------------------------------------------------------+
+;;; Note that a terminating zero is added to the string.
+;;; Therefore len+1 bytes may be written!
+;;; Output:
+;;; -
+;;; Changes:
+;;; a, y, ACAI registers
+
+INPUTS .macro
+ lda #<\1
+ sta io.gets_str
+ lda #>\1
+ sta io.gets_str+1
+ lda \2
+ sta io.gets_len
+ jsr io.gets
+ .endm
+
+;;; PRINTNL
+;;; Send newline via acia
+;;; Input:
+;;; -
+;;; Output:
+;;; -
+;;; Changes:
+;;; a, x, acai registers
+
+PRINTNL .macro
+ lda #$0d
+ jsr io.putc
+ lda #$0a
+ jsr io.putc
+ .endm
+
+.endn
diff --git a/roms/os/lfsr.asm b/roms/os/lfsr.asm
@@ -0,0 +1,72 @@
+;;; Copyright 2021 Gerd Beuster (gerd@frombelow.net). This is free
+;;; software under the GNU GPL v3 license or any later version. See
+;;; COPYING in the root directory for details.
+
+;;; 16 Bit Galouis LFSR
+;;; If you use the whole state as RNG, the quality of the
+;;; random numbers is rather poor. Just extracting one
+;;; bit from the state yields random numbers with reasonable
+;;; statistical properties.
+
+;;; init
+;;; Initialize lfsr
+;;; Input:
+;;; -
+;;; Output:
+;;; -
+;;; Changes:
+;;; lfsr
+lfsr .namespace
+
+.section zero_page
+ ;; LFSR
+state: .word ?
+.send zero_page
+
+.section rom
+
+init:
+ .block
+ #mem.STORE_WORD $beef, lfsr.state
+ .bend
+
+;;; step
+;;; update lfsr
+;;; Input:
+;;; -
+;;; Output:
+;;; -
+;;; Changes:
+;;; lfsr
+step:
+ .block
+ asl lfsr.state
+ lda lfsr.state+1
+ rol a
+ bcc cont
+ eor #$0b
+cont:
+ sta lfsr.state+1
+ lda lfsr.state
+ adc #$00
+ sta lfsr.state
+ rts
+ .bend
+
+.if false
+;;; Test function for LFSR
+test_lfsr: .block
+loop:
+ jsr lfsr.step
+ lda lfsr.state+1
+ jsr puth
+ lda lfsr.state
+ jsr io.puth
+ PRINTSNL("")
+ jsr io.getc
+ jmp loop
+ .bend
+.endif ; false
+
+.send rom
+.endn
diff --git a/roms/os/os.asm b/roms/os/os.asm
@@ -0,0 +1,234 @@
+;;; Copyright 2021 Gerd Beuster (gerd@frombelow.net). This is free
+;;; software under the GNU GPL v3 license or any later version. See
+;;; COPYING in the root directory for details.
+
+ DEBUG = false
+ ;; DEBUG = true
+
+ CLOCK_SPEED = 4 ;4 Mhz Clock
+
+.if SYMON
+ rom_start = $c000
+.else
+ rom_start = $e000
+.endif
+
+.include "os.inc"
+
+ * = $00
+.dsection zero_page
+.cerror * > $ff, "Zero page exhausted"
+ * = rom_start
+.dsection rom
+.cerror * > $ffff, "ROM exhausted"
+
+os .namespace
+ram_end = $7fff
+.endn
+
+;;; System jumps into memdump on NMIs. Since we want to be
+;;; able to handle IRQs in RAM, the ROM isr routine jump to the
+;;; address given here. After a reset, this RAM address is set to
+;;; default_irq_handler.
+.namespace os
+irq_vector = os.ram_end - $1
+.endn
+
+ .section rom
+;;; ----------------------------------------------------------
+;;; RESET
+;;;
+;;; Initialize serial interface, read code from serial
+;;; interface into RAM, execute it.
+;;; ----------------------------------------------------------
+
+boot: .block
+ sei
+ cld
+ ;; Reset interrupt vector
+ #mem.STORE_WORD os.default_irq_handler, os.irq_vector
+ jsr via.init
+ #io.SETUP
+ cli
+ #ds.INIT_STACK os.ram_end-$206+$1 ;Minimal stack size to access SD card
+ #io.PRINTSNL "***** Eris 2010 8-Bit System (" .. VERSION .. ") *****"
+ jsr check_for_program_download
+ bcs no_program_download
+ jmp download_program
+no_program_download:
+ ;; No program download?
+ ;; Start embedded program.
+ jmp default_program
+ .bend
+
+;;; If we receive a byte right after a reset, this
+;;; triggers a program download via serial line.
+;;; The value of the byte is the number of $100 byte
+;;; blocks to receive.
+check_for_program_download: .block
+ #io.PRINTSNL "Kallisti!"
+ #io.PRINTNL
+ ldy #$20 ; Outer loop counter
+ ldx #$00 ; Inner counter
+loop:
+ ;; Try to get number of 0x100 byte blocks to be read
+ jsr io.getc_nonblocking
+ bcc got_byte
+ dex
+ bne loop
+ dey
+ bne loop
+ sec
+got_byte:
+ rts
+ .bend
+
+download_program: .block
+ ;; Program is loaded to this memory address
+ start_addr = $0200
+ ;; Location of temporary variables
+ addr_pointer = $12 ; addr_pointer+1 stores MSB
+ msb_last_addr = $14
+ add_checksum = $15
+ xor_checksum = $16
+ ;; We expect the number of $100 blocks to be
+ ;; downloaded in A.
+ ;; The msb of the last block to be written is the msb
+ ;; of the start address plus the number of blocks.
+ clc
+ adc #>start_addr
+ sta msb_last_addr
+ ;; Read n * 0x100 bytes from acia
+ lda #<start_addr
+ sta addr_pointer
+ lda #>start_addr
+ sta addr_pointer+1
+ ;; We calculate a two checksums over the data:
+ ;; The first byte is the addition of all bytes
+ ;; received. The second byte is the xor of all
+ ;; bytes received.
+ lda #$00
+ sta add_checksum
+ sta xor_checksum
+transmit_block:
+ jsr io.getc
+ ;; Store data
+ ldy #$00
+ sta (addr_pointer), y
+ ;; Update checksums
+ pha
+ clc
+ adc add_checksum
+ sta add_checksum
+ pla
+ eor xor_checksum
+ sta xor_checksum
+ ;; Update LSB of target address and check if
+ ;; transmission of block is completed.
+ inc addr_pointer
+ bne transmit_block
+ ;; Transmission of one block completed.
+ ;; If we are not done, increase
+ ;; MSB of target address and continue
+ inc addr_pointer+1
+ lda addr_pointer+1
+ cmp msb_last_addr
+ bne transmit_block
+ ;; Transmission completed.
+ ;; Transmit result of checksum calculation
+ lda add_checksum
+ jsr io.putc
+ lda xor_checksum
+ jsr io.putc
+ ;; Start program
+ jmp $0200
+ .bend
+
+memdump:
+ .block
+ ldx #$00 ; Delay loop loop
+ ldy #$00 ; to make sure that
+delay: ; we do not mess up
+ dey ; serial transfer.
+ bne delay
+ dex
+ bne delay
+ #io.PRINTSNL x"0d" .. x"0a" .. "-- BEGIN MEMORY DUMP --"
+ #io.PRINTS "Stack Pointer: "
+ tsx
+ txa
+ jsr io.puth
+ #mem.STORE_WORD $0000, os.memdump_ptr ; Start address of memory dump
+next_line:
+ jsr print_nl_and_address
+ ldy #$00
+next_byte:
+ lda (os.memdump_ptr),y ; Print next memory address.
+ phy
+ jsr io.puth
+ lda #' '
+ jsr io.putc
+ ply
+ iny ; Advance byte counter.
+ cpy #16 ; 16 entries
+ bne next_byte ; per line.
+ lda os.memdump_ptr ; Check
+ cmp #<(os.ram_end-15) ; if
+ bne not_finished ; we
+ lda os.memdump_ptr+1 ; are
+ cmp #>(os.ram_end-15) ; done.
+ bne not_finished
+ #io.PRINTSNL x"0d" .. x"0a" .. "-- END MEMORY DUMP --"
+ rti
+not_finished:
+ #mem.ADD_WORD os.memdump_ptr, 16 ; Advance memory pointer
+ jmp next_line
+print_nl_and_address:
+ #io.PRINTNL
+ lda os.memdump_ptr+1
+ jsr io.puth
+ lda os.memdump_ptr
+ jsr io.puth
+ #io.PRINTS ": "
+ rts
+ .bend
+
+
+.include "io.asm"
+.include "ds.asm"
+.include "lfsr.asm"
+.include "via.asm"
+.include "sd.asm"
+.include "term.asm"
+
+.namespace os
+.section zero_page
+memdump_ptr: .word ?
+rom_zero_page_end:
+.send zero_page
+.endn
+
+;;; Default IRQ handler. Unless the user program changes
+;;; the irq_vector, IRQs are handled here.
+.namespace os
+default_irq_handler:
+.endn
+ rti
+
+derefer_ram_irq:
+ ;; Jump to the address given in the IRQ vector
+ ;; in RAM
+ jmp (os.irq_vector)
+
+default_program:
+;; .include "../../sw/10print/10print.asm"
+;; .include "../../sw/ttt/ttt.asm"
+.include "../../sw/load_from_card/load_from_card.asm"
+
+;;; Vectors
+ .fill $fffa-*, $ff
+ .word memdump ; nmi
+ .word boot ; reset
+ .word derefer_ram_irq ; irq
+
+.send rom
diff --git a/roms/os/os.inc b/roms/os/os.inc
@@ -0,0 +1,123 @@
+;;; -*- asm -*-
+
+;;; Copyright 2021 Gerd Beuster (gerd@frombelow.net). This is free
+;;; software under the GNU GPL v3 license or any later version. See
+;;; COPYING in the root directory for details.
+
+;;; This file may be included from os.asm or from
+;;; some userland program.
+;;; We use the definition of CLOCK_SPPED in os.asm to
+;;; check if we are compiling in the context of a userland
+;;; program.
+.weak
+ CLOCK_SPEED = 0
+.endweak
+.if CLOCK_SPEED == 0
+ .if SYMON
+ filename_addition = "_symon"
+ start_address = $0300
+ .else
+ filename_addition = ""
+ start_address = $0200
+ .endif
+os: .binclude "os" .. filename_addition .. ".l"
+io: .binclude "io" .. filename_addition .. ".l"
+ds: .binclude "ds" .. filename_addition .. ".l"
+sd: .binclude "sd" .. filename_addition .. ".l"
+lfsr: .binclude "lfsr" .. filename_addition .. ".l"
+via: .binclude "via" .. filename_addition .. ".l"
+term: .binclude "term" .. filename_addition .. ".l"
+ BOOT_EMBEDDED = false
+ ;; Set start addresses for program and
+ ;; zero page variables
+ * = os.rom_zero_page_end
+ .dsection zero_page
+ * = start_address
+.else
+ BOOT_EMBEDDED = true
+.endif
+
+.include "io.inc"
+.include "ds.inc"
+.include "sd.inc"
+.include "term.inc"
+
+;;; -----------------------------------
+;;;
+;;; Miscellaneous utility functions
+;;;
+;;; -----------------------------------
+
+mem .namespace
+
+SET_BITS .macro
+ lda \1
+ ora \2
+ sta \1
+ .endm
+
+CLEAR_BITS .macro
+ lda \1
+ and ~\2
+ sta \1
+ .endm
+
+TOGGLE_BITS .macro
+ lda \1
+ eor \2
+ sta \1
+ .endm
+
+;;; STORE_WORD(word,dst)
+;;; Store word at dst
+;;; Input:
+;;; word
+;;; Output:
+;;; -
+;;; Changes:
+;;; a, dst
+STORE_WORD .macro
+ lda #<\1
+ sta \2
+ lda #>\1
+ sta \2+1
+ .endm
+
+
+COPY_WORD_IMMEDIATE: .macro
+ lda #<\1
+ sta \2
+ lda #>\1
+ sta \2+1
+ .endm
+
+ ;; Copy macro for absolute and indirect addressing modes
+COPY_WORD_ABSOLUTE_INDIRECT: .macro
+ lda \1
+ sta \2
+ ldy #$01
+ lda \1,y
+ sta \2,y
+ .endm
+
+SUB_WORD: .macro
+ lda \1
+ sec
+ sbc #<\2
+ sta \1
+ lda \1+1
+ sbc #>\2
+ sta \1+1
+ .endm
+
+ADD_WORD: .macro
+ lda \1
+ clc
+ adc #<\2
+ sta \1
+ lda \1+1
+ adc #>\2
+ sta \1+1
+ .endm
+
+.endn
diff --git a/roms/os/sd.asm b/roms/os/sd.asm
@@ -0,0 +1,273 @@
+;;; Copyright 2021 Gerd Beuster (gerd@frombelow.net). This is free
+;;; software under the GNU GPL v3 license or any later version. See
+;;; COPYING in the root directory for details.
+
+;; -----------------------------------
+;;;
+;;; SD card communication interface
+;;;
+;;; -----------------------------------
+
+
+sd .namespace
+
+.section zero_page
+data: .word ?
+ ;; type does not need to be on the zero page.
+type: .byte ? ; $05 = SD; $c1 = SDHC
+ ;; Everything beyond these reserved variables available to
+ ;; programs in RAM.
+.send zero_page
+
+.section rom
+
+send_cmd:
+ .block
+ ldy #$00
+cmd_bytes_loop:
+ lda (sd.data),y
+ jsr via.spi_set
+ iny
+ cpy #$06
+ bne cmd_bytes_loop
+ lda #$ff ; Finalize transmission
+ jsr via.spi_set ; with dummy byte
+ rts
+ .bend
+
+
+print_block:
+ .block
+ ldx #$02
+ ldy #$00
+loop:
+ phx
+ phy
+ lda (sd.data),y
+ jsr io.puth
+ #io.PRINTS " "
+ ply
+ plx
+ iny
+ bne loop
+ #mem.ADD_WORD sd.data, $0100
+ dex
+ bne loop
+ #io.PRINTNL
+ #mem.SUB_WORD sd.data, $0200
+ rts
+ .bend
+
+read_block:
+ ;; Read a block of data
+ ;; Block number is passed via stack.
+ .block
+ jsr ds.create_stack_frame
+ #ds.PUSH $04 ; Local variable (32 bit block number)
+.if DEBUG
+ io.PRINTSNL "Sending CMD17"
+.endif
+ lda #$51 ; CMD17 (READ_SINGLE_BLOCK)
+ jsr via.spi_set
+ jsr send_block_number
+ lda #$01 ; Dummy CRC
+ jsr via.spi_set
+ jsr via.spi_get
+ cmp #$00
+ beq wait_for_start_token
+ jmp read_failed
+wait_for_start_token:
+ jsr via.spi_get_nonblocking
+ cmp #$ff
+ beq wait_for_start_token
+ cmp #$fe
+ bne read_failed
+ ldx #$02
+ ldy #$00
+loop:
+ phx
+ phy
+ jsr via.spi_get_nonblocking
+ ply
+ plx
+ sta (sd.data),y
+ iny
+ bne loop
+ #mem.ADD_WORD sd.data, $0100 ; Second half of block
+ dex
+ bne loop
+ #mem.SUB_WORD sd.data, $0200 ; Restore pointer
+ clc
+ #sd.RECEIVE $2 ; Get CRC
+ jsr ds.delete_stack_frame
+ rts
+read_failed:
+ #io.PRINTSNL "Error reading SD card!"
+ sec
+ jsr ds.delete_stack_frame
+ rts
+ .bend
+
+send_block_number:
+ .block
+ lda sd.type ; Old cards expect the position
+ cmp #$05 ; as bytes, not blocks
+ beq old_card
+ #ds.lda_LOCAL 0 ; Block number
+ jsr via.spi_set
+ #ds.lda_LOCAL 1
+ jsr via.spi_set
+ #ds.lda_LOCAL 2
+ jsr via.spi_set
+ #ds.lda_LOCAL 3
+ jsr via.spi_set
+ rts
+old_card:
+ #ds.lda_LOCAL 3
+ asl a
+ #ds.sta_LOCAL 3
+ #ds.lda_LOCAL 2
+ rol a
+ #ds.sta_LOCAL 2
+ #ds.lda_LOCAL 1
+ rol a
+ jsr via.spi_set
+ #ds.lda_LOCAL 2
+ jsr via.spi_set
+ #ds.lda_LOCAL 3
+ jsr via.spi_set
+ lda #$00
+ jsr via.spi_set
+ rts
+ .bend
+
+write_block:
+ ;; Write block at data to card.
+ ;; Block number is passed via stack.
+ .block
+ jsr ds.create_stack_frame
+ #ds.PUSH $04 ; Local variable (32 bit block number)
+.if DEBUG
+ io.PRINTSNL "Sending CMD24"
+.endif
+ lda #$58 ; CMD24 (WRITE_BLOCK)
+ jsr via.spi_set
+ jsr send_block_number
+ lda #$01 ; Dummy CRC
+ jsr via.spi_set
+ lda #$ff
+ jsr via.spi_set ; Dummy byte
+ #sd.RECEIVE $1
+ ;; Send start token
+ lda #$fe
+ jsr via.spi_set
+ ;; Send data
+ ldx #$02
+ ldy #$00
+write_loop:
+ phx
+ phy
+ lda (sd.data),y
+ jsr via.spi_set
+ ply
+ plx
+ iny
+ cpy #$00
+ bne write_loop
+ #mem.ADD_WORD sd.data, $0100 ; Second half of block
+ dex
+ cpx #$00
+ bne write_loop
+ #mem.SUB_WORD sd.data, $0200 ; Restore pointer
+ ;; Wait for write operation to complete
+ #sd.RECEIVE $1
+ cmp #$e5
+ bne error_writing
+wait_loop:
+ #sd.RECEIVE $1
+ beq wait_loop
+ jsr ds.delete_stack_frame
+ clc
+ rts
+error_writing:
+ #io.PRINTSNL "Error writing to SD card!"
+ jsr ds.delete_stack_frame
+ sec
+ rts
+ .bend
+
+close:
+ #mem.SET_BITS via.ra, #via.CS
+ rts
+
+open:
+ .block
+.if DEBUG
+ #io.PRINTSNL "Initializing SD Card"
+.endif
+ ;; Configure ports for in- and output
+ #mem.SET_BITS via.ddra, #(via.CS | via.SCK | via.MOSI)
+ #mem.CLEAR_BITS via.ddra, #via.MISO
+ ldy #$10 ; Error counter
+ phy
+start_init:
+ ;; Card power on sequence:
+ ;; Keep CS high, toggle clock for at least 74 cycles.
+ #mem.SET_BITS via.ra, #(via.CS | via.MOSI)
+ ;; Wait for 80 clock pulses
+ ldx #160
+boot_wait_loop:
+ #mem.TOGGLE_BITS via.ra, #via.SCK
+ dex
+ bne boot_wait_loop
+ #mem.CLEAR_BITS via.ra, #(via.CS|via.MOSI|via.SCK)
+ #sd.SEND_CMD [$40, $00, $00, $00, $00, $95] ; CMD0
+ #sd.RECEIVE $1
+ cmp #$01 ; Should be $01 (In idle state).
+ beq cont
+ ply
+ dey
+ phy
+ beq failed_init
+ jmp start_init ; If not, start over.
+failed_init:
+ ply
+ #io.PRINTSNL "Error initializing SD card!"
+ sec
+ rts
+cont:
+ ;; Card is in SPI mode
+ ply
+ #sd.SEND_CMD [$48, $00, $00, $01, $aa, $87] ; CMD8
+ #sd.RECEIVE $1
+ #sd.SEND_CMD [$45, $00, $00, $00, $00, $5b] ; CMD5
+ #sd.RECEIVE $1 ; $05 = SD; $c1 = SDHC
+ sta sd.type
+ #sd.SEND_CMD [$7b, $00, $00, $00, $00, $83] ; CMD59
+ #sd.RECEIVE $1 ; should be $01
+ ;; Send ACMD41 until card is initialized
+ ldy #$10 ; Fail counter
+ phy
+wait_for_card_initialized:
+ #sd.SEND_CMD [$77, $00, $00, $00, $00, $65] ; CMD55
+ #sd.RECEIVE $1 ; should be $01
+ #sd.SEND_CMD [$69, $40, $00, $00, $00, $95] ; ACMD41 (v1 & v2 card)
+ #sd.RECEIVE $1 ; $01 if busy, $00 if ready (initialized)
+ cmp #$00
+ beq initialized
+ ply
+ dey
+ phy
+ bne cont2 ; More tries?
+cont2:
+ jmp wait_for_card_initialized
+initialized:
+ ply
+ #sd.SEND_CMD [$50, $00, $00, $02, $00, $15] ; CMD16
+ #sd.RECEIVE $1
+ clc
+ rts
+ .bend
+
+.send rom
+.endn
diff --git a/roms/os/sd.inc b/roms/os/sd.inc
@@ -0,0 +1,55 @@
+;;; -*- asm -*-
+
+;;; Copyright 2021 Gerd Beuster (gerd@frombelow.net). This is free
+;;; software under the GNU GPL v3 license or any later version. See
+;;; COPYING in the root directory for details.
+
+;;; Macros for accessing SD cards
+
+.namespace sd
+
+SEND_CMD .macro
+.if DEBUG
+ PRINTS "Sending CMD$"
+ lda cmd
+ sec
+ sbc #$40
+ jsr io.puth
+ PRINTNL
+.endif
+ lda #<cmd
+ sta sd.data
+ lda #>cmd
+ sta sd.data+1
+ jsr sd.send_cmd
+ jmp cont_SEND_CMD
+cmd: .byte \1
+cont_SEND_CMD:
+ .endm
+
+
+
+RECEIVE .macro
+ ldx #\1
+loop_RECEIVE:
+ phx
+ jsr via.spi_get
+.if DEBUG
+ pha
+ jsr io.puth
+ PRINTS " "
+ pla
+.endif
+ plx
+ dex
+ bne loop_RECEIVE
+ pha
+.if DEBUG
+ PRINTNL
+.endif
+ ;; Read non-existing dummy byte for snychronization
+ jsr via.spi_get_nonblocking
+ pla
+ .endm
+
+.endn
diff --git a/roms/os/term.asm b/roms/os/term.asm
@@ -0,0 +1,264 @@
+;;; Copyright 2021 Gerd Beuster (gerd@frombelow.net). This is free
+;;; software under the GNU GPL v3 license or any later version. See
+;;; COPYING in the root directory for details.
+
+;;;
+;;; Special commands for ANSI terminals
+;;;
+
+term .namespace
+.section rom
+
+;;; ANSI text attributes
+plain = 0
+bold = 1
+blink = 5
+reverse = 7
+
+;;; ANSI colors
+black = 0
+red = 1
+green = 2
+yellow = 3
+blue = 4
+magenta = 5
+cyan = 6
+white = 7
+bright_black = 8
+bright_red = 9
+bright_green = 10
+bright_yellow = 11
+bright_blue = 12
+bright_magenta = 13
+bright_cyan = 14
+bright_white = 15
+
+;;; get_screen_size
+;;; Returns screen size in X, A.
+;;; If not supported by terminal, C is set
+;;; Input:
+;;; -
+;;; Output:
+;;; X: Screen width
+;;; A: Screen height
+;;; Changes:
+;;; a, x, y, c
+get_screen_size:
+ .block
+ jsr ds.create_stack_frame
+ #ds.PUSH $02
+ cursor_save_x = 0 ; Local variable
+ cursor_save_y = 1 ; Local variable
+ ;; Local variables 0 & 1: Saved cursor position
+ jsr term.get_cursor ; Save cursor position
+ bcs terminal_does_not_answer
+ #ds.sta_LOCAL cursor_save_y
+ txa
+ #ds.sta_LOCAL cursor_save_x
+ #SET_CURSOR #$FF, #$FF ; Try to move cursor to max. position
+ jsr term.get_cursor ; Get cursor position (= screen size)
+ pha ; Save it temporary
+ txa ; on the
+ pha ; hardware stack.
+ #ds.lda_LOCAL cursor_save_x ; Restore
+ tax ; old
+ #ds.lda_LOCAL cursor_save_y ; cursor
+ jsr term.set_cursor ; position
+ jsr ds.delete_stack_frame
+ pla
+ tax
+ pla
+ rts
+terminal_does_not_answer:
+ jsr ds.delete_stack_frame
+ ldx #80
+ lda #25
+ rts
+ .bend
+
+clear_screen:
+ #io.PRINTS x"1b" .. "[2J"
+ rts
+
+set_color:
+ pha
+ #io.PRINTS x"1b" .. "[38;5;"
+ pla
+ jsr io.putd
+ lda #"m"
+ jmp io.putc
+
+set_background_color:
+ pha
+ #io.PRINTS x"1b" .. "[48;5;"
+ pla
+ jsr io.putd
+ lda #"m"
+ jmp io.putc
+
+set_text:
+ tax
+ lda #"m"
+ jmp send_CSI
+
+cursor_up:
+ tax
+ lda #"A"
+ jmp send_CSI
+
+cursor_down:
+ tax
+ lda #"B"
+ jmp send_CSI
+
+cursor_forward:
+ tax
+ lda #"C"
+ jmp send_CSI
+
+cursor_back:
+ tax
+ lda #"D"
+ jmp send_CSI
+
+scroll_up:
+ tax
+ lda #"S"
+ jmp send_CSI
+
+scroll_down:
+ tax
+ lda #"T"
+ jmp send_CSI
+
+;;; Send ANSI escape sequence to terminal.
+;;; A contains command, X value
+send_CSI:
+ pha
+ phx
+ lda #$1b
+ jsr io.putc
+ lda #"["
+ jsr io.putc
+ pla
+ jsr io.putd
+ pla
+ jmp io.putc
+
+;;; set_cursor
+;;; Sets cursor to position x, a.
+;;; Position starts at 1, not 0!
+;;; Input:
+;;; x: x position
+;;; a: y position
+;;; Output:
+;;; -
+;;; Changes:
+;;; a, x, y, c
+;;; Set Cursor; x position in X, y position in A
+set_cursor:
+ phx
+ pha
+ lda #$1b
+ jsr io.putc
+ lda #"["
+ jsr io.putc
+ pla
+ jsr io.putd
+ lda #";"
+ jsr io.putc
+ pla
+ jsr io.putd
+ lda #"H"
+ jmp io.putc
+
+;;; get_cursor
+;;; Store cursor position in x, a.
+;;; Position starts at 1, not 0!
+;;; Input:
+;;; -
+;;; Output:
+;;; x: x position
+;;; a: y position
+;;; Changes:
+;;; a, x, y, c
+get_cursor:
+ .block
+ jsr ds.create_stack_frame
+ #ds.PUSH $08 ; Position as string
+ ;; We store the position string as local variables.
+ ;; By initializing the local variables with #$00,
+ ;; the strings terminate automatically when no
+ ;; more digits are written.
+ lda #$00
+ .for i = 0, i < 8, i += 1
+ #ds.sta_LOCAL i
+ .next
+ ;; Request cursor position
+ #io.PRINTS x"1b" .. "[6n"
+ ;; Read answer
+ ldx #$08 ; Number of tries
+ ldy #$ff ; to get an answer.
+wait_for_answer:
+ phx
+ phy
+ jsr io.getc_nonblocking ; $1b
+ ply
+ plx
+ bcc terminal_answers
+ dey
+ cpy #$00
+ bne wait_for_answer
+ dex
+ cpx #$00
+ bne wait_for_answer
+ ;; Terminal does not answer.
+ jsr ds.delete_stack_frame
+ lda #$00
+ ldx #$00
+ sec
+ rts
+terminal_answers:
+ jsr io.getc ; "["
+ jsr io.getc ; First digit y
+ #ds.sta_LOCAL 0
+ jsr io.getc ; Second digit y
+ cmp #";"
+ beq got_y_position
+ #ds.sta_LOCAL 1
+ jsr io.getc ; Third digit y
+ cmp #";"
+ beq got_y_position
+ #ds.sta_LOCAL 2
+ jsr io.getc ; Read and ignore terminating ';'
+got_y_position:
+ jsr io.getc ; First digit x
+ #ds.sta_LOCAL 4
+ jsr io.getc ; Second digit x
+ cmp #"R"
+ beq got_x_position
+ #ds.sta_LOCAL 5
+ jsr io.getc ; Third digit x
+ cmp #"R"
+ beq got_x_position
+ #ds.sta_LOCAL 6
+ jsr io.getc ; Read and ignore terminating 'R'
+got_x_position:
+ ;; x position is in local variable(s) 4...
+ ;; y position is in local variable(s) 0...
+ ;; Convert them to ints
+ #ds.CALL io.str2byte, [0, 1, 2, 3], #ds.lda_LOCAL
+ #ds.lda_PARAM 0
+ pha
+ #ds.CALL io.str2byte, [4, 5, 6, 7], #ds.lda_LOCAL
+ #ds.lda_PARAM 0
+ pha
+ jsr ds.delete_stack_frame
+ plx
+ pla
+ clc
+ rts
+ .bend
+
+.send rom
+.endn
diff --git a/roms/os/term.inc b/roms/os/term.inc
@@ -0,0 +1,66 @@
+;;; -*- asm -*-
+
+;;; Copyright 2021 Gerd Beuster (gerd@frombelow.net). This is free
+;;; software under the GNU GPL v3 license or any later version. See
+;;; COPYING in the root directory for details.
+
+;;; -----------------------------------
+;;;
+;;; ANSI Terminal
+;;;
+;;; -----------------------------------
+
+.namespace term
+
+SET_COLOR .macro color
+ lda #color
+ jsr term.set_color
+ .endm
+
+SET_BACKGROUND_COLOR .macro color
+ lda #color
+ jsr term.set_background_color
+ .endm
+
+SET_TEXT .macro attribute
+ lda #\attribute
+ jsr term.set_text
+ .endm
+
+CURSOR_UP .macro steps
+ lda #\steps
+ jsr term.cursor_up
+ .endm
+
+CURSOR_DOWN .macro steps
+ lda #\steps
+ jsr term.cursor_down
+ .endm
+
+CURSOR_FORWARD .macro steps
+ lda #\steps
+ jsr term.cursor_forward
+ .endm
+
+CURSOR_BACK .macro steps
+ lda #\steps
+ jsr term.cursor_back
+ .endm
+
+SCROLL_UP .macro steps
+ lda #\steps
+ jsr term.scroll_up
+ .endm
+
+SCROLL_DOWN .macro steps
+ lda #\steps
+ jsr term.scroll_DOWN
+ .endm
+
+SET_CURSOR .macro x, y
+ lda \y
+ ldx \x
+ jsr term.set_cursor
+ .endm
+
+.endn
diff --git a/roms/os/via.asm b/roms/os/via.asm
@@ -0,0 +1,146 @@
+;;; Copyright 2021 Gerd Beuster (gerd@frombelow.net). This is free
+;;; software under the GNU GPL v3 license or any later version. See
+;;; COPYING in the root directory for details.
+
+via .namespace
+
+.if SYMON
+ start = $8000
+.else
+ start = $c800
+.endif
+
+;;; -----------------------------------
+;;;
+;;; Generic VIA definitions & operations
+;;;
+;;; -----------------------------------
+
+ rb = start
+ ra = start+$1
+ ddrb = start+$2
+ ddra = start+$3
+ t1cl = start+$4
+ t1ch = start+$5
+ t1ll = start+$6
+ t1lh = start+$7
+ t2cl = start+$8
+ t2ch = start+$9
+ sr = start+$a
+ acr = start+$b
+ pcr = start+$c
+ ifr = start+$d
+ ier = start+$e
+ ra2 = start+$f
+
+;;; Initialize VIA: Switch all ports to output
+;;; and all pins to low, disable interrupts
+init:
+ lda #$ff
+ sta via.ddra
+ sta via.ddrb
+ lda #$00
+ sta via.ra
+ sta via.rb
+ lda #%01111111
+ sta via.ier ; Disable VIA interrupts
+ lda #$00
+ sta acr ; Disable timers & shift register
+ rts
+
+
+;;; -----------------------------------
+;;;
+;;; SPI read and write operations
+;;;
+;;; -----------------------------------
+
+
+.section zero_page
+ ;; Used by VIA for (de-)serialization
+spi_buffer: .byte ?
+.send zero_page
+
+ ;; Wiring:
+ ;; PA0: CS
+ CS = %00000001
+ ;; PA1: SCK
+ SCK = %00000010
+ ;; PA2: MOSI
+ MOSI = %00000100
+ ;; PA3: MISO
+ MISO = %00001000
+
+
+
+.section rom
+
+;;; Blocking read - read until
+;;; byte != $ff is read or retry counter
+;;; expires.
+spi_get:
+ .block
+ ldy #$00 ; Try up to $ff times
+retry_loop:
+ jsr via.spi_get_nonblocking
+ ;; If we read $ff, we did not receive
+ ;; a byte.
+ cmp #$ff
+ bne done
+ dey ; Try again unless retry
+ bne retry_loop ; counter expired.
+done:
+ #mem.CLEAR_BITS via.ra, #(MOSI|SCK) ; Set MOSI and SCK low on completion.
+ lda via.spi_buffer
+ rts
+ .bend
+
+;;; Return next byte; may be $ff in case of no transmission
+spi_get_nonblocking:
+ #mem.SET_BITS via.ra, #MOSI ; Keep MOSI high during read
+ lda #$00 ; Clear
+ sta via.spi_buffer ; receive buffer.
+ ldx #$08 ; 8 bit to read.
+read_bits_loop:
+ .block
+ asl via.spi_buffer ; Make room for next bit
+ #mem.CLEAR_BITS via.ra, #SCK ; Generate clock impulse
+ #mem.SET_BITS via.ra, #SCK
+ lda via.ra ; Get next bit.
+ and #MISO
+ cmp #MISO
+ bne cont ; Nothing to do if we got a zero bit.
+ inc via.spi_buffer ; Set lowest bit in spi_buffer
+cont:
+ dex
+ bne read_bits_loop
+ #mem.CLEAR_BITS via.ra, #(MOSI|SCK) ; Set MOSI and SCK low on completion.
+ lda via.spi_buffer
+ rts
+ .bend
+
+;;; Write byte via SPI
+spi_set:
+ .block
+ sta via.spi_buffer ; We send from spi_buffer
+ ldx #$08 ; 8 bit to write
+write_bits_loop:
+ #mem.CLEAR_BITS via.ra, #SCK ; Set bits on low clock
+ #mem.CLEAR_BITS via.ra,#MOSI
+ lda via.spi_buffer ; Get next bit to send
+ and #%10000000 ; Isolate bit to send.
+ beq cont ; Set MOSI accordingly.
+ #mem.SET_BITS via.ra,#MOSI
+ jmp cont
+cont:
+ #mem.SET_BITS via.ra, #SCK ; Send bits on high clock
+ asl via.spi_buffer ; Advance to next bit
+ dex
+ bne write_bits_loop
+ #mem.CLEAR_BITS via.ra,#SCK
+ rts
+ .bend
+
+.send rom
+
+.endn
diff --git a/roms/serial_char_out/Makefile b/roms/serial_char_out/Makefile
@@ -1,3 +0,0 @@
-TARGET=serial_char_out
-
-include ../Makefile.common
diff --git a/roms/serial_char_out/count_receive_errors.py b/roms/serial_char_out/count_receive_errors.py
@@ -1,25 +0,0 @@
-#!/usr/bin/env python3
-"""Count receive errors."""
-
-import pdb
-import serial
-
-character = b'A'
-block_size = 1000
-SERIAL_PORT = '/dev/ttyUSB0'
-SERIAL_SPEED = 19200
-
-with serial.Serial(SERIAL_PORT, SERIAL_SPEED) as s:
- errors = 0
- total = 0
- while True:
- for _ in range(block_size):
- total += 1
- c = s.read(1)
- if c != character:
- # pdb.set_trace()
- print('Got "{}"'.format(c))
- errors += 1
- print('{} errors of {} ({:01.4f} %)'.format(errors, total,
- float(errors)/total))
-
diff --git a/roms/serial_char_out/serial_char_out.asm b/roms/serial_char_out/serial_char_out.asm
@@ -1,51 +0,0 @@
- ;; Output sequence of 'A' via ACIA 65C51
-
-export .namespace
-.if SYMON
- * = $c000
- acia_base = $8800
-.else
- * = $e000
- acia_base = $dc00
-.endif
- acia_data_reg = acia_base
- acia_status_reg = acia_base + 1
- acia_cmd_reg = acia_base + 2
- acia_ctrl_reg = acia_base + 3
-
-init:
- ;; Reset acai
- sta acia_status_reg
- ;; 300 bps, 8 data bits, 1 stop bit
- lda #%00010110
- sta acia_ctrl_reg
- ;; No parity, no echo, no interrupts, DTR ready
- lda #%11001011
- sta acia_cmd_reg
-send_loop:
- lda #"A"
- sta acia_data_reg
- ;; 4 cycles per inner loop run
- ;; At 1 Mhz and 300 bps, transmitting one bit
- ;; takes 1*10**6/300 = 3333.3 cycles
- ;; Including start bit and stop bit, we have
- ;; to transmit 10 bit for 1 byte.
- ;; Thus, we should run the loop for
- ;; 3333.3*10/4 = 8333.25 0 $208d iterations.
- ;; We run for 2100 iterations.
- ldx #$21 ; 2 cycles
-wait_loop:
- ldy #$ff ; 2 cycles
-inner_loop:
- dey ; 2 cycles
- bne inner_loop ; 2 cycles
- dex ; 2 cycles
- bne wait_loop ; 2 cycles
- jmp send_loop ; 4 cycles
-
-;;; Vectors
- .fill $fffa-*, $ff
- .word $0000 ; nmi
- .word init ; reset
- .word $0000 ; irq
-.endn
diff --git a/roms/simple_loop/Makefile b/roms/simple_loop/Makefile
@@ -1,3 +0,0 @@
-TARGET=loop
-
-include ../Makefile.common
diff --git a/roms/simple_loop/loop.asm b/roms/simple_loop/loop.asm
@@ -1,23 +0,0 @@
-export .namespace
-.if SYMON
- rom_start = $c000
-.else
- rom_start = $e000
-.endif
- * = rom_start
-start:
- jmp middle
-
- .fill (rom_start+$10)-*, $ff
-middle:
- jmp high
-
- .fill (rom_start+$100)-*, $ff
-high: jmp start
-
-;;; Vectors
- .fill $fffa-*, $ff
- .word $0000 ; nmi
- .word start ; reset
- .word $0000 ; irq
-.endn
diff --git a/sw/10print/10print.asm b/sw/10print/10print.asm
@@ -1,14 +1,18 @@
+;;; Copyright 2021 Gerd Beuster (gerd@frombelow.net). This is free
+;;; software under the GNU GPL v3 license or any later version. See
+;;; COPYING in the root directory for details.
+
;;; If we are included in boot.asm, we
;;; should not set the start address
.weak
BOOT_EMBEDDED = false
.endweak
.if !BOOT_EMBEDDED
- .include "boot.inc"
+ .include "os.inc"
.endif
init:
cld
- jsr io.init_acia
+ #io.SETUP
jsr lfsr.init
jmp ten_print
@@ -41,5 +45,12 @@ slash:
#io.PRINTS '╱'
.endif
jsr io.putc
+ ;; Check if it is time to quit
+ jsr io.getc_nonblocking
+ tax
+ cmp #'q' ; Quit?
+ beq reset
jmp ten_print
+reset:
+ jmp ($fffc) ; Reset
.bend
diff --git a/sw/10print/lfsr.py b/sw/10print/lfsr.py
@@ -1,64 +0,0 @@
-#!/usr/bin/env python3
-"""Script to find constants for LSFR and to generate test values."""
-
-import pdb
-
-def find_poly():
- for p in range(0x100):
- poly = p * 0x100
- start = 0xBEEF
- state = start
- period = 0
- while True:
- # print("0x{:04x}".format(state))
- state <<= 1
- if (state >> 16) == 1:
- state ^= poly
- state += 1
- state &= 0xFFFF
- if state == start:
- break
- period += 1
- print("0x{:04X}: {}".format(poly, period))
-
-
-class LFSR:
- def __init__(self, state=0xBEEF, polynomial=0x0B00):
- self.state = state
- self.polynomial = polynomial
-
- def getBit(self):
- self.state <<= 1
- if (self.state >> 16) == 1:
- self.state ^= self.polynomial
- self.state += 1
- self.state &= 0xFFFF
- return (self.state & 1)
-
- def getByte(self):
- res = 0
- for _ in range(8):
- res <<= 1
- res += self.getBit()
- return bytes([res])
-
-def check_poly(poly):
- state = 0xBEEF
- for _ in range(65535):
- state <<= 1
- if (state >> 16) == 1:
- state ^= poly
- state += 1
- state &= 0xFFFF
- print("0x{:04X}".format(state))
-
-def write_random_numbers():
- with open('random.dat', 'wb') as f:
- l = LFSR()
- for _ in range(2500):
- f.write(l.getByte())
-
-if __name__ == '__main__':
- # find_poly()
- check_poly(0x0B00)
- # write_random_numbers()
diff --git a/sw/10print/test_rng.py b/sw/10print/test_rng.py
@@ -1,156 +0,0 @@
-#!/usr/bin/env python2
-
-"""FIPS 140-2: RNG Power-Up Tests"""
-
-# Asses the quality of your TRNG by running the statistical random
-# number generator tests from Chapter 4.9.1 (Power-Up Tests) of "FIPS
-# PUB 140-2 - SECURITY REQUIREMENTS FOR CRYPTOGRAPHIC MODULES". The
-# document is available on the handout server.
-
-FILENAME='random.dat'
-#FILENAME='random_radio_noise.dat'
-
-def readRandomBits(filename):
- """Read file and return it as list of bits."""
- rn = []
- rnFile = open(filename, 'rb')
- rn = map(ord, rnFile.read())
- rnFile.close()
- return(reduce(lambda x,y: x+int2bin(y,8), rn, []))
-
-def int2bin(x, n):
- """Convert integer to array of bits.
-
- x : integer
- n : length of bit array"""
- b = map(lambda x: ord(x)-ord('0'), list(bin(x)[2:]))
- return([0]*(n-len(b)) + b)
-
-def bin2int(b):
- """Convert array of bits to integer."""
- return(int("".join(map(lambda x: chr(x+ord('0')), b)), 2))
-
-def testRandomNumbers(randomBits):
- print('Monobit Test: %s' % repr(monobitTest(randomBits)))
- print('Poker Test: %s' % repr(pokerTest(randomBits)))
- print('Runs Test: %s' % repr(runsTest(randomBits)))
- print('Long Runs Test: %s' % repr(longRunsTest(randomBits)))
-
-def monobitTest(randomBits):
- """FIPS 140-2 monobit test"""
- # Count the number of ones in the 20,000 bit stream. Denote this
- # quantity by x.
- #
- # The test is passed if 9725 < x < 10275
- pass
-## BEGIN CODE SNIPPET ASSIGNMENT
- x = sum(randomBits)
- return((9725 < x) and (x < 10275))
-## END CODE SNIPPET
-
-def pokerTest(randomBits):
- """FIPS 140-2 poker test"""
- # Divide the 20000 bit stream into 5000 contiguous 4 bit
- # segments. Count and store the number of occurrences of the 16
- # possible 4 bit values. Denote f[i] as the number of each 4 bit
- # value i where 0 < i < 15.
- #
- # Evaluate the following:
- # 15
- # --
- # x = (16/5000) * ( \ f[i]^2 ) - 5000
- # /
- # --
- # i=0
- #
- # The test is passed if 2.16 < x < 46.17
- #
- # See fips_140_2.pdf, page 39-40
- pass
-## BEGIN CODE SNIPPET ASSIGNMENT
- f = [0]*16
- for i in xrange(0, len(randomBits), 4):
- f[bin2int(randomBits[i:i+4])] += 1
- cum = sum(map(lambda x: x*x, f))
- x = 16.0/5000*cum-5000
- return((2.16 < x) and (x < 46.17))
-## END CODE SNIPPET
-
-def runsTest(randomBits):
- """FIPS 140-2 runs test"""
- # A run is defined as a maximal sequence of consecutive bits of
- # either all ones or all zeros that is part of the 20000 bit
- # sample stream. The incidences of runs (for both consecutive
- # zeros and consecutive ones) of all lengths (>= 1) in the
- # sample stream should be counted and stored.
- #
- # The test is passed if the runs that occur (of lengths 1 through
- # 6) are each within the corresponding interval specified in the
- # table below. This must hold for both the zeros and ones (i.e.,
- # all 12 counts must lie in the specified interval). For the
- # purposes of this test, runs of greater than 6 are considered to
- # be of length 6.
- #
- # Length Required Interval
- # of Run
- # 1 2343 - 2657
- # 2 1135 - 1365
- # 3 542 - 708
- # 4 251 - 373
- # 5 111 - 201
- # 6+ 111 - 201
- #
- # See fips_140_2.pdf, page 40
- pass
-## BEGIN CODE SNIPPET ASSIGNMENT
- lowerLimits = [0, 2343, 1135, 542, 251, 111, 111]
- upperLimits = [25000, 2657, 1365, 708, 373, 201, 201]
- runs = [[0]*7]+[[0]*7]
- currentRun = 0
- for target in [0,1]:
- for i in randomBits:
- if(i == target):
- currentRun += 1
- if(currentRun > 6):
- currentRun = 6
- else:
- runs[target][currentRun] += 1
- currentRun = 0
- # Do not forget the last run!
- if(currentRun != 0):
- runs[target][currentRun] += 1
- return(all(map(lambda (x,y): x >= y, zip(runs[0], lowerLimits)))
- and
- all(map(lambda (x,y): x <= y, zip(runs[0], upperLimits)))
- and
- all(map(lambda (x,y): x >= y, zip(runs[1], lowerLimits)))
- and
- all(map(lambda (x,y): x <= y, zip(runs[1], upperLimits))))
-
-## END CODE SNIPPET
-
-def longRunsTest(randomBits):
- """FIPS 140-2 long runs test"""
- # A long run is defined to be a run of length 26 or more (of
- # either zeros or ones). On the sample of 20000 bits, the test is
- # passed if there are no long runs.
- #
- # See fips_140_2.pdf, page 40
- pass
-## BEGIN CODE SNIPPET ASSIGNMENT
- for target in [0,1]:
- currentRun = 0
- for i in randomBits:
- if(i == target):
- currentRun += 1
- if(currentRun == 26):
- return(False)
- else:
- currentRun = 0
- return(True)
-## END CODE SNIPPET
-
-
-if __name__ == "__main__":
- randomBits = readRandomBits(filename=FILENAME)
- testRandomNumbers(randomBits=randomBits)
diff --git a/sw/Makefile.common b/sw/Makefile.common
@@ -1,12 +1,13 @@
-CFLAGS=-C --line-numbers --tab-size=1 -Wall -c -b -I../../roms/boot/
+CFLAGS=-C --line-numbers --tab-size=1 -Wall -c -b -I../../roms/os/
+VERSION="$(shell git describe --tags)"
all: $(TARGET).bin $(TARGET)_symon.bin
%.bin: %.asm
- 64tass $(CFLAGS) -DSYMON=false -l $(TARGET).l -L $(TARGET).lst "$<" -o "$@"
+ 64tass $(CFLAGS) $(DEFS) -DVERSION="'$(VERSION)'" -DSYMON=false -l $(TARGET).l -L $(TARGET).lst "$<" -o "$@"
%_symon.bin: %.asm
- 64tass $(CFLAGS) -DSYMON=true -l $(TARGET)_symon.l -L $(TARGET)_symon.lst "$<" -o "$@"
+ 64tass $(CFLAGS) $(DEFS) -DVERSION="'$(VERSION)'" -DSYMON=true -l $(TARGET)_symon.l -L $(TARGET)_symon.lst "$<" -o "$@"
upload: $(TARGET).bin
../../tools/boot.py $(TARGET).bin
diff --git a/sw/SW.txt b/sw/SW.txt
@@ -9,11 +9,6 @@ access the SD card and how to use the data stack.
Tic-Tac-Toe
-* mem_test/
-
-Tests RAM write and read operations. Used to test if the CPU interacts
-with the RAM.
-
* serial_line_echo/
Test serial input/output.
@@ -22,15 +17,10 @@ Test serial input/output.
https://10print.org/
-* aaa/
-
-Output stream of 'a'. Used to calliberate serial interface.
-
* interrupts/
Testing interrupt handling.
-
* stack_test/
Testing the data stack.
diff --git a/sw/aaa/Makefile b/sw/aaa/Makefile
@@ -1,3 +0,0 @@
-TARGET=aaa
-
-include ../Makefile.common
diff --git a/sw/aaa/aaa.asm b/sw/aaa/aaa.asm
@@ -1,44 +0,0 @@
-.if SYMON
- .include "boot_symon.l"
-.else
- .include "boot.l"
-.endif
- .include "boot_macros.inc"
- * = $0300
-
-init: .block
- jsr init_acia
-loop:
- lda #'A'
- jsr my_putc
- jmp loop
- .bend
-
- clock = 4
-.switch CLOCK_SPEED
-.case 4 ; 4 Mhz Clock
- SERIAL_SEND_DELAY_X = $c3
- SERIAL_SEND_DELAY_Y = $02
-.case 2 ; 4 Mhz Clock
- SERIAL_SEND_DELAY_X = $c2
- SERIAL_SEND_DELAY_Y = $01
-.default ; 1 Mhz Clock
- SERIAL_SEND_DELAY_X = $88
- SERIAL_SEND_DELAY_Y = $01
-.endswitch
-my_putc: .block
- ;; Send character.
- ;; Due to bugs in the register and interrupt
- ;; handling of the WDC 65C02, we have to use
- ;; a manual delay.
- sta acia_data_reg
- ldy #SERIAL_SEND_DELAY_Y
-outer_loop:
- ldx #SERIAL_SEND_DELAY_X
-inner_loop:
- dex
- bne inner_loop
- dey
- bne outer_loop
- rts
- .bend
diff --git a/sw/ansi_test/ansi_test.asm b/sw/ansi_test/ansi_test.asm
@@ -1,25 +1,20 @@
- .include "boot.inc"
+;;; Copyright 2021 Gerd Beuster (gerd@frombelow.net). This is free
+;;; software under the GNU GPL v3 license or any later version. See
+;;; COPYING in the root directory for details.
+
+ .include "os.inc"
.dsection code
.section code
start_of_stack = end_of_code ; Stack starts right after code
init:
cld
- jsr io.init_acia
- #ds.INIT_STACK start_of_stack
- ;; Termin program on PC needs some time to start up
- ldy #$ff
- ldx #$ff
-delay:
- dex
- bne delay
- dey
- bne delay
+ #io.SETUP
jsr ansi_test
jsr io.getc
jsr print_all_colors
jsr io.getc
- jmp init
+ jmp ($fffc) ; Reset
ansi_test:
.block
diff --git a/sw/interrupts/interrupts.asm b/sw/interrupts/interrupts.asm
@@ -1,74 +1,80 @@
- .include "boot.inc"
+;;; Copyright 2021 Gerd Beuster (gerd@frombelow.net). This is free
+;;; software under the GNU GPL v3 license or any later version. See
+;;; COPYING in the root directory for details.
+
+ .include "os.inc"
.dsection code
.section zero_page
-interrupt_counter: .byte ?
+number_of_interrupts: .byte ?
.send zero_page
.section code
init:
- ;; jmp transmitter_interrupt
- ;; jmp receiver_interrupt
- jsr manual_nmi_irq
+ #io.SETUP
+ jsr timer_interrupt
jmp receiver_interrupt
-manual_nmi_irq:
- ;; Expects NMI or IRQ triggered manually.
- ;; Prints a message when an interrupt is received
+
+timer_interrupt:
.block
- jsr io.init_acia
- #mem.STORE_WORD isr_print_message_irq, os.irq_vector
- #mem.STORE_WORD isr_print_message_nmi, os.nmi_vector
- jsr io.getc
+ #io.PRINTSNL "Testing timer interrupt."
+ #mem.STORE_WORD isr_inc_interrupt, os.irq_vector
+ lda #%11000000 ; Enable timer1 interrupt
+ sta via.ier
+ lda #%01000000 ; Continous interrupts
+ sta via.acr
+ lda #$ff
+ sta via.t1cl ; Set timer1 start
+ sta via.t1ch ; value & start counter
lda #$00
- sta interrupt_counter
- #io.PRINTSNL 'Please trigger an NMI.'
-wait:
- lda interrupt_counter
- cmp #10
- bne wait
+ sta number_of_interrupts
+loop:
+ lda number_of_interrupts
+ cmp #$10
+ bne loop
+ lda #%01111111 ; Disable timer1 interrupt
+ sta via.ier
rts
-
-isr_print_message_irq:
- #io.PRINTSNL 'IRQ triggered.'
- rti
-
-isr_print_message_nmi:
- #io.PRINTS 'NMI triggered ('
- inc interrupt_counter
- lda interrupt_counter
- jsr io.putd
- #io.PRINTSNL ')'
- rti
.bend
-
+
receiver_interrupt:
.block
+ sei
#io.PRINTSNL "Testing ACIA receiver interrupt."
jsr io.init_acia
lda #$00
- sta interrupt_counter
- #mem.STORE_WORD isr_inc_interrupt_counter, os.irq_vector
+ sta number_of_interrupts
+ #mem.STORE_WORD isr_inc_interrupt, os.irq_vector
;; Receiver interrupt on
lda io.acia_cmd_reg
lda #%11001001
sta io.acia_cmd_reg
#io.PRINTSNL "Start typing ..."
+ cli
loop:
- wa
- jmp loop
+ lda number_of_interrupts
+ cmp #$10
+ bne loop
+ jmp ($fffc) ; Reset
.bend
-isr_inc_interrupt_counter:
+isr_inc_interrupt:
+ .block
+ ;; Clear ACIA interrupt
lda io.acia_status_reg
- lda interrupt_counter
+ ;; Clear timer interrupt
+ lda #%01111111
+ sta via.ifr
+ lda number_of_interrupts
inc a
- sta interrupt_counter
+ sta number_of_interrupts
jsr io.puth
#io.PRINTNL
rti
+ .bend
putc_irq:
diff --git a/sw/load_from_card/load_from_card.asm b/sw/load_from_card/load_from_card.asm
@@ -1,3 +1,7 @@
+;;; Copyright 2021 Gerd Beuster (gerd@frombelow.net). This is free
+;;; software under the GNU GPL v3 license or any later version. See
+;;; COPYING in the root directory for details.
+
;;; Start program form SD card.
;;; If we are included in boot.asm, we
;;; should not set the start address
@@ -5,7 +9,7 @@
BOOT_EMBEDDED = false
.endweak
.if !BOOT_EMBEDDED
- .include "boot.inc"
+ .include "os.inc"
.endif
@@ -19,37 +23,66 @@
;;;
;;; -----------------------------------
+.section zero_page
+delay0: .byte ?
+delay1: .byte ?
+delay2: .byte ?
+.send zero_page
+
init:
.block
.if SYMON
cld
jsr io.init_acia
- jsr spi.init_via
+ jsr via.init
.endif
.if !BOOT_EMBEDDED
;; Wait for key to start
jsr io.getc
.endif
#ds.INIT_STACK $7fff-$0400
-loop:
- #io.PRINTSNL "***** Eris 2010 8-Bit System *****"
- #io.PRINTNL
jsr ls
jsr choose_program
- bcs loop
- jsr execute
- jmp init
+ bcc execute
+ jmp ($fffc) ; Reset
.bend
load_address = $0200
choose_program:
+ .block
+ lda via.ddra ; Set
+ ora #%00010000 ; PA4
+ sta via.ddra ; as output.
+ lda #$00 ; Prepare
+ sta delay0 ; for
+ sta delay1 ; PA4
+ lda #$03 ; blink.
+ sta delay2
#io.PRINTSNL "Choose program (0-9). Any other key reloads card."
- jsr io.getc
+wait_for_input:
+ dec delay0
+ bne dont_blink
+ dec delay1
+ bne dont_blink
+ dec delay2
+ bne dont_blink
+ lda #$03
+ sta delay2
+ lda via.ra ; Blink
+ eor #%00010000 ; PA4
+ sta via.ra
+dont_blink:
+ jsr io.getc_nonblocking
+ bcs wait_for_input
sec
sbc #'0'
cmp #$0a ; Sets carry if not a digit
+ pha
+ jsr via.init ; Reset I/O lines
+ pla
rts
+ .bend
execute:
diff --git a/sw/mem_test/Makefile b/sw/mem_test/Makefile
@@ -1,3 +0,0 @@
-TARGET=mem_test
-
-include ../Makefile.common
diff --git a/sw/mem_test/mem_test.asm b/sw/mem_test/mem_test.asm
@@ -1,60 +0,0 @@
-.if SYMON
- .include "boot_symon.l"
-.else
- .include "boot.l"
-.endif
- .include "boot_macros.inc"
- * = $0300
-init:
- .block
- jsr init_acia
-loop:
- jsr mem_test
-end:
- jmp loop
- ;; jmp end
- .bend
-
-mem_test:
- .block
- #PRINTSNL "Memory test"
- ;; Address to be tested is stored as 16 bit value at $40.
- ;; We start the memory test at address $0400.
- ;; The test runs up to $8000. It fails at the last address,
- ;; because memory ends at $7fff.
- start = $0400
- lda #<start
- sta $40
- lda #>start
- sta $41
-loop:
- jsr mem_cell_test
- inc $40
- bne loop
- inc $41
- lda $41
- cmp #$80
- bne loop
- rts
-
-mem_cell_test:
- lda $41
- jsr puth
- lda $40
- jsr puth
- lda #$00
- sta ($40)
- lda ($40)
- cmp #$00
- bne fail
- lda #$ff
- sta ($40)
- lda ($40)
- cmp #$ff
- bne fail
- #PRINTSNL " OK"
- rts
-fail:
- #PRINTSNL " Fail!"
- rts
- .bend
diff --git a/sw/serial_line_echo/serial_line_echo.asm b/sw/serial_line_echo/serial_line_echo.asm
@@ -1,7 +1,12 @@
- .include "boot.inc"
+;;; Copyright 2021 Gerd Beuster (gerd@frombelow.net). This is free
+;;; software under the GNU GPL v3 license or any later version. See
+;;; COPYING in the root directory for details.
+
+ .include "os.inc"
init:
- jsr io.init_acia
+ cld
+ #io.SETUP
loop:
#io.PRINTSNL "Serial line echo"
#io.INPUTS $1000, #$10
diff --git a/sw/stack_test/stack_test.asm b/sw/stack_test/stack_test.asm
@@ -1,5 +1,9 @@
+;;; Copyright 2021 Gerd Beuster (gerd@frombelow.net). This is free
+;;; software under the GNU GPL v3 license or any later version. See
+;;; COPYING in the root directory for details.
+
;;; A data stack
- .include "boot.inc"
+ .include "os.inc"
.dsection code
.section code
@@ -10,16 +14,12 @@ init:
.block
cld
#ds.INIT_STACK start_of_stack
- jsr io.init_acia
-.if SYMON
-.else
- jsr io.getc
-.endif
+ #io.SETUP
jsr stack_test
jsr test_local_variables
jsr test_recursion
-.if false
-.endif
+ jsr io.getc
+ jmp ($fffc) ; Reset
loop:
jmp loop
.bend
@@ -110,14 +110,12 @@ stack_test:
.bend
CHECK_ERROR: .macro
- .block
beq error_cont
error:
#io.PRINTSNL "Error!"
error_loop:
jmp error_loop
error_cont:
- .bend
.endm
test_local_variables:
diff --git a/sw/ttt/board.asm b/sw/ttt/board.asm
@@ -1,3 +1,7 @@
+;;; Copyright 2021 Gerd Beuster (gerd@frombelow.net). This is free
+;;; software under the GNU GPL v3 license or any later version. See
+;;; COPYING in the root directory for details.
+
;;; This file contains the definitions and subroutines
;;; related to the game boards, like printing the
;;; game board and placing pieces. It also contains
@@ -305,6 +309,12 @@ print_board:
.block
ldx #$ff
phx
+.if !RUN_TESTS
+ jsr term.clear_screen
+ #term.SET_CURSOR #$01, #$01
+ #io.PRINTSNL '** Tic-Tac-Toe **'
+ #io.PRINTNL
+.endif
#io.PRINTSNL '+---+---+---+'
print_field:
lda #$03
@@ -576,6 +586,9 @@ print_game_state:
cmp #piece_none
bne no_draw
#io.PRINTSNL "Draw!"
+.if !RUN_TESTS
+ jsr io.getc
+.endif
rts
no_draw:
#io.PRINTSNL "Let the game continue!"
@@ -588,5 +601,8 @@ somebody_won:
adc #'0'
jsr io.putc
#io.PRINTSNL " wins!"
+.if !RUN_TESTS
+ jsr io.getc
+.endif
rts
.bend
diff --git a/sw/ttt/board_test.asm b/sw/ttt/board_test.asm
@@ -1,3 +1,7 @@
+;;; Copyright 2021 Gerd Beuster (gerd@frombelow.net). This is free
+;;; software under the GNU GPL v3 license or any later version. See
+;;; COPYING in the root directory for details.
+
;;; Expected value is given in res.
;;; A is compared to this value.
CHECK_ERROR .macro
diff --git a/sw/ttt/computer_player.asm b/sw/ttt/computer_player.asm
@@ -1,3 +1,7 @@
+;;; Copyright 2021 Gerd Beuster (gerd@frombelow.net). This is free
+;;; software under the GNU GPL v3 license or any later version. See
+;;; COPYING in the root directory for details.
+
;;; ************************************************
;;;
;;; Random Computer Player
diff --git a/sw/ttt/computer_player_test.asm b/sw/ttt/computer_player_test.asm
@@ -1,5 +1,10 @@
+;;; Copyright 2021 Gerd Beuster (gerd@frombelow.net). This is free
+;;; software under the GNU GPL v3 license or any later version. See
+;;; COPYING in the root directory for details.
+
computer_player_test:
.block
+ jsr clear_memory
;; jsr mirror_board_test
;; jsr win_by_column
;; jsr win_by_diagonal
@@ -15,6 +20,27 @@ loop:
jmp loop
.bend
+clear_memory:
+ ;; Clear RAM for debugging purposes
+ .block
+ lda #<end_of_code
+ sta tmp
+ lda #>end_of_code
+ sta tmp+1
+clear_memory_loop:
+ lda #$aa
+ sta (tmp)
+ inc tmp
+ bne clear_memory_loop
+ inc tmp+1
+ lda tmp+1
+ cmp #$80
+ bne clear_memory_loop
+ rts
+ .bend
+
+
+
SET_BOARD: .macro
lda #\1
sta main_board
diff --git a/sw/ttt/human_player.asm b/sw/ttt/human_player.asm
@@ -1,3 +1,7 @@
+;;; Copyright 2021 Gerd Beuster (gerd@frombelow.net). This is free
+;;; software under the GNU GPL v3 license or any later version. See
+;;; COPYING in the root directory for details.
+
;;; No initialization necessary
human_player_init:
rts
@@ -23,6 +27,11 @@ human_player_ply:
;; if move is valid.
jsr io.getc
jsr io.putc
+ ;; Check for quit
+ cmp #"q"
+ bne not_quit
+ jmp ($fffc) ; Reset
+not_quit:
;; Check if user input
;; is in range [1..9]
cmp #"1"
diff --git a/sw/ttt/ttt.asm b/sw/ttt/ttt.asm
@@ -1,7 +1,11 @@
+;;; Copyright 2021 Gerd Beuster (gerd@frombelow.net). This is free
+;;; software under the GNU GPL v3 license or any later version. See
+;;; COPYING in the root directory for details.
+
;;; If we are included in boot.asm, we
;;; should not set the start address
- .include "boot.inc"
+ .include "os.inc"
.dsection ttt_game
@@ -46,12 +50,14 @@ board_ascii: .byte ?, ?, ?, ?, ?, ?, ?, ?, ?
start_ttt:
.block
cld
- jsr io.init_acia
jsr lfsr.init
.if RUN_TESTS
jsr run_tests
.endif
game_loop:
+.if !RUN_TESTS
+ #io.SETUP
+.endif
#io.PRINTSNL '** Tic-Tac-Toe **'
jsr io.putnl
lda #piece_x
@@ -77,6 +83,10 @@ choose_player:
#io.PRINTSNL '2 - Computer (perfect)'
#io.PRINTSNL '3 - Computer (random)'
jsr io.getc_seed_rng
+ cmp #'q'
+ bne not_quit
+ jmp ($fffc) ; Reset
+not_quit:
cmp #'1'
beq set_human_player
cmp #'2'
@@ -146,5 +156,6 @@ run_tests:
jmp run_tests
.endif
+end_of_code:
.send ttt_game
diff --git a/sw/via_test/via_test.asm b/sw/via_test/via_test.asm
@@ -1,34 +1,45 @@
+;;; Copyright 2021 Gerd Beuster (gerd@frombelow.net). This is free
+;;; software under the GNU GPL v3 license or any later version. See
+;;; COPYING in the root directory for details.
+
;;; Connect LEDs to the 65C22 and enjoy some blinkenlights
- .include "boot.inc"
+ .include "os.inc"
.dsection code
.section code
init:
.block
cld
- jsr io.init_acia
- jsr io.getc
+ #io.SETUP
#io.PRINTSNL "VIA Test"
- ;; Configure all ports of A as input
+ ;; Configure all ports of A except PA4 as input
lda #$00
- sta io.via_ddra
+ lda #%00010000
+ sta via.ddra
;; Configure all ports of B as output
- lda #$ff
- sta io.via_ddrb
+ lda #%11111111
+ sta via.ddrb
loop: jsr read_and_write_via
+ jsr io.getc_nonblocking
+ cmp #'q'
+ beq reset
+ cmp #'Q'
+ beq reset
jmp loop
+reset:
+ jmp ($fffc)
.bend
read_and_write_via:
.block
- inc io.via_orb
- #io.PRINTS "IRA: "
- lda io.via_ira
+ inc via.rb
+ #io.PRINTS "RA: "
+ lda via.ra
jsr io.puth
lda #" "
jsr io.putc
- #io.PRINTS "IRB: "
- lda io.via_irb
+ #io.PRINTS "RB: "
+ lda via.rb
jsr io.puth
jsr io.putnl
ldx #$00
@@ -38,6 +49,9 @@ delay:
bne delay
dex
bne delay
+ lda via.ra ; Blink PA4
+ eor #%00010000
+ sta via.ra
rts
.bend
diff --git a/tools/boot.py b/tools/boot.py
@@ -11,8 +11,8 @@ import os
SERIAL_PORT = '/dev/ttyUSB0'
SERIAL_SPEED = 19200
-# File upload does not work if Eris 2010 is run 100 kHz clock frequency
-# unless SLOW_CPU is set.
+# File upload does not work if Eris 2010 is run at 100 kHz clock
+# frequency unless SLOW_CPU is set.
SLOW_CPU = False
# SLOW_CPU = True
@@ -27,11 +27,12 @@ def upload_program(filename):
with serial.Serial(SERIAL_PORT, SERIAL_SPEED) as s:
print("Triggering reset.")
os.system('stty -F ' + SERIAL_PORT + ' -hup')
+ s.setDTR(False)
+ s.setDTR(True)
l = ""
- while l != b'READY!\r\n':
- s.setDTR(False)
- s.setDTR(True)
+ while l != b'Kallisti!\r\n':
l = s.readline()
+ s.readline()
print("Uploading program.")
e = str.encode(chr(blocks))
s.write(e)
diff --git a/tools/gfs.py b/tools/gfs.py
@@ -23,6 +23,8 @@ class GFS:
def find_device(self):
# Find device
+ #
+ # Security check: Only small SD cards of 1 GB or 4 GB are accepted.
known_devices = [['Generic-', 'SD_MMC', '512', '968.8M'],
['Generic', 'STORAGE_DEVICE', '512', '968.8M'],
['Generic', 'STORAGE_DEVICE', '512', '3.8G']]
diff --git a/tools/reset.py b/tools/reset.py
@@ -11,7 +11,5 @@ with serial.Serial(SERIAL_PORT, SERIAL_SPEED) as s:
print("Triggering reset.")
os.system('stty -F ' + SERIAL_PORT + ' -hup')
l = ""
- while l != b'READY!\r\n':
- s.setDTR(False)
- s.setDTR(True)
- l = s.readline()
+ s.setDTR(False)
+ s.setDTR(True)