eris2010

Documentation: http://frombelow.net/projects/eris2010/
Clone: git clone https://git.frombelow.net/eris2010.git
Log | Files | Refs | Submodules | README | LICENSE

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:
M.gitignore | 6------
ACOPYING | 674+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AREADME.md | 405+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
DREADME.txt | 1-
Ddoc/Documentation.txt | 197-------------------------------------------------------------------------------
Mhw/bus_logic/BUS_LOGIC.PLD | 31++++++++++++++++++-------------
Dmisc/6502_monitor/Makefile | 7-------
Dmisc/6502_monitor/machine.py | 22----------------------
Dmisc/6502_monitor/monitor.py | 266-------------------------------------------------------------------------------
Dmisc/clock_reset_attiny45/Makefile | 35-----------------------------------
Dmisc/clock_reset_attiny45/clock_and_reset.c | 71-----------------------------------------------------------------------
Dmisc/write_default_files.sh | 21---------------------
Mroms/Makefile.common | 11++++++-----
Mroms/ROMS.txt | 17+----------------
Droms/boot/Makefile | 3---
Droms/boot/boot.asm | 193-------------------------------------------------------------------------------
Droms/boot/boot.inc | 133-------------------------------------------------------------------------------
Droms/boot/ds.asm | 76----------------------------------------------------------------------------
Droms/boot/ds.inc | 79-------------------------------------------------------------------------------
Droms/boot/io.asm | 406-------------------------------------------------------------------------------
Droms/boot/io.inc | 103-------------------------------------------------------------------------------
Droms/boot/lfsr.asm | 68--------------------------------------------------------------------
Droms/boot/sd.asm | 269-------------------------------------------------------------------------------
Droms/boot/sd.inc | 51---------------------------------------------------
Droms/boot/spi.asm | 107-------------------------------------------------------------------------------
Droms/boot/term.asm | 260-------------------------------------------------------------------------------
Droms/boot/term.inc | 62--------------------------------------------------------------
Droms/nop/nop.bin | 2--
Aroms/os/Makefile | 3+++
Aroms/os/ds.asm | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aroms/os/ds.inc | 83+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aroms/os/io.asm | 388+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aroms/os/io.inc | 130+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aroms/os/lfsr.asm | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aroms/os/os.asm | 234+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aroms/os/os.inc | 123+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aroms/os/sd.asm | 273+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aroms/os/sd.inc | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aroms/os/term.asm | 264+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aroms/os/term.inc | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aroms/os/via.asm | 146+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Droms/serial_char_out/Makefile | 3---
Droms/serial_char_out/count_receive_errors.py | 25-------------------------
Droms/serial_char_out/serial_char_out.asm | 51---------------------------------------------------
Droms/simple_loop/Makefile | 3---
Droms/simple_loop/loop.asm | 23-----------------------
Msw/10print/10print.asm | 15+++++++++++++--
Dsw/10print/lfsr.py | 64----------------------------------------------------------------
Dsw/10print/test_rng.py | 156-------------------------------------------------------------------------------
Msw/Makefile.common | 7++++---
Msw/SW.txt | 10----------
Dsw/aaa/Makefile | 3---
Dsw/aaa/aaa.asm | 44--------------------------------------------
Msw/ansi_test/ansi_test.asm | 19+++++++------------
Msw/interrupts/interrupts.asm | 82++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Msw/load_from_card/load_from_card.asm | 51++++++++++++++++++++++++++++++++++++++++++---------
Dsw/mem_test/Makefile | 3---
Dsw/mem_test/mem_test.asm | 60------------------------------------------------------------
Msw/serial_line_echo/serial_line_echo.asm | 9+++++++--
Msw/stack_test/stack_test.asm | 18++++++++----------
Msw/ttt/board.asm | 16++++++++++++++++
Msw/ttt/board_test.asm | 4++++
Msw/ttt/computer_player.asm | 4++++
Msw/ttt/computer_player_test.asm | 26++++++++++++++++++++++++++
Msw/ttt/human_player.asm | 9+++++++++
Msw/ttt/ttt.asm | 15+++++++++++++--
Msw/via_test/via_test.asm | 38++++++++++++++++++++++++++------------
Mtools/boot.py | 11++++++-----
Mtools/gfs.py | 2++
Mtools/reset.py | 6++----
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)