Initial checkin. rel-0.1.0
authorKevin O'Connor <kevin@koconnor.net>
Tue, 26 Feb 2008 03:25:15 +0000 (22:25 -0500)
committerKevin O'Connor <kevin@koconnor.net>
Tue, 26 Feb 2008 03:25:15 +0000 (22:25 -0500)
27 files changed:
COPYING [new file with mode: 0644]
Makefile [new file with mode: 0644]
README [new file with mode: 0644]
TODO [new file with mode: 0644]
src/biosvar.h [new file with mode: 0644]
src/boot.c [new file with mode: 0644]
src/cbt.c [new file with mode: 0644]
src/clock.c [new file with mode: 0644]
src/cmos.h [new file with mode: 0644]
src/config.h [new file with mode: 0644]
src/disk.c [new file with mode: 0644]
src/disk.h [new file with mode: 0644]
src/farptr.h [new file with mode: 0644]
src/floppy.c [new file with mode: 0644]
src/font.c [new file with mode: 0644]
src/ioport.h [new file with mode: 0644]
src/kbd.c [new file with mode: 0644]
src/output.c [new file with mode: 0644]
src/post.c [new file with mode: 0644]
src/rombios32.lds.S [new file with mode: 0644]
src/romlayout.S [new file with mode: 0644]
src/serial.c [new file with mode: 0644]
src/system.c [new file with mode: 0644]
src/types.h [new file with mode: 0644]
src/util.h [new file with mode: 0644]
tools/buildrom.py [new file with mode: 0755]
tools/defsyms.py [new file with mode: 0755]

diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..94a9ed0
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://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 <http://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
+<http://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
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..28ac276
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,88 @@
+# Legacy Bios build system
+#
+# Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
+#
+# This file may be distributed under the terms of the GNU GPLv3 license.
+
+# Output directory
+OUT=out/
+
+# Source files
+SRC16=floppy.c disk.c system.c clock.c serial.c kbd.c output.c boot.c
+SRC32=post.c output.c
+
+# Default compiler flags (note -march=armv4 is needed for 16 bit insns)
+CFLAGS = -Wall -Os -MD -m32 -march=i386 -mregparm=2 -ffreestanding
+CFLAGS16 = -Wall -Os -MD -m32 -DMODE16 -march=i386 -mregparm=2 -ffreestanding -fno-jump-tables
+
+all: $(OUT) $(OUT)rom.bin
+
+# Run with "make V=1" to see the actual compile commands
+ifdef V
+Q=
+else
+Q=@
+endif
+
+.PHONY : all FORCE
+
+vpath %.c src
+vpath %.S src
+
+################ Build rules
+$(OUT)%.proc.16.s: $(OUT)%.16.s
+       @echo "  Moving data sections to text in $<"
+       $(Q)sed 's/\t.section\t.rodata.*// ; s/\t.data//' < $< > $@
+
+$(OUT)%.16.s: %.c
+       @echo "  Generating assembler for $<"
+       $(Q)$(CC) $(CFLAGS16) -fwhole-program -S -combine -c $< -o $@
+
+$(OUT)%.lds: %.lds.S
+       @echo "  Precompiling $<"
+       $(Q)$(CPP) -P $< -o $@
+
+$(OUT)%.bin: $(OUT)%.o
+       @echo "  Extracting binary $@"
+       $(Q)objcopy -O binary $< $@
+
+$(OUT)%.offset.auto.h: $(OUT)%.o
+       @echo "  Generating symbol offset header $@"
+       $(Q)nm $< | ./tools/defsyms.py > $@
+
+$(OUT)blob.16.s:
+       @echo "  Generating whole program assembler $@"
+       $(Q)$(CC) $(CFLAGS16) -fwhole-program -S -combine -c $(addprefix src/, $(SRC16)) -o $@
+
+$(OUT)romlayout16.o: romlayout.S $(OUT)blob.proc.16.s $(OUT)font.proc.16.s $(OUT)cbt.proc.16.s
+       @echo "  Generating 16bit layout of $@"
+       $(Q)$(CC) $(CFLAGS16) -c $< -o $@
+
+$(OUT)rom16.o: $(OUT)romlayout16.o
+       @echo "  Linking $@"
+       $(Q)ld -melf_i386 -Ttext 0 $< -o $@
+
+$(OUT)rom16.bin: $(OUT)rom16.o
+       @echo "  Extracting binary $@"
+       $(Q)objcopy -O binary $< $@
+
+$(OUT)romlayout32.o: $(OUT)rom16.offset.auto.h
+       @echo "  Compiling whole program $@"
+       $(Q)$(CC) $(CFLAGS) -fwhole-program -combine -c $(addprefix src/, $(SRC32)) -o $@
+
+$(OUT)rom32.o: $(OUT)romlayout32.o $(OUT)rombios32.lds
+       @echo "  Linking $@"
+       $(Q)ld -T $(OUT)rombios32.lds $< -o $@
+
+$(OUT)rom.bin: $(OUT)rom16.bin $(OUT)rom32.bin $(OUT)rom16.offset.auto.h $(OUT)rom32.offset.auto.h
+       @echo "  Building $@"
+       $(Q)./tools/buildrom.py
+
+####### Generic rules
+clean:
+       rm -rf $(OUT)
+
+$(OUT):
+       mkdir $@
+
+-include $(OUT)*.d
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..ce80499
--- /dev/null
+++ b/README
@@ -0,0 +1,71 @@
+This code implements an X86 legacy bios.  It is intended to be
+compiled using standard gnu tools (eg, gas and gcc).
+
+To build, one should be able to run "make" in the main directory.  The
+resulting file "out/rom.bin" contains the processed bios image.
+
+The code has been successfully compiled with gcc 4.1.2 and gas
+2.17.50.0.18.
+
+
+Overview of files:
+
+The src/ directory contains the bios source code.  The post.c code is
+compiled in 32bit mode.  The output.c code is compiled twice - once in
+16bit mode and once in 32bit mode.  The remaining c files are compiled
+in 16bit mode.
+
+The tools/ directory contains helper utilities for manipulating and
+building the final rom.
+
+The out/ directory is created by the build process - it contains all
+temporary and final files.
+
+
+Build overview:
+
+The 16bit code is compiled via gcc to assembler (file out/blob.16.s).
+The gcc "-fwhole-program" option is used to optimize the process so
+that gcc can efficiently compile and discard unneeded code.
+
+This resulting assembler code is pulled into romlayout.S.  The gas
+option ".code16gcc" is used prior to including the gcc generated
+assembler - this option enables gcc to be used to generate valid 16
+bit code.  The romlayout.S also defines all the mandatory bios visible
+memory locations.
+
+The post code (post.c) is written in 32bits.  The 16bit post vector
+(in romlayout.S) transitions the cpu into 32 bit mode before calling
+the initialization code in post.c.
+
+In the last step, the compiled 32 bit code is merged into the 16 bit
+code so that one binary file contains both.  Currently, both 16bit and
+32bit code will be located in the 64K block at segment 0xf000.
+
+
+GCC 16 bit limitations:
+
+Although the 16bit code is compiled with gcc, developers need to be
+aware of the environment.  In particular, global variables _must_ be
+treated specially.
+
+The code has full access to stack variables and general purpose
+registers.  The entry code in romlayout.S will push the original
+registers on the stack before calling the C code and then pop them off
+(including any required changes) before returning from the interrupt.
+Changes to CS, DS, and ES segment registers in C code is also safe.
+Changes to other segment registers (SS, FS, GS) need to be restored
+manually.
+
+Stack variables (and pointers to stack variables) work as they
+normally do in standard C code.
+
+However, variables stored outside the stack need to be accessed via
+the GET_VAR and SET_VAR macros.  This is due to the 16bit segment
+nature of the X86 cpu when it is in "real mode".  The C entry code
+will set DS and SS to point to the stack segment.  Variables not on
+the stack need to be accessed via an explicit segment register.
+Global constant definitions (those in 0xf000) can be accessed via the
+CS segment register.  Any other access requires altering one of the
+other segment registers (usually ES) and then accessing the variable
+via that segment register.
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..a528399
--- /dev/null
+++ b/TODO
@@ -0,0 +1,22 @@
+Make header files work with either 32bit or 16bit code.
+
+Fix makefiles so that they rebuild the required files automatically.
+
+Make sure gdt/idt tables are properly aligned
+
+Cleanup setting of ES on GET/SET_BDA
+
+Make sure inline assembly isn't preventing inlining of calling
+functions.
+
+Convert remaining parts of rombios.c to new code.
+
+Convert rombios32 and apm bios stuff to new code.
+
+Allow one to select adding 32 bit code to 0xf000 or in a separate
+location.
+
+Try generating bios tables at compile time.
+
+Move e820 map generation to post time (just have e820 code copy pre
+made tables back to user).
diff --git a/src/biosvar.h b/src/biosvar.h
new file mode 100644 (file)
index 0000000..8b15f5b
--- /dev/null
@@ -0,0 +1,184 @@
+// Variable layouts of bios.
+//
+// Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include "types.h" // u8
+#include "farptr.h" // SET_SEG
+
+
+/****************************************************************
+ * Bios Data Area (BDA)
+ ****************************************************************/
+
+struct ivec {
+    u16 offset;
+    u16 seg;
+};
+
+struct bios_data_area_s {
+    // 00:00
+    struct ivec ivecs[256];
+    // 30:00
+//    u8 stack[256];
+    // 40:00
+    u16 port_com1, port_com2, port_com3, port_com4;
+    u16 port_lpt1, port_lpt2, port_lpt3;
+    u16 ebda_seg;
+    // 40:10
+    u16 equipment_list_flags;
+    u8 pad1;
+    u16 mem_size_kb;
+    u8 pad2;
+    u8 ps2_ctrl_flag;
+    u16 kbd_flag;
+    u8 alt_keypad;
+    u16 kbd_buf_head;
+    u16 kbd_buf_tail;
+    // 40:1e
+    u8 kbd_buf[32];
+    u8 floppy_recalibration_status;
+    u8 floppy_motor_status;
+    // 40:40
+    u8 floppy_motor_counter;
+    u8 floppy_last_status;
+    u8 floppy_return_status[7];
+    u8 other1[0x7];
+    // 40:50
+    u8 other2[0x1c];
+    // 40:6c
+    u32 timer_counter;
+    // 40:70
+    u8 timer_rollover;
+    u8 other4[0x0f];
+    // 40:80
+    u16 kbd_buf_start_offset;
+    u16 kbd_buf_end_offset;
+    u8 other5[7];
+    u8 floppy_last_data_rate;
+    u8 other6[3];
+    u8 floppy_harddisk_info;
+    // 40:90
+    u8 floppy_media_state[4];
+    u8 floppy_track0;
+    u8 floppy_track1;
+    u8 kbd_mode;
+    u8 kbd_led;
+    u32 ptr_user_wait_complete_flag;
+    u32 user_wait_timeout;
+    // 40:A0
+    u8 rtc_wait_flag;
+} __attribute__((packed));
+
+// BDA floppy_recalibration_status bitdefs
+#define FRS_TIMEOUT (1<<7)
+
+// BDA rtc_wait_flag bitdefs
+#define RWS_WAIT_PENDING (1<<0)
+#define RWS_WAIT_ELAPSED (1<<7)
+
+// BDA floppy_media_state bitdefs
+#define FMS_DRIVE_STATE_MASK        (0x07)
+#define FMS_MEDIA_DRIVE_ESTABLISHED (1<<4)
+#define FMS_DOUBLE_STEPPING         (1<<5)
+#define FMS_DATA_RATE_MASK          (0xc0)
+
+// Accessor functions
+#define GET_BDA(var) ({                                         \
+    SET_SEG(ES, 0x0000);                                        \
+    GET_VAR(ES, ((struct bios_data_area_s *)0)->var); })
+#define SET_BDA(var, val) do {                                  \
+        SET_SEG(ES, 0x0000);                                    \
+        SET_VAR(ES, ((struct bios_data_area_s *)0)->var, val);  \
+    } while (0)
+#define CLEARBITS_BDA(var, val) do {                                    \
+        typeof(((struct bios_data_area_s *)0)->var) __val = GET_BDA(var); \
+        SET_BDA(var, (__val & ~(val)));                                 \
+    } while (0)
+#define SETBITS_BDA(var, val) do {                                      \
+        typeof(((struct bios_data_area_s *)0)->var) __val = GET_BDA(var); \
+        SET_BDA(var, (__val | (val)));                                  \
+    } while (0)
+
+
+/****************************************************************
+ * Extended Bios Data Area (EBDA)
+ ****************************************************************/
+
+struct extended_bios_data_area_s {
+    u8 size;
+    u8 other1[0x3c];
+
+    // FDPT - Can be splitted in data members if needed
+    u8 fdpt0[0x10];
+    u8 fdpt1[0x10];
+
+    u8 other2[0xC4];
+
+    // ATA Driver data
+    //ata_t   ata;
+
+#if BX_ELTORITO_BOOT
+    // El Torito Emulation data
+    cdemu_t cdemu;
+#endif // BX_ELTORITO_BOOT
+};
+
+
+/****************************************************************
+ * Extended Bios Data Area (EBDA)
+ ****************************************************************/
+
+#define UREG(ER, R, RH, RL) union { u32 ER; struct { u16 R; u16 R ## _hi; }; struct { u8 RL; u8 RH; u8 R ## _hilo; u8 R ## _hihi; }; }
+
+struct bregs {
+    u16 ds;
+    u16 es;
+    UREG(edi, di, di_hi, di_lo);
+    UREG(esi, si, si_hi, si_lo);
+    UREG(ebp, bp, bp_hi, bp_lo);
+    UREG(esp, sp, sp_hi, sp_lo);
+    UREG(ebx, bx, bh, bl);
+    UREG(edx, dx, dh, dl);
+    UREG(ecx, cx, ch, cl);
+    UREG(eax, ax, ah, al);
+    u16 ip;
+    u16 cs;
+    u16 flags;
+} __attribute__((packed));
+
+// bregs flags bitdefs
+#define F_CF (1<<9)
+
+static inline void
+set_cf(struct bregs *regs, int cond)
+{
+    if (cond)
+        regs->flags |= F_CF;
+    else
+        regs->flags &= ~F_CF;
+}
+
+
+/****************************************************************
+ * Bios Config Table
+ ****************************************************************/
+
+struct bios_config_table_s {
+    // XXX
+    u8 x;
+};
+
+extern struct bios_config_table_s BIOS_CONFIG_TABLE;
+
+
+/****************************************************************
+ * Memory layout info
+ ****************************************************************/
+
+#define SEG_BIOS     0xf000
+
+#define EBDA_SEG           0x9FC0
+#define EBDA_SIZE          1              // In KiB
+#define BASE_MEM_IN_K   (640 - EBDA_SIZE)
diff --git a/src/boot.c b/src/boot.c
new file mode 100644 (file)
index 0000000..828be14
--- /dev/null
@@ -0,0 +1,118 @@
+// 16bit code to load disk image and start system boot.
+//
+// Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2002  MandrakeSoft S.A.
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include "types.h" // VISIBLE
+#include "util.h" // irq_enable
+#include "biosvar.h" // struct bregs
+#include "farptr.h" // SET_SEG
+
+static inline void
+__call_irq(u8 nr)
+{
+    asm volatile("int %0" : : "N" (nr));
+}
+
+static inline u32
+call_irq(u8 nr, struct bregs *callregs)
+{
+    u32 flags;
+    asm volatile(
+        // Save current registers
+        "pushal\n"
+        // Pull in calling registers.
+        "movl 0x04(%%eax), %%edi\n"
+        "movl 0x08(%%eax), %%esi\n"
+        "movl 0x0c(%%eax), %%ebp\n"
+        "movl 0x14(%%eax), %%ebx\n"
+        "movl 0x18(%%eax), %%edx\n"
+        "movl 0x1c(%%eax), %%ecx\n"
+        "movl 0x20(%%eax), %%eax\n"
+        // Invoke interrupt
+        "int %1\n"
+        // Restore registers
+        "popal\n"
+        // Exract flags
+        "pushfw\n"
+        "popl %%eax\n"
+        : "=a" (flags): "N" (nr), "a" (callregs), "m" (*callregs));
+    return flags;
+}
+
+static void
+print_boot_failure()
+{
+    bprintf(0, "Boot failed\n");
+}
+
+static void
+try_boot()
+{
+    // XXX - assume floppy
+    u16 bootseg = 0x07c0;
+    u8 bootdrv = 0;
+
+    // Read sector
+    struct bregs cr;
+    memset(&cr, 0, sizeof(cr));
+    cr.dl = bootdrv;
+    SET_SEG(ES, bootseg);
+    cr.bx = 0;
+    cr.ah = 2;
+    cr.al = 1;
+    cr.ch = 0;
+    cr.cl = 1;
+    cr.dh = 0;
+    u32 status = call_irq(0x13, &cr);
+
+    if (status & F_CF) {
+        print_boot_failure();
+        return;
+    }
+
+    u16 bootip = (bootseg & 0x0fff) << 4;
+    bootseg &= 0xf000;
+
+    u32 segoff = (bootseg << 16) | bootip;
+    asm volatile (
+        "pushf\n"
+        "pushl %0\n"
+        "movb %b1, %%dl\n"
+        // Set the magic number in ax and the boot drive in dl.
+        "movw $0xaa55, %%ax\n"
+        // Zero some of the other registers.
+        "xorw %%bx, %%bx\n"
+        "movw %%bx, %%ds\n"
+        "movw %%bx, %%es\n"
+        "movw %%bx, %%bp\n"
+        // Go!
+        "iretw\n"
+        : : "r" (segoff), "ri" (bootdrv));
+}
+
+// Boot Failure recovery: try the next device.
+void VISIBLE
+handle_18(struct bregs *regs)
+{
+    debug_enter(regs);
+    try_boot();
+}
+
+// INT 19h Boot Load Service Entry Point
+void VISIBLE
+handle_19(struct bregs *regs)
+{
+    debug_enter(regs);
+    try_boot();
+}
+
+// Callback from 32bit entry - start boot process
+void VISIBLE
+begin_boot()
+{
+    irq_enable();
+    __call_irq(0x19);
+}
diff --git a/src/cbt.c b/src/cbt.c
new file mode 100644 (file)
index 0000000..015f16f
--- /dev/null
+++ b/src/cbt.c
@@ -0,0 +1,8 @@
+#include "biosvar.h" // CONFIG_BIOS_TABLE
+
+// bios variables
+
+struct bios_config_table_s BIOS_CONFIG_TABLE = {
+    // XXX
+    18,
+};
diff --git a/src/clock.c b/src/clock.c
new file mode 100644 (file)
index 0000000..5ca2b5c
--- /dev/null
@@ -0,0 +1,54 @@
+// 16bit code to handle system clocks.
+//
+// Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2002  MandrakeSoft S.A.
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include "biosvar.h" // struct bregs
+#include "util.h" // debug_enter
+#include "disk.h" // floppy_tick
+
+// INT 1Ah Time-of-day Service Entry Point
+void VISIBLE
+handle_1a(struct bregs *regs)
+{
+    debug_enter(regs);
+    set_cf(regs, 1);
+}
+
+// User Timer Tick
+void VISIBLE
+handle_1c(struct bregs *regs)
+{
+    debug_enter(regs);
+}
+
+// INT 08h System Timer ISR Entry Point
+void VISIBLE
+handle_08(struct bregs *regs)
+{
+//    debug_enter(regs);
+
+    floppy_tick();
+
+    u32 counter = GET_BDA(timer_counter);
+    counter++;
+    // compare to one days worth of timer ticks at 18.2 hz
+    if (counter >= 0x001800B0) {
+        // there has been a midnight rollover at this point
+        counter = 0;
+        SET_BDA(timer_rollover, GET_BDA(timer_rollover) + 1);
+    }
+
+    SET_BDA(timer_counter, counter);
+    // XXX - int #0x1c
+    eoi_master_pic();
+}
+
+// int70h: IRQ8 - CMOS RTC
+void VISIBLE
+handle_70(struct bregs *regs)
+{
+    debug_enter(regs);
+}
diff --git a/src/cmos.h b/src/cmos.h
new file mode 100644 (file)
index 0000000..33cde16
--- /dev/null
@@ -0,0 +1,51 @@
+// Definitions for X86 CMOS non-volatile memory access.
+//
+// Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+#ifndef __CMOS_H
+#define __CMOS_H
+
+#include "ioport.h" // inb, outb
+
+#define CMOS_RTC_SECONDS         0x00
+#define CMOS_RTC_SECONDS_ALARM   0x01
+#define CMOS_RTC_MINUTES         0x02
+#define CMOS_RTC_MINUTES_ALARM   0x03
+#define CMOS_RTC_HOURS           0x04
+#define CMOS_RTC_HOURS_ALARM     0x05
+#define CMOS_STATUS_B            0x0b
+#define CMOS_RESET_CODE          0x0f
+#define CMOS_FLOPPY_DRIVE_TYPE   0x10
+#define CMOS_EQUIPMENT_INFO      0x14
+#define CMOS_EXTMEM_LOW          0x30
+#define CMOS_EXTMEM_HIGH         0x31
+#define CMOS_EXTMEM2_LOW         0x34
+#define CMOS_EXTMEM2_HIGH        0x35
+
+// CMOS_STATUS_B bitdefs
+#define CSB_EN_ALARM_IRQ (1<<5)
+
+// CMOS_FLOPPY_DRIVE_TYPE bitdefs
+#define CFD_NO_DRIVE 0
+#define CFD_360KB    1
+#define CFD_12MB     2
+#define CFD_720KB    3
+#define CFD_144MB    4
+#define CFD_288MB    5
+
+static inline u8
+inb_cmos(u8 reg)
+{
+    outb(reg, PORT_CMOS_INDEX);
+    return inb(PORT_CMOS_DATA);
+}
+
+static inline void
+outb_cmos(u8 val, u8 reg)
+{
+    outb(reg, PORT_CMOS_INDEX);
+    outb(val, PORT_CMOS_DATA);
+}
+
+#endif // cmos.h
diff --git a/src/config.h b/src/config.h
new file mode 100644 (file)
index 0000000..53996b4
--- /dev/null
@@ -0,0 +1,8 @@
+// Configuration definitions.
+
+#define CONFIG_FLOPPY_SUPPORT 1
+#define CONFIG_PS2_MOUSE 0
+#define CONFIG_ATA 0
+#define CONFIG_STACK16_SEGMENT 0x00
+#define CONFIG_STACK16_OFFSET  0xfffe
+#define CONFIG_STACK32_OFFSET 0x80000
diff --git a/src/disk.c b/src/disk.c
new file mode 100644 (file)
index 0000000..8901b7d
--- /dev/null
@@ -0,0 +1,67 @@
+// 16bit code to access hard drives.
+//
+// Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2002  MandrakeSoft S.A.
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include "disk.h" // floppy_13
+#include "biosvar.h" // struct bregs
+#include "util.h" // debug_enter
+
+static void
+disk_13(struct bregs *regs, u8 drive)
+{
+    set_cf(regs, 1);
+}
+
+static void
+handle_legacy_disk(struct bregs *regs, u8 drive)
+{
+    if (drive < 0x80) {
+        floppy_13(regs, drive);
+        return;
+    }
+#if BX_USE_ATADRV
+    if (drive >= 0xE0) {
+        int13_cdrom(regs); // xxx
+        return;
+    }
+#endif
+
+    disk_13(regs, drive);
+}
+
+void VISIBLE
+handle_40(struct bregs *regs)
+{
+    debug_enter(regs);
+    handle_legacy_disk(regs, regs->dl);
+    debug_exit(regs);
+}
+
+// INT 13h Fixed Disk Services Entry Point
+void VISIBLE
+handle_13(struct bregs *regs)
+{
+    debug_enter(regs);
+    u8 drive = regs->dl;
+#if BX_ELTORITO_BOOT
+    if (regs->ah >= 0x4a || regs->ah <= 0x4d) {
+        int13_eltorito(regs);
+    } else if (cdemu_isactive() && cdrom_emulated_drive()) {
+        int13_cdemu(regs);
+    } else
+#endif
+        handle_legacy_disk(regs, drive);
+    debug_exit(regs);
+}
+
+// record completion in BIOS task complete flag
+void VISIBLE
+handle_76(struct bregs *regs)
+{
+    debug_enter(regs);
+    SET_BDA(floppy_harddisk_info, 0xff);
+    eoi_both_pics();
+}
diff --git a/src/disk.h b/src/disk.h
new file mode 100644 (file)
index 0000000..d7b3547
--- /dev/null
@@ -0,0 +1,33 @@
+// Definitions for X86 bios disks.
+//
+// Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include "ioport.h" // outb
+
+#define DISK_RET_SUCCESS     0x00
+#define DISK_RET_EPARAM      0x01
+#define DISK_RET_ECHANGED    0x06
+#define DISK_RET_EBOUNDARY   0x09
+#define DISK_RET_ECONTROLLER 0x20
+#define DISK_RET_ETIMEOUT    0x80
+#define DISK_RET_EMEDIA      0xC0
+
+static inline void
+eoi_master_pic()
+{
+    outb(PIC1_IRQ5, PORT_PIC1);
+}
+
+static inline void
+eoi_both_pics()
+{
+    outb(PIC2_IRQ13, PORT_PIC2);
+    eoi_master_pic();
+}
+
+// floppy.c
+struct bregs;
+void floppy_13(struct bregs *regs, u8 drive);
+void floppy_tick();
diff --git a/src/farptr.h b/src/farptr.h
new file mode 100644 (file)
index 0000000..1c3044b
--- /dev/null
@@ -0,0 +1,57 @@
+// Code to access multiple segments within gcc.
+//
+// Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#define READ8_SEG(SEG, var) ({                                          \
+    u8 __value;                                                         \
+    __asm__ __volatile__("movb %%" #SEG ":%1, %b0"                      \
+                         : "=Qi"(__value) : "m"(var));                  \
+    __value; })
+#define READ16_SEG(SEG, var) ({                                         \
+    u16 __value;                                                        \
+    __asm__ __volatile__("movw %%" #SEG ":%1, %w0"                      \
+                         : "=ri"(__value) : "m"(var));                  \
+    __value; })
+#define READ32_SEG(SEG, var) ({                                         \
+    u32 __value;                                                        \
+    __asm__ __volatile__("movl %%" #SEG ":%1, %0"                       \
+                         : "=ri"(__value) : "m"(var));                  \
+    __value; })
+#define WRITE8_SEG(SEG, var, value)                     \
+    __asm__ __volatile__("movb %b0, %%" #SEG ":%1"      \
+                         : : "Q"(value), "m"(var))
+#define WRITE16_SEG(SEG, var, value)                    \
+    __asm__ __volatile__("movw %w0, %%" #SEG ":%1"      \
+                         : : "r"(value), "m"(var))
+#define WRITE32_SEG(SEG, var, value)                    \
+    __asm__ __volatile__("movl %0, %%" #SEG ":%1"       \
+                         : : "r"(value), "m"(var))
+
+#define GET_VAR(seg, var) ({                                    \
+    typeof(var) __val;                                          \
+    if (__builtin_types_compatible_p(typeof(__val), u8))        \
+        __val = READ8_SEG(seg, var);                            \
+    else if (__builtin_types_compatible_p(typeof(__val), u16))  \
+        __val = READ16_SEG(seg, var);                           \
+    else if (__builtin_types_compatible_p(typeof(__val), u32))  \
+        __val = READ32_SEG(seg, var);                           \
+    __val; })
+
+#define SET_VAR(seg, var, val) do {                               \
+        if (__builtin_types_compatible_p(typeof(var), u8))        \
+            WRITE8_SEG(seg, var, (val));                          \
+        else if (__builtin_types_compatible_p(typeof(var), u16))  \
+            WRITE16_SEG(seg, var, (val));                         \
+        else if (__builtin_types_compatible_p(typeof(var), u32))  \
+            WRITE32_SEG(seg, var, (val));                         \
+    } while (0)
+
+#define SET_SEG(SEG, value)                                     \
+    __asm__ __volatile__("movw %w0, %%" #SEG : : "r"(value))
+#define GET_SEG(SEG) ({                                         \
+    u16 __seg;                                                  \
+    __asm__ __volatile__("movw %%" #SEG ", %w0" : "=r"(__seg)); \
+    __seg;})
+
diff --git a/src/floppy.c b/src/floppy.c
new file mode 100644 (file)
index 0000000..5e70df2
--- /dev/null
@@ -0,0 +1,757 @@
+// 16bit code to access floppy drives.
+//
+// Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2002  MandrakeSoft S.A.
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include "types.h" // u8
+#include "disk.h" // DISK_RET_SUCCESS
+#include "config.h" // CONFIG_FLOPPY_SUPPORT
+#include "biosvar.h" // struct bregs
+#include "util.h" // irq_disable
+#include "cmos.h" // inb_cmos
+
+#define BX_FLOPPY_ON_CNT 37   /* 2 seconds */
+
+////.org 0xefc7
+// Since no provisions are made for multiple drive types, most
+// values in this table are ignored.  I set parameters for 1.44M
+// floppy here
+char diskette_param_table[11] = {
+    0xAF,
+    0x02, // head load time 0000001, DMA used
+    0x25,
+    0x02,
+    18,
+    0x1B,
+    0xFF,
+    0x6C,
+    0xF6,
+    0x0F,
+    0x08,
+};
+
+// New diskette parameter table adding 3 parameters from IBM
+// Since no provisions are made for multiple drive types, most
+// values in this table are ignored.  I set parameters for 1.44M
+// floppy here
+char diskette_param_table2[14] VISIBLE = {
+    0xAF,
+    0x02, // head load time 0000001, DMA used
+    0x25,
+    0x02,
+    18,
+    0x1B,
+    0xFF,
+    0x6C,
+    0xF6,
+    0x0F,
+    0x08,
+    79,   // maximum track
+    0,    // data transfer rate
+    4,    // drive type in cmos
+};
+
+// Oddities:
+//   Return codes vary greatly - AL not cleared consistenlty, BDA return
+//      status not set consistently, sometimes panics.
+//   Extra outb(0x000a, 0x02) in read?
+//   Does not disable interrupts on failure paths.
+//   numfloppies used before set in int_1308
+//   int_1305 verifies track but doesn't use it?
+
+static inline void
+set_diskette_current_cyl(u8 drive, u8 cyl)
+{
+    if (drive)
+        SET_BDA(floppy_track1, cyl);
+    else
+        SET_BDA(floppy_track0, cyl);
+}
+
+static u16
+get_drive_type(u8 drive)
+{
+    // check CMOS to see if drive exists
+    u8 drive_type = inb_cmos(CMOS_FLOPPY_DRIVE_TYPE);
+    if (drive == 0)
+        drive_type >>= 4;
+    else
+        drive_type &= 0x0f;
+    return drive_type;
+}
+
+static u16
+floppy_media_known(u8 drive)
+{
+    if (!(GET_BDA(floppy_recalibration_status) & (1<<drive)))
+        return 0;
+    u8 v = GET_BDA(floppy_media_state[drive]);
+    if (!(v & FMS_MEDIA_DRIVE_ESTABLISHED))
+        return 0;
+    return 1;
+}
+
+static void
+floppy_reset_controller()
+{
+    // Reset controller
+    u8 val8 = inb(PORT_FD_DOR);
+    outb(val8 & ~0x04, PORT_FD_DOR);
+    outb(val8 | 0x04, PORT_FD_DOR);
+
+    // Wait for controller to come out of reset
+    while ((inb(PORT_FD_STATUS) & 0xc0) != 0x80)
+        ;
+}
+
+static void
+floppy_prepare_controller(u8 drive)
+{
+    CLEARBITS_BDA(floppy_recalibration_status, FRS_TIMEOUT);
+
+    // turn on motor of selected drive, DMA & int enabled, normal operation
+    u8 prev_reset = inb(PORT_FD_DOR) & 0x04;
+    u8 dor = 0x10;
+    if (drive)
+        dor = 0x20;
+    dor |= 0x0c;
+    dor |= drive;
+    outb(dor, PORT_FD_DOR);
+
+    // reset the disk motor timeout value of INT 08
+    SET_BDA(floppy_motor_counter, BX_FLOPPY_ON_CNT);
+
+    // wait for drive readiness
+    while ((inb(PORT_FD_STATUS) & 0xc0) != 0x80)
+        ;
+
+    if (prev_reset == 0) {
+        irq_enable();
+        u8 v;
+        do {
+            v = GET_BDA(floppy_recalibration_status);
+        } while ((v & FRS_TIMEOUT) == 0);
+        irq_disable();
+
+        v &= ~FRS_TIMEOUT;
+        SET_BDA(floppy_recalibration_status, v);
+    }
+}
+
+static u8
+floppy_pio(u8 *cmd, u8 cmdlen)
+{
+    floppy_prepare_controller(cmd[1] & 1);
+
+    // send command to controller
+    u8 i;
+    for (i=0; i<cmdlen; i++)
+        outb(cmd[i], PORT_FD_DATA);
+
+    irq_enable();
+    u8 v;
+    do {
+        if (!GET_BDA(floppy_motor_counter)) {
+            irq_disable();
+            floppy_reset_controller();
+            return DISK_RET_ETIMEOUT;
+        }
+        v = GET_BDA(floppy_recalibration_status);
+    } while (!(v & FRS_TIMEOUT));
+    irq_disable();
+
+    v &= ~FRS_TIMEOUT;
+    SET_BDA(floppy_recalibration_status, v);
+
+    if ((inb(PORT_FD_STATUS) & 0xc0) != 0xc0)
+        BX_PANIC("int13_diskette: ctrl not ready\n");
+
+    return 0;
+}
+
+static u8
+floppy_cmd(struct bregs *regs, u16 count, u8 *cmd, u8 cmdlen)
+{
+    // es:bx = pointer to where to place information from diskette
+    // port 04: DMA-1 base and current address, channel 2
+    // port 05: DMA-1 base and current count, channel 2
+    u16 page = regs->es >> 12;   // upper 4 bits
+    u16 base_es = regs->es << 4; // lower 16bits contributed by ES
+    u16 base_address = base_es + regs->bx; // lower 16 bits of address
+    // contributed by ES:BX
+    if (base_address < base_es)
+        // in case of carry, adjust page by 1
+        page++;
+
+    // check for 64K boundary overrun
+    u16 last_addr = base_address + count;
+    if (last_addr < base_address)
+        return DISK_RET_EBOUNDARY;
+
+    u8 mode_register = 0x4a; // single mode, increment, autoinit disable,
+    if (cmd[0] == 0xe6)
+        // read
+        mode_register = 0x46;
+
+    DEBUGF("floppy dma c2");
+    outb(0x06, PORT_DMA1_MASK_REG);
+    outb(0x00, PORT_DMA1_CLEAR_FF_REG); // clear flip-flop
+    outb(base_address, PORT_DMA_ADDR_2);
+    outb(base_address>>8, PORT_DMA_ADDR_2);
+    outb(0x00, PORT_DMA1_CLEAR_FF_REG); // clear flip-flop
+    outb(count, PORT_DMA_CNT_2);
+    outb(count>>8, PORT_DMA_CNT_2);
+
+    // port 0b: DMA-1 Mode Register
+    // transfer type=write, channel 2
+    outb(mode_register, PORT_DMA1_MODE_REG);
+
+    // port 81: DMA-1 Page Register, channel 2
+    outb(page, PORT_DMA_PAGE_2);
+
+    outb(0x02, PORT_DMA1_MASK_REG); // unmask channel 2
+
+    u8 ret = floppy_pio(cmd, cmdlen);
+    if (ret)
+        return ret;
+
+    // read 7 return status bytes from controller
+    u8 i;
+    for (i=0; i<7; i++) {
+        u8 v = inb(PORT_FD_DATA);
+        cmd[i] = v;
+        SET_BDA(floppy_return_status[i], v);
+    }
+
+    return 0;
+}
+
+static void
+floppy_drive_recal(u8 drive)
+{
+    // send Recalibrate command (2 bytes) to controller
+    u8 data[12];
+    data[0] = 0x07;  // 07: Recalibrate
+    data[1] = drive; // 0=drive0, 1=drive1
+    floppy_pio(data, 2);
+
+    SETBITS_BDA(floppy_recalibration_status, 1<<drive);
+    set_diskette_current_cyl(drive, 0);
+}
+
+static u16
+floppy_media_sense(u8 drive)
+{
+    u16 rv;
+    u8 config_data, media_state;
+
+    floppy_drive_recal(drive);
+
+    // for now cheat and get drive type from CMOS,
+    // assume media is same as drive type
+
+    // ** config_data **
+    // Bitfields for diskette media control:
+    // Bit(s)  Description (Table M0028)
+    //  7-6  last data rate set by controller
+    //        00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
+    //  5-4  last diskette drive step rate selected
+    //        00=0Ch, 01=0Dh, 10=0Eh, 11=0Ah
+    //  3-2  {data rate at start of operation}
+    //  1-0  reserved
+
+    // ** media_state **
+    // Bitfields for diskette drive media state:
+    // Bit(s)  Description (Table M0030)
+    //  7-6  data rate
+    //    00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
+    //  5  double stepping required (e.g. 360kB in 1.2MB)
+    //  4  media type established
+    //  3  drive capable of supporting 4MB media
+    //  2-0  on exit from BIOS, contains
+    //    000 trying 360kB in 360kB
+    //    001 trying 360kB in 1.2MB
+    //    010 trying 1.2MB in 1.2MB
+    //    011 360kB in 360kB established
+    //    100 360kB in 1.2MB established
+    //    101 1.2MB in 1.2MB established
+    //    110 reserved
+    //    111 all other formats/drives
+
+    switch (get_drive_type(drive)) {
+    case 1:
+        // 360K 5.25" drive
+        config_data = 0x00; // 0000 0000
+        media_state = 0x25; // 0010 0101
+        rv = 1;
+        break;
+    case 2:
+        // 1.2 MB 5.25" drive
+        config_data = 0x00; // 0000 0000
+        media_state = 0x25; // 0010 0101   // need double stepping??? (bit 5)
+        rv = 1;
+        break;
+    case 3:
+        // 720K 3.5" drive
+        config_data = 0x00; // 0000 0000 ???
+        media_state = 0x17; // 0001 0111
+        rv = 1;
+        break;
+    case 4:
+        // 1.44 MB 3.5" drive
+        config_data = 0x00; // 0000 0000
+        media_state = 0x17; // 0001 0111
+        rv = 1;
+        break;
+    case 5:
+        // 2.88 MB 3.5" drive
+        config_data = 0xCC; // 1100 1100
+        media_state = 0xD7; // 1101 0111
+        rv = 1;
+        break;
+    //
+    // Extended floppy size uses special cmos setting
+    case 6:
+        // 160k 5.25" drive
+        config_data = 0x00; // 0000 0000
+        media_state = 0x27; // 0010 0111
+        rv = 1;
+        break;
+    case 7:
+        // 180k 5.25" drive
+        config_data = 0x00; // 0000 0000
+        media_state = 0x27; // 0010 0111
+        rv = 1;
+        break;
+    case 8:
+        // 320k 5.25" drive
+        config_data = 0x00; // 0000 0000
+        media_state = 0x27; // 0010 0111
+        rv = 1;
+        break;
+    default:
+        // not recognized
+        config_data = 0x00; // 0000 0000
+        media_state = 0x00; // 0000 0000
+        rv = 0;
+    }
+
+    SET_BDA(floppy_last_data_rate, config_data);
+    SET_BDA(floppy_media_state[drive], media_state);
+    return rv;
+}
+
+static inline void
+floppy_ret(struct bregs *regs, u8 code)
+{
+    regs->ah = code;
+    SET_BDA(floppy_last_status, code);
+    set_cf(regs, code);
+}
+
+static inline void
+floppy_fail(struct bregs *regs, u8 code)
+{
+    regs->al = 0; // no sectors read
+    floppy_ret(regs, code);
+}
+
+static u16
+check_drive(struct bregs *regs, u8 drive)
+{
+    // see if drive exists
+    if (drive > 1 || !get_drive_type(drive)) {
+        floppy_fail(regs, DISK_RET_ETIMEOUT);
+        return 1;
+    }
+
+    // see if media in drive, and type is known
+    if (floppy_media_known(drive) == 0 && floppy_media_sense(drive) == 0) {
+        floppy_fail(regs, DISK_RET_EMEDIA);
+        return 1;
+    }
+    return 0;
+}
+
+// diskette controller reset
+static void
+floppy_1300(struct bregs *regs, u8 drive)
+{
+    if (drive > 1) {
+        floppy_ret(regs, DISK_RET_EPARAM);
+        return;
+    }
+    if (!get_drive_type(drive)) {
+        floppy_ret(regs, DISK_RET_ETIMEOUT);
+        return;
+    }
+    set_diskette_current_cyl(drive, 0); // current cylinder
+    floppy_ret(regs, DISK_RET_SUCCESS);
+}
+
+// Read Diskette Status
+static void
+floppy_1301(struct bregs *regs, u8 drive)
+{
+    u8 v = GET_BDA(floppy_last_status);
+    regs->ah = v;
+    set_cf(regs, v);
+}
+
+// Read Diskette Sectors
+static void
+floppy_1302(struct bregs *regs, u8 drive)
+{
+    if (check_drive(regs, drive))
+        return;
+
+    u8 num_sectors = regs->al;
+    u8 track       = regs->ch;
+    u8 sector      = regs->cl;
+    u8 head        = regs->dh;
+
+    if (head > 1 || sector == 0 || num_sectors == 0
+        || track > 79 || num_sectors > 72) {
+        BX_INFO("int13_diskette: read/write/verify: parameter out of range\n");
+        floppy_fail(regs, DISK_RET_EPARAM);
+        return;
+    }
+
+    // send read-normal-data command (9 bytes) to controller
+    u8 data[12];
+    data[0] = 0xe6; // e6: read normal data
+    data[1] = (head << 2) | drive; // HD DR1 DR2
+    data[2] = track;
+    data[3] = head;
+    data[4] = sector;
+    data[5] = 2; // 512 byte sector size
+    data[6] = sector + num_sectors - 1; // last sector to read on track
+    data[7] = 0; // Gap length
+    data[8] = 0xff; // Gap length
+
+    u16 ret = floppy_cmd(regs, (num_sectors * 512) - 1, data, 9);
+    if (ret) {
+        floppy_fail(regs, ret);
+        return;
+    }
+
+    if (data[0] & 0xc0) {
+        floppy_fail(regs, DISK_RET_ECONTROLLER);
+        return;
+    }
+
+    // ??? should track be new val from return_status[3] ?
+    set_diskette_current_cyl(drive, track);
+    // AL = number of sectors read (same value as passed)
+    floppy_ret(regs, DISK_RET_SUCCESS);
+}
+
+// Write Diskette Sectors
+static void
+floppy_1303(struct bregs *regs, u8 drive)
+{
+    if (check_drive(regs, drive))
+        return;
+
+    u8 num_sectors = regs->al;
+    u8 track       = regs->ch;
+    u8 sector      = regs->cl;
+    u8 head        = regs->dh;
+
+    if (head > 1 || sector == 0 || num_sectors == 0
+        || track > 79 || num_sectors > 72) {
+        BX_INFO("int13_diskette: read/write/verify: parameter out of range\n");
+        floppy_fail(regs, DISK_RET_EPARAM);
+        return;
+    }
+
+    // send write-normal-data command (9 bytes) to controller
+    u8 data[12];
+    data[0] = 0xc5; // c5: write normal data
+    data[1] = (head << 2) | drive; // HD DR1 DR2
+    data[2] = track;
+    data[3] = head;
+    data[4] = sector;
+    data[5] = 2; // 512 byte sector size
+    data[6] = sector + num_sectors - 1; // last sector to write on track
+    data[7] = 0; // Gap length
+    data[8] = 0xff; // Gap length
+
+    u8 ret = floppy_cmd(regs, (num_sectors * 512) - 1, data, 9);
+    if (ret) {
+        floppy_fail(regs, ret);
+        return;
+    }
+
+    if (data[0] & 0xc0) {
+        if (data[1] & 0x02) {
+            regs->ax = 0x0300;
+            set_cf(regs, 1);
+            return;
+        }
+        BX_PANIC("int13_diskette_function: read error\n");
+    }
+
+    // ??? should track be new val from return_status[3] ?
+    set_diskette_current_cyl(drive, track);
+    // AL = number of sectors read (same value as passed)
+    floppy_ret(regs, DISK_RET_SUCCESS);
+}
+
+// Verify Diskette Sectors
+static void
+floppy_1304(struct bregs *regs, u8 drive)
+{
+    if (check_drive(regs, drive))
+        return;
+
+    u8 num_sectors = regs->al;
+    u8 track       = regs->ch;
+    u8 sector      = regs->cl;
+    u8 head        = regs->dh;
+
+    if (head > 1 || sector == 0 || num_sectors == 0
+        || track > 79 || num_sectors > 72) {
+        BX_INFO("int13_diskette: read/write/verify: parameter out of range\n");
+        floppy_fail(regs, DISK_RET_EPARAM);
+        return;
+    }
+
+    // ??? should track be new val from return_status[3] ?
+    set_diskette_current_cyl(drive, track);
+    // AL = number of sectors verified (same value as passed)
+    floppy_ret(regs, DISK_RET_SUCCESS);
+}
+
+// format diskette track
+static void
+floppy_1305(struct bregs *regs, u8 drive)
+{
+    DEBUGF("floppy f05\n");
+
+    if (check_drive(regs, drive))
+        return;
+
+    u8 num_sectors = regs->al;
+    u8 head        = regs->dh;
+
+    if (head > 1 || num_sectors == 0 || num_sectors > 18) {
+        BX_INFO("int13_diskette: read/write/verify: parameter out of range\n");
+        floppy_fail(regs, DISK_RET_EPARAM);
+        return;
+    }
+
+    // send format-track command (6 bytes) to controller
+    u8 data[12];
+    data[0] = 0x4d; // 4d: format track
+    data[1] = (head << 2) | drive; // HD DR1 DR2
+    data[2] = 2; // 512 byte sector size
+    data[3] = num_sectors; // number of sectors per track
+    data[4] = 0; // Gap length
+    data[5] = 0xf6; // Fill byte
+
+    u8 ret = floppy_cmd(regs, (num_sectors * 4) - 1, data, 6);
+    if (ret) {
+        floppy_fail(regs, ret);
+        return;
+    }
+
+    if (data[0] & 0xc0) {
+        if (data[1] & 0x02) {
+            regs->ax = 0x0300;
+            set_cf(regs, 1);
+            return;
+        }
+        BX_PANIC("int13_diskette_function: read error\n");
+    }
+
+    set_diskette_current_cyl(drive, 0);
+    floppy_ret(regs, 0);
+}
+
+// read diskette drive parameters
+static void
+floppy_1308(struct bregs *regs, u8 drive)
+{
+    DEBUGF("floppy f08\n");
+
+    u8 drive_type = inb_cmos(CMOS_FLOPPY_DRIVE_TYPE);
+    u8 num_floppies = 0;
+    if (drive_type & 0xf0)
+        num_floppies++;
+    if (drive_type & 0x0f)
+        num_floppies++;
+
+    if (drive > 1) {
+        regs->ax = 0;
+        regs->bx = 0;
+        regs->cx = 0;
+        regs->dx = 0;
+        regs->es = 0;
+        regs->di = 0;
+        regs->dl = num_floppies;
+        set_cf(regs, 0);
+        return;
+    }
+
+    if (drive == 0)
+        drive_type >>= 4;
+    else
+        drive_type &= 0x0f;
+
+    regs->bh = 0;
+    regs->bl = drive_type;
+    regs->ah = 0;
+    regs->al = 0;
+    regs->dl = num_floppies;
+
+    switch (drive_type) {
+    case 0: // none
+        regs->cx = 0;
+        regs->dh = 0; // max head #
+        break;
+
+    case 1: // 360KB, 5.25"
+        regs->cx = 0x2709; // 40 tracks, 9 sectors
+        regs->dh = 1; // max head #
+        break;
+
+    case 2: // 1.2MB, 5.25"
+        regs->cx = 0x4f0f; // 80 tracks, 15 sectors
+        regs->dh = 1; // max head #
+        break;
+
+    case 3: // 720KB, 3.5"
+        regs->cx = 0x4f09; // 80 tracks, 9 sectors
+        regs->dh = 1; // max head #
+        break;
+
+    case 4: // 1.44MB, 3.5"
+        regs->cx = 0x4f12; // 80 tracks, 18 sectors
+        regs->dh = 1; // max head #
+        break;
+
+    case 5: // 2.88MB, 3.5"
+        regs->cx = 0x4f24; // 80 tracks, 36 sectors
+        regs->dh = 1; // max head #
+        break;
+
+    case 6: // 160k, 5.25"
+        regs->cx = 0x2708; // 40 tracks, 8 sectors
+        regs->dh = 0; // max head #
+        break;
+
+    case 7: // 180k, 5.25"
+        regs->cx = 0x2709; // 40 tracks, 9 sectors
+        regs->dh = 0; // max head #
+        break;
+
+    case 8: // 320k, 5.25"
+        regs->cx = 0x2708; // 40 tracks, 8 sectors
+        regs->dh = 1; // max head #
+        break;
+
+    default: // ?
+        BX_PANIC("floppy: int13: bad floppy type\n");
+    }
+
+    /* set es & di to point to 11 byte diskette param table in ROM */
+    regs->es = SEG_BIOS;
+    regs->di = (u16)diskette_param_table2;
+    /* disk status not changed upon success */
+}
+
+// read diskette drive type
+static void
+floppy_1315(struct bregs *regs, u8 drive)
+{
+    DEBUGF("floppy f15\n");
+    if (drive > 1) {
+        regs->ah = 0; // only 2 drives supported
+        // set_diskette_ret_status here ???
+        set_cf(regs, 1);
+        return;
+    }
+    u8 drive_type = get_drive_type(drive);
+
+    regs->ah = (drive_type != 0);
+    set_cf(regs, 0);
+}
+
+// get diskette change line status
+static void
+floppy_1316(struct bregs *regs, u8 drive)
+{
+    DEBUGF("floppy f16\n");
+    if (drive > 1) {
+        floppy_ret(regs, DISK_RET_EPARAM);
+        return;
+    }
+    floppy_ret(regs, DISK_RET_ECHANGED);
+}
+
+static void
+floppy_13XX(struct bregs *regs, u8 drive)
+{
+    BX_INFO("int13_diskette: unsupported AH=%02x\n", GET_AH());
+    floppy_ret(regs, DISK_RET_EPARAM);
+}
+
+void
+floppy_13(struct bregs *regs, u8 drive)
+{
+    if (CONFIG_FLOPPY_SUPPORT) {
+        switch (regs->ah) {
+        case 0x00: floppy_1300(regs, drive); break;
+        case 0x01: floppy_1301(regs, drive); break;
+        case 0x02: floppy_1302(regs, drive); break;
+        case 0x03: floppy_1303(regs, drive); break;
+        case 0x04: floppy_1304(regs, drive); break;
+        case 0x05: floppy_1305(regs, drive); break;
+        case 0x08: floppy_1308(regs, drive); break;
+        case 0x15: floppy_1315(regs, drive); break;
+        case 0x16: floppy_1316(regs, drive); break;
+        default:   floppy_13XX(regs, drive); break;
+        }
+    } else {
+        switch (regs->ah) {
+        case 0x01: floppy_1301(regs, drive); break;
+        default:   floppy_13XX(regs, drive); break;
+        }
+    }
+}
+
+// INT 0Eh Diskette Hardware ISR Entry Point
+void VISIBLE
+handle_0e(struct bregs *regs)
+{
+    debug_enter(regs);
+    if ((inb(PORT_FD_STATUS) & 0xc0) != 0xc0) {
+        outb(0x08, PORT_FD_DATA); // sense interrupt status
+        while ((inb(PORT_FD_STATUS) & 0xc0) != 0xc0)
+            ;
+        do {
+            inb(PORT_FD_DATA);
+        } while ((inb(PORT_FD_STATUS) & 0xc0) == 0xc0);
+    }
+    eoi_master_pic();
+    // diskette interrupt has occurred
+    SETBITS_BDA(floppy_recalibration_status, FRS_TIMEOUT);
+}
+
+// Called from int08 handler.
+void
+floppy_tick()
+{
+    // time to turn off drive(s)?
+    u8 fcount = GET_BDA(floppy_motor_counter);
+    if (fcount) {
+        fcount--;
+        SET_BDA(floppy_motor_counter, fcount);
+        if (fcount == 0)
+            // turn motor(s) off
+            outb(inb(PORT_FD_DOR) & 0xcf, PORT_FD_DOR);
+    }
+}
diff --git a/src/font.c b/src/font.c
new file mode 100644 (file)
index 0000000..01c73ed
--- /dev/null
@@ -0,0 +1,139 @@
+#include "types.h" // u8
+
+// Character Font for 320x200 & 640x200 Graphics (lower 128 characters)
+
+/*
+ * This font comes from the fntcol16.zip package (c) by  Joseph Gil
+ * found at ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip
+ * This font is public domain
+ */
+const u8 vgafont8[128*8] __attribute__((aligned (1))) = {
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e,
+    0x7e, 0xff, 0xdb, 0xff, 0xc3, 0xe7, 0xff, 0x7e,
+    0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00,
+    0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00,
+    0x38, 0x7c, 0x38, 0xfe, 0xfe, 0x7c, 0x38, 0x7c,
+    0x10, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x7c,
+    0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00,
+    0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff,
+    0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00,
+    0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff,
+    0x0f, 0x07, 0x0f, 0x7d, 0xcc, 0xcc, 0xcc, 0x78,
+    0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18,
+    0x3f, 0x33, 0x3f, 0x30, 0x30, 0x70, 0xf0, 0xe0,
+    0x7f, 0x63, 0x7f, 0x63, 0x63, 0x67, 0xe6, 0xc0,
+    0x99, 0x5a, 0x3c, 0xe7, 0xe7, 0x3c, 0x5a, 0x99,
+    0x80, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0x80, 0x00,
+    0x02, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x02, 0x00,
+    0x18, 0x3c, 0x7e, 0x18, 0x18, 0x7e, 0x3c, 0x18,
+    0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00,
+    0x7f, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x00,
+    0x3e, 0x63, 0x38, 0x6c, 0x6c, 0x38, 0xcc, 0x78,
+    0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x00,
+    0x18, 0x3c, 0x7e, 0x18, 0x7e, 0x3c, 0x18, 0xff,
+    0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00,
+    0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00,
+    0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00,
+    0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00,
+    0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00,
+    0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00,
+    0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0x00,
+    0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x30, 0x78, 0x78, 0x30, 0x30, 0x00, 0x30, 0x00,
+    0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00,
+    0x30, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x30, 0x00,
+    0x00, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xc6, 0x00,
+    0x38, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0x76, 0x00,
+    0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00,
+    0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00,
+    0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00,
+    0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60,
+    0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00,
+    0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00,
+    0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0x7c, 0x00,
+    0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00,
+    0x78, 0xcc, 0x0c, 0x38, 0x60, 0xcc, 0xfc, 0x00,
+    0x78, 0xcc, 0x0c, 0x38, 0x0c, 0xcc, 0x78, 0x00,
+    0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x1e, 0x00,
+    0xfc, 0xc0, 0xf8, 0x0c, 0x0c, 0xcc, 0x78, 0x00,
+    0x38, 0x60, 0xc0, 0xf8, 0xcc, 0xcc, 0x78, 0x00,
+    0xfc, 0xcc, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x00,
+    0x78, 0xcc, 0xcc, 0x78, 0xcc, 0xcc, 0x78, 0x00,
+    0x78, 0xcc, 0xcc, 0x7c, 0x0c, 0x18, 0x70, 0x00,
+    0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00,
+    0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x60,
+    0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00,
+    0x00, 0x00, 0xfc, 0x00, 0x00, 0xfc, 0x00, 0x00,
+    0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00,
+    0x78, 0xcc, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00,
+    0x7c, 0xc6, 0xde, 0xde, 0xde, 0xc0, 0x78, 0x00,
+    0x30, 0x78, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0x00,
+    0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xfc, 0x00,
+    0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x00,
+    0xf8, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00,
+    0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0xfe, 0x00,
+    0xfe, 0x62, 0x68, 0x78, 0x68, 0x60, 0xf0, 0x00,
+    0x3c, 0x66, 0xc0, 0xc0, 0xce, 0x66, 0x3e, 0x00,
+    0xcc, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0xcc, 0x00,
+    0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
+    0x1e, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00,
+    0xe6, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00,
+    0xf0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00,
+    0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00,
+    0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00,
+    0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00,
+    0xfc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00,
+    0x78, 0xcc, 0xcc, 0xcc, 0xdc, 0x78, 0x1c, 0x00,
+    0xfc, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0xe6, 0x00,
+    0x78, 0xcc, 0xe0, 0x70, 0x1c, 0xcc, 0x78, 0x00,
+    0xfc, 0xb4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
+    0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xfc, 0x00,
+    0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
+    0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00,
+    0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00,
+    0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x78, 0x00,
+    0xfe, 0xc6, 0x8c, 0x18, 0x32, 0x66, 0xfe, 0x00,
+    0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00,
+    0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00,
+    0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00,
+    0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
+    0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00,
+    0xe0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0xdc, 0x00,
+    0x00, 0x00, 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x00,
+    0x1c, 0x0c, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00,
+    0x00, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00,
+    0x38, 0x6c, 0x60, 0xf0, 0x60, 0x60, 0xf0, 0x00,
+    0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
+    0xe0, 0x60, 0x6c, 0x76, 0x66, 0x66, 0xe6, 0x00,
+    0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00,
+    0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78,
+    0xe0, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0xe6, 0x00,
+    0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
+    0x00, 0x00, 0xcc, 0xfe, 0xfe, 0xd6, 0xc6, 0x00,
+    0x00, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0xcc, 0x00,
+    0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00,
+    0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0xf0,
+    0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0x1e,
+    0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0xf0, 0x00,
+    0x00, 0x00, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x00,
+    0x10, 0x30, 0x7c, 0x30, 0x30, 0x34, 0x18, 0x00,
+    0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00,
+    0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
+    0x00, 0x00, 0xc6, 0xd6, 0xfe, 0xfe, 0x6c, 0x00,
+    0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00,
+    0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
+    0x00, 0x00, 0xfc, 0x98, 0x30, 0x64, 0xfc, 0x00,
+    0x1c, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x1c, 0x00,
+    0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00,
+    0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00,
+    0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00,
+};
diff --git a/src/ioport.h b/src/ioport.h
new file mode 100644 (file)
index 0000000..344803e
--- /dev/null
@@ -0,0 +1,56 @@
+// Definitions for X86 IO port access.
+//
+// Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+#ifndef __IOPORT_H
+#define __IOPORT_H
+
+#include "types.h" // u8
+
+#define PORT_DMA_ADDR_2        0x0004
+#define PORT_DMA_CNT_2         0x0005
+#define PORT_DMA1_MASK_REG     0x000a
+#define PORT_DMA1_MODE_REG     0x000b
+#define PORT_DMA1_CLEAR_FF_REG 0x000c
+#define PORT_DMA1_MASTER_CLEAR 0x000d
+#define PORT_PIC1              0x0020
+#define PORT_PIC1_DATA         0x0021
+#define PORT_PIT_COUNTER0      0x0040
+#define PORT_PIT_COUNTER1      0x0041
+#define PORT_PIT_COUNTER2      0x0042
+#define PORT_PIT_MODE          0x0043
+#define PORT_KBD_CTRLB         0x0061
+#define PORT_CMOS_INDEX        0x0070
+#define PORT_CMOS_DATA         0x0071
+#define PORT_DMA_PAGE_2        0x0081
+#define PORT_A20               0x0092
+#define PORT_PIC2              0x00a0
+#define PORT_PIC2_DATA         0x00a1
+#define PORT_DMA2_MASK_REG     0x00d4
+#define PORT_DMA2_MODE_REG     0x00d6
+#define PORT_DMA2_MASTER_CLEAR 0x00da
+#define PORT_FD_DOR            0x03f2
+#define PORT_FD_STATUS         0x03f4
+#define PORT_FD_DATA           0x03f5
+
+// PORT_PIC1 bitdefs
+#define PIC1_IRQ5  (1<<5)
+// PORT_PIC2 bitdefs
+#define PIC2_IRQ8  (1<<0)
+#define PIC2_IRQ13 (1<<5)
+
+// PORT_KBD_CTRLB bitdefs
+#define KBD_REFRESH (1<<4)
+
+
+static inline void outb(u8 value, u16 port) {
+    __asm__ __volatile__("outb %b0, %w1" : : "a"(value), "Nd"(port));
+}
+static inline u8 inb(u16 port) {
+    u8 value;
+    __asm__ __volatile__("inb %w1, %b0" : "=a"(value) : "Nd"(port));
+    return value;
+}
+
+#endif // ioport.h
diff --git a/src/kbd.c b/src/kbd.c
new file mode 100644 (file)
index 0000000..bcc1a59
--- /dev/null
+++ b/src/kbd.c
@@ -0,0 +1,35 @@
+// 16bit code to handle keyboard requests.
+//
+// Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2002  MandrakeSoft S.A.
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include "biosvar.h" // struct bregs
+#include "util.h" // debug_enter
+
+void
+handle_15c2(struct bregs *regs)
+{
+}
+
+// INT 16h Keyboard Service Entry Point
+void VISIBLE
+handle_16(struct bregs *regs)
+{
+    //debug_enter(regs);
+}
+
+// INT09h : Keyboard Hardware Service Entry Point
+void VISIBLE
+handle_09(struct bregs *regs)
+{
+    debug_enter(regs);
+}
+
+// INT74h : PS/2 mouse hardware interrupt
+void VISIBLE
+handle_74(struct bregs *regs)
+{
+    debug_enter(regs);
+}
diff --git a/src/output.c b/src/output.c
new file mode 100644 (file)
index 0000000..9670163
--- /dev/null
@@ -0,0 +1,161 @@
+// Raw screen writing code.
+//
+// Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include <stdarg.h> // va_list
+
+#include "farptr.h" // GET_VAR
+#include "util.h" // bprintf
+#include "biosvar.h" // struct bregs
+
+static void
+screenc(char c)
+{
+    // XXX
+}
+
+// XXX
+#define PORT_DEBUG  0x403
+
+// Write a charcter to the framebuffer.
+static void
+putc(u16 action, char c)
+{
+    screenc(c);
+    outb(c, PORT_DEBUG);
+}
+
+// Write a string to the framebuffer.
+static void
+puts(u16 action, const char *s)
+{
+    for (; *s; s++)
+        putc(action, *s);
+}
+
+// Write a string to the framebuffer.
+static void
+puts_cs(u16 action, const char *s)
+{
+    for (;; s++) {
+        char c = GET_VAR(CS, (u8)*s);
+        if (!c)
+            break;
+        putc(action, c);
+    }
+}
+
+// Write an unsigned integer to the screen.
+static void
+putuint(u16 action, u32 val)
+{
+    char buf[12];
+    char *d = &buf[sizeof(buf) - 1];
+    *d-- = '\0';
+    for (;;) {
+        *d = val % 10;
+        val /= 10;
+        if (!val)
+            break;
+        d--;
+    }
+    puts(action, d);
+}
+
+// Write a single digit hex character to the screen.
+static inline void
+putsinglehex(u16 action, u32 val)
+{
+    if (val <= 9)
+        val = '0' + val;
+    else
+        val = 'a' + val - 10;
+    putc(action, val);
+}
+
+// Write an integer in hexadecimal to the screen.
+static void
+puthex(u16 action, u32 val)
+{
+    putsinglehex(action, (val >> 28) & 0xf);
+    putsinglehex(action, (val >> 24) & 0xf);
+    putsinglehex(action, (val >> 20) & 0xf);
+    putsinglehex(action, (val >> 16) & 0xf);
+    putsinglehex(action, (val >> 12) & 0xf);
+    putsinglehex(action, (val >> 8) & 0xf);
+    putsinglehex(action, (val >> 4) & 0xf);
+    putsinglehex(action, (val >> 0) & 0xf);
+}
+
+void
+bprintf(u16 action, const char *fmt, ...)
+{
+    va_list args;
+    va_start(args, fmt);
+    const char *s = fmt;
+    for (;; s++) {
+        char c = GET_VAR(CS, (u8)*s);
+        if (!c)
+            break;
+        if (c != '%') {
+            putc(action, c);
+            continue;
+        }
+        const char *n = s+1;
+        c = GET_VAR(CS, (u8)*n);
+        s32 val;
+        const char *sarg;
+        switch (c) {
+        case '%':
+            putc(action, '%');
+            break;
+        case 'd':
+            val = va_arg(args, s32);
+            if (val < 0) {
+                putc(action, '-');
+                val = -val;
+            }
+            putuint(action, val);
+            break;
+        case 'u':
+            val = va_arg(args, s32);
+            putuint(action, val);
+            break;
+        case 'x':
+            val = va_arg(args, s32);
+            puthex(action, val);
+            break;
+        case 's':
+            sarg = va_arg(args, const char *);
+            puts_cs(action, sarg);
+            break;
+        default:
+            putc(action, *s);
+            n = s;
+        }
+        s = n;
+    }
+    va_end(args);
+}
+
+// Function called on handler startup.
+void
+__debug_enter(const char *fname, struct bregs *regs)
+{
+    bprintf(0, "enter %s: a=%x b=%x c=%x d=%x si=%x di=%x\n"
+            , fname, regs->eax, regs->ebx, regs->ecx, regs->edx
+            , regs->esi, regs->edi);
+    bprintf(0, "&=%x ds=%x es=%x bp=%x sp=%x ip=%x cs=%x f=%x\n"
+            , (u32)regs, regs->ds, regs->es, regs->ebp, regs->esp
+            , regs->ip, regs->cs, regs->flags);
+}
+
+void
+__debug_exit(const char *fname, struct bregs *regs)
+{
+    bprintf(0, "exit %s: a=%x b=%x c=%x d=%x s=%x i=%x\n"
+            , fname, regs->eax, regs->ebx, regs->ecx, regs->edx
+            , regs->esi, regs->edi);
+}
diff --git a/src/post.c b/src/post.c
new file mode 100644 (file)
index 0000000..8d35f97
--- /dev/null
@@ -0,0 +1,312 @@
+// 32bit code to Power On Self Test (POST) a machine.
+//
+// Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2002  MandrakeSoft S.A.
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include "ioport.h" // PORT_*
+#include "../out/rom16.offset.auto.h" // OFFSET_*
+#include "config.h" // CONFIG_*
+#include "cmos.h" // CMOS_*
+#include "util.h" // memset
+#include "biosvar.h" // struct bios_data_area_s
+
+#define bda ((struct bios_data_area_s *)0)
+#define ebda ((struct extended_bios_data_area_s *)(EBDA_SEG<<4))
+
+static void
+init_bda()
+{
+    memset(bda, 0, sizeof(*bda));
+
+    int i;
+    for (i=0; i<256; i++) {
+        bda->ivecs[i].seg = 0xf000;
+        bda->ivecs[i].offset = OFFSET_dummy_iret_handler;
+    }
+
+    bda->mem_size_kb = BASE_MEM_IN_K;
+}
+
+static void
+init_handlers()
+{
+    // set vector 0x79 to zero
+    // this is used by 'gardian angel' protection system
+    bda->ivecs[0x79].seg = 0;
+    bda->ivecs[0x79].offset = 0;
+
+    bda->ivecs[0x40].offset = OFFSET_entry_40;
+    bda->ivecs[0x0e].offset = OFFSET_entry_0e;
+    bda->ivecs[0x13].offset = OFFSET_entry_13;
+    bda->ivecs[0x76].offset = OFFSET_entry_76;
+    bda->ivecs[0x17].offset = OFFSET_entry_17;
+    bda->ivecs[0x18].offset = OFFSET_entry_18;
+    bda->ivecs[0x19].offset = OFFSET_entry_19;
+    bda->ivecs[0x1c].offset = OFFSET_entry_1c;
+    bda->ivecs[0x12].offset = OFFSET_entry_12;
+    bda->ivecs[0x11].offset = OFFSET_entry_11;
+    bda->ivecs[0x15].offset = OFFSET_entry_15;
+    bda->ivecs[0x08].offset = OFFSET_entry_08;
+    bda->ivecs[0x09].offset = OFFSET_entry_09;
+    bda->ivecs[0x16].offset = OFFSET_entry_16;
+    bda->ivecs[0x14].offset = OFFSET_entry_14;
+    bda->ivecs[0x1a].offset = OFFSET_entry_1a;
+    bda->ivecs[0x70].offset = OFFSET_entry_70;
+    bda->ivecs[0x74].offset = OFFSET_entry_74;
+    bda->ivecs[0x75].offset = OFFSET_entry_75;
+    bda->ivecs[0x10].offset = OFFSET_entry_10;
+}
+
+static void
+init_ebda()
+{
+    ebda->size = EBDA_SIZE;
+    bda->ebda_seg = EBDA_SEG;
+    bda->ivecs[0x41].seg = EBDA_SEG;
+    bda->ivecs[0x41].offset = 0x3d; // XXX
+    bda->ivecs[0x46].seg = EBDA_SEG;
+    bda->ivecs[0x46].offset = 0x4d; // XXX
+}
+
+static void
+pit_setup()
+{
+    // timer0: binary count, 16bit count, mode 2
+    outb(0x34, PORT_PIT_MODE);
+    // maximum count of 0000H = 18.2Hz
+    outb(0x0, PORT_PIT_COUNTER0);
+    outb(0x0, PORT_PIT_COUNTER0);
+}
+
+static void
+kbd_init()
+{
+}
+
+static void
+kbd_setup()
+{
+    bda->kbd_mode = 0x10;
+    bda->kbd_buf_head = bda->kbd_buf_tail = offsetof(struct bios_data_area_s, kbd_buf);
+    bda->kbd_buf_start_offset = offsetof(struct bios_data_area_s, kbd_buf);
+    bda->kbd_buf_end_offset = offsetof(struct bios_data_area_s, kbd_buf[sizeof(bda->kbd_buf)]);
+    kbd_init();
+
+    // XXX
+    u16 eqb = bda->equipment_list_flags;
+    eqb = (eqb & 0xff00) | inb_cmos(CMOS_EQUIPMENT_INFO);
+    bda->equipment_list_flags = eqb;
+}
+
+static void
+lpt_setup()
+{
+    // XXX
+}
+
+static void
+serial_setup()
+{
+    // XXX
+}
+
+static u32
+bcd2bin(u8 val)
+{
+    return (val & 0xf) + ((val >> 4) * 10);
+}
+
+static void
+timer_setup()
+{
+    u32 seconds = bcd2bin(inb_cmos(CMOS_RTC_SECONDS));
+    u32 ticks = (seconds * 18206507) / 1000000;
+    u32 minutes = bcd2bin(inb_cmos(CMOS_RTC_MINUTES));
+    ticks += (minutes * 10923904) / 10000;
+    u32 hours = bcd2bin(inb_cmos(CMOS_RTC_HOURS));
+    ticks += (hours * 65543427) / 1000;
+    bda->timer_counter = ticks;
+    bda->timer_rollover = 0;
+}
+
+static void
+pic_setup()
+{
+    outb(0x11, PORT_PIC1);
+    outb(0x11, PORT_PIC2_DATA);
+    outb(0x08, PORT_PIC1_DATA);
+    outb(0x70, PORT_PIC2_DATA);
+    outb(0x04, PORT_PIC1_DATA);
+    outb(0x02, PORT_PIC2_DATA);
+    outb(0x01, PORT_PIC1_DATA);
+    outb(0x01, PORT_PIC2_DATA);
+    outb(0xb8, PORT_PIC1_DATA);
+    if (CONFIG_PS2_MOUSE)
+        outb(0x8f, PORT_PIC2_DATA);
+    else
+        outb(0x9f, PORT_PIC2_DATA);
+}
+
+static void
+floppy_drive_post()
+{
+    u8 type = inb_cmos(CMOS_FLOPPY_DRIVE_TYPE);
+    u8 out = 0;
+    if (type & 0xf0)
+        out |= 0x07;
+    if (type & 0x0f)
+        out |= 0x70;
+    bda->floppy_harddisk_info = out;
+    outb(0x02, PORT_DMA1_MASK_REG);
+
+    bda->ivecs[0x1E].offset = OFFSET_diskette_param_table2;
+}
+
+static void
+cdemu_init()
+{
+    //ebda->cdemu.active = 0;
+}
+
+static void
+ata_init()
+{
+}
+
+static void
+ata_detect()
+{
+}
+
+static void
+hard_drive_post()
+{
+}
+
+static void
+init_boot_vectors()
+{
+}
+
+static void __attribute__((noinline))
+call16(u16 seg, u16 offset)
+{
+    u32 segoff = (seg << 16) | offset;
+    asm volatile(
+        "pushal\n"  // Save registers
+        "ljmp $0x20, %0\n" // Jump to 16bit transition code
+        ".globl call16_resume\n"
+        "call16_resume:\n"  // point of return
+        "popal\n"   // restore registers
+        : : "Z" (OFFSET_call16), "b" (segoff));
+}
+
+static int
+checksum(u8 *p, u32 len)
+{
+    u32 i;
+    u8 sum = 0;
+    for (i=0; i<len; i++)
+        sum += p[i];
+    return sum;
+}
+
+#define PTR_TO_SEG(p) ((((u32)(p)) >> 4) & 0xf000)
+#define PTR_TO_OFFSET(p) (((u32)(p)) & 0xffff)
+
+static void
+rom_scan()
+{
+    u8 *p = (u8*)0xc0000;
+    for (; p <= (u8*)0xe0000; p += 2048) {
+        u8 *rom = p;
+        if (*(u16*)rom != 0xaa55)
+            continue;
+        u32 len = rom[2] * 512;
+        if (checksum(rom, len) != 0)
+            continue;
+        p = (u8*)(((u32)p + len) / 2048 * 2048);
+        call16(PTR_TO_SEG(rom), PTR_TO_OFFSET(rom + 3));
+
+        // Look at the ROM's PnP Expansion header.  Properly, we're supposed
+        // to init all the ROMs and then go back and build an IPL table of
+        // all the bootable devices, but we can get away with one pass.
+        if (rom[0x1a] != '$' || rom[0x1b] != 'P'
+            || rom[0x1c] != 'n' || rom[0x1d] != 'P')
+            continue;
+        // 0x1A is also the offset into the expansion header of...
+        // the Bootstrap Entry Vector, or zero if there is none.
+        u16 entry = *(u16*)&rom[0x1a+0x1a];
+        if (!entry)
+            continue;
+        // Found a device that thinks it can boot the system.  Record
+        // its BEV and product name string.
+
+        // XXX
+    }
+}
+
+static void
+status_restart(u8 status)
+{
+#if 0
+    if (status == 0x05)
+        eoi_jmp_post();
+#endif
+
+    BX_PANIC("Unimplemented shutdown status: %02x\n",(Bit8u)status);
+}
+
+static void
+post()
+{
+    // first reset the DMA controllers
+    outb(0, PORT_DMA1_MASTER_CLEAR);
+    outb(0, PORT_DMA2_MASTER_CLEAR);
+
+    // then initialize the DMA controllers
+    outb(0xc0, PORT_DMA2_MODE_REG);
+    outb(0x00, PORT_DMA2_MASK_REG);
+
+    // Get and then clear CMOS shutdown status.
+    u8 status = inb_cmos(CMOS_RESET_CODE);
+    outb_cmos(0, CMOS_RESET_CODE);
+
+    if (status != 0x00 && status != 0x09 && status < 0x0d)
+        status_restart(status);
+
+    BX_INFO("Start bios");
+
+    init_bda();
+    init_handlers();
+    init_ebda();
+
+    pit_setup();
+    kbd_setup();
+    lpt_setup();
+    serial_setup();
+    timer_setup();
+    pic_setup();
+    //pci_setup();
+    init_boot_vectors();
+    rom_scan();
+
+    printf("BIOS - begin\n\n");
+
+    floppy_drive_post();
+    hard_drive_post();
+    if (CONFIG_ATA) {
+        ata_init();
+        ata_detect();
+    }
+    cdemu_init();
+    call16(0xf000, OFFSET_begin_boot);
+}
+
+void VISIBLE
+_start()
+{
+    post();
+}
diff --git a/src/rombios32.lds.S b/src/rombios32.lds.S
new file mode 100644 (file)
index 0000000..dae62d8
--- /dev/null
@@ -0,0 +1,31 @@
+// Linker definitions for 32bit code
+//
+// Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2002  MandrakeSoft S.A.
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include "config.h"
+#include "../out/rom16.offset.auto.h"
+
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+ENTRY(_start);
+SECTIONS
+{
+        . = (OFFSET_bios16c_end | 0xf0000);
+        . = ALIGN(16);
+        _text32_start = . ;
+        .text     : { *(.text)    }
+        .rodata    : { *(.rodata) }
+        . = ALIGN(16);
+        .data     : { *(.data)    }
+        __bss_start = . ;
+        .bss      : { *(.bss) *(COMMON) }
+        _end = . ;
+        /DISCARD/ : { *(.stab)
+                     *(.stabstr)
+                     *(.comment)
+                     *(.note)
+                  }
+}
diff --git a/src/romlayout.S b/src/romlayout.S
new file mode 100644 (file)
index 0000000..c9cc6ef
--- /dev/null
@@ -0,0 +1,304 @@
+// Rom layout and bios assembler to C interface.
+//
+// Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2002  MandrakeSoft S.A.
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include "config.h"
+
+        .code16gcc
+        .text
+        .globl bios16c_start, bios16c_end
+bios16c_start:
+.include "out/blob.proc.16.s"
+        .text
+bios16c_end:
+
+
+        .org 0xe05b
+        .globl _start
+_start:
+        .globl post16
+post16:
+
+        // Entry point of rombios32 code - the actual instruction is
+        // altered later in the build process.
+        .globl set_entry32
+set_entry32:
+        mov $0xf0000000, %ebx
+
+        // init the stack pointer
+        movl $ CONFIG_STACK32_OFFSET , %esp
+
+transition32:
+        // Disable irqs
+        cli
+
+        // enable a20
+        inb $0x92, %al
+        orb $0x02, %al
+        outb %al, $0x92
+
+        // Set segment descriptors
+        lidt %cs:pmode_IDT_info
+        lgdt %cs:rombios32_gdt_48
+
+        // set PE bit in CR0
+        movl  %cr0, %eax
+        orb   $0x01, %al
+        movl  %eax, %cr0
+
+        // start protected mode code
+        .word 0xea66, 1f, 0x000f, 0x0010 // ljmpl $0x10, $(post32 | 0xf0000)
+
+        .code32
+1:
+        // init data segments
+        movl $0x18, %eax
+        movw %ax, %ds
+        movw %ax, %es
+        movw %ax, %ss
+        xorl %eax, %eax
+        movw %ax, %fs
+        movw %ax, %gs
+
+        cld
+
+        jmp *%ebx
+
+        .code16gcc
+
+// We need a copy of this string, but we are not actually a PnP BIOS,
+// so make sure it is *not* aligned, so OSes will not see it if they
+// scan.
+        .align 2
+        .byte 0
+pnp_string:
+        .ascii "$PnP"
+
+// Return from 32bit code to 16bit code - must pass in destination
+// code segment,offset (%ebx) and the return stack position (%esp).
+
+        .globl call16
+call16:
+        // restore data segment limits to 0xffff
+        movw $0x28, %ax
+        movw %ax, %ds
+        movw %ax, %es
+        movw %ax, %ss
+        movw %ax, %fs
+        movw %ax, %gs
+
+        // reset PE bit in CR0
+        movl %cr0, %eax
+        andb $0xfe, %al
+        movl %eax, %cr0
+
+        // far jump to flush CPU queue after transition to real mode
+        ljmpw $0xf000, $1f
+1:
+        // restore IDT to normal real-mode defaults
+        lidt %cs:rmode_IDT_info
+
+        // Setup segment registers
+        xorw %ax, %ax
+        movw %ax, %ds
+        movw %ax, %fs
+        movw %ax, %gs
+        movw $0xf000, %ax
+        movw %ax, %es
+        lea pnp_string, %di
+        movw $ CONFIG_STACK16_SEGMENT , %ax
+        movw %ax, %ss
+        movl %esp, %eax
+        movl $ CONFIG_STACK16_OFFSET , %esp
+
+        // Save info
+        pushl %eax
+        pushl %ebx
+        movl %esp, %ebp
+
+        lcallw %ss:*(%bp)
+
+        // Restore stack and jump back to 32bit mode.
+        popl %eax
+        popl %esp
+
+        // Resume point of rombios32 code - the actual instruction is
+        // altered later in the build process.
+        .globl set_resume32
+set_resume32:
+        mov $0xf0000000, %ebx
+
+        jmp transition32
+
+
+// Protected mode IDT descriptor
+//
+// I just make the limit 0, so the machine will shutdown
+// if an exception occurs during protected mode memory
+// transfers.
+//
+// Set base to f0000 to correspond to beginning of BIOS,
+// in case I actually define an IDT later
+// Set limit to 0
+pmode_IDT_info:
+        .word 0x0000  // limit 15:00
+        .word 0x0000  // base  15:00
+        .byte 0x0f    // base  23:16
+
+// Real mode IDT descriptor
+//
+// Set to typical real-mode values.
+// base  = 000000
+// limit =   03ff
+rmode_IDT_info:
+        .word 0x03ff  // limit 15:00
+        .word 0x0000  // base  15:00
+        .byte 0x00    // base  23:16
+
+rombios32_gdt_48:
+        .word 0x30
+        .word rombios32_gdt
+        .word 0x000f
+
+rombios32_gdt:
+        .word 0, 0, 0, 0
+        .word 0, 0, 0, 0
+        .word 0xffff, 0, 0x9b00, 0x00cf // 32 bit flat code segment (0x10)
+        .word 0xffff, 0, 0x9300, 0x00cf // 32 bit flat data segment (0x18)
+        .word 0xffff, 0, 0x9b0f, 0x0000 // 16 bit code segment base=0xf0000 limit=0xffff
+        .word 0xffff, 0, 0x9300, 0x0000 // 16 bit data segment base=0x0 limit=0xffff
+
+
+        .macro ENTRY cfunc
+        pushal
+        pushw %es
+        pushw %ds
+        movw %ss, %ax
+        movw %ax, %ds
+        mov %esp, %eax
+        call \cfunc
+        popw %ds
+        popw %es
+        popal
+        .endm
+
+        .macro IRQ_ENTRY num
+        .globl entry_\num
+        entry_\num :
+        ENTRY handle_\num
+        iretw
+        .endm
+
+
+        .org 0xe2c3
+        IRQ_ENTRY nmi
+
+        IRQ_ENTRY 13
+        IRQ_ENTRY 19
+        IRQ_ENTRY 12
+        IRQ_ENTRY 11
+        IRQ_ENTRY 76
+        IRQ_ENTRY 18
+        IRQ_ENTRY 1c
+        IRQ_ENTRY 70
+        IRQ_ENTRY 74
+        IRQ_ENTRY 75
+
+        .org 0xe3fe
+        jmp entry_13
+
+        .org 0xe401
+        // XXX - Fixed Disk Parameter Table
+
+        .org 0xe6f2
+        jmp entry_19
+
+        .org 0xe6f5
+.include "out/cbt.proc.16.s"
+        .text
+
+        .org 0xe729
+        // XXX - Baud Rate Generator Table
+
+        .org 0xe739
+        IRQ_ENTRY 14
+
+        .org 0xe82e
+        IRQ_ENTRY 16
+
+        .org 0xe987
+        IRQ_ENTRY 09
+
+        .org 0xec59
+        IRQ_ENTRY 40
+
+        .org 0xef57
+        IRQ_ENTRY 0e
+
+        .org 0xefc7
+        // XXX - Diskette Controller Parameter Table
+
+        .org 0xefd2
+        IRQ_ENTRY 17
+
+        .org 0xf045
+        // XXX int 10
+        iretw
+
+        .org 0xf065
+        IRQ_ENTRY 10
+
+        .org 0xf0a4
+        // XXX int 1D
+        iretw
+
+        .org 0xf841
+        jmp entry_12
+
+        .org 0xf84d
+        jmp entry_11
+
+        .org 0xf859
+        IRQ_ENTRY 15
+
+        .org 0xfa6e
+.include "out/font.proc.16.s"
+        .text
+
+        .org 0xfe6e
+        IRQ_ENTRY 1a
+
+        .org 0xfea5
+        IRQ_ENTRY 08
+
+        .org 0xfef3
+        // XXX - Initial Interrupt Vector Offsets Loaded by POST
+
+        .org 0xff00
+        // XXX - BIOS_COPYRIGHT_STRING
+        .ascii "(c) 2002 MandrakeSoft S.A. Written by Kevin Lawton & the Bochs team."
+
+        .org 0xff53
+        .globl dummy_iret_handler
+dummy_iret_handler:
+        iretw
+
+        .org 0xff54
+        IRQ_ENTRY 05
+
+        .org 0xfff0 // Power-up Entry Point
+        ljmpw $0xf000, $post16
+
+        .org 0xfff5
+        // BIOS build date
+        .ascii "06/23/99"
+
+        .org 0xfffe
+        // model byte 0xFC = AT
+        .byte 0xfc
+        .byte 0x00
+
+        .end
diff --git a/src/serial.c b/src/serial.c
new file mode 100644 (file)
index 0000000..5541089
--- /dev/null
@@ -0,0 +1,23 @@
+// 16bit code to handle serial and printer services.
+//
+// Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2002  MandrakeSoft S.A.
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include "biosvar.h" // struct bregs
+#include "util.h" // debug_enter
+
+// INT 14h Serial Communications Service Entry Point
+void VISIBLE
+handle_14(struct bregs *regs)
+{
+    debug_enter(regs);
+}
+
+// INT17h : Printer Service Entry Point
+void VISIBLE
+handle_17(struct bregs *regs)
+{
+    debug_enter(regs);
+}
diff --git a/src/system.c b/src/system.c
new file mode 100644 (file)
index 0000000..3967fc4
--- /dev/null
@@ -0,0 +1,529 @@
+// 16bit system callbacks
+//
+// Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2002  MandrakeSoft S.A.
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include "util.h" // irq_restore
+#include "biosvar.h" // CONFIG_BIOS_TABLE
+#include "ioport.h" // inb
+#include "cmos.h" // inb_cmos
+
+#define RET_EUNSUPPORTED 0x86
+
+
+// Use PS2 System Control port A to set A20 enable
+static inline u8
+set_a20(u8 cond)
+{
+    // get current setting first
+    u8 newval, oldval = inb(PORT_A20);
+    if (cond)
+        newval = oldval | 0x02;
+    else
+        newval = oldval & ~0x02;
+    outb(newval, PORT_A20);
+
+    return (newval & 0x02) != 0;
+}
+
+static inline void
+handle_ret(struct bregs *regs, u8 code)
+{
+    regs->ah = code;
+    set_cf(regs, code);
+}
+
+static void
+handle_152400(struct bregs *regs)
+{
+    set_a20(0);
+    handle_ret(regs, 0);
+}
+
+static void
+handle_152401(struct bregs *regs)
+{
+    set_a20(1);
+    handle_ret(regs, 0);
+}
+
+static void
+handle_152402(struct bregs *regs)
+{
+    regs->al = !!(inb(PORT_A20) & 0x20);
+    handle_ret(regs, 0);
+}
+
+static void
+handle_152403(struct bregs *regs)
+{
+    regs->bx = 3;
+    handle_ret(regs, 0);
+}
+
+static void
+handle_1524XX(struct bregs *regs)
+{
+    handle_ret(regs, RET_EUNSUPPORTED);
+}
+
+// removable media eject
+static void
+handle_1552(struct bregs *regs)
+{
+    handle_ret(regs, 0);
+}
+
+// Set Interval requested.
+static void
+handle_158300(struct bregs *regs)
+{
+    if (GET_BDA(rtc_wait_flag) & RWS_WAIT_PENDING) {
+        // Interval already set.
+        DEBUGF("int15: Func 83h, failed, already waiting.\n" );
+        handle_ret(regs, RET_EUNSUPPORTED);
+    }
+    // Interval not already set.
+    SET_BDA(rtc_wait_flag, RWS_WAIT_PENDING);  // Set status byte.
+    u32 v = (regs->es << 16) | regs->bx;
+    SET_BDA(ptr_user_wait_complete_flag, v);
+    v = (regs->dx << 16) | regs->cx;
+    SET_BDA(user_wait_timeout, v);
+
+    // Unmask IRQ8 so INT70 will get through.
+    u8 irqDisable = inb(PORT_PIC2_DATA);
+    outb(irqDisable & ~PIC2_IRQ8, PORT_PIC2_DATA);
+    // Turn on the Periodic Interrupt timer
+    u8 bRegister = inb_cmos(CMOS_STATUS_B);
+    outb_cmos(CMOS_STATUS_B, bRegister | CSB_EN_ALARM_IRQ);
+
+    set_cf(regs, 0); // XXX - no set ah?
+}
+
+// Clear interval requested
+static void
+handle_158301(struct bregs *regs)
+{
+    SET_BDA(rtc_wait_flag, 0); // Clear status byte
+    // Turn off the Periodic Interrupt timer
+    u8 bRegister = inb_cmos(CMOS_STATUS_B);
+    outb_cmos(CMOS_STATUS_B, bRegister & ~CSB_EN_ALARM_IRQ);
+    set_cf(regs, 0); // XXX - no set ah?
+}
+
+static void
+handle_1583XX(struct bregs *regs)
+{
+    regs->al--;
+    handle_ret(regs, RET_EUNSUPPORTED);
+}
+
+// Sleep for n microseconds. currently using the
+// refresh request port 0x61 bit4, toggling every 15usec
+static void
+usleep(u32 count)
+{
+    count = count / 15;
+    u8 kbd = inb(PORT_KBD_CTRLB);
+    while (count)
+        if ((inb(PORT_KBD_CTRLB) ^ kbd) & KBD_REFRESH)
+            count--;
+}
+
+// Wait for CX:DX microseconds. currently using the
+// refresh request port 0x61 bit4, toggling every 15usec
+static void
+handle_1586(struct bregs *regs)
+{
+    irq_enable();
+    usleep((regs->cx << 16) | regs->dx);
+    irq_disable();
+}
+
+static void
+handle_1587(struct bregs *regs)
+{
+    // +++ should probably have descriptor checks
+    // +++ should have exception handlers
+
+    // turn off interrupts
+    unsigned long flags = irq_save();
+
+    u8 prev_a20_enable = set_a20(1); // enable A20 line
+
+    // 128K max of transfer on 386+ ???
+    // source == destination ???
+
+    // ES:SI points to descriptor table
+    // offset   use     initially  comments
+    // ==============================================
+    // 00..07   Unused  zeros      Null descriptor
+    // 08..0f   GDT     zeros      filled in by BIOS
+    // 10..17   source  ssssssss   source of data
+    // 18..1f   dest    dddddddd   destination of data
+    // 20..27   CS      zeros      filled in by BIOS
+    // 28..2f   SS      zeros      filled in by BIOS
+
+    //es:si
+    //eeee0
+    //0ssss
+    //-----
+
+// check for access rights of source & dest here
+
+    // Initialize GDT descriptor
+    u16 si = regs->si;
+    u16 base15_00 = (regs->es << 4) + si;
+    u16 base23_16 = regs->es >> 12;
+    if (base15_00 < (regs->es<<4))
+        base23_16++;
+    SET_VAR(ES, *(u16*)(si+0x08+0), 47);       // limit 15:00 = 6 * 8bytes/descriptor
+    SET_VAR(ES, *(u16*)(si+0x08+2), base15_00);// base 15:00
+    SET_VAR(ES, *(u8 *)(si+0x08+4), base23_16);// base 23:16
+    SET_VAR(ES, *(u8 *)(si+0x08+5), 0x93);     // access
+    SET_VAR(ES, *(u16*)(si+0x08+6), 0x0000);   // base 31:24/reserved/limit 19:16
+
+    // Initialize CS descriptor
+    SET_VAR(ES, *(u16*)(si+0x20+0), 0xffff);// limit 15:00 = normal 64K limit
+    SET_VAR(ES, *(u16*)(si+0x20+2), 0x0000);// base 15:00
+    SET_VAR(ES, *(u8 *)(si+0x20+4), 0x000f);// base 23:16
+    SET_VAR(ES, *(u8 *)(si+0x20+5), 0x9b);  // access
+    SET_VAR(ES, *(u16*)(si+0x20+6), 0x0000);// base 31:24/reserved/limit 19:16
+
+    // Initialize SS descriptor
+    u16 ss = GET_SEG(SS);
+    base15_00 = ss << 4;
+    base23_16 = ss >> 12;
+    SET_VAR(ES, *(u16*)(si+0x28+0), 0xffff);   // limit 15:00 = normal 64K limit
+    SET_VAR(ES, *(u16*)(si+0x28+2), base15_00);// base 15:00
+    SET_VAR(ES, *(u8 *)(si+0x28+4), base23_16);// base 23:16
+    SET_VAR(ES, *(u8 *)(si+0x28+5), 0x93);     // access
+    SET_VAR(ES, *(u16*)(si+0x28+6), 0x0000);   // base 31:24/reserved/limit 19:16
+
+    asm volatile(
+        // Save registers
+        "pushw %%ds\n"
+        "pushw %%es\n"
+        "pushal\n"
+
+        // Load new descriptor tables
+        "lgdt %%es:(%1)\n"
+        "lidt %%cs:pmode_IDT_info\n"
+
+        // set PE bit in CR0
+        "movl %%cr0, %%eax\n"
+        "orb $0x01, %%al\n"
+        "movl %%eax, %%cr0\n"
+
+        // far jump to flush CPU queue after transition to protected mode
+        "ljmpw $0xf000, $1f\n"
+        "1:\n"
+
+        // GDT points to valid descriptor table, now load DS, ES
+        "movw $0x10, %%ax\n" // 010 000 = 2nd descriptor in table, TI=GDT, RPL=00
+        "movw %%ax, %%ds\n"
+        "movw $0x18, %%ax\n" // 011 000 = 3rd descriptor in table, TI=GDT, RPL=00
+        "movw %%ax, %%es\n"
+
+        // move CX words from DS:SI to ES:DI
+        "xorw %%si, %%si\n"
+        "xorw %%di, %%di\n"
+        "cld\n"
+        "rep movsw\n"
+
+        // reset PG bit in CR0 ???
+        "movl %%cr0, %%eax\n"
+        "andb $0xfe, %%al\n"
+        "movl %%eax, %%cr0\n"
+
+        // far jump to flush CPU queue after transition to real mode
+        "ljmpw $0xf000, $2f\n"
+        "2:\n"
+
+        // restore IDT to normal real-mode defaults
+        "lidt %%cs:rmode_IDT_info\n"
+
+        // restore regisers
+        "popal\n"
+        "popw %%es\n"
+        "popw %%ds\n" : : "c" (regs->cx), "r" (si + 8));
+
+    set_a20(prev_a20_enable);
+
+    irq_restore(flags);
+
+    handle_ret(regs, 0);
+}
+
+// Get the amount of extended memory (above 1M)
+static void
+handle_1588(struct bregs *regs)
+{
+    regs->al = inb_cmos(CMOS_EXTMEM_LOW);
+    regs->ah = inb_cmos(CMOS_EXTMEM_HIGH);
+    // According to Ralf Brown's interrupt the limit should be 15M,
+    // but real machines mostly return max. 63M.
+    if (regs->ax > 0xffc0)
+        regs->ax = 0xffc0;
+    set_cf(regs, 0);
+}
+
+// Device busy interrupt.  Called by Int 16h when no key available
+static void
+handle_1590(struct bregs *regs)
+{
+}
+
+// Interrupt complete.  Called by Int 16h when key becomes available
+static void
+handle_1591(struct bregs *regs)
+{
+}
+
+static void
+handle_15c0(struct bregs *regs)
+{
+    regs->es = SEG_BIOS;
+    regs->bx = (u16)&BIOS_CONFIG_TABLE;
+}
+
+static void
+handle_15c1(struct bregs *regs)
+{
+    regs->es = GET_BDA(ebda_seg);
+    set_cf(regs, 0);
+}
+
+static void
+handle_15e801(struct bregs *regs)
+{
+    // my real system sets ax and bx to 0
+    // this is confirmed by Ralph Brown list
+    // but syslinux v1.48 is known to behave
+    // strangely if ax is set to 0
+    // regs.u.r16.ax = 0;
+    // regs.u.r16.bx = 0;
+
+    // Get the amount of extended memory (above 1M)
+    regs->cl = inb_cmos(CMOS_EXTMEM_LOW);
+    regs->ch = inb_cmos(CMOS_EXTMEM_HIGH);
+
+    // limit to 15M
+    if (regs->cx > 0x3c00)
+        regs->cx = 0x3c00;
+
+    // Get the amount of extended memory above 16M in 64k blocs
+    regs->dl = inb_cmos(CMOS_EXTMEM2_LOW);
+    regs->dh = inb_cmos(CMOS_EXTMEM2_HIGH);
+
+    // Set configured memory equal to extended memory
+    regs->ax = regs->cx;
+    regs->bx = regs->dx;
+
+    set_cf(regs, 0);
+}
+
+#define ACPI_DATA_SIZE    0x00010000L
+
+static void
+set_e820_range(u16 DI, u32 start, u32 end, u16 type)
+{
+    SET_VAR(ES, *(u16*)(DI+0), start);
+    SET_VAR(ES, *(u16*)(DI+2), start >> 16);
+    SET_VAR(ES, *(u16*)(DI+4), 0x00);
+    SET_VAR(ES, *(u16*)(DI+6), 0x00);
+
+    end -= start;
+    SET_VAR(ES, *(u16*)(DI+8), end);
+    SET_VAR(ES, *(u16*)(DI+10), end >> 16);
+    SET_VAR(ES, *(u16*)(DI+12), 0x0000);
+    SET_VAR(ES, *(u16*)(DI+14), 0x0000);
+
+    SET_VAR(ES, *(u16*)(DI+16), type);
+    SET_VAR(ES, *(u16*)(DI+18), 0x0);
+}
+
+// XXX - should create e820 memory map in post and just copy it here.
+static void
+handle_15e820(struct bregs *regs)
+{
+    if (regs->edx != 0x534D4150) {
+        handle_ret(regs, RET_EUNSUPPORTED);
+        return;
+    }
+
+    u32 extended_memory_size = inb_cmos(CMOS_EXTMEM2_HIGH);
+    extended_memory_size <<= 8;
+    extended_memory_size |= inb_cmos(CMOS_EXTMEM2_LOW);
+    extended_memory_size *= 64;
+    // greater than EFF00000???
+    if (extended_memory_size > 0x3bc000)
+        // everything after this is reserved memory until we get to 0x100000000
+        extended_memory_size = 0x3bc000;
+    extended_memory_size *= 1024;
+    extended_memory_size += (16L * 1024 * 1024);
+
+    if (extended_memory_size <= (16L * 1024 * 1024)) {
+        extended_memory_size = inb_cmos(CMOS_EXTMEM_HIGH);
+        extended_memory_size <<= 8;
+        extended_memory_size |= inb_cmos(CMOS_EXTMEM_LOW);
+        extended_memory_size *= 1024;
+    }
+
+    switch (regs->bx) {
+    case 0:
+        set_e820_range(regs->di, 0x0000000L, 0x0009fc00L, 1);
+        regs->ebx = 1;
+        regs->eax = 0x534D4150;
+        regs->ecx = 0x14;
+        set_cf(regs, 0);
+        break;
+    case 1:
+        set_e820_range(regs->di, 0x0009fc00L, 0x000a0000L, 2);
+        regs->ebx = 2;
+        regs->eax = 0x534D4150;
+        regs->ecx = 0x14;
+        set_cf(regs, 0);
+        break;
+    case 2:
+        set_e820_range(regs->di, 0x000e8000L, 0x00100000L, 2);
+        regs->ebx = 3;
+        regs->eax = 0x534D4150;
+        regs->ecx = 0x14;
+        set_cf(regs, 0);
+        break;
+    case 3:
+        set_e820_range(regs->di, 0x00100000L,
+                       extended_memory_size - ACPI_DATA_SIZE, 1);
+        regs->ebx = 4;
+        regs->eax = 0x534D4150;
+        regs->ecx = 0x14;
+        set_cf(regs, 0);
+        break;
+    case 4:
+        set_e820_range(regs->di,
+                       extended_memory_size - ACPI_DATA_SIZE,
+                       extended_memory_size, 3); // ACPI RAM
+        regs->ebx = 5;
+        regs->eax = 0x534D4150;
+        regs->ecx = 0x14;
+        set_cf(regs, 0);
+        break;
+    case 5:
+        /* 256KB BIOS area at the end of 4 GB */
+        set_e820_range(regs->di, 0xfffc0000L, 0x00000000L, 2);
+        regs->ebx = 0;
+        regs->eax = 0x534D4150;
+        regs->ecx = 0x14;
+        set_cf(regs, 0);
+        break;
+    default:  /* AX=E820, DX=534D4150, BX unrecognized */
+        handle_ret(regs, RET_EUNSUPPORTED);
+    }
+}
+
+static void
+handle_15e8XX(struct bregs *regs)
+{
+    regs->al--;
+    handle_ret(regs, RET_EUNSUPPORTED);
+}
+
+static void
+handle_15XX(struct bregs *regs)
+{
+    regs->al--;
+    handle_ret(regs, RET_EUNSUPPORTED);
+}
+
+// INT 15h System Services Entry Point
+void VISIBLE
+handle_15(struct bregs *regs)
+{
+    debug_enter(regs);
+    switch (regs->ah) {
+    case 0x24:
+        switch (regs->al) {
+        case 0x00: handle_152400(regs); break;
+        case 0x01: handle_152401(regs); break;
+        case 0x02: handle_152402(regs); break;
+        case 0x03: handle_152403(regs); break;
+        default:   handle_1524XX(regs); break;
+        }
+        break;
+    case 0x52: handle_1552(regs); break;
+    case 0x83:
+        switch (regs->al) {
+        case 0x00: handle_158300(regs); break;
+        case 0x01: handle_158301(regs); break;
+        default:   handle_1583XX(regs); break;
+        }
+        break;
+    case 0x86: handle_1586(regs); break;
+    case 0x87: handle_1587(regs); break;
+    case 0x88: handle_1588(regs); break;
+    case 0x90: handle_1590(regs); break;
+    case 0x91: handle_1591(regs); break;
+    case 0xc0: handle_15c0(regs); break;
+    case 0xc1: handle_15c1(regs); break;
+    case 0xc2: handle_15c2(regs); break;
+    case 0xe8:
+        switch (regs->al) {
+        case 0x01: handle_15e801(regs); break;
+        case 0x20: handle_15e820(regs); break;
+        default:   handle_15e8XX(regs); break;
+        }
+        break;
+    default:   handle_15XX(regs); break;
+    }
+    debug_exit(regs);
+}
+
+// INT 12h Memory Size Service Entry Point
+void VISIBLE
+handle_12(struct bregs *regs)
+{
+    debug_enter(regs);
+    regs->ax = GET_BDA(mem_size_kb);
+    debug_exit(regs);
+}
+
+// INT 11h Equipment List Service Entry Point
+void VISIBLE
+handle_11(struct bregs *regs)
+{
+    debug_enter(regs);
+    regs->ax = GET_BDA(equipment_list_flags);
+    debug_exit(regs);
+}
+
+// INT 05h Print Screen Service Entry Point
+void VISIBLE
+handle_05(struct bregs *regs)
+{
+    debug_enter(regs);
+}
+
+// INT 10h Video Support Service Entry Point
+void VISIBLE
+handle_10(struct bregs *regs)
+{
+    debug_enter(regs);
+    // dont do anything, since the VGA BIOS handles int10h requests
+}
+
+void VISIBLE
+handle_nmi(struct bregs *regs)
+{
+    debug_enter(regs);
+    // XXX
+}
+
+// INT 75 - IRQ13 - MATH COPROCESSOR EXCEPTION
+void VISIBLE
+handle_75(struct bregs *regs)
+{
+    debug_enter(regs);
+}
diff --git a/src/types.h b/src/types.h
new file mode 100644 (file)
index 0000000..ea245bf
--- /dev/null
@@ -0,0 +1,21 @@
+// Basic type definitions for X86 cpus.
+//
+// Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+#ifndef __TYPES_H
+#define __TYPES_H
+
+typedef unsigned char u8;
+typedef signed char s8;
+typedef unsigned short u16;
+typedef signed short s16;
+typedef unsigned int u32;
+typedef signed int s32;
+typedef u32 size_t;
+
+#define VISIBLE __attribute__((externally_visible))
+
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+
+#endif // types.h
diff --git a/src/util.h b/src/util.h
new file mode 100644 (file)
index 0000000..0870ad5
--- /dev/null
@@ -0,0 +1,55 @@
+// Basic x86 asm functions and function defs.
+//
+// Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include "ioport.h" // outb
+
+static inline void irq_disable(void) {
+        asm volatile("cli": : :"memory");
+}
+
+static inline void irq_enable(void) {
+        asm volatile("sti": : :"memory");
+}
+
+static inline unsigned long irq_save(void)
+{
+    unsigned long flags;
+    asm volatile("pushfl ; popl %0" : "=g" (flags));
+    irq_disable();
+    return flags;
+}
+
+static inline void irq_restore(unsigned long flags)
+{
+    asm volatile("pushl %0 ; popfl" : : "g" (flags) : "memory", "cc");
+}
+
+#define DEBUGF(fmt, args...)
+#define BX_PANIC(fmt, args...)
+#define BX_INFO(fmt, args...)
+
+static inline void
+memset(void *s, int c, size_t n)
+{
+    while (n)
+        ((char *)s)[n--] = c;
+}
+
+// output.c
+void bprintf(u16 action, const char *fmt, ...)
+    __attribute__ ((format (printf, 2, 3)));
+struct bregs;
+void __debug_enter(const char *fname, struct bregs *regs);
+void __debug_exit(const char *fname, struct bregs *regs);
+#define debug_enter(regs) \
+    __debug_enter(__func__, regs)
+#define debug_exit(regs) \
+    __debug_exit(__func__, regs)
+#define printf(fmt, args...)                     \
+    bprintf(0, fmt , ##args )
+
+// kbd.c
+void handle_15c2(struct bregs *regs);
diff --git a/tools/buildrom.py b/tools/buildrom.py
new file mode 100755 (executable)
index 0000000..beb51ac
--- /dev/null
@@ -0,0 +1,78 @@
+#!/usr/bin/env python
+# Script to merge a rom32.bin file into a rom16.bin file.
+#
+# Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
+#
+# This file may be distributed under the terms of the GNU GPLv3 license.
+
+import sys
+import struct
+
+ROM16='out/rom16.bin'
+ROM32='out/rom32.bin'
+OFFSETS16='out/rom16.offset.auto.h'
+OFFSETS32='out/rom32.offset.auto.h'
+OUT='out/rom.bin'
+
+def align(v, a):
+    return (v + a - 1) // a * a
+
+def scanconfig(file):
+    f = open(file, 'rb')
+    opts = {}
+    for l in f.readlines():
+        parts = l.split()
+        if len(parts) != 3:
+            continue
+        if parts[0] != '#define':
+            continue
+        opts[parts[1]] = parts[2]
+    return opts
+
+def alteraddr(data, offset, ptr):
+    rel = struct.pack("<i", ptr)
+    return data[:offset] + rel + data[offset+4:]
+
+
+def main():
+    # Read in files
+    f = open(ROM16, 'rb')
+    data16 = f.read()
+    f = open(ROM32, 'rb')
+    data32 = f.read()
+
+    if len(data16) != 65536:
+        print "16bit code is not 65536 bytes long"
+        sys.exit(1)
+
+    # Get config options
+    o16 = scanconfig(OFFSETS16)
+    o32 = scanconfig(OFFSETS32)
+
+    # Inject 32bit code
+    spos = align(int(o16['OFFSET_bios16c_end'], 16), 16)
+    epos = int(o16['OFFSET_post16'], 16)
+    size32 = len(data32)
+    freespace = epos - spos
+    if size32 > freespace:
+        print "32bit code too large (%d vs %d)" % (size32, freespace)
+        sys.exit(1)
+    outrom = data16[:spos] + data32 + data16[spos+size32:]
+
+    # Fixup initial jump to 32 bit code
+    jmppos = int(o16['OFFSET_set_entry32'], 16)
+    start32 = int(o32['OFFSET__start'], 16)
+    outrom = alteraddr(outrom, jmppos+2, start32)
+
+    # Fixup resume from 16 jump to 32 bit code
+    jmppos = int(o16['OFFSET_set_resume32'], 16)
+    resume32 = int(o32['OFFSET_call16_resume'], 16)
+    outrom = alteraddr(outrom, jmppos+2, resume32)
+
+    # Write output rom
+    f = open(OUT, 'wb')
+    f.write(outrom)
+    f.close()
+
+if __name__ == '__main__':
+    main()
diff --git a/tools/defsyms.py b/tools/defsyms.py
new file mode 100755 (executable)
index 0000000..fe18d90
--- /dev/null
@@ -0,0 +1,38 @@
+#!/usr/bin/env python
+# Simple script to convert the output from 'nm' to a C style header
+# file with defined offsets.
+#
+# Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
+#
+# This file may be distributed under the terms of the GNU GPLv3 license.
+
+import sys
+import string
+
+def main():
+    syms = []
+    lines = sys.stdin.readlines()
+    for line in lines:
+        addr, type, sym = line.split()
+        if type not in 'TA':
+            # Only interested in global symbols in text segment
+            continue
+        for c in sym:
+            if c not in string.letters + string.digits + '_':
+                break
+        else:
+            syms.append((sym, addr))
+    print """
+#ifndef __OFFSET16_AUTO_H
+#define __OFFSET16_AUTO_H
+// Auto generated file - please see defsyms.py.
+// This file contains symbol offsets of a compiled binary.
+"""
+    for sym, addr in syms:
+        print "#define OFFSET_%s 0x%s" % (sym, addr)
+    print """
+#endif // __OFFSET16_AUTO_H
+"""
+
+if __name__ == '__main__':
+    main()